ata-validator 1.61.3 → 1.62.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 (48) hide show
  1. package/README.md +67 -10
  2. package/bin/ata.js +3 -2
  3. package/compat.js +15 -2
  4. package/index.d.ts +7 -0
  5. package/index.js +10 -0
  6. package/lib/aot-build.js +27 -1
  7. package/lib/aot-impl.js +108 -17
  8. package/lib/buffer-gate.js +25 -14
  9. package/lib/bun-target.js +21 -5
  10. package/lib/clone-emit.js +115 -10
  11. package/lib/codegen-engine.js +94 -18
  12. package/lib/compiled-common.js +11 -4
  13. package/lib/compiled-verdict.js +2 -2
  14. package/lib/compiled.js +1 -1
  15. package/lib/data-positions.js +18 -0
  16. package/lib/defaults.js +101 -23
  17. package/lib/describe-schema.js +38 -5
  18. package/lib/diagnose.js +51 -29
  19. package/lib/enrich-error.js +23 -1
  20. package/lib/error-sort-rank.js +12 -0
  21. package/lib/formats-source.js +12 -1
  22. package/lib/formats.js +19 -19
  23. package/lib/interpreter-core.js +23 -4
  24. package/lib/js-compiler-errors.js +70 -72
  25. package/lib/js-compiler.js +212 -72
  26. package/lib/json-rejections.js +1 -1
  27. package/lib/migrate.js +24 -5
  28. package/lib/native-attach.browser.js +7 -0
  29. package/lib/native-attach.js +76 -0
  30. package/lib/native-positions.js +7 -0
  31. package/lib/plan-compiler.js +7 -1
  32. package/lib/regex-linear.js +88 -0
  33. package/lib/rejections.js +13 -3
  34. package/lib/render-compact.js +5 -5
  35. package/lib/render-pretty.js +23 -10
  36. package/lib/safe-regex.js +29 -4
  37. package/lib/scan-compiler.js +16 -4
  38. package/lib/stream.js +293 -0
  39. package/lib/tier0.js +6 -2
  40. package/lib/ts-gen.js +25 -12
  41. package/lib/unique-items.js +11 -3
  42. package/lib/validator-core.js +61 -66
  43. package/lib/verify.js +25 -6
  44. package/lib/version.js +1 -1
  45. package/package.json +17 -8
  46. package/stream.d.ts +51 -0
  47. package/stream.js +3 -0
  48. package/stream.mjs +3 -0
package/lib/verify.js CHANGED
@@ -20,7 +20,8 @@ const { pathToFileURL } = require('url')
20
20
  function rng (seed) {
21
21
  let x = (seed >>> 0) || 0x9e3779b9
22
22
  const next = () => { x ^= x << 13; x ^= x >>> 17; x ^= x << 5; return (x >>> 0) / 4294967296 }
23
- return { next, int: (n) => Math.floor(next() * n), pick: (a) => a[Math.floor(next() * a.length)], chance: (p) => next() < p }
23
+ // `nodes` counts the values of the document being made (makeValid).
24
+ return { next, int: (n) => Math.floor(next() * n), pick: (a) => a[Math.floor(next() * a.length)], chance: (p) => next() < p, nodes: 0 }
24
25
  }
25
26
 
26
27
  const WORDS = ['ada', 'Zoë', 'order', 'x', 'north-west', 'a b', 'ÄÖÜ', 'sku-1001', 'TENOFF', 'hello world', '', '0', 'null', '😀', 'line\nbreak', ' ']
@@ -48,9 +49,18 @@ const FORMAT_EXAMPLES = {
48
49
  'json-pointer': ['/a/b', 'a/b', '/a~1b', '/a~2'],
49
50
  }
50
51
 
52
+ const { patternIsSafe, compileSafe } = require('./safe-regex')
53
+
54
+ // How many values one generated document may hold. Nested minItems multiply
55
+ // (20 at six levels is 64 million integers) and ran the process out of
56
+ // memory; past the budget the document is refused with a message instead.
57
+ const NODE_BUDGET = 100000
58
+
51
59
  // A value meant to satisfy `schema`, built from its keywords; where the
52
60
  // schema says little, something plausible of the kind.
