ata-validator 1.34.0 → 1.36.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.
package/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import mod from './index.js';
2
- export const { Validator, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, defineSchema, renderPretty, renderCompact, renderJSON } = mod;
2
+ export const { Validator, compile, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, parseJSON, toTypeScript, defineSchema, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON } = mod;
3
3
  export default mod;
package/index.node.mjs ADDED
@@ -0,0 +1,10 @@
1
+ // The ESM entry Node itself resolves (the `module-sync` condition, Node 22.10
2
+ // and later). Importing index.js with an `import` statement makes Node run
3
+ // its CommonJS lexer over every module the package requires, to find named
4
+ // exports this file lists by hand anyway: 7.7 ms to load against 5.5 through
5
+ // require. Bundlers do not match `module-sync` and keep index.mjs, whose
6
+ // static import they can follow.
7
+ import { createRequire } from 'node:module';
8
+ const mod = createRequire(import.meta.url)('./index.js');
9
+ export const { Validator, compile, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, parseJSON, toTypeScript, defineSchema, renderPretty, renderCompact, toOutput, toRetryMessage, describeSchema, renderJSON } = mod;
10
+ export default mod;
package/lib/aot-build.js CHANGED
@@ -288,7 +288,53 @@ function toStandaloneModule(schema, opts) {
288
288
 
289
289
  const { schemaHash } = require('./schema-hash');
290
290
 
291
+ // Whether `new Validator(schema)` with default options can be replaced by
292
+ // fromCompiled() around the module toStandaloneModule(schema) writes, which a
293
+ // bundler plugin does so the runtime compiler stays out of the bundle. The
294
+ // wrapper reproduces a default Validator's results exactly for these schemas;
295
+ // tests/test_compiled_parity.js holds that. Declined: custom error messages,
296
+ // which the core applies in a layer the wrapper does not carry. A caller must also get a
297
+ // module back from toStandaloneModule, which returns null for a schema it
298
+ // cannot compile. The text checks are deliberately coarse: a property named
299
+ // `default` declines too, and declining costs only the saving.
300
+ function compiledEligible(schema) {
301
+ if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) return false;
302
+ return !JSON.stringify(schema).includes('"errorMessage"');
303
+ }
304
+
305
+ // The schema a default Validator reads, after normalization: what
306
+ // fromCompiled() takes, so its defaults, sorting and diagnostics follow the
307
+ // same document the runtime does.
308
+ function compiledSchemaFor(schema) {
309
+ const { Validator } = require('../index.js');
310
+ return new Validator(schema)._schemaObj;
311
+ }
312
+
313
+ // The module that replaces `new Validator(schema)`, or null where the
314
+ // replacement would not answer as the runtime does: a schema
315
+ // compiledEligible() declines, one the emitter cannot compile, and one whose
316
+ // error function the generator declines (unevaluated* among them), where the
317
+ // module would report every failure as the ATA9000 stub while the runtime
318
+ // reports the real errors.
319
+ function compiledModuleFor(schema, opts) {
320
+ if (!compiledEligible(schema)) return null;
321
+ let degraded = false;
322
+ let src;
323
+ try {
324
+ src = toStandaloneModule(schema, {
325
+ format: (opts && opts.format) || 'esm',
326
+ onWarning: (msg, info) => { if (info && info.kind === 'error-detail') degraded = true; },
327
+ });
328
+ } catch {
329
+ return null;
330
+ }
331
+ return src && !degraded ? src : null;
332
+ }
333
+
291
334
  module.exports = {
335
+ compiledEligible,
336
+ compiledSchemaFor,
337
+ compiledModuleFor,
292
338
  build,
293
339
  expandGlobs,
294
340
  parseSchemaFile,
@@ -106,7 +106,6 @@ function installSlowBufferApis(validator) {
106
106
  return validator.validate(value).valid;
107
107
  };
108
108
  validator.isValid = (input) => isValidText(toText(input, 'isValid'));
109
- validator.isValidJSON = (jsonStr) => isValidText(jsonStr);
110
109
  validator.isValidPrepadded = (paddedBuffer, jsonLength) =>
111
110
  isValidText(Buffer.from(paddedBuffer.buffer, paddedBuffer.byteOffset, jsonLength).toString('utf8'));
112
111
  const ndjson = (input, name) => {
@@ -0,0 +1,96 @@
1
+ 'use strict';
2
+
3
+ // A Validator-shaped object around a module the code generator wrote ahead of
4
+ // time: what a bundler plugin puts in place of `new Validator(schema)` when the
5
+ // schema is known at build time, so the runtime compiler does not ship. It
6
+ // answers validate(), isValidObject(), validateJSON() and isValidJSON() the way
7
+ // a Validator built with default options answers them, down to the error
8
+ // objects, which come from the same rejection classes the validator core uses.
9
+ // tests/test_compiled_parity.js holds that over the official suite.
10
+ //
11
+ // Only for a schema compiledEligible() accepts, and with the schema
12
+ // compiledSchemaFor() returns: the one the runtime reads, after normalization.
13
+ // Custom error messages and every option other than the defaults stay on the
14
+ // runtime. Defaults are applied as a default Validator applies them, before
15
+ // any check, with the same code.
16
+
17
+ const { LazyRejection, RichRejection, LazyJsonRejection, _enrichLazy } = require('./rejections');
18
+ const { buildDefaultsApplier } = require('./defaults');
19
+
20
+ const VALID_RESULT = Object.freeze({ valid: true, errors: Object.freeze([]) });
21
+ const EMPTY_ERRORS = Object.freeze([]);
22
+ const VERDICT_DISAGREES = Object.freeze({
23
+ valid: false,
24
+ errors: Object.freeze([Object.freeze({ keyword: 'validation', instancePath: '', schemaPath: '#', params: Object.freeze({}), message: 'schema validation failed' })]),
25
+ });
26
+
27
+ function syntaxRejection(e) {
28
+ return { valid: false, errors: [{ keyword: '__parse__', instancePath: '', schemaPath: '', params: {}, message: 'invalid JSON: ' + e.message }] };
29
+ }
30
+
31
+ // The fields of a Validator the rejection classes read.
32
+ class CompiledState {
33
+ constructor(schema) {
34
+ this._schemaObj = schema;
35
+ this._schemaPositions = undefined;
36
+ this._source = undefined;
37
+ this._mutatesInput = false;
38
+ this._preprocess = null;
39
+ this._posCache = null;
40
+ }
41
+ _pos() {
42
+ return this._posCache || (this._posCache = require('./data-position-cache').createCache());
43
+ }
44
+ }
45
+
46
+ function fromCompiled(mod, schema) {
47
+ const self = new CompiledState(schema);
48
+ const fill = buildDefaultsApplier(schema);
49
+ if (fill) {
50
+ self._mutatesInput = true;
51
+ self._preprocess = fill;
52
+ }
53
+ // With defaults, the document is filled in before it is checked, on every
54
+ // entry point, as the runtime does.
55
+ const isValid = fill ? (d) => { fill(d); return mod.isValid(d); } : mod.isValid;
56
+ const errFn = mod.validate;
57
+ // The generated function's own result, rejected: if it says valid where the
58
+ // verdict said no, the disagreement is reported rather than turned into an
59
+ // acceptance, as the core does.
60
+ const raw = (data) => {
61
+ const r = errFn(data);
62
+ return r && r.valid === false ? r : VERDICT_DISAGREES;
63
+ };
64
+ const rich = (data) => new RichRejection(raw(data), data, null, self, schema, _enrichLazy);
65
+ const buildErrors = (data) => rich(data).errors;
66
+ const buildRawErrors = (data) => rich(data)._ataRaw();
67
+ const parse = (text) => {
68
+ try { return { value: JSON.parse(text) }; } catch (e) {
69
+ if (!(e instanceof SyntaxError)) throw e;
70
+ return { error: e };
71
+ }
72
+ };
73
+ return {
74
+ validate(data) {
75
+ if (isValid(data)) return { valid: true, data, errors: EMPTY_ERRORS };
76
+ // The runtime returns the rich rejection directly when it rewrites input.
77
+ if (fill) return rich(data);
78
+ return new LazyRejection(buildErrors, data, buildRawErrors);
79
+ },
80
+ isValidObject(data) {
81
+ return isValid(data);
82
+ },
83
+ validateJSON(text) {
84
+ const p = parse(text);
85
+ if (p.error) return new LazyJsonRejection(syntaxRejection(p.error), text, self, _enrichLazy);
86
+ if (isValid(p.value)) return VALID_RESULT;
87
+ return new LazyJsonRejection(raw(p.value), text, self, _enrichLazy);
88
+ },
89
+ isValidJSON(text) {
90
+ const p = parse(text);
91
+ return p.error ? false : isValid(p.value);
92
+ },
93
+ };
94
+ }
95
+
96
+ module.exports = { fromCompiled };
@@ -0,0 +1,88 @@
1
+ 'use strict';
2
+
3
+ // The closure pass that fills `default` values, shared by the validator core
4
+ // and by the wrapper around a compiled module, so both fill defaults the same
5
+ // way. The generated pass in index.js is held to this one by
6
+ // tests/test_nested_defaults_engines.js.
7
+
8
+ // Extract default values from a schema tree. Returns a function that applies
9
+ // defaults to an object in-place (mutates), or null if no defaults exist.
10
+ function buildDefaultsApplier(schema) {
11
+ if (typeof schema !== "object" || schema === null) return null;
12
+ const actions = [];
13
+ collectDefaults(schema, actions);
14
+ if (actions.length === 0) return null;
15
+ return (data) => {
16
+ for (let i = 0; i < actions.length; i++) actions[i](data);
17
+ };
18
+ }
19
+
20
+ // Write an own property. Plain assignment of a key named `__proto__` does
21
+ // not create a property at all: it hits the Object.prototype setter and
22
+ // rewrites the object's prototype, which is how a schema could reach
23
+ // Object.prototype itself. defineProperty has no such special case.
24
+ function setOwn(obj, key, val) {
25
+ if (key === "__proto__") {
26
+ Object.defineProperty(obj, key, {
27
+ value: val,
28
+ writable: true,
29
+ enumerable: true,
30
+ configurable: true,
31
+ });
32
+ } else {
33
+ obj[key] = val;
34
+ }
35
+ }
36
+
37
+ function collectDefaults(schema, actions, path) {
38
+ if (typeof schema !== "object" || schema === null) return;
39
+ const props = schema.properties;
40
+ if (!props) return;
41
+ for (const [key, prop] of Object.entries(props)) {
42
+ if (prop && typeof prop === "object" && prop.default !== undefined) {
43
+ const defaultVal = prop.default;
44
+ if (!path) {
45
+ actions.push((data) => {
46
+ if (typeof data === "object" && data !== null && !Object.hasOwn(data, key)) {
47
+ setOwn(data,
48
+ key,
49
+ typeof defaultVal === "object" && defaultVal !== null
50
+ ? JSON.parse(JSON.stringify(defaultVal))
51
+ : defaultVal);
52
+ }
53
+ });
54
+ } else {
55
+ const parentPath = path;
56
+ actions.push((data) => {
57
+ let target = data;
58
+ for (let j = 0; j < parentPath.length; j++) {
59
+ if (typeof target !== "object" || target === null) return;
60
+ // Own keys only. `target[key]` for an inherited name walks the
61
+ // prototype chain: a parent named `__proto__` that the instance
62
+ // does not carry resolved to Object.prototype, and the child
63
+ // defaults were written onto it, for every object in the realm.
64
+ if (!Object.hasOwn(target, parentPath[j])) return;
65
+ target = target[parentPath[j]];
66
+ }
67
+ if (
68
+ typeof target === "object" &&
69
+ target !== null &&
70
+ !Object.hasOwn(target, key)
71
+ ) {
72
+ setOwn(target,
73
+ key,
74
+ typeof defaultVal === "object" && defaultVal !== null
75
+ ? JSON.parse(JSON.stringify(defaultVal))
76
+ : defaultVal);
77
+ }
78
+ });
79
+ }
80
+ }
81
+ // Recurse into nested object schemas
82
+ if (prop && typeof prop === "object" && prop.properties) {
83
+ collectDefaults(prop, actions, (path || []).concat(key));
84
+ }
85
+ }
86
+ }
87
+
88
+ module.exports = { buildDefaultsApplier, setOwn };
@@ -194,8 +194,15 @@ function enrich (rawErr, opts) {
194
194
  // Prefer a code the codegen already attached (e.g. branch-collapse emits
195
195
  // ATA4001/4002/4003 distinguishing zero/multi/anyOf failure modes). The
196
196
  // keyword-derived lookup only finds the first match for `keyword: 'oneOf'`.
197
+ // A property that dependentRequired asks for is reported with the keyword
198
+ // `required`, and its documented code is ATA7005, not required's ATA7001.
199
+ // The error generator stamped 7005 and the other engines left it to the
200
+ // keyword here, so the same failure carried either code depending on which
201
+ // generator answered.
202
+ const dependent = keyword === 'required' && typeof rawErr.schemaPath === 'string' && rawErr.schemaPath.endsWith('/dependentRequired');
197
203
  const code = (fromAddon && fromAddon.code) ||
198
204
  (typeof rawErr.code === 'string' && rawErr.code) ||
205
+ (dependent && 'ATA7005') ||
199
206
  codeFor(keyword, format) ||
200
207
  'ATA9001';
201
208
  const meta = CODES[code];
@@ -0,0 +1,323 @@
1
+ 'use strict';
2
+
3
+ // The source emitters for the formats: each returns JavaScript source that
4
+ // checks a string in place, which the code generator inlines into the
5
+ // function it builds. Split from lib/formats.js, which keeps the runtime
6
+ // checks the interpreted engine calls, so a bundle without the code generator
7
+ // (ata-validator/lite) does not carry them.
8
+
9
+ const { DURATION_RE, URI_TEMPLATE_EXPR, URI_FAST } = require('./formats');
10
+
11
+ const uriCharsSource = (v, from) =>
12
+ `for(let _ri=${from};_ri<${v}.length;_ri++){const _rc=${v}.charCodeAt(_ri);` +
13
+ 'if(_rc<33||_rc>126||_rc===34||_rc===60||_rc===62||_rc===92||_rc===94||_rc===96||_rc===123||_rc===124||_rc===125)return false;' +
14
+ `if(_rc===37){const _h1=${v}.charCodeAt(_ri+1),_h2=${v}.charCodeAt(_ri+2);` +
15
+ 'if(!((_h1>=48&&_h1<=57)||(_h1>=97&&_h1<=102)||(_h1>=65&&_h1<=70))||' +
16
+ '!((_h2>=48&&_h2<=57)||(_h2>=97&&_h2<=102)||(_h2>=65&&_h2<=70)))return false;_ri+=2}}';
17
+ const uriAuthoritySource = (v, start) =>
18
+ `if(${v}.charCodeAt(${start})===47&&${v}.charCodeAt(${start}+1)===47){` +
19
+ `let _ae=${v}.length;` +
20
+ `for(let _ai=${start}+2;_ai<${v}.length;_ai++){const _ac=${v}.charCodeAt(_ai);` +
21
+ 'if(_ac===47||_ac===63||_ac===35){_ae=_ai;break}}' +
22
+ `const _au=${v}.slice(${start}+2,_ae);const _aat=_au.lastIndexOf('@');` +
23
+ 'if(_aat!==-1&&/[[\\]]/.test(_au.slice(0,_aat)))return false;' +
24
+ 'const _hp=_aat===-1?_au:_au.slice(_aat+1);' +
25
+ 'if(_hp.charCodeAt(0)===91){const _cl=_hp.indexOf("]");' +
26
+ 'if(_cl===-1)return false;const _rs=_hp.slice(_cl+1);' +
27
+ 'if(_rs!==""){if(_rs.charCodeAt(0)!==58||!/^[0-9]*$/.test(_rs.slice(1)))return false}}' +
28
+ 'else{const _co=_hp.lastIndexOf(":");' +
29
+ 'if(_co!==-1){if(_hp.indexOf(":")!==_co)return false;' +
30
+ 'if(!/^[0-9]*$/.test(_hp.slice(_co+1)))return false}}}';
31
+ const iriCharsSource = (v, from) =>
32
+ `for(let _ii=${from};_ii<${v}.length;_ii++){const _ic=${v}.charCodeAt(_ii);` +
33
+ 'if(_ic===127||(_ic<127&&(_ic<33||_ic===34||_ic===60||_ic===62||_ic===92||_ic===94||_ic===96||_ic===123||_ic===124||_ic===125)))return false;' +
34
+ `if(_ic===37){const _j1=${v}.charCodeAt(_ii+1),_j2=${v}.charCodeAt(_ii+2);` +
35
+ 'if(!((_j1>=48&&_j1<=57)||(_j1>=97&&_j1<=102)||(_j1>=65&&_j1<=70))||' +
36
+ '!((_j2>=48&&_j2<=57)||(_j2>=97&&_j2<=102)||(_j2>=65&&_j2<=70)))return false;_ii+=2}}';
37
+ function iriSource (v, isStr) {
38
+ return guard(v, isStr, `const _n=${v}.length;if(_n===0)return false;` +
39
+ `const _f=${v}.charCodeAt(0);if(!((_f>=97&&_f<=122)||(_f>=65&&_f<=90)))return false;` +
40
+ `let _co=-1;for(let _i=1;_i<_n;_i++){const _c=${v}.charCodeAt(_i);if(_c===58){_co=_i;break}` +
41
+ `if(!((_c>=48&&_c<=57)||(_c>=97&&_c<=122)||(_c>=65&&_c<=90)||_c===43||_c===45||_c===46))return false}` +
42
+ `if(_co===-1)return false;` +
43
+ iriCharsSource(v, '_co+1') +
44
+ uriAuthoritySource(v, '_co+1'));
45
+ }
46
+ function iriReferenceSource (v, isStr) { return guard(v, isStr, iriCharsSource(v, '0')); }
47
+ function idnEmailSource (v, isStr) {
48
+ const inner =
49
+ `const _at=${v}.lastIndexOf('@');` +
50
+ `if(_at<=0||_at===${v}.length-1)return false;` +
51
+ `const _lp=${v}.slice(0,_at),_dm=${v}.slice(_at+1);` +
52
+ `if(_lp.charCodeAt(0)===34){if(_lp.length<2||_lp.charCodeAt(_lp.length-1)!==34||_dm.length===0)return false}` +
53
+ `else{` +
54
+ `if(_lp.charCodeAt(0)===46||_lp.charCodeAt(_lp.length-1)===46)return false;` +
55
+ `if(_lp.indexOf('..')!==-1)return false;` +
56
+ `if(_dm.charCodeAt(0)===91){if(_dm.charCodeAt(_dm.length-1)!==93||_dm.length<=2)return false}` +
57
+ `else{` +
58
+ `if(_dm.charCodeAt(0)===46||_dm.charCodeAt(_dm.length-1)===46)return false;` +
59
+ `if(_dm.indexOf('..')!==-1)return false;` +
60
+ `for(let _di=0;_di<_dm.length;_di++){const _dc=_dm.charCodeAt(_di);` +
61
+ `if(_dc<=32||_dc===127||_dc===64)return false}` +
62
+ `}` +
63
+ `}`;
64
+ return isStr ? `{${inner}}` : `if(typeof ${v}==='string'){${inner}}`;
65
+ }
66
+
67
+ // Source twin of noReserved, same two tiers. `from` is an expression for the
68
+ // first index the loop reads; the variable names stay clear of the ones the
69
+ // other format emitters use.
70
+ function noReservedSource (v, from) {
71
+ return `if(/[^\\u0021-\\u007e]/.test(${v})){` +
72
+ `for(let _ri=${from};_ri<${v}.length;_ri++){const _rc=${v}.charCodeAt(_ri);` +
73
+ 'if(_rc>32&&_rc<127)continue;' +
74
+ 'if(_rc<=32||_rc===127)return false;' +
75
+ 'if(_rc===160||_rc===5760||(_rc>=8192&&_rc<=8202)||_rc===8232||' +
76
+ '_rc===8233||_rc===8239||_rc===8287||_rc===12288||_rc===65279)return false}}';
77
+ }
78
+
79
+ // Source twins. `v` is the expression holding the string; when `isStr` is
80
+ // false the check is wrapped in a typeof guard, matching FORMAT_CODEGEN's
81
+ // convention. Each returns a statement that `return false`s on mismatch.
82
+ function guard (v, isStr, body) { return isStr ? `{${body}}` : `if(typeof ${v}==='string'){${body}}`; }
83
+
84
+ function dateSource (v, isStr) {
85
+ return guard(v, isStr, `if(${v}.length!==10)return false;for(let _i=0;_i<10;_i++){const _c=${v}.charCodeAt(_i);if(_i===4||_i===7){if(_c!==45)return false}else if(_c<48||_c>57)return false}const _m=(${v}.charCodeAt(5)-48)*10+(${v}.charCodeAt(6)-48),_d=(${v}.charCodeAt(8)-48)*10+(${v}.charCodeAt(9)-48);if(_m<1||_m>12||_d<1)return false;const _y=(${v}.charCodeAt(0)-48)*1000+(${v}.charCodeAt(1)-48)*100+(${v}.charCodeAt(2)-48)*10+(${v}.charCodeAt(3)-48);const _dim=_m===2?(((_y%4===0&&_y%100!==0)||_y%400===0)?29:28):((_m===4||_m===6||_m===9||_m===11)?30:31);if(_d>_dim)return false`);
86
+ }
87
+
88
+ function dateTimeSource (v, isStr) {
89
+ return guard(v, isStr, `const _n=${v}.length;if(_n<20)return false;` +
90
+ `if(${v}.charCodeAt(4)!==45||${v}.charCodeAt(7)!==45)return false;` +
91
+ `const _sep=${v}.charCodeAt(10);if(_sep!==84&&_sep!==116)return false;` +
92
+ `if(${v}.charCodeAt(13)!==58||${v}.charCodeAt(16)!==58)return false;` +
93
+ `const _y0=${v}.charCodeAt(0)-48,_y1=${v}.charCodeAt(1)-48,_y2=${v}.charCodeAt(2)-48,_y3=${v}.charCodeAt(3)-48;` +
94
+ 'if((_y0>>>0)>9||(_y1>>>0)>9||(_y2>>>0)>9||(_y3>>>0)>9)return false;' +
95
+ 'const _y=_y0*1000+_y1*100+_y2*10+_y3;' +
96
+ `const _mo0=${v}.charCodeAt(5)-48,_mo1=${v}.charCodeAt(6)-48;` +
97
+ 'if((_mo0>>>0)>9||(_mo1>>>0)>9)return false;const _mo=_mo0*10+_mo1;if(_mo<1||_mo>12)return false;' +
98
+ `const _dm=_mo===2?(((_y%4===0&&_y%100!==0)||_y%400===0)?29:28):(_mo===4||_mo===6||_mo===9||_mo===11?30:31);` +
99
+ `const _d0=${v}.charCodeAt(8)-48,_d1=${v}.charCodeAt(9)-48;` +
100
+ 'if((_d0>>>0)>9||(_d1>>>0)>9)return false;const _d=_d0*10+_d1;if(_d<1||_d>_dm)return false;' +
101
+ `const _h0=${v}.charCodeAt(11)-48,_h1=${v}.charCodeAt(12)-48;` +
102
+ 'if((_h0>>>0)>9||(_h1>>>0)>9||_h0*10+_h1>23)return false;' +
103
+ `const _mi0=${v}.charCodeAt(14)-48,_mi1=${v}.charCodeAt(15)-48;` +
104
+ 'if((_mi0>>>0)>5||(_mi1>>>0)>9)return false;' +
105
+ `const _se0=${v}.charCodeAt(17)-48,_se1=${v}.charCodeAt(18)-48;` +
106
+ 'if((_se0>>>0)>6||(_se1>>>0)>9||_se0*10+_se1>60)return false;' +
107
+ `let _i2=19;if(${v}.charCodeAt(19)===46){_i2=20;const _st=_i2;while(_i2<_n){const _c2=${v}.charCodeAt(_i2);if(_c2<48||_c2>57)break;_i2++}if(_i2===_st)return false}` +
108
+ `const _tz=${v}.charCodeAt(_i2);let _dtoff=0;` +
109
+ `if(_tz===90||_tz===122){if(_i2!==_n-1)return false}` +
110
+ `else{if(_tz!==43&&_tz!==45)return false;if(_n-_i2!==6)return false;` +
111
+ `const _oh=${v}.charCodeAt(_i2+1),_oh2=${v}.charCodeAt(_i2+2),_om=${v}.charCodeAt(_i2+4),_om2=${v}.charCodeAt(_i2+5);` +
112
+ `if(_oh<48||_oh>57||_oh2<48||_oh2>57||_om<48||_om>57||_om2<48||_om2>57)return false;` +
113
+ `if(${v}.charCodeAt(_i2+3)!==58)return false;` +
114
+ `const _ohv=(_oh-48)*10+(_oh2-48),_omv=(_om-48)*10+(_om2-48);` +
115
+ `if(_ohv>23||_omv>59)return false;` +
116
+ `_dtoff=(_tz===43?1:-1)*(_ohv*60+_omv)}` +
117
+ `if(_se0*10+_se1===60){` +
118
+ `const _dtu=(((_h0*10+_h1)*60+(_mi0*10+_mi1)-_dtoff)%1440+1440)%1440;` +
119
+ `if(_dtu!==1439)return false}`);
120
+ }
121
+
122
+ // The scan, over a range of the string given as source expressions. The email
123
+ // check reads its domain half through this, without slicing it out, which was an
124
+ // allocation on every call.
125
+ //
126
+ // A separate function rather than optional parameters on hostnameSource: the code
127
+ // generator calls every entry of its format table as `fn(v, isStr, ctx)`, so a
128
+ // third parameter here would silently receive a context object and emit
129
+ // `_st=[object Object]`. That declines the whole compile, which is how it was
130
+ // found.
131
+ function hostnameRangeSource (v, st, en) {
132
+ return `const _st=${st},_en=${en},_n=_en-_st;if(_n===0||_n>253)return false;let _ll=0,_pv=46;` +
133
+ `for(let _i=_st;_i<_en;_i++){const _c=${v}.charCodeAt(_i);` +
134
+ `if(_c===46){if(_ll===0||_pv===45)return false;_ll=0;_pv=_c;continue}` +
135
+ `const _an=(_c>=48&&_c<=57)||(_c>=97&&_c<=122)||(_c>=65&&_c<=90);` +
136
+ `if(!_an&&_c!==45)return false;` +
137
+ `if(_c===45&&_pv===46)return false;` +
138
+ `if(++_ll>63)return false;_pv=_c}` +
139
+ `if(_ll===0||_pv===45)return false`;
140
+ }
141
+
142
+ function hostnameSource (v, isStr) {
143
+ return guard(v, isStr, hostnameRangeSource(v, '0', `${v}.length`));
144
+ }
145
+
146
+ function uuidSource (v, isStr) {
147
+ const run = (from, to) => `for(let _ui=${from};_ui<${to};_ui++){const _uc=${v}.charCodeAt(_ui);` +
148
+ 'if(!((_uc-48>>>0)<10||((_uc|32)-97>>>0)<6))return false}';
149
+ return guard(v, isStr, `if(${v}.length!==36)return false;` +
150
+ `if(${v}.charCodeAt(8)!==45||${v}.charCodeAt(13)!==45||` +
151
+ `${v}.charCodeAt(18)!==45||${v}.charCodeAt(23)!==45)return false;` +
152
+ run(0, 8) + run(9, 13) + run(14, 18) + run(19, 23) + run(24, 36));
153
+ }
154
+
155
+ function timeSource (v, isStr) {
156
+ return guard(v, isStr, `const _n=${v}.length;if(_n<8)return false;` +
157
+ `const _h1=${v}.charCodeAt(0)-48,_h2=${v}.charCodeAt(1)-48;` +
158
+ 'if((_h1>>>0)>9||(_h2>>>0)>9||_h1*10+_h2>23)return false;' +
159
+ `if(${v}.charCodeAt(2)!==58||${v}.charCodeAt(5)!==58)return false;` +
160
+ `const _m1=${v}.charCodeAt(3)-48,_m2=${v}.charCodeAt(4)-48;` +
161
+ 'if((_m1>>>0)>5||(_m2>>>0)>9)return false;' +
162
+ `const _s1=${v}.charCodeAt(6)-48,_s2=${v}.charCodeAt(7)-48;` +
163
+ 'if((_s1>>>0)>6||(_s2>>>0)>9||_s1*10+_s2>60)return false;' +
164
+ `let _ti=8;if(_ti<_n&&${v}.charCodeAt(_ti)===46){_ti++;const _tf=_ti;` +
165
+ `while(_ti<_n){const _tc=${v}.charCodeAt(_ti)-48;if((_tc>>>0)>9)break;_ti++}` +
166
+ 'if(_ti===_tf)return false}' +
167
+ 'if(_ti===_n)return false;' +
168
+ `const _tz=${v}.charCodeAt(_ti);let _off=0;` +
169
+ 'if(_tz===90||_tz===122){if(_ti!==_n-1)return false}' +
170
+ 'else{if(_tz!==43&&_tz!==45)return false;' +
171
+ `if(_n-_ti!==6||${v}.charCodeAt(_ti+3)!==58)return false;` +
172
+ `if((${v}.charCodeAt(_ti+1)-48>>>0)>9||(${v}.charCodeAt(_ti+2)-48>>>0)>9||` +
173
+ `(${v}.charCodeAt(_ti+4)-48>>>0)>9||(${v}.charCodeAt(_ti+5)-48>>>0)>9)return false;` +
174
+ `const _oh=(${v}.charCodeAt(_ti+1)-48)*10+(${v}.charCodeAt(_ti+2)-48),` +
175
+ `_om=(${v}.charCodeAt(_ti+4)-48)*10+(${v}.charCodeAt(_ti+5)-48);` +
176
+ 'if(_oh>23||_om>59)return false;' +
177
+ '_off=(_tz===43?1:-1)*(_oh*60+_om)}' +
178
+ 'if(_s1*10+_s2===60){' +
179
+ 'const _u=(((_h1*10+_h2)*60+(_m1*10+_m2)-_off)%1440+1440)%1440;' +
180
+ 'if(_u!==1439)return false}');
181
+ }
182
+
183
+ // The same walk as `uri` above, as source, for the code generator to hoist
184
+ // once per compiled function and call. Two copies of one algorithm is what
185
+ // this file has always done for every format, so they sit together here and
186
+ // tests/test_uri_helper_parity.js holds them to the same answer over a corpus
187
+ // built from the walk's own boundaries.
188
+ const URI_HELPER_TABLES = 'const _uct=new Uint8Array(128);for(let _i=33;_i<127;_i++)_uct[_i]=1;' +
189
+ '_uct[34]=_uct[60]=_uct[62]=_uct[92]=_uct[94]=_uct[96]=_uct[123]=_uct[124]=_uct[125]=0;' +
190
+ 'const _uch=new Uint8Array(128);for(let _i=48;_i<58;_i++)_uch[_i]=1;' +
191
+ 'for(let _i=97;_i<103;_i++)_uch[_i]=1;for(let _i=65;_i<71;_i++)_uch[_i]=1;' +
192
+ 'const _ucs=new Uint8Array(128);for(let _i=48;_i<58;_i++)_ucs[_i]=1;' +
193
+ 'for(let _i=97;_i<123;_i++)_ucs[_i]=1;for(let _i=65;_i<91;_i++)_ucs[_i]=1;' +
194
+ '_ucs[43]=1;_ucs[45]=1;_ucs[46]=1;const _ufa=/' + URI_FAST.source + '/';
195
+ const URI_HELPER_BODY = 'if(_ufa.test(_s))return true;const _n=_s.length;if(_n===0)return false;let _c=_s.charCodeAt(0);if(_c>127||_ucs[_c]===0||(_c>=48&&_c<=57)||_c===43||_c===45||_c===46)return false;let _co=-1;for(let _i=1;_i<_n;_i++){_c=_s.charCodeAt(_i);if(_c===58){_co=_i;break}if(_c>127||_ucs[_c]===0)return false}if(_co===-1)return false;let _i=_co+1;let _ae=_n;if(_s.charCodeAt(_i)===47&&_s.charCodeAt(_i+1)===47){const _as=_i+2;let _at=-1,_fc=-1,_lc=-1,_br=0,_ba=0,_hs=_as;_ae=-1;for(_i=_as;_i<_n;_i++){_c=_s.charCodeAt(_i);if(_c>127||_uct[_c]===0)return false;if(_c===47||_c===63||_c===35){_ae=_i;break}if(_c===37){const _h1=_s.charCodeAt(_i+1),_h2=_s.charCodeAt(_i+2);if(!(_h1<=127)||!(_h2<=127)||_uch[_h1]===0||_uch[_h2]===0)return false;_i+=2;continue}if(_c===64){_ba=_br;_at=_i;_fc=-1;_lc=-1;_hs=_i+1}else if(_c===58){if(_fc===-1)_fc=_i;_lc=_i}else if(_c===91||_c===93){_br=1}}if(_ae===-1)_ae=_n;if(_at!==-1&&_ba)return false;if(_s.charCodeAt(_hs)===91){let _cl=-1;for(let _j=_hs+1;_j<_ae;_j++){if(_s.charCodeAt(_j)===93){_cl=_j;break}}if(_cl===-1)return false;if(_cl+1!==_ae){if(_s.charCodeAt(_cl+1)!==58)return false;for(let _j=_cl+2;_j<_ae;_j++){const _d=_s.charCodeAt(_j);if(_d<48||_d>57)return false}}}else if(_lc!==-1){if(_fc!==_lc)return false;for(let _j=_lc+1;_j<_ae;_j++){const _d=_s.charCodeAt(_j);if(_d<48||_d>57)return false}}_i=_ae}for(;_i<_n;_i++){_c=_s.charCodeAt(_i);if(_c>127||_uct[_c]===0)return false;if(_c===37){const _h1=_s.charCodeAt(_i+1),_h2=_s.charCodeAt(_i+2);if(!(_h1<=127)||!(_h2<=127)||_uch[_h1]===0||_uch[_h2]===0)return false;_i+=2}}return true;';
196
+ const uriHelperSource = (name) =>
197
+ URI_HELPER_TABLES + ';function ' + name + '(_s){' + URI_HELPER_BODY + '}';
198
+
199
+ function uriSource (v, isStr) {
200
+ return guard(v, isStr, `const _n=${v}.length;if(_n===0)return false;` +
201
+ `const _f=${v}.charCodeAt(0);if(!((_f>=97&&_f<=122)||(_f>=65&&_f<=90)))return false;` +
202
+ `let _co=-1;for(let _i=1;_i<_n;_i++){const _c=${v}.charCodeAt(_i);if(_c===58){_co=_i;break}` +
203
+ `if(!((_c>=48&&_c<=57)||(_c>=97&&_c<=122)||(_c>=65&&_c<=90)||_c===43||_c===45||_c===46))return false}` +
204
+ `if(_co===-1)return false;` +
205
+ uriCharsSource(v, '_co+1') +
206
+ uriAuthoritySource(v, '_co+1'));
207
+ }
208
+
209
+ function ipv6Source (v, isStr) {
210
+ return guard(v, isStr, `const _n=${v}.length;if(_n<2||_n>45)return false;let _end=_n,_g=0;` +
211
+ `const _dot=${v}.indexOf('.');` +
212
+ `if(_dot!==-1){const _lc=${v}.lastIndexOf(':',_dot);if(_lc===-1)return false;` +
213
+ `{const _f=_lc+1,_t=_n,_ln=_t-_f;if(_ln<7||_ln>15)return false;let _o=0,_val=0,_dg=0;` +
214
+ `for(let _i=_f;_i<=_t;_i++){const _c=_i<_t?${v}.charCodeAt(_i):46;` +
215
+ `if(_c===46){if(_dg===0||_val>255)return false;_o++;_val=0;_dg=0;if(_o>4)return false}` +
216
+ `else if(_c>=48&&_c<=57){if(_dg===1&&_val===0)return false;_val=_val*10+(_c-48);_dg++;if(_dg>3)return false}` +
217
+ `else return false}if(_o!==4)return false}` +
218
+ `_end=_lc+1;_g=2}` +
219
+ `let _cp=false,_dg2=0,_i2=0;` +
220
+ `if(${v}.charCodeAt(0)===58&&${v}.charCodeAt(1)!==58)return false;` +
221
+ `while(_i2<_end){const _c2=${v}.charCodeAt(_i2);` +
222
+ `if(_c2===58){if(_dg2>0){_g++;_dg2=0}` +
223
+ `if(_i2+1<_n&&${v}.charCodeAt(_i2+1)===58){if(_cp)return false;_cp=true;_i2+=2;if(_i2<_n&&${v}.charCodeAt(_i2)===58)return false;continue}` +
224
+ `_i2++;if(_i2===_end&&_end===_n)return false;continue}` +
225
+ `if((_c2>=48&&_c2<=57)||(_c2>=97&&_c2<=102)||(_c2>=65&&_c2<=70)){if(++_dg2>4)return false;_i2++;continue}` +
226
+ `return false}` +
227
+ `if(_dg2>0)_g++;if(_g>8)return false;if(_cp){if(_g>=8)return false}else if(_g!==8)return false`);
228
+ }
229
+
230
+ function ipv4Source (v, isStr) {
231
+ return guard(v, isStr, `const _n=${v}.length;if(_n<7||_n>15)return false;let _o=0,_val=0,_dg=0;for(let _i=0;_i<=_n;_i++){const _c=_i<_n?${v}.charCodeAt(_i):46;if(_c===46){if(_dg===0||_val>255)return false;_o++;_val=0;_dg=0;if(_o>4)return false}else if(_c>=48&&_c<=57){if(_dg===1&&_val===0)return false;_val=_val*10+(_c-48);_dg++;if(_dg>3)return false}else return false}if(_o!==4)return false`);
232
+ }
233
+ function jsonPointerSource(v, isStr) {
234
+ const body = `{let _ok=${v}==='';if(!_ok&&${v}.charCodeAt(0)===47){_ok=true;for(let _i=0;_i<${v}.length;_i++){if(${v}.charCodeAt(_i)!==126)continue;const _n=${v}.charCodeAt(_i+1);if(_n!==48&&_n!==49){_ok=false;break}}}if(!_ok)return false}`;
235
+ return isStr ? body : `if(typeof ${v}==='string')${body}`;
236
+ }
237
+ function relativeJsonPointerSource(v, isStr) {
238
+ const body = `{let _i=0;while(_i<${v}.length){const _c=${v}.charCodeAt(_i);if(_c<48||_c>57)break;_i++}let _ok=_i>0&&!(_i>1&&${v}.charCodeAt(0)===48);if(_ok){const _r=${v}.slice(_i);if(_r!=='#'){_ok=_r==='';if(!_ok&&_r.charCodeAt(0)===47){_ok=true;for(let _j=0;_j<_r.length;_j++){if(_r.charCodeAt(_j)!==126)continue;const _n=_r.charCodeAt(_j+1);if(_n!==48&&_n!==49){_ok=false;break}}}}}if(!_ok)return false}`;
239
+ return isStr ? body : `if(typeof ${v}==='string')${body}`;
240
+ }
241
+ function uriTemplateSource (v, isStr) {
242
+ const inner =
243
+ `let _i=0;` +
244
+ `while(_i<${v}.length){` +
245
+ `const _c=${v}.charCodeAt(_i);` +
246
+ `if(_c===125)return false;` +
247
+ `if(_c!==123&&(_c<=32||_c===127))return false;` +
248
+ `if(_c!==123){_i++;continue}` +
249
+ // `_ute`, not `_e`: this source is spliced into a generated function whose
250
+ // error accumulator is `_e`, and a `const _e` here shadowed it, so
251
+ // `(_e||(_e=[])).push(...)` ran against a number and threw. The verdict was
252
+ // right, so the suite never saw it; only a caller reading `.errors` did.
253
+ `const _ute=${v}.indexOf('}',_i+1);` +
254
+ `if(_ute===-1)return false;` +
255
+ `const _x=${v}.slice(_i+1,_ute);` +
256
+ `if(_x===''||_x.indexOf('{')!==-1)return false;` +
257
+ `if(!${URI_TEMPLATE_EXPR.toString()}.test(_x))return false;` +
258
+ `_i=_ute+1` +
259
+ `}`;
260
+ return isStr ? `{${inner}}` : `if(typeof ${v}==='string'){${inner}}`;
261
+ }
262
+
263
+ // --- RFC 5321 mailbox -------------------------------------------------------
264
+ // Local part is either a quoted string or dot-separated atoms, so a leading,
265
+ // trailing or doubled dot is not a mailbox. Domain is a hostname or a bracketed
266
+ // address literal.
267
+ // Local-part characters: alphanumerics, the RFC 5322 atext punctuation, and
268
+ // the dot that separates atoms. Written as code ranges rather than a regular
269
+ // expression because this lands inside every emitted module that validates an
270
+ // email, and the expression cost more bytes there than the whole check.
271
+ // The emitted form keeps the comparison chain: a standalone module pays for the
272
+ // bytes of a 128-entry table in every file that validates an email, and the
273
+ // runtime pays for the chain on every call. Different budgets, same answers,
274
+ // which `tests/test_format_engine_parity.js` holds.
275
+ // Stated as the complement: of the printable range, only twelve characters are
276
+ // not atext or the dot, so excluding them is 54 source bytes shorter than listing
277
+ // what is allowed, and a standalone module carries this expression. Checked
278
+ // against the allowed-list form over every code point from 0 to 65536 in
279
+ // tests/test_formats_single_pass.js.
280
+ const EMAIL_LOCAL_SRC = (v) => `(${v}>32&&${v}<127&&${v}!==34&&${v}!==40&&${v}!==41&&${v}!==44&&${v}!==58&&${v}!==59&&${v}!==60&&${v}!==62&&${v}!==64&&${v}!==91&&${v}!==92&&${v}!==93)`;
281
+ // Emitted rather than bound as a closure: a standalone module embeds format
282
+ // functions by their source text, so anything reaching a free variable would
283
+ // not survive the trip. The domain half reuses hostnameSource, which keeps
284
+ // one hostname implementation for both halves of this file.
285
+ function emailSource (v, isStr) {
286
+ const inner =
287
+ // One forward pass, and neither half sliced out. '@' is not a local-part
288
+ // character, so in an unquoted address the first one is the separator and the
289
+ // local part is checked on the way to it. A quoted local part may contain
290
+ // '@' and keeps the backward scan, which is the rare shape.
291
+ // The bounds tests are merged into one condition per branch, and there is no
292
+ // separate length check: an empty local part fails `_at<=0` and an empty
293
+ // domain fails `_at===_n0-1`, so a short string is already refused. This
294
+ // lands in every emitted module that validates an email, and 25 bytes of it
295
+ // put the CLI smoke build over its --max-size once.
296
+ `const _n0=${v}.length;let _at;` +
297
+ `if(${v}.charCodeAt(0)===34){` +
298
+ `_at=${v}.lastIndexOf('@');` +
299
+ `if(_at<2||_at===_n0-1||_at>64||${v}.charCodeAt(_at-1)!==34)return false` +
300
+ `}else{` +
301
+ `if(${v}.charCodeAt(0)===46)return false;` +
302
+ `_at=-1;let _pd=false;` +
303
+ `for(let _i=0;_i<_n0;_i++){const _lc=${v}.charCodeAt(_i);if(_lc===64){_at=_i;break}if(!${EMAIL_LOCAL_SRC('_lc')})return false;if(_lc===46){if(_pd)return false;_pd=true}else _pd=false}` +
304
+ `if(_at<=0||_at===_n0-1||_at>64||${v}.charCodeAt(_at-1)===46)return false` +
305
+ `}` +
306
+ // An address literal is rare enough to pay for its own slice inside the
307
+ // branch that needs it; the hostname path, which is every real address,
308
+ // reads the domain in place.
309
+ `if(${v}.charCodeAt(_at+1)===91){` +
310
+ `const _dm=${v}.slice(_at+1);` +
311
+ `if(_dm.charCodeAt(_dm.length-1)!==93||_dm.length<=2)return false;` +
312
+ `const _in=_dm.slice(1,-1);` +
313
+ `if(_in.startsWith('IPv6:')){const _i6=_in.slice(5);${ipv6Source('_i6', true)}}` +
314
+ `else{${ipv4Source('_in', true)}}` +
315
+ `}` +
316
+ `else{${hostnameRangeSource(v, '_at+1', `${v}.length`)}}`;
317
+ return isStr ? `{${inner}}` : `if(typeof ${v}==='string'){${inner}}`;
318
+ }
319
+ function durationSource (v, isStr) {
320
+ const inner = `if(!${DURATION_RE.toString()}.test(${v}))return false`;
321
+ return isStr ? inner : `if(typeof ${v}==='string'&&${inner.slice(3)}`;
322
+ }
323
+ module.exports = { uriHelperSource, uriCharsSource, uriAuthoritySource, iriSource, iriReferenceSource, idnEmailSource, dateSource, ipv4Source, dateTimeSource, ipv6Source, hostnameSource, uriSource, uuidSource, timeSource, noReservedSource, jsonPointerSource, relativeJsonPointerSource, uriTemplateSource, emailSource, durationSource };