diff --git a/index.ts b/index.ts index 20141ae..d993309 100644 --- a/index.ts +++ b/index.ts @@ -1,8 +1,8 @@ -export { pushDataWithSchemaRepair, isSchemaValidationError } from './src/pushDataWithSchemaRepair.js'; +export { pushDataWithSchemaRepair, isSchemaValidationError } from './src/push-data-with-schema-repair/index.js'; export type { ValidationError, DroppedItem, PushDataWithSchemaRepairResult, PushDataWithSchemaRepairOptions, PushFn, -} from './src/pushDataWithSchemaRepair.js'; +} from './src/push-data-with-schema-repair/index.js'; diff --git a/package-lock.json b/package-lock.json index c098875..02d676b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,10 +31,10 @@ "node": ">=22.6.0" }, "peerDependencies": { - "@apify/log": "^2.5.44", - "@crawlee/core": ">=3.16.0", - "apify": ">=3.7.0", - "apify-client": ">=2.17.0" + "@apify/log": "^2", + "@crawlee/core": "^3", + "apify": "^3", + "apify-client": "^2" }, "peerDependenciesMeta": { "@crawlee/core": { @@ -1173,9 +1173,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1193,9 +1190,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1213,9 +1207,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1233,9 +1224,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1253,9 +1241,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1273,9 +1258,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5638,9 +5620,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5662,9 +5641,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5686,9 +5662,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -5710,9 +5683,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ diff --git a/package.json b/package.json index fabc8fc..f492c83 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,10 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" }, + "./push-data-with-schema-repair": { + "types": "./dist/src/push-data-with-schema-repair/index.d.ts", + "default": "./dist/src/push-data-with-schema-repair/index.js" + }, "./qc-logger": { "types": "./dist/src/qc-logger/index.d.ts", "default": "./dist/src/qc-logger/index.js" diff --git a/src/pushDataWithSchemaRepair.ts b/src/push-data-with-schema-repair/index.ts similarity index 100% rename from src/pushDataWithSchemaRepair.ts rename to src/push-data-with-schema-repair/index.ts diff --git a/src/type-utils/README.md b/src/type-utils/README.md new file mode 100644 index 0000000..836126c --- /dev/null +++ b/src/type-utils/README.md @@ -0,0 +1,57 @@ +# type-utils + +`@apify/actor-utils/type-utils` is a separate export holding small, +dependency-free TypeScript helpers — one runtime function and a set of +type-level operators. It imports nothing, so pulling it in costs nothing +beyond the helpers themselves. + +```ts +import { unreachable } from '@apify/actor-utils/type-utils'; +import type { Truthy, Falsy, Nullish, Defined } from '@apify/actor-utils/type-utils'; +``` + +## `unreachable(x: never): never` + +Asserts a code path can't be reached, giving you a compile-time +exhaustiveness check. Pass a value that should be `never` — if a new union +variant is added but not handled, `x` is no longer `never` and the call +fails to compile. At runtime it always throws. + +```ts +type Shape = 'circle' | 'square'; + +function area(shape: Shape): number { + switch (shape) { + case 'circle': + return Math.PI; + case 'square': + return 1; + default: + // Compile-time error here if a new Shape variant is added. + return unreachable(shape); + } +} +``` + +## Type-level helpers + +| Type | Result | +| ------------ | ---------------------------------------------------------------- | +| `Falsy` | `false \| 0 \| '' \| null \| undefined` — the falsy primitives. | +| `Nullish` | `null \| undefined`. | +| `Defined` | `T` with `null` and `undefined` removed (`Exclude`). | +| `Truthy` | `T` narrowed to only its truthy members. | + +```ts +type A = Truthy; // string +type B = Truthy<0 | 1 | 2>; // 1 | 2 +type C = Truthy<'' | 'hello'>; // 'hello' +type D = Truthy; // never +``` + +`Truthy` checks each falsy literal individually rather than using +`Exclude`, so a broad type like `boolean` or `number` is preserved +instead of collapsed — `boolean` only loses `false` when `false` is the +whole type, not when it's one member of `boolean`. Object types +short-circuit first, so `''` / `0` never accidentally match structural +(`{}`) types. diff --git a/src/type-utils/index.ts b/src/type-utils/index.ts index c6b1e34..2132220 100644 --- a/src/type-utils/index.ts +++ b/src/type-utils/index.ts @@ -1,2 +1,3 @@ export { unreachable } from './unreachable.js'; export type { Falsy, Nullish, Truthy, Defined } from './types.js'; +export { type NonEmptyList, isNonEmptyList } from './non-empty.js'; diff --git a/src/type-utils/non-empty.ts b/src/type-utils/non-empty.ts new file mode 100644 index 0000000..10193f9 --- /dev/null +++ b/src/type-utils/non-empty.ts @@ -0,0 +1,39 @@ +/** + * An array of `T` guaranteed to hold at least one element. + * + * Modelled as a tuple with a required head and a variadic tail (`[T, ...T[]]`), + * so the compiler knows `list[0]` always exists and treats it as `T` rather + * than `T | undefined`. Narrow a plain `T[]` to this with {@link isNonEmptyList}. + * + * @example + * ```ts + * function first(list: NonEmptyList): T { + * return list[0]; // safe: no undefined + * } + * ``` + */ +export type NonEmptyList = [T, ...T[]]; + +/** + * Type guard that narrows an array to a {@link NonEmptyList} when it holds at + * least one element. + * + * Use it to let the compiler know `list[0]` is safe to access after the check, + * without a non-null assertion. + * + * @param list - The array to check. + * @returns `true` if `list` has at least one element, narrowing it to + * `NonEmptyList`. + * + * @example + * ```ts + * const items: number[] = getItems(); + * + * if (isNonEmptyList(items)) { + * const first = items[0]; // typed as number, not number | undefined + * } + * ``` + */ +export function isNonEmptyList(list: T[]): list is NonEmptyList { + return Boolean(list.length); +} diff --git a/test/pushDataWithSchemaRepair.test.ts b/test/push-data-with-schema-repair/pushDataWithSchemaRepair.test.ts similarity index 99% rename from test/pushDataWithSchemaRepair.test.ts rename to test/push-data-with-schema-repair/pushDataWithSchemaRepair.test.ts index d0db184..5ac565b 100644 --- a/test/pushDataWithSchemaRepair.test.ts +++ b/test/push-data-with-schema-repair/pushDataWithSchemaRepair.test.ts @@ -7,7 +7,7 @@ import { pushDataWithSchemaRepair, type PushFn, type ValidationError, -} from '../src/pushDataWithSchemaRepair.js'; +} from '../../src/push-data-with-schema-repair/index.js'; // Shape of an item used across tests. interface Item {