53
61
  function makeValid (schema, r, defs, depth) {
62
+ if (depth === 0) r.nodes = 0
63
+ if (++r.nodes > NODE_BUDGET) throw new Error(`a document for this schema holds more than ${NODE_BUDGET} values (nested minItems or required properties); ata verify does not generate it`)
54
64
  if (schema === true || schema === undefined) return r.pick([1, 'x', true, null, {}, []])
55
65
  if (schema === false) return undefined
56
66
  if (typeof schema !== 'object' || schema === null) return null
@@ -103,16 +113,22 @@ function makeValid (schema, r, defs, depth) {
103
113
  // Any other pattern: a candidate that matches, when one does, so the
104
114
  // generated documents reach past the pattern; else a word, which the
105
115
  // schema then refuses, and that is a document too.
106
- let re = null
107
- try { re = new RegExp(schema.pattern, 'u') } catch {}
116
+ // On the linear engine: the candidates are fixed strings, and a
117
+ // pattern that backtracks on them (^(((a+)+)+)+b$ on sixteen a's)
118
+ // held the platform RegExp for seconds. A pattern the engine does not
119
+ // take gets no candidate.
120
+ const re = patternIsSafe(schema.pattern) ? compileSafe(schema.pattern) : null
108
121
  if (re) {
109
122
  const matching = PATTERN_CANDIDATES.filter((c) => re.test(c) && [...c].length >= min && [...c].length <= max)
110
123
  if (matching.length) return r.pick(matching)
111
124
  }
112
125
  }
113
126
  let s = r.pick(WORDS)
114
- while ([...s].length < min) s += 'a'
115
- if ([...s].length > max) s = [...s].slice(0, max).join('')
127
+ // Counted once: padding one character at a time with the string spread
128
+ // for each count took 32 s for a minLength of 100000.
129
+ const len = [...s].length
130
+ if (len < min) s += 'a'.repeat(min - len)
131
+ else if (len > max) s = [...s].slice(0, max).join('')
116
132
  return s
117
133
  }
118
134
  case 'array': {
@@ -321,7 +337,10 @@ async function verifyOne ({ schemaFile, moduleFile, schema, generate: count = 20
321
337
  if (mod.schemaHash !== undefined && mod.schemaHash !== hash) report.stale = true
322
338
  let runtime
323
339
  try { runtime = new Validator(schema, {}) } catch (e) { report.error = `schema does not compile: ${e.message}`; return report }
324
- const docs = generate(schema, count, seed, (d) => { try { return runtime.isValidObject(clone(d)) } catch { return false } }).map((d, i) => ({ source: `generated #${i + 1}`, doc: d }))
340
+ let docs
341
+ try {
342
+ docs = generate(schema, count, seed, (d) => { try { return runtime.isValidObject(clone(d)) } catch { return false } }).map((d, i) => ({ source: `generated #${i + 1}`, doc: d }))
343
+ } catch (e) { report.error = `documents cannot be generated: ${e.message}`; return report }
325
344
  report.generated = docs.length
326
345
  const sampleDocs = readSamples(samples)
327
346
  report.samples = sampleDocs.length
package/lib/version.js CHANGED
@@ -7,4 +7,4 @@
7
7
  //
8
8
  // Kept in lockstep with package.json by `tests/test_version_sync.js`.
9
9
 
10
- module.exports = '1.61.3';
10
+ module.exports = '1.62.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "1.61.3",
3
+ "version": "1.62.0",
4
4
  "description": "JSON Schema validator that compiles to plain JavaScript, still runs where code generation is blocked, and can be compiled away at build time. Passes the full official test suite.",
5
5
  "type": "commonjs",
6
6
  "main": "index.js",
@@ -89,6 +89,11 @@
89
89
  "import": "./compiled-standard.mjs",
90
90
  "default": "./lib/compiled-standard.js"
91
91
  },
92
+ "./stream": {
93
+ "types": "./stream.d.ts",
94
+ "import": "./stream.mjs",
95
+ "default": "./stream.js"
96
+ },
92
97
  "./lite": {
93
98
  "types": "./lite.d.ts",
94
99
  "import": "./lite.mjs",
@@ -118,6 +123,7 @@
118
123
  },
119
124
  "browser": {
120
125
  "./lib/native-load.js": "./lib/native-load.browser.js",
126
+ "./lib/native-attach.js": "./lib/native-attach.browser.js",
121
127
  "./lib/aot.js": "./lib/aot.browser.js",
122
128
  "./lib/buffer-gate.js": "./lib/buffer-gate.browser.js",
123
129
  "./lib/interpreter.js": "./lib/interpreter.browser.js",
@@ -224,6 +230,9 @@
224
230
  "compiled-bare-rich.d.ts",
225
231
  "compiled-standard.mjs",
226
232
  "compiled-standard.d.ts",
233
+ "stream.js",
234
+ "stream.mjs",
235
+ "stream.d.ts",
227
236
  "lite.js",
228
237
  "lite.mjs",
229
238
  "lite.d.ts",
@@ -238,13 +247,13 @@
238
247
  "codegen.d.ts"
239
248
  ],
240
249
  "optionalDependencies": {
241
- "@ata-validator/native-darwin-arm64": "1.61.3",
242
- "@ata-validator/native-darwin-x64": "1.61.3",
243
- "@ata-validator/native-linux-arm64-gnu": "1.61.3",
244
- "@ata-validator/native-linux-arm64-musl": "1.61.3",
245
- "@ata-validator/native-linux-x64-gnu": "1.61.3",
246
- "@ata-validator/native-linux-x64-musl": "1.61.3",
247
- "@ata-validator/native-win32-x64": "1.61.3"
250
+ "@ata-validator/native-darwin-arm64": "1.62.0",
251
+ "@ata-validator/native-darwin-x64": "1.62.0",
252
+ "@ata-validator/native-linux-arm64-gnu": "1.62.0",
253
+ "@ata-validator/native-linux-arm64-musl": "1.62.0",
254
+ "@ata-validator/native-linux-x64-gnu": "1.62.0",
255
+ "@ata-validator/native-linux-x64-musl": "1.62.0",
256
+ "@ata-validator/native-win32-x64": "1.62.0"
248
257
  },
249
258
  "peerDependencies": {
250
259
  "yaml": "^2.0.0"
package/stream.d.ts ADDED
@@ -0,0 +1,51 @@
1
+ import { Validator, ValidationError } from './index';
2
+
3
+ /** Options of a record stream. */
4
+ export interface StreamOptions {
5
+ /** How the body carries its records: one JSON document per line, a JSON array, or decided from the first non-blank character (default). */
6
+ format?: 'ndjson' | 'array' | 'auto';
7
+ /** Stop at the first record that fails (default true); false sees every record and lists every failure. */
8
+ stopOnFirst?: boolean;
9
+ /** Called with each record that passed, parsed, and its index from zero. */
10
+ onRecord?: (record: unknown, index: number) => void;
11
+ }
12
+
13
+ /** One record's failure: its index from zero and the errors validate() gives for it, or a parse error with keyword `__parse__`. */
14
+ export interface RecordFailure {
15
+ valid: false;
16
+ record: number;
17
+ errors: ValidationError[];
18
+ }
19
+
20
+ /** What end() returns: whether every record passed, how many were read, and the failures seen. */
21
+ export interface StreamResult {
22
+ valid: boolean;
23
+ records: number;
24
+ errors: RecordFailure[];
25
+ }
26
+
27
+ /** A record stream: feed chunks as they arrive, get the first failure the moment its record is complete. */
28
+ export class RecordStream {
29
+ constructor(validator: Validator<unknown>, opts?: StreamOptions);
30
+ /** Records read so far. */
31
+ readonly records: number;
32
+ /** Failures so far. */
33
+ readonly failures: RecordFailure[];
34
+ /** True once the stream stopped, at a failure or at end(). */
35
+ readonly done: boolean;
36
+ /** Feed a chunk. Returns null while every record so far passes, or the first failure. */
37
+ push(chunk: string | Uint8Array): RecordFailure | null;
38
+ /** Close the input. An unfinished record or an unclosed array is a parse failure. */
39
+ end(): StreamResult;
40
+ }
41
+
42
+ export function streamValidator(validator: Validator<unknown>, opts?: StreamOptions): RecordStream;
43
+
44
+ /**
45
+ * Read an async source (a Node Readable, a Web ReadableStream, any iterable of
46
+ * chunks) through a record stream. At the first failure the source is stopped:
47
+ * a Node stream is destroyed, a Web stream's reader cancelled.
48
+ */
49
+ export function validateStream(validator: Validator<unknown>, source: AsyncIterable<string | Uint8Array> | Iterable<string | Uint8Array> | ReadableStream<Uint8Array | string>, opts?: StreamOptions): Promise<StreamResult>;
50
+
51
+ export const PARSE_KEYWORD: '__parse__';
package/stream.js ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+ // ata-validator/stream: validation as the body arrives, record by record.
3
+ module.exports = require('./lib/stream.js');
package/stream.mjs ADDED
@@ -0,0 +1,3 @@
1
+ import mod from './lib/stream.js';
2
+ export const { RecordStream, streamValidator, validateStream, PARSE_KEYWORD } = mod;
3
+ export default mod;