@apify/actor-utils 0.1.0 → 0.1.3-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +22 -17
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +2 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/qc-logger/assert.d.ts +17 -0
  7. package/dist/src/qc-logger/assert.d.ts.map +1 -0
  8. package/dist/src/qc-logger/assert.js +23 -0
  9. package/dist/src/qc-logger/assert.js.map +1 -0
  10. package/dist/src/qc-logger/checkpoint.d.ts +40 -0
  11. package/dist/src/qc-logger/checkpoint.d.ts.map +1 -0
  12. package/dist/src/qc-logger/checkpoint.js +45 -0
  13. package/dist/src/qc-logger/checkpoint.js.map +1 -0
  14. package/dist/src/qc-logger/index.d.ts +6 -0
  15. package/dist/src/qc-logger/index.d.ts.map +1 -0
  16. package/dist/src/qc-logger/index.js +5 -0
  17. package/dist/src/qc-logger/index.js.map +1 -0
  18. package/dist/src/qc-logger/is.d.ts +116 -0
  19. package/dist/src/qc-logger/is.d.ts.map +1 -0
  20. package/dist/src/qc-logger/is.js +26 -0
  21. package/dist/src/qc-logger/is.js.map +1 -0
  22. package/dist/src/qc-logger/structured-logger.d.ts +9 -0
  23. package/dist/src/qc-logger/structured-logger.d.ts.map +1 -0
  24. package/dist/src/qc-logger/structured-logger.js +38 -0
  25. package/dist/src/qc-logger/structured-logger.js.map +1 -0
  26. package/dist/src/safePushData.d.ts +46 -0
  27. package/dist/src/safePushData.d.ts.map +1 -0
  28. package/dist/src/safePushData.js +285 -0
  29. package/dist/src/safePushData.js.map +1 -0
  30. package/dist/src/type-utils/index.d.ts +3 -0
  31. package/dist/src/type-utils/index.d.ts.map +1 -0
  32. package/dist/src/type-utils/index.js +2 -0
  33. package/dist/src/type-utils/index.js.map +1 -0
  34. package/dist/src/type-utils/types.d.ts +23 -0
  35. package/dist/src/type-utils/types.d.ts.map +1 -0
  36. package/dist/src/type-utils/types.js +2 -0
  37. package/dist/src/type-utils/types.js.map +1 -0
  38. package/dist/src/type-utils/unreachable.d.ts +30 -0
  39. package/dist/src/type-utils/unreachable.d.ts.map +1 -0
  40. package/dist/src/type-utils/unreachable.js +32 -0
  41. package/dist/src/type-utils/unreachable.js.map +1 -0
  42. package/package.json +70 -59
package/README.md CHANGED
@@ -47,25 +47,30 @@ every AJV error per item:
47
47
  ### Placeholder defaults
48
48
 
49
49
  When a constraint fires on a path we placeholder'd ourselves, the wrapper
50
- picks a value that should satisfy it:
51
-
52
- | AJV keyword | Placeholder value |
53
- | ------------------------------------------------------- | ------------------------------- |
54
- | `type: string` | `''` |
55
- | `type: integer` / `number` | `0` |
56
- | `type: boolean` | `false` |
57
- | `type: array` | `[]` |
58
- | `type: object` | `{}` |
59
- | `type: null` | `null` |
60
- | `minLength: N` / `maxLength` | `'_'.repeat(N)` / `''` |
61
- | `minimum: N` / `maximum` | `N` |
62
- | `exclusiveMinimum: N` / `exclusiveMaximum` | `N + 1` / `N - 1` |
63
- | `enum` | First allowed value |
64
- | `format: email` / `uri` / `date` / `date-time` / `uuid` | a static valid example for each |
65
- | Anything else (`pattern`, custom formats…) | Item is dropped. |
50
+ picks a value that should satisfy it. We deliberately only fill in the four
51
+ **empty** values below — they're unambiguously empty and can't be mistaken
52
+ for real data:
53
+
54
+ | AJV keyword | Placeholder value |
55
+ | -------------- | ----------------- |
56
+ | `type: string` | `''` |
57
+ | `type: array` | `[]` |
58
+ | `type: object` | `{}` |
59
+ | `type: null` | `null` |
60
+ | Anything else | Item is dropped. |
61
+
62
+ When a field allows **multiple types** (e.g. `['string', 'null']`), the
63
+ wrapper always picks `null` — it's the cleanest placeholder because it
64
+ commits to no concrete value at all.
65
+
66
+ Everything else (`enum`, `format`, `minLength`, numeric bounds, `type:
67
+ integer` / `number` / `boolean`, …) is **not** placeholdered: a made-up
68
+ email, a first-enum-value, or a fabricated number would silently poison the
69
+ customer's dataset with plausible-looking junk, so the item is dropped
70
+ instead.
66
71
 
67
72
  The retry loop chases one layer of errors per round
68
- (`required` → `type` → `minLength` → push) until either the push succeeds
73
+ (`required` → `type` → push) until either the push succeeds
69
74
  or `maxAttempts` (default 5) is hit.
70
75
 
71
76
  ## Options
@@ -0,0 +1,3 @@
1
+ export { safePushData, isSchemaValidationError } from './src/safePushData.js';
2
+ export type { ValidationError, DroppedItem, SafePushDataResult, SafePushDataOptions, PushFn, } from './src/safePushData.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAC9E,YAAY,EACR,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACnB,MAAM,GACT,MAAM,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { safePushData, isSchemaValidationError } from './src/safePushData.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { type Ctx } from './structured-logger.js';
2
+ /**
3
+ * Glorified assertion that also logs at `error` level. Throws a plain `Error`
4
+ * by default; pass e.g. Crawlee's `NonRetryableError` or `CriticalError` to
5
+ * control what the failure means to the crawler.
6
+ *
7
+ *
8
+ * ```ts
9
+ * const someEnvVar = process.env.SOME_ENV_VAR; // string | undefined
10
+ * assert(typeof someEnvVar === "string", "some-env-var");
11
+ * // someEnvVar is now guaranteed to be a `string`, unless the assertion fails.
12
+ * assert(someEnvVar, "some-env-var");
13
+ * // someEnvVar is now guaranteed to be a truthy `string` (non-empty), unless the assertion fails.
14
+ * ```
15
+ */
16
+ export declare function assert(cond: unknown, key: string, ctx?: Ctx, ErrorCtor?: new (message: string) => Error): asserts cond;
17
+ //# sourceMappingURL=assert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../../../src/qc-logger/assert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAuB,MAAM,wBAAwB,CAAC;AAEvE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,MAAM,CAClB,IAAI,EAAE,OAAO,EACb,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,GAAG,EACT,SAAS,GAAE,KAAK,OAAO,EAAE,MAAM,KAAK,KAAa,GAClD,OAAO,CAAC,IAAI,CAKd"}
@@ -0,0 +1,23 @@
1
+ import { emit, formatMessage } from './structured-logger.js';
2
+ /**
3
+ * Glorified assertion that also logs at `error` level. Throws a plain `Error`
4
+ * by default; pass e.g. Crawlee's `NonRetryableError` or `CriticalError` to
5
+ * control what the failure means to the crawler.
6
+ *
7
+ *
8
+ * ```ts
9
+ * const someEnvVar = process.env.SOME_ENV_VAR; // string | undefined
10
+ * assert(typeof someEnvVar === "string", "some-env-var");
11
+ * // someEnvVar is now guaranteed to be a `string`, unless the assertion fails.
12
+ * assert(someEnvVar, "some-env-var");
13
+ * // someEnvVar is now guaranteed to be a truthy `string` (non-empty), unless the assertion fails.
14
+ * ```
15
+ */
16
+ export function assert(cond, key, ctx, ErrorCtor = Error) {
17
+ if (cond)
18
+ return;
19
+ const error = new ErrorCtor(formatMessage('assert', key));
20
+ emit('error', 'assert', key, { ...ctx, name: error.name, stack: error.stack });
21
+ throw error;
22
+ }
23
+ //# sourceMappingURL=assert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert.js","sourceRoot":"","sources":["../../../src/qc-logger/assert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,IAAI,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvE;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,MAAM,CAClB,IAAa,EACb,GAAW,EACX,GAAS,EACT,YAA4C,KAAK;IAEjD,IAAI,IAAI;QAAE,OAAO;IACjB,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;IAC1D,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/E,MAAM,KAAK,CAAC;AAChB,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { type Ctx } from './structured-logger.js';
2
+ /**
3
+ * Logs an `info`-level checkpoint marking that a point in the run was reached.
4
+ * Unlike the `is*` predicates or {@link assert}, this has no condition — it
5
+ * always emits — so it's meant for tracing progress through a run.
6
+ *
7
+ * @param key - Identifies the checkpoint; emitted as `qc:checkpoint:<key>`.
8
+ * @param ctx - Optional extra fields to attach to the log line.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * checkpoint("started");
13
+ * // logs `qc:checkpoint:started`
14
+ *
15
+ * await doWork();
16
+ * checkpoint("work-done", { itemsProcessed: 42 });
17
+ * // logs `qc:checkpoint:work-done` with { itemsProcessed: 42 }
18
+ * ```
19
+ */
20
+ export declare function checkpoint(key: string, ctx?: Ctx): void;
21
+ /**
22
+ * Conditionally logs an `info`-level checkpoint — only when `cond` is truthy.
23
+ * A convenience wrapper over {@link checkpoint} for guarding a marker behind a
24
+ * condition without an explicit `if` at the call site.
25
+ *
26
+ * @param cond - The condition; the checkpoint is emitted only when it's truthy.
27
+ * @param key - Identifies the checkpoint; emitted as `qc:checkpoint:<key>`.
28
+ * @param ctx - Optional extra fields to attach to the log line.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * checkpointIf(items.length === 0, "empty-batch");
33
+ * // logs `qc:checkpoint:empty-batch` only when the batch is empty
34
+ *
35
+ * checkpointIf(retries > 3, "many-retries", { retries });
36
+ * // logs `qc:checkpoint:many-retries` with { retries } only past the threshold
37
+ * ```
38
+ */
39
+ export declare function checkpointIf(cond: unknown, key: string, ctx?: Ctx): void;
40
+ //# sourceMappingURL=checkpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../../../src/qc-logger/checkpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAQ,MAAM,wBAAwB,CAAC;AAExD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,CAEvD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,CAExE"}
@@ -0,0 +1,45 @@
1
+ import { emit } from './structured-logger.js';
2
+ /**
3
+ * Logs an `info`-level checkpoint marking that a point in the run was reached.
4
+ * Unlike the `is*` predicates or {@link assert}, this has no condition — it
5
+ * always emits — so it's meant for tracing progress through a run.
6
+ *
7
+ * @param key - Identifies the checkpoint; emitted as `qc:checkpoint:<key>`.
8
+ * @param ctx - Optional extra fields to attach to the log line.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * checkpoint("started");
13
+ * // logs `qc:checkpoint:started`
14
+ *
15
+ * await doWork();
16
+ * checkpoint("work-done", { itemsProcessed: 42 });
17
+ * // logs `qc:checkpoint:work-done` with { itemsProcessed: 42 }
18
+ * ```
19
+ */
20
+ export function checkpoint(key, ctx) {
21
+ emit('info', 'checkpoint', key, ctx);
22
+ }
23
+ /**
24
+ * Conditionally logs an `info`-level checkpoint — only when `cond` is truthy.
25
+ * A convenience wrapper over {@link checkpoint} for guarding a marker behind a
26
+ * condition without an explicit `if` at the call site.
27
+ *
28
+ * @param cond - The condition; the checkpoint is emitted only when it's truthy.
29
+ * @param key - Identifies the checkpoint; emitted as `qc:checkpoint:<key>`.
30
+ * @param ctx - Optional extra fields to attach to the log line.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * checkpointIf(items.length === 0, "empty-batch");
35
+ * // logs `qc:checkpoint:empty-batch` only when the batch is empty
36
+ *
37
+ * checkpointIf(retries > 3, "many-retries", { retries });
38
+ * // logs `qc:checkpoint:many-retries` with { retries } only past the threshold
39
+ * ```
40
+ */
41
+ export function checkpointIf(cond, key, ctx) {
42
+ if (cond)
43
+ checkpoint(key, ctx);
44
+ }
45
+ //# sourceMappingURL=checkpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../../../src/qc-logger/checkpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,GAAS;IAC7C,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,YAAY,CAAC,IAAa,EAAE,GAAW,EAAE,GAAS;IAC9D,IAAI,IAAI;QAAE,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { checkpoint, checkpointIf } from './checkpoint.js';
2
+ export { assert } from './assert.js';
3
+ export { isFalsy, isTruthy, isNullish, isDefined } from './is.js';
4
+ export { emit, formatMessage } from './structured-logger.js';
5
+ export type { Ctx } from './structured-logger.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/qc-logger/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC7D,YAAY,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { checkpoint, checkpointIf } from './checkpoint.js';
2
+ export { assert } from './assert.js';
3
+ export { isFalsy, isTruthy, isNullish, isDefined } from './is.js';
4
+ export { emit, formatMessage } from './structured-logger.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/qc-logger/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,116 @@
1
+ import type { Defined, Falsy, Nullish, Truthy } from '../type-utils/types.js';
2
+ import { type Ctx } from './structured-logger.js';
3
+ /**
4
+ * Checks for falsy values and logs a `warning` when they are.
5
+ *
6
+ * Sample usage:
7
+ * ```ts
8
+ * if (isFalsy(someValue, "my-key-here")) {
9
+ * // someValue is falsy, warning gets logged `qc:is:falsy:my-key-here`
10
+ * return; // do my early return
11
+ * }
12
+ * ```
13
+ *
14
+ * The overloads narrow the return type based on what's statically known about
15
+ * `value`: a {@link Truthy} input returns the literal `false`, a {@link Falsy}
16
+ * input returns the literal `true`, and only genuinely uncertain values get the
17
+ * `value is Falsy & T` type guard. This means a redundant call — e.g.
18
+ * `isFalsy` on something the compiler already knows is truthy — produces a
19
+ * branch whose condition is a constant.
20
+ *
21
+ * Enable the `@typescript-eslint/no-unnecessary-condition` rule to have that
22
+ * surfaced as a lint error instead of a silent dead branch. The rule ships with
23
+ * both [typescript-eslint](https://typescript-eslint.io/rules/no-unnecessary-condition/)
24
+ * and [oxlint](https://oxc.rs/docs/guide/usage/linter/rules/typescript/no-unnecessary-condition.html)
25
+ * (note: it requires type-aware linting).
26
+ */
27
+ export declare function isFalsy<T>(value: Truthy<T>, key: string, ctx?: Ctx): false;
28
+ export declare function isFalsy(value: Falsy, key: string, ctx?: Ctx): true;
29
+ export declare function isFalsy<T>(value: T, key: string, ctx?: Ctx): value is Falsy & T;
30
+ /**
31
+ * Checks for truthy values and logs a `warning` when they are — the
32
+ * inverse of {@link isFalsy}.
33
+ *
34
+ * Sample usage:
35
+ * ```ts
36
+ * if (isTruthy(someValue, "my-key-here")) {
37
+ * // someValue is truthy, warning gets logged `qc:is:truthy:my-key-here`
38
+ * return someValue; // do my early return
39
+ * }
40
+ * // fallback behavior here
41
+ * ```
42
+ *
43
+ * Mirroring {@link isFalsy}, the overloads narrow the return type based on what's
44
+ * statically known about `value`: a {@link Falsy} input returns the literal
45
+ * `false`, a {@link Truthy} input returns the literal `true`, and only genuinely
46
+ * uncertain values get the `value is Truthy<T>` type guard. A redundant call —
47
+ * e.g. `isTruthy` on something the compiler already knows is falsy — produces a
48
+ * branch whose condition is a constant.
49
+ *
50
+ * Enable the `@typescript-eslint/no-unnecessary-condition` rule to have that
51
+ * surfaced as a lint error instead of a silent dead branch. The rule ships with
52
+ * both [typescript-eslint](https://typescript-eslint.io/rules/no-unnecessary-condition/)
53
+ * and [oxlint](https://oxc.rs/docs/guide/usage/linter/rules/typescript/no-unnecessary-condition.html)
54
+ * (note: it requires type-aware linting).
55
+ */
56
+ export declare function isTruthy(value: Falsy, key: string, ctx?: Ctx): false;
57
+ export declare function isTruthy<T>(value: Truthy<T>, key: string, ctx?: Ctx): true;
58
+ export declare function isTruthy<T>(value: T, key: string, ctx?: Ctx): value is Truthy<T>;
59
+ /**
60
+ * Checks for `null | undefined` values and logs a `warning` when they are.
61
+ * Unlike {@link isFalsy}, `0`, `false`, and `""` are not considered a match.
62
+ *
63
+ * Sample usage:
64
+ * ```ts
65
+ * if (isNullish(someValue, "my-key-here")) {
66
+ * // someValue is null or undefined, warning gets logged `qc:is:nullish:my-key-here`
67
+ * return; // do my early return
68
+ * }
69
+ * // keep on truckin'
70
+ * ```
71
+ *
72
+ * Like {@link isFalsy}, the overloads narrow the return type based on what's
73
+ * statically known about `value`: a {@link Defined} input returns the literal
74
+ * `false`, a {@link Nullish} input returns the literal `true`, and only
75
+ * genuinely uncertain values get the `value is Nullish & T` type guard. A
76
+ * redundant call — e.g. `isNullish` on something the compiler already knows is
77
+ * defined — produces a branch whose condition is a constant.
78
+ *
79
+ * Enable the `@typescript-eslint/no-unnecessary-condition` rule to have that
80
+ * surfaced as a lint error instead of a silent dead branch. The rule ships with
81
+ * both [typescript-eslint](https://typescript-eslint.io/rules/no-unnecessary-condition/)
82
+ * and [oxlint](https://oxc.rs/docs/guide/usage/linter/rules/typescript/no-unnecessary-condition.html)
83
+ * (note: it requires type-aware linting).
84
+ */
85
+ export declare function isNullish<T>(value: Defined<T>, key: string, ctx?: Ctx): false;
86
+ export declare function isNullish(value: Nullish, key: string, ctx?: Ctx): true;
87
+ export declare function isNullish<T>(value: T, key: string, ctx?: Ctx): value is Nullish & T;
88
+ /**
89
+ * Checks that a value is neither `null` nor `undefined` and logs a
90
+ * `warning` when that's the case — the inverse of {@link isNullish}.
91
+ *
92
+ * Sample usage:
93
+ * ```ts
94
+ * if (isDefined(someValue, "my-key-here")) {
95
+ * // someValue is neither null nor undefined, warning gets logged `qc:is:defined:my-key-here`
96
+ * return; // do my early return
97
+ * }
98
+ * ```
99
+ *
100
+ * Like {@link isFalsy}, the overloads narrow the return type based on what's
101
+ * statically known about `value`: a {@link Nullish} input returns the literal
102
+ * `false`, a {@link Defined} input returns the literal `true`, and only
103
+ * genuinely uncertain values get the `value is Defined<T>` type guard. A
104
+ * redundant call — e.g. `isDefined` on something the compiler already knows is
105
+ * defined — produces a branch whose condition is a constant.
106
+ *
107
+ * Enable the `@typescript-eslint/no-unnecessary-condition` rule to have that
108
+ * surfaced as a lint error instead of a silent dead branch. The rule ships with
109
+ * both [typescript-eslint](https://typescript-eslint.io/rules/no-unnecessary-condition/)
110
+ * and [oxlint](https://oxc.rs/docs/guide/usage/linter/rules/typescript/no-unnecessary-condition.html)
111
+ * (note: it requires type-aware linting).
112
+ */
113
+ export declare function isDefined(value: Nullish, key: string, ctx?: Ctx): false;
114
+ export declare function isDefined<T>(value: Defined<T>, key: string, ctx?: Ctx): true;
115
+ export declare function isDefined<T>(value: T, key: string, ctx?: Ctx): value is Defined<T>;
116
+ //# sourceMappingURL=is.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is.d.ts","sourceRoot":"","sources":["../../../src/qc-logger/is.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,KAAK,GAAG,EAAQ,MAAM,wBAAwB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC;AAC5E,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;AACpE,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC,CAAC;AAOjF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC;AACtE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;AAC5E,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;AAOlF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC;AAC/E,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;AACxE,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,IAAI,OAAO,GAAG,CAAC,CAAC;AAOrF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC;AACzE,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;AAC9E,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { emit } from './structured-logger.js';
2
+ export function isFalsy(value, key, ctx) {
3
+ const result = !value;
4
+ if (result)
5
+ emit('warning', 'is:falsy', key, ctx);
6
+ return result;
7
+ }
8
+ export function isTruthy(value, key, ctx) {
9
+ const result = Boolean(value);
10
+ if (result)
11
+ emit('warning', 'is:truthy', key, ctx);
12
+ return result;
13
+ }
14
+ export function isNullish(value, key, ctx) {
15
+ const result = value === undefined || value === null;
16
+ if (result)
17
+ emit('warning', 'is:nullish', key, ctx);
18
+ return result;
19
+ }
20
+ export function isDefined(value, key, ctx) {
21
+ const result = value !== undefined && value !== null;
22
+ if (result)
23
+ emit('warning', 'is:defined', key, ctx);
24
+ return result;
25
+ }
26
+ //# sourceMappingURL=is.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is.js","sourceRoot":"","sources":["../../../src/qc-logger/is.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,IAAI,EAAE,MAAM,wBAAwB,CAAC;AA6BxD,MAAM,UAAU,OAAO,CAAI,KAAQ,EAAE,GAAW,EAAE,GAAS;IACvD,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC;IACtB,IAAI,MAAM;QAAE,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAClD,OAAO,MAAM,CAAC;AAClB,CAAC;AA+BD,MAAM,UAAU,QAAQ,CAAI,KAAQ,EAAE,GAAW,EAAE,GAAS;IACxD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,MAAM;QAAE,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACnD,OAAO,MAAM,CAAC;AAClB,CAAC;AA+BD,MAAM,UAAU,SAAS,CAAI,KAAQ,EAAE,GAAW,EAAE,GAAS;IACzD,MAAM,MAAM,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,CAAC;IACrD,IAAI,MAAM;QAAE,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,MAAM,CAAC;AAClB,CAAC;AA8BD,MAAM,UAAU,SAAS,CAAI,KAAQ,EAAE,GAAW,EAAE,GAAS;IACzD,MAAM,MAAM,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,CAAC;IACrD,IAAI,MAAM;QAAE,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,MAAM,CAAC;AAClB,CAAC"}
@@ -0,0 +1,9 @@
1
+ export type Ctx = Record<string, unknown>;
2
+ /**
3
+ * Builds the canonical log line (`qc:<verb>:<key>`) without emitting it.
4
+ * Exposed so callers (e.g. `assert`) can reuse the exact same message when
5
+ * constructing an error to throw.
6
+ */
7
+ export declare function formatMessage(qcVerb: string, rawKey: string): string;
8
+ export declare function emit(level: 'debug' | 'info' | 'warning' | 'error', qcVerb: string, rawKey: string, ctx?: Ctx): string;
9
+ //# sourceMappingURL=structured-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structured-logger.d.ts","sourceRoot":"","sources":["../../../src/qc-logger/structured-logger.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAiB1C;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,MAAM,CAcrH"}
@@ -0,0 +1,38 @@
1
+ import log from '@apify/log';
2
+ /**
3
+ * Keys are part of the log line's parse structure (`qc:<verb>:<key>`)
4
+ * Turns any string into a valid qc key.
5
+ *
6
+ * If a key is completely invalid, it is replaced with "invalid-key".
7
+ */
8
+ function normalizeKey(key) {
9
+ return (String(key)
10
+ .toLowerCase()
11
+ .replace(/[^a-z0-9-]+/g, '-')
12
+ .replace(/^-|-$/g, '') || 'invalid-key');
13
+ }
14
+ /**
15
+ * Builds the canonical log line (`qc:<verb>:<key>`) without emitting it.
16
+ * Exposed so callers (e.g. `assert`) can reuse the exact same message when
17
+ * constructing an error to throw.
18
+ */
19
+ export function formatMessage(qcVerb, rawKey) {
20
+ return `qc:${qcVerb}:${normalizeKey(rawKey)}`;
21
+ }
22
+ export function emit(level, qcVerb, rawKey, ctx) {
23
+ let message;
24
+ try {
25
+ const qcKey = normalizeKey(rawKey);
26
+ message = formatMessage(qcVerb, rawKey);
27
+ // Caller ctx is spread first so it can never clobber the canonical fields.
28
+ // Run/actor identity is not attached here — Mezmo enriches lines with it.
29
+ log[level](message, { ...ctx, level, qcKey, qcVerb });
30
+ return message;
31
+ }
32
+ catch {
33
+ // Telemetry must never break a run.
34
+ message ??= `qc:unknown:qc-error-generating-key`;
35
+ return message;
36
+ }
37
+ }
38
+ //# sourceMappingURL=structured-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structured-logger.js","sourceRoot":"","sources":["../../../src/qc-logger/structured-logger.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,YAAY,CAAC;AAI7B;;;;;GAKG;AACH,SAAS,YAAY,CAAC,GAAW;IAC7B,OAAO,CACH,MAAM,CAAC,GAAG,CAAC;SACN,WAAW,EAAE;SACb,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;SAC5B,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,aAAa,CAC9C,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,MAAc;IACxD,OAAO,MAAM,MAAM,IAAI,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,KAA6C,EAAE,MAAc,EAAE,MAAc,EAAE,GAAS;IACzG,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACD,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;QACnC,OAAO,GAAG,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACxC,2EAA2E;QAC3E,0EAA0E;QAC1E,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QACtD,OAAO,OAAO,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACL,oCAAoC;QACpC,OAAO,KAAK,oCAAoC,CAAC;QACjD,OAAO,OAAO,CAAC;IACnB,CAAC;AACL,CAAC"}
@@ -0,0 +1,46 @@
1
+ declare const SCHEMA_ERROR_TYPE = "schema-validation-error";
2
+ export interface ValidationError {
3
+ instancePath: string;
4
+ schemaPath?: string;
5
+ keyword: string;
6
+ params?: Record<string, unknown>;
7
+ message?: string;
8
+ }
9
+ interface InvalidItem {
10
+ itemPosition: number;
11
+ validationErrors: ValidationError[];
12
+ }
13
+ interface SchemaValidationError {
14
+ type: typeof SCHEMA_ERROR_TYPE;
15
+ statusCode: 400;
16
+ message: string;
17
+ data: {
18
+ invalidItems: InvalidItem[];
19
+ };
20
+ }
21
+ export declare function isSchemaValidationError(err: unknown): err is SchemaValidationError;
22
+ export interface DroppedItem<T> {
23
+ item: T;
24
+ errors: ValidationError[];
25
+ }
26
+ export interface SafePushDataResult<T> {
27
+ pushed: number;
28
+ dropped: DroppedItem<T>[];
29
+ attempts: number;
30
+ }
31
+ export interface SafePushDataOptions {
32
+ maxAttempts?: number;
33
+ }
34
+ export type PushFn<T> = (items: T[]) => Promise<unknown>;
35
+ /**
36
+ * Push `input` via `pushFn`, surviving Apify dataset schema-validation
37
+ * failures by cleaning offending items and retrying.
38
+ *
39
+ * `pushFn` is mandatory and intentionally not bundled — a CI check forbids
40
+ * direct `.pushData()` calls in this library, so the binding lives at the
41
+ * call site: `(b) => Actor.pushData(b)` or
42
+ * `(b) => client.dataset(id).pushItems(b)`.
43
+ */
44
+ export declare function safePushData<T>(pushFn: PushFn<T>, input: T | T[], options?: SafePushDataOptions): Promise<SafePushDataResult<T>>;
45
+ export {};
46
+ //# sourceMappingURL=safePushData.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safePushData.d.ts","sourceRoot":"","sources":["../../src/safePushData.ts"],"names":[],"mappings":"AAWA,QAAA,MAAM,iBAAiB,4BAA4B,CAAC;AAKpD,MAAM,WAAW,eAAe;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,WAAW;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,eAAe,EAAE,CAAC;CACvC;AAKD,UAAU,qBAAqB;IAC3B,IAAI,EAAE,OAAO,iBAAiB,CAAC;IAC/B,UAAU,EAAE,GAAG,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QAAE,YAAY,EAAE,WAAW,EAAE,CAAA;KAAE,CAAC;CACzC;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,qBAAqB,CAMlF;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEzD;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAChC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EACjB,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,EACd,OAAO,GAAE,mBAAwB,GAClC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAehC"}
@@ -0,0 +1,285 @@
1
+ // safePushData: parse the Apify dataset schema-validation error, repair the
2
+ // offending items (strip bad fields, placeholder missing required ones), and
3
+ // retry the push.
4
+ //
5
+ // NOTE: instead of recursively healing the data one error-round at a time, we
6
+ // could parse the Actor's `dataset_schema.json` up front and fix every item in
7
+ // a single pass (we'd know each field's expected type / constraints without
8
+ // waiting for the API to report them). That would avoid the multi-round retry
9
+ // loop, but requires heavier code (locating + loading the schema, resolving
10
+ // $refs, walking the schema tree). Something to consider in the future.
11
+ const SCHEMA_ERROR_TYPE = 'schema-validation-error';
12
+ export function isSchemaValidationError(err) {
13
+ if (!err || typeof err !== 'object')
14
+ return false;
15
+ const e = err;
16
+ if (e.type !== SCHEMA_ERROR_TYPE)
17
+ return false;
18
+ if (e.statusCode !== 400)
19
+ return false;
20
+ return Array.isArray(e.data?.invalidItems);
21
+ }
22
+ /**
23
+ * Push `input` via `pushFn`, surviving Apify dataset schema-validation
24
+ * failures by cleaning offending items and retrying.
25
+ *
26
+ * `pushFn` is mandatory and intentionally not bundled — a CI check forbids
27
+ * direct `.pushData()` calls in this library, so the binding lives at the
28
+ * call site: `(b) => Actor.pushData(b)` or
29
+ * `(b) => client.dataset(id).pushItems(b)`.
30
+ */
31
+ export async function safePushData(pushFn, input, options = {}) {
32
+ const items = Array.isArray(input) ? input : [input];
33
+ // Happy path: assume validation will succeed (the overwhelmingly common
34
+ // case). No working copies, no maps, no per-item wrapper objects — just
35
+ // hand the caller's array to pushFn and return on success.
36
+ try {
37
+ await pushFn(items);
38
+ return { pushed: items.length, dropped: [], attempts: 1 };
39
+ }
40
+ catch (err) {
41
+ if (!isSchemaValidationError(err))
42
+ throw err;
43
+ // Clamp to >=1: the initial push above always counts as one attempt,
44
+ // so 0/negative would make the reported `attempts` lie about it.
45
+ return cleanAndRetry(pushFn, items, err, Math.max(1, options.maxAttempts ?? 5));
46
+ }
47
+ }
48
+ async function cleanAndRetry(pushFn, originalItems, initialError, maxAttempts) {
49
+ // working[i] is what we'll send on the next push. We mutate this array
50
+ // in place (splicing drops, replacing cleaned entries); the caller's
51
+ // `originalItems` is never touched.
52
+ const working = originalItems.slice();
53
+ // Parallel to `working`. Holds the original (untouched) reference so a
54
+ // dropped report shows what the caller actually passed in, even if the
55
+ // item was partially cleaned before being dropped on a later round.
56
+ const originalAt = originalItems.slice();
57
+ // Parallel to `working`. Tracks which paths inside each item are
58
+ // placeholders we set (vs. real user data). Needed so we can chase
59
+ // follow-up type/minLength/enum errors on the placeholder field
60
+ // without looping forever; for user-supplied fields the existing
61
+ // "delete the field" behaviour stays in effect.
62
+ const placeholderPaths = originalItems.map(() => new Set());
63
+ // Parallel to `working`. The validation errors that were last reported
64
+ // for this position — kept so a give-up drop (maxAttempts exceeded) can
65
+ // still report *why*, not just that it gave up.
66
+ const lastErrorsAt = originalItems.map(() => []);
67
+ const dropped = [];
68
+ let attempts = 1;
69
+ let lastError = initialError;
70
+ while (true) {
71
+ // Process this round's errors. Highest position first so the splices
72
+ // below don't shift positions we still need to look at.
73
+ const invalids = lastError.data.invalidItems.slice().sort((a, b) => b.itemPosition - a.itemPosition);
74
+ for (const invalid of invalids) {
75
+ const i = invalid.itemPosition;
76
+ // Guard against a malformed/unexpected error payload (e.g. a
77
+ // position outside the batch we actually sent) instead of
78
+ // crashing on `working[i]` being undefined.
79
+ if (i < 0 || i >= working.length) {
80
+ console.log(`safePushData: ignoring out-of-range itemPosition ${i} in validation error response.`);
81
+ continue;
82
+ }
83
+ const cleaned = cleanItemFields(working[i], invalid.validationErrors, placeholderPaths[i]);
84
+ if (cleaned === null) {
85
+ dropped.push({ item: originalAt[i], errors: invalid.validationErrors });
86
+ working.splice(i, 1);
87
+ originalAt.splice(i, 1);
88
+ placeholderPaths.splice(i, 1);
89
+ lastErrorsAt.splice(i, 1);
90
+ }
91
+ else {
92
+ working[i] = cleaned;
93
+ lastErrorsAt[i] = invalid.validationErrors;
94
+ }
95
+ }
96
+ console.log(`safePushData: schema validation failed on attempt ${attempts}: ` +
97
+ `${lastError.data.invalidItems.length} invalid item(s); ` +
98
+ `retrying with ${working.length} item(s).`);
99
+ if (working.length === 0) {
100
+ return { pushed: originalItems.length - dropped.length, dropped, attempts };
101
+ }
102
+ attempts++;
103
+ if (attempts > maxAttempts) {
104
+ console.log(`safePushData: gave up after ${maxAttempts} attempts with ${working.length} item(s) still failing.`);
105
+ for (let i = 0; i < working.length; i++) {
106
+ dropped.push({ item: originalAt[i], errors: lastErrorsAt[i] });
107
+ }
108
+ return { pushed: originalItems.length - dropped.length, dropped, attempts: maxAttempts };
109
+ }
110
+ try {
111
+ await pushFn(working);
112
+ return { pushed: originalItems.length - dropped.length, dropped, attempts };
113
+ }
114
+ catch (err) {
115
+ if (!isSchemaValidationError(err))
116
+ throw err;
117
+ lastError = err;
118
+ }
119
+ }
120
+ }
121
+ // Try to repair a single item given its AJV errors. Returns null when the
122
+ // item can't be salvaged.
123
+ //
124
+ // Mutates `placeholderPaths` to record any fields we filled in ourselves,
125
+ // so the caller can keep iterating on them across rounds.
126
+ function cleanItemFields(item, validationErrors, placeholderPaths) {
127
+ // structuredClone so we never mutate the caller's data.
128
+ const cloned = structuredClone(item);
129
+ for (const err of validationErrors) {
130
+ const instancePath = err.instancePath || '';
131
+ const path = parseJsonPointer(instancePath);
132
+ // Root-level errors describe the item as a whole.
133
+ if (path.length === 0) {
134
+ // Missing required field: insert a placeholder and remember we
135
+ // did so. The next round will see a `type` error on this path
136
+ // and we'll upgrade null to a type-appropriate value.
137
+ if (err.keyword === 'required' && typeof err.params?.missingProperty === 'string') {
138
+ const prop = err.params.missingProperty;
139
+ cloned[prop] = null;
140
+ placeholderPaths.add(`/${prop}`);
141
+ continue;
142
+ }
143
+ // Strip an unknown property reported at the root.
144
+ if (err.keyword === 'additionalProperties' && typeof err.params?.additionalProperty === 'string') {
145
+ delete cloned[err.params.additionalProperty];
146
+ continue;
147
+ }
148
+ // type/format/etc. at the root means the item itself is the wrong shape.
149
+ return null;
150
+ }
151
+ // Errors on a path we placeholder'd: try to satisfy the constraint
152
+ // rather than delete the field (deleting would re-trigger required).
153
+ if (placeholderPaths.has(instancePath)) {
154
+ const fix = placeholderFor(err);
155
+ if (fix.ok) {
156
+ setAtPath(cloned, path, fix.value);
157
+ continue;
158
+ }
159
+ // Constraint we don't know how to satisfy on a placeholder field
160
+ // (e.g. pattern, custom format). Give up on this item.
161
+ return null;
162
+ }
163
+ // additionalProperties errors point at the *parent* object; the
164
+ // offending key is in params, not in instancePath.
165
+ if (err.keyword === 'additionalProperties' && typeof err.params?.additionalProperty === 'string') {
166
+ deleteAtPath(cloned, [...path, err.params.additionalProperty]);
167
+ continue;
168
+ }
169
+ // User-supplied field with a violation we don't try to coerce.
170
+ // Strip it; if the schema declares it required, the next push will
171
+ // re-add a placeholder.
172
+ deleteAtPath(cloned, path);
173
+ }
174
+ return cloned;
175
+ }
176
+ // Pick a value that will satisfy `err.keyword` on a placeholder field.
177
+ // Returns ok:false when we don't have a sensible default; the caller drops
178
+ // the item in that case.
179
+ //
180
+ // We deliberately only placeholder the four "empty" values — `''`, `[]`, `{}`,
181
+ // and `null`. These are unambiguously empty and can't be mistaken for real
182
+ // data. We do NOT fabricate values for `enum`, `format`, `minLength`, numeric
183
+ // bounds, etc.: a made-up email, a first-enum-value, or a `'_'.repeat(N)`
184
+ // string all silently poison the customer's dataset with plausible-looking
185
+ // junk. Better to drop the item than to lie about its contents. As a result
186
+ // the only keyword we handle is `type` (only for those four target types) —
187
+ // everything else falls through to ok:false and the item is dropped.
188
+ function placeholderFor(err) {
189
+ const params = err.params ?? {};
190
+ if (err.keyword !== 'type')
191
+ return { ok: false };
192
+ // params.type is the expected type as a string, or an array of strings
193
+ // when the field allows multiple types (e.g. `['string', 'null']`).
194
+ const types = Array.isArray(params.type) ? params.type : [params.type];
195
+ // Union type that permits null: prefer null. It's the cleanest possible
196
+ // placeholder — it commits to no concrete value at all — so whenever the
197
+ // schema allows it, that's what we use.
198
+ if (types.length > 1 && types.includes('null')) {
199
+ return { ok: true, value: null };
200
+ }
201
+ // Otherwise take the first allowed type we have an "empty" default for.
202
+ // integer / number / boolean are intentionally absent: 0 / false read as
203
+ // real data, so a field of only those types is dropped instead.
204
+ for (const t of types) {
205
+ switch (t) {
206
+ case 'null':
207
+ return { ok: true, value: null };
208
+ case 'string':
209
+ return { ok: true, value: '' };
210
+ case 'array':
211
+ return { ok: true, value: [] };
212
+ case 'object':
213
+ return { ok: true, value: {} };
214
+ default:
215
+ break;
216
+ }
217
+ }
218
+ return { ok: false };
219
+ }
220
+ // Parse a JSON Pointer (RFC 6901) into decoded segments.
221
+ // "" -> []; "/foo/bar" -> ["foo","bar"]; "/tags/0" -> ["tags","0"].
222
+ function parseJsonPointer(pointer) {
223
+ if (!pointer)
224
+ return [];
225
+ return pointer
226
+ .split('/')
227
+ .slice(1)
228
+ .map((seg) => seg.replace(/~1/g, '/').replace(/~0/g, '~'));
229
+ }
230
+ // Delete the value at `path` inside `obj`. Arrays splice (so later
231
+ // `/tags/N` errors in the same round line up with their original indices).
232
+ function deleteAtPath(obj, path) {
233
+ if (path.length === 0)
234
+ return false;
235
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
236
+ let cursor = obj;
237
+ for (let i = 0; i < path.length - 1; i++) {
238
+ const key = path[i];
239
+ if (cursor == null || typeof cursor !== 'object')
240
+ return false;
241
+ cursor = Array.isArray(cursor) ? cursor[Number(key)] : cursor[key];
242
+ }
243
+ if (cursor == null || typeof cursor !== 'object')
244
+ return false;
245
+ const last = path[path.length - 1];
246
+ if (Array.isArray(cursor)) {
247
+ const idx = Number(last);
248
+ if (!Number.isInteger(idx) || idx < 0 || idx >= cursor.length)
249
+ return false;
250
+ cursor.splice(idx, 1);
251
+ return true;
252
+ }
253
+ if (Object.prototype.hasOwnProperty.call(cursor, last)) {
254
+ delete cursor[last];
255
+ return true;
256
+ }
257
+ return false;
258
+ }
259
+ // Set the value at `path` inside `obj`. Creates nothing — the path must
260
+ // already exist (placeholder fields are created at the root, not nested).
261
+ function setAtPath(obj, path, value) {
262
+ if (path.length === 0)
263
+ return false;
264
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
265
+ let cursor = obj;
266
+ for (let i = 0; i < path.length - 1; i++) {
267
+ const key = path[i];
268
+ if (cursor == null || typeof cursor !== 'object')
269
+ return false;
270
+ cursor = Array.isArray(cursor) ? cursor[Number(key)] : cursor[key];
271
+ }
272
+ if (cursor == null || typeof cursor !== 'object')
273
+ return false;
274
+ const last = path[path.length - 1];
275
+ if (Array.isArray(cursor)) {
276
+ const idx = Number(last);
277
+ if (!Number.isInteger(idx) || idx < 0)
278
+ return false;
279
+ cursor[idx] = value;
280
+ return true;
281
+ }
282
+ cursor[last] = value;
283
+ return true;
284
+ }
285
+ //# sourceMappingURL=safePushData.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safePushData.js","sourceRoot":"","sources":["../../src/safePushData.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { unreachable } from './unreachable.js';
2
+ export type { Falsy, Nullish, Truthy, Defined } from './types.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/type-utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { unreachable } from './unreachable.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/type-utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,23 @@
1
+ export type Falsy = false | 0 | '' | null | undefined;
2
+ export type Nullish = null | undefined;
3
+ /**
4
+ * Narrows `T` to only its truthy members by stripping every {@link Falsy}
5
+ * value (`false`, `0`, `''`, `null`, `undefined`).
6
+ *
7
+ * Unlike `Exclude<T, Falsy>`, this checks each falsy literal individually so a
8
+ * broad type such as `boolean` or `number` is preserved rather than collapsed:
9
+ * `boolean` only loses `false` if `false` is the whole type, not when it's part
10
+ * of `boolean`. Object types short-circuit first, so `''`/`0` don't accidentally
11
+ * match structural (`{}`) types.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * type A = Truthy<string | undefined>; // string
16
+ * type B = Truthy<0 | 1 | 2>; // 1 | 2
17
+ * type C = Truthy<'' | 'hello'>; // 'hello'
18
+ * type D = Truthy<false>; // never
19
+ * ```
20
+ */
21
+ export type Truthy<T> = T extends object ? T : false extends T ? never : 0 extends T ? never : '' extends T ? never : null extends T ? never : undefined extends T ? never : T;
22
+ export type Defined<T> = Exclude<T, Nullish>;
23
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/type-utils/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;AACtD,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAEvC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,IAEhB,CAAC,SAAS,MAAM,GACV,CAAC,GACD,KAAK,SAAS,CAAC,GACb,KAAK,GACL,CAAC,SAAS,CAAC,GACT,KAAK,GACL,EAAE,SAAS,CAAC,GACV,KAAK,GACL,IAAI,SAAS,CAAC,GACZ,KAAK,GACL,SAAS,SAAS,CAAC,GACjB,KAAK,GACL,CAAC,CAAC;AAEtB,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/type-utils/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Asserts that a code path is unreachable, enabling exhaustiveness checks.
3
+ *
4
+ * Pass a value of type `never` to this function to let the TypeScript compiler
5
+ * verify that all cases of a union have been handled. If a new variant is added
6
+ * to the union but not handled, the call becomes a compile-time error. At
7
+ * runtime, reaching this function throws an `Error`.
8
+ *
9
+ * @param x - The value that should be of type `never` at this point.
10
+ * @throws {Error} Always throws, since this code should never be reached.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * type Shape = 'circle' | 'square';
15
+ *
16
+ * function area(shape: Shape): number {
17
+ * switch (shape) {
18
+ * case 'circle':
19
+ * return Math.PI;
20
+ * case 'square':
21
+ * return 1;
22
+ * default:
23
+ * // Compile-time error here if a new Shape variant is unhandled.
24
+ * return unreachable(shape);
25
+ * }
26
+ * }
27
+ * ```
28
+ */
29
+ export declare function unreachable(x: never): never;
30
+ //# sourceMappingURL=unreachable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unreachable.d.ts","sourceRoot":"","sources":["../../../src/type-utils/unreachable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,CAE3C"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Asserts that a code path is unreachable, enabling exhaustiveness checks.
3
+ *
4
+ * Pass a value of type `never` to this function to let the TypeScript compiler
5
+ * verify that all cases of a union have been handled. If a new variant is added
6
+ * to the union but not handled, the call becomes a compile-time error. At
7
+ * runtime, reaching this function throws an `Error`.
8
+ *
9
+ * @param x - The value that should be of type `never` at this point.
10
+ * @throws {Error} Always throws, since this code should never be reached.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * type Shape = 'circle' | 'square';
15
+ *
16
+ * function area(shape: Shape): number {
17
+ * switch (shape) {
18
+ * case 'circle':
19
+ * return Math.PI;
20
+ * case 'square':
21
+ * return 1;
22
+ * default:
23
+ * // Compile-time error here if a new Shape variant is unhandled.
24
+ * return unreachable(shape);
25
+ * }
26
+ * }
27
+ * ```
28
+ */
29
+ export function unreachable(x) {
30
+ throw new Error(`unreachable: ${x}`);
31
+ }
32
+ //# sourceMappingURL=unreachable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unreachable.js","sourceRoot":"","sources":["../../../src/type-utils/unreachable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,WAAW,CAAC,CAAQ;IAChC,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;AACzC,CAAC"}
package/package.json CHANGED
@@ -1,63 +1,74 @@
1
1
  {
2
- "name": "@apify/actor-utils",
3
- "version": "0.1.0",
4
- "type": "module",
5
- "description": "Wrapper around Actor.pushData that survives dataset schema-validation failures by stripping offending fields and retrying. Callers pass the actual push function as pushFn.",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/apify-projects/apify-actor-utils.git"
2
+ "name": "@apify/actor-utils",
3
+ "version": "0.1.3-beta.0",
4
+ "type": "module",
5
+ "description": "Wrapper around Actor.pushData that survives dataset schema-validation failures by stripping offending fields and retrying. Callers pass the actual push function as pushFn.",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/apify/apify-actor-utils.git"
9
+ },
10
+ "engines": {
11
+ "node": ">=22.6.0"
12
+ },
13
+ "types": "./dist/index.d.ts",
14
+ "main": "./dist/index.js",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
9
19
  },
10
- "engines": {
11
- "node": ">=22.6.0"
20
+ "./qc-logger": {
21
+ "types": "./dist/src/qc-logger/index.d.ts",
22
+ "default": "./dist/src/qc-logger/index.js"
12
23
  },
13
- "types": "./dist/index.d.ts",
14
- "main": "./dist/index.js",
15
- "exports": {
16
- ".": {
17
- "types": "./dist/index.d.ts",
18
- "default": "./dist/index.js"
19
- }
20
- },
21
- "files": [
22
- "dist/index.js",
23
- "dist/index.js.map",
24
- "dist/index.d.ts",
25
- "dist/index.d.ts.map",
26
- "dist/src"
27
- ],
28
- "scripts": {
29
- "build": "tsc",
30
- "typecheck": "tsc --noEmit",
31
- "lint": "eslint",
32
- "lint:fix": "eslint --fix",
33
- "format": "prettier --write .",
34
- "format:check": "prettier --check .",
35
- "test": "tsc && node --test dist/test/*.test.js",
36
- "check:pushdata": "node scripts/check-pushdata.mjs",
37
- "check-unused": "knip",
38
- "ci": "npm run typecheck && npm run lint && npm run format:check && npm run check:pushdata && npm test",
39
- "probe": "node scripts/probe-errors.mjs",
40
- "prepare": "husky .husky || true"
41
- },
42
- "lint-staged": {
43
- "*.js": "eslint",
44
- "*.ts": "eslint",
45
- "*.mjs": "eslint"
46
- },
47
- "devDependencies": {
48
- "@apify/eslint-config": "^1.2.0",
49
- "@apify/tsconfig": "^0.1.2",
50
- "@types/node": "^25.9.1",
51
- "apify-client": "^2.10.0",
52
- "eslint": "^9.39.4",
53
- "eslint-config-prettier": "^10.1.8",
54
- "globals": "^17.7.0",
55
- "husky": "^9.1.7",
56
- "knip": "^5.88.1",
57
- "lint-staged": "^15.5.2",
58
- "prettier": "^3.9.4",
59
- "typescript": "^6.0.3",
60
- "typescript-eslint": "^8.62.1"
61
- },
62
- "license": "ISC"
24
+ "./type-utils": {
25
+ "types": "./dist/src/type-utils/index.d.ts",
26
+ "default": "./dist/src/type-utils/index.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist/index.js",
31
+ "dist/index.js.map",
32
+ "dist/index.d.ts",
33
+ "dist/index.d.ts.map",
34
+ "dist/src"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsc",
38
+ "typecheck": "tsc --noEmit",
39
+ "lint": "eslint",
40
+ "lint:fix": "eslint --fix",
41
+ "format": "prettier --write .",
42
+ "format:check": "prettier --check .",
43
+ "test": "tsc && node --test dist/test/*.test.js",
44
+ "check:pushdata": "node scripts/check-pushdata.mjs",
45
+ "check-unused": "knip",
46
+ "ci": "npm run typecheck && npm run lint && npm run format:check && npm run check:pushdata && npm test",
47
+ "probe": "node scripts/probe-errors.mjs",
48
+ "prepare": "husky .husky || true"
49
+ },
50
+ "lint-staged": {
51
+ "*.js": "eslint",
52
+ "*.ts": "eslint",
53
+ "*.mjs": "eslint"
54
+ },
55
+ "devDependencies": {
56
+ "@apify/eslint-config": "^1.2.0",
57
+ "@apify/tsconfig": "^0.1.2",
58
+ "@types/node": "^25.9.1",
59
+ "apify-client": "^2.10.0",
60
+ "eslint": "^9.39.4",
61
+ "eslint-config-prettier": "^10.1.8",
62
+ "globals": "^17.7.0",
63
+ "husky": "^9.1.7",
64
+ "knip": "^5.88.1",
65
+ "lint-staged": "^15.5.2",
66
+ "prettier": "^3.9.4",
67
+ "typescript": "^6.0.3",
68
+ "typescript-eslint": "^8.62.1"
69
+ },
70
+ "license": "ISC",
71
+ "peerDependencies": {
72
+ "@apify/log": "^2.5.44"
73
+ }
63
74
  }