ata-validator 1.61.2 → 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.
- package/README.md +159 -37
- package/bin/ata.js +35 -2
- package/compat.js +15 -2
- package/compiled-standard.d.ts +6 -0
- package/compiled-standard.mjs +66 -0
- package/index.d.ts +7 -0
- package/index.js +10 -0
- package/lib/aot-build.js +27 -1
- package/lib/aot-impl.js +108 -17
- package/lib/buffer-gate.js +25 -14
- package/lib/bun-target.js +21 -5
- package/lib/clone-emit.js +115 -10
- package/lib/codegen-engine.js +94 -18
- package/lib/compiled-common.js +11 -4
- package/lib/compiled-standard.js +33 -0
- package/lib/compiled-verdict.js +2 -2
- package/lib/compiled.js +1 -1
- package/lib/data-positions.js +18 -0
- package/lib/defaults.js +101 -23
- package/lib/describe-schema.js +38 -5
- package/lib/diagnose.js +51 -29
- package/lib/enrich-error.js +23 -1
- package/lib/error-sort-rank.js +12 -0
- package/lib/formats-source.js +12 -1
- package/lib/formats.js +19 -19
- package/lib/interpreter-core.js +23 -4
- package/lib/js-compiler-errors.js +70 -72
- package/lib/js-compiler.js +212 -72
- package/lib/json-rejections.js +1 -1
- package/lib/migrate.js +24 -5
- package/lib/native-attach.browser.js +7 -0
- package/lib/native-attach.js +76 -0
- package/lib/native-positions.js +7 -0
- package/lib/plan-compiler.js +7 -1
- package/lib/regex-linear.js +88 -0
- package/lib/rejections.js +13 -3
- package/lib/render-compact.js +5 -5
- package/lib/render-pretty.js +23 -10
- package/lib/safe-regex.js +29 -4
- package/lib/scan-compiler.js +16 -4
- package/lib/schema-diff.js +103 -0
- package/lib/stream.js +293 -0
- package/lib/tier0.js +6 -2
- package/lib/ts-gen.js +25 -12
- package/lib/unique-items.js +11 -3
- package/lib/validator-core.js +61 -66
- package/lib/verify.js +25 -6
- package/lib/version.js +1 -1
- package/package.json +24 -8
- package/stream.d.ts +51 -0
- package/stream.js +3 -0
- package/stream.mjs +3 -0
package/lib/diagnose.js
CHANGED
|
@@ -111,21 +111,53 @@ function frameFor (err, resolved) {
|
|
|
111
111
|
// A caret on the key token when the error names a property, on the value
|
|
112
112
|
// otherwise. Never wider than the line it is drawn under.
|
|
113
113
|
const useKey = named != null && hit.keyOffset !== undefined;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const lineLen = (hit.text || '').length;
|
|
117
|
-
const length = Math.max(1, Math.min(rawLen || 1, Math.max(1, lineLen - col + 1)));
|
|
114
|
+
return frameOf(hit, useKey ? hit : null, resolved.synthesized);
|
|
115
|
+
}
|
|
118
116
|
|
|
117
|
+
// The frame a renderer draws. `col` is the column in the line, for the
|
|
118
|
+
// location; `textCol` is where the caret goes in `text`, which differs when
|
|
119
|
+
// the line was long and `text` is the part of it around the value. Never
|
|
120
|
+
// wider than the text it is drawn under.
|
|
121
|
+
function frameOf (at, key, synthesized) {
|
|
122
|
+
const shift = typeof at.textCol === 'number' && (!key || key.keyLine === at.line) ? at.col - at.textCol : 0;
|
|
123
|
+
const col = key ? key.keyCol : at.col;
|
|
124
|
+
const textCol = Math.max(1, col - shift);
|
|
125
|
+
const rawLen = key ? key.keyLength : at.length;
|
|
126
|
+
const lineLen = termLine(at.text || '').length;
|
|
127
|
+
const length = Math.max(1, Math.min(rawLen || 1, Math.max(1, lineLen - textCol + 1)));
|
|
119
128
|
return {
|
|
120
|
-
line:
|
|
129
|
+
line: key ? key.keyLine : at.line,
|
|
121
130
|
col,
|
|
131
|
+
textCol,
|
|
122
132
|
length,
|
|
123
|
-
text:
|
|
133
|
+
text: termLine(at.text),
|
|
124
134
|
spans: rawLen > length,
|
|
125
|
-
synthesized
|
|
135
|
+
synthesized,
|
|
126
136
|
};
|
|
127
137
|
}
|
|
128
138
|
|
|
139
|
+
// Text from a document or a schema on its way to a terminal: control
|
|
140
|
+
// characters, DEL, the C1 range and the Unicode line separators written as
|
|
141
|
+
// escapes, so a property name cannot start a line, move the cursor or set a
|
|
142
|
+
// colour.
|
|
143
|
+
const TERM_UNSAFE = /[\x00-\x1f\x7f-\x9f\u2028\u2029]/g;
|
|
144
|
+
function term (s) {
|
|
145
|
+
if (typeof s !== 'string') return s;
|
|
146
|
+
return s.replace(TERM_UNSAFE, (c) => {
|
|
147
|
+
if (c === '\n') return '\\n';
|
|
148
|
+
if (c === '\r') return '\\r';
|
|
149
|
+
if (c === '\t') return '\\t';
|
|
150
|
+
return '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0');
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// A source line drawn above a caret: one character for one, a space for a
|
|
154
|
+
// tab or a carriage return and U+FFFD for the rest, so the caret stays under
|
|
155
|
+
// the value.
|
|
156
|
+
function termLine (s) {
|
|
157
|
+
if (typeof s !== 'string') return s;
|
|
158
|
+
return s.replace(TERM_UNSAFE, (c) => (c === '\t' || c === '\r' ? ' ' : '\ufffd'));
|
|
159
|
+
}
|
|
160
|
+
|
|
129
161
|
// No position map in hand, but the error itself may carry one: validateJSON
|
|
130
162
|
// attaches `dataFrame` and `anchor` at enrichment time, and errors handed
|
|
131
163
|
// over from another process or a fixture arrive with only those.
|
|
@@ -135,18 +167,7 @@ function frameFromError (err) {
|
|
|
135
167
|
const a = err.anchor;
|
|
136
168
|
const named = (err.params && (err.params.additionalProperty || err.params.unevaluatedProperty)) || null;
|
|
137
169
|
const useKey = named != null && a && a.keyLine !== undefined;
|
|
138
|
-
|
|
139
|
-
const rawLen = useKey ? a.keyLength : df.length;
|
|
140
|
-
const lineLen = (df.text || '').length;
|
|
141
|
-
const length = Math.max(1, Math.min(rawLen || 1, Math.max(1, lineLen - col + 1)));
|
|
142
|
-
return {
|
|
143
|
-
line: useKey ? a.keyLine : df.line,
|
|
144
|
-
col,
|
|
145
|
-
length,
|
|
146
|
-
text: df.text,
|
|
147
|
-
spans: rawLen > length,
|
|
148
|
-
synthesized: false,
|
|
149
|
-
};
|
|
170
|
+
return frameOf(df, useKey ? a : null, false);
|
|
150
171
|
}
|
|
151
172
|
|
|
152
173
|
function headlineFor (err, src) {
|
|
@@ -210,6 +231,7 @@ function decorateBranches (subs, src) {
|
|
|
210
231
|
const got = valueAt(src.data, sub.instancePath);
|
|
211
232
|
if (got !== null) copy.received = got;
|
|
212
233
|
}
|
|
234
|
+
for (const k of ['keyword', 'message', 'expected', 'received']) if (typeof copy[k] === 'string') copy[k] = term(copy[k]);
|
|
213
235
|
if (Array.isArray(sub.branchErrors)) copy.branchErrors = decorateBranches(sub.branchErrors, src);
|
|
214
236
|
return copy;
|
|
215
237
|
});
|
|
@@ -262,13 +284,13 @@ function singleDiagnostic (e, index, resolved, src) {
|
|
|
262
284
|
|
|
263
285
|
return {
|
|
264
286
|
code: e.code,
|
|
265
|
-
headline: headlineFor(e, src),
|
|
287
|
+
headline: term(headlineFor(e, src)),
|
|
266
288
|
pointer,
|
|
267
|
-
dotted: pathToDotted(anchorErr.instancePath != null ? anchorErr.instancePath : pointer),
|
|
289
|
+
dotted: term(pathToDotted(anchorErr.instancePath != null ? anchorErr.instancePath : pointer)),
|
|
268
290
|
frame,
|
|
269
|
-
found,
|
|
270
|
-
help: e.suggestion ? e.suggestion.text : null,
|
|
271
|
-
notes,
|
|
291
|
+
found: term(found),
|
|
292
|
+
help: e.suggestion ? term(e.suggestion.text) : null,
|
|
293
|
+
notes: notes.map(term),
|
|
272
294
|
branchErrors: e.branchErrors ? decorateBranches(e.branchErrors, src) : null,
|
|
273
295
|
mergedFrom: [e],
|
|
274
296
|
rank: typeof e.rank === 'number' ? e.rank : 2,
|
|
@@ -295,13 +317,13 @@ function mergedDiagnostic (errors, i, j, resolved, src) {
|
|
|
295
317
|
const pointer = missing.instancePath != null ? missing.instancePath : (missing.path || '');
|
|
296
318
|
return {
|
|
297
319
|
code: missing.code,
|
|
298
|
-
headline: `unknown property "${extraName}"
|
|
320
|
+
headline: term(`unknown property "${extraName}"`),
|
|
299
321
|
pointer,
|
|
300
|
-
dotted: pathToDotted(pointer),
|
|
322
|
+
dotted: term(pathToDotted(pointer)),
|
|
301
323
|
frame,
|
|
302
324
|
found: null,
|
|
303
|
-
help: `did you mean "${missingName}"
|
|
304
|
-
notes,
|
|
325
|
+
help: term(`did you mean "${missingName}"?`),
|
|
326
|
+
notes: notes.map(term),
|
|
305
327
|
branchErrors: null,
|
|
306
328
|
mergedFrom: [missing, extra],
|
|
307
329
|
rank: 0,
|
|
@@ -309,4 +331,4 @@ function mergedDiagnostic (errors, i, j, resolved, src) {
|
|
|
309
331
|
};
|
|
310
332
|
}
|
|
311
333
|
|
|
312
|
-
module.exports = { toDiagnostics };
|
|
334
|
+
module.exports = { term, termLine, toDiagnostics };
|
package/lib/enrich-error.js
CHANGED
|
@@ -87,6 +87,15 @@ function reprValue (v) {
|
|
|
87
87
|
// fallback prints, so it is taken once either way; the walk and the
|
|
88
88
|
// serialisation used to run on every container an error named, and a
|
|
89
89
|
// `required` or `unevaluatedProperties` error names its container.
|
|
90
|
+
// A wide object is counted once per synchronous run: every error that
|
|
91
|
+
// names the same container (additionalProperties: false on an object
|
|
92
|
+
// with N unknown keys gives N of them) counted its keys again, N times N
|
|
93
|
+
// key reads, and 16000 extra keys took 11.8 s to read. The answer is
|
|
94
|
+
// kept until the running synchronous code finishes (a microtask drops
|
|
95
|
+
// it), which covers reading one list. The limit: an object changed and
|
|
96
|
+
// read again within that same run reports the count it had first.
|
|
97
|
+
const known = _wide !== null ? _wide.get(v) : undefined;
|
|
98
|
+
if (known !== undefined) return known;
|
|
90
99
|
let n;
|
|
91
100
|
try {
|
|
92
101
|
n = Object.keys(v).length;
|
|
@@ -101,10 +110,21 @@ function reprValue (v) {
|
|
|
101
110
|
return '[object, unserializable]';
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
|
-
|
|
113
|
+
const r = `[object, ${n} ${n === 1 ? 'key' : 'keys'}]`;
|
|
114
|
+
if (n > WIDE) {
|
|
115
|
+
if (_wide === null) {
|
|
116
|
+
_wide = new Map();
|
|
117
|
+
queueMicrotask(() => { _wide = null; });
|
|
118
|
+
}
|
|
119
|
+
_wide.set(v, r);
|
|
120
|
+
}
|
|
121
|
+
return r;
|
|
105
122
|
}
|
|
106
123
|
return `[${t}]`;
|
|
107
124
|
}
|
|
125
|
+
// Past this many keys a count is worth keeping (see reprValue).
|
|
126
|
+
const WIDE = 64;
|
|
127
|
+
let _wide = null;
|
|
108
128
|
|
|
109
129
|
function expectedFor (err) {
|
|
110
130
|
switch (err.keyword) {
|
|
@@ -298,6 +318,7 @@ function enrich (rawErr, opts) {
|
|
|
298
318
|
if (positions && positions[path]) {
|
|
299
319
|
const p = positions[path];
|
|
300
320
|
out.dataFrame = { byteOffset: p.byteOffset, length: p.length, line: p.line, col: p.col, text: p.text };
|
|
321
|
+
if (p.textCol !== undefined) out.dataFrame.textCol = p.textCol;
|
|
301
322
|
} else if (!positions && opts && opts.lazyPositions) {
|
|
302
323
|
defineLazyFrames(out, path, named, opts.lazyPositions);
|
|
303
324
|
}
|
|
@@ -360,6 +381,7 @@ function defineLazyFrames (out, path, named, lazy) {
|
|
|
360
381
|
const positions = lazy();
|
|
361
382
|
const own = positions ? positions[path] : null;
|
|
362
383
|
const dataFrame = own ? { byteOffset: own.byteOffset, length: own.length, line: own.line, col: own.col, text: own.text } : undefined;
|
|
384
|
+
if (dataFrame !== undefined && own.textCol !== undefined) dataFrame.textCol = own.textCol;
|
|
363
385
|
const child = positions && named ? positions[(path === '' ? '' : path) + '/' + named] : null;
|
|
364
386
|
const src = child || own;
|
|
365
387
|
let anchor;
|
package/lib/error-sort-rank.js
CHANGED
|
@@ -46,6 +46,18 @@ function sortErrorsByRank(rootSchema, errors) {
|
|
|
46
46
|
prev = r;
|
|
47
47
|
}
|
|
48
48
|
if (sorted) return errors;
|
|
49
|
+
// Insertion sort is quadratic: a document with 20000 unknown keys under
|
|
50
|
+
// additionalProperties: false, reported out of order, took seconds here.
|
|
51
|
+
// Past a short list the platform's sort, which is stable, gives the same
|
|
52
|
+
// order, ties kept as they came.
|
|
53
|
+
if (n > 16) {
|
|
54
|
+
const idx = new Array(n);
|
|
55
|
+
for (let i = 0; i < n; i++) idx[i] = i;
|
|
56
|
+
idx.sort((a, b) => compareRanks(keys[a], keys[b]) || a - b);
|
|
57
|
+
const res = new Array(n);
|
|
58
|
+
for (let i = 0; i < n; i++) res[i] = errors[idx[i]];
|
|
59
|
+
return res;
|
|
60
|
+
}
|
|
49
61
|
const res = errors.slice();
|
|
50
62
|
for (let i = 1; i < n; i++) {
|
|
51
63
|
const k = keys[i];
|
package/lib/formats-source.js
CHANGED
|
@@ -111,6 +111,17 @@ function hostnameSource (v, isStr) {
|
|
|
111
111
|
return guard(v, isStr, hostnameRangeSource(v, '0', `${v}.length`));
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// The hoisted form: two tables built once per compiled function, a character
|
|
115
|
+
// class (1 hex digit, 2 hyphen) and the class each of the 36 positions wants,
|
|
116
|
+
// and a loop that ORs the mismatches instead of branching on every character.
|
|
117
|
+
// On the 900 ids of the order body: 85 to 35 ns against the per-run loops below.
|
|
118
|
+
const uuidHelperSource = (name) =>
|
|
119
|
+
`const ${name}H=new Uint8Array(128),${name}M=new Uint8Array(36).fill(1);` +
|
|
120
|
+
`for(let _i=48;_i<58;_i++)${name}H[_i]=1;for(let _i=97;_i<103;_i++)${name}H[_i]=1;for(let _i=65;_i<71;_i++)${name}H[_i]=1;` +
|
|
121
|
+
`${name}H[45]=2;${name}M[8]=${name}M[13]=${name}M[18]=${name}M[23]=2;` +
|
|
122
|
+
`function ${name}(_s){if(_s.length!==36)return false;let _b=0;for(let _i=0;_i<36;_i++)_b|=${name}H[_s.charCodeAt(_i)]^${name}M[_i];return _b===0}`
|
|
123
|
+
|
|
124
|
+
// The inline form, for a caller with no function to hoist into.
|
|
114
125
|
function uuidSource (v, isStr) {
|
|
115
126
|
const run = (from, to) => `for(let _ui=${from};_ui<${to};_ui++){const _uc=${v}.charCodeAt(_ui);` +
|
|
116
127
|
'if(!((_uc-48>>>0)<10||((_uc|32)-97>>>0)<6))return false}';
|
|
@@ -321,4 +332,4 @@ function durationSource (v, isStr) {
|
|
|
321
332
|
const inner = `if(!${_formats().DURATION_RE.toString()}.test(${v}))return false`;
|
|
322
333
|
return isStr ? inner : `if(typeof ${v}==='string'&&${inner.slice(3)}`;
|
|
323
334
|
}
|
|
324
|
-
module.exports = { _literalByParsers, uriHelperSource, uriHelperCompactSource, uriSource, uriReferenceSource, iriSource, iriReferenceSource, idnEmailSource, dateSource, ipv4Source, dateTimeSource, ipv6Source, hostnameSource, uuidSource, timeSource, noReservedSource, jsonPointerSource, relativeJsonPointerSource, uriTemplateSource, emailSource, durationSource };
|
|
335
|
+
module.exports = { _literalByParsers, uriHelperSource, uriHelperCompactSource, uriSource, uriReferenceSource, iriSource, iriReferenceSource, idnEmailSource, dateSource, ipv4Source, dateTimeSource, ipv6Source, hostnameSource, uuidSource, uuidHelperSource, timeSource, noReservedSource, jsonPointerSource, relativeJsonPointerSource, uriTemplateSource, emailSource, durationSource };
|
package/lib/formats.js
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
// and no allocation. Each has a twin that emits the same check as source for
|
|
5
5
|
// the code generator; tests/test_formats_single_pass.js holds the two
|
|
6
6
|
// together and fuzzes both against the regular-expression forms they replaced.
|
|
7
|
-
// Measured, interleaved medians: date 45.7 to 14.2 ns, ipv4 54.5 to 27.1 ns
|
|
8
|
-
// uuid
|
|
9
|
-
// expression for a fixed-length hex pattern is already that fast, so it stays.
|
|
7
|
+
// Measured, interleaved medians: date 45.7 to 14.2 ns, ipv4 54.5 to 27.1 ns,
|
|
8
|
+
// uuid 85 to 35 ns with the class tables below.
|
|
10
9
|
|
|
11
10
|
function isDigit (c) { return c >= 48 && c <= 57; }
|
|
12
11
|
|
|
@@ -328,25 +327,26 @@ function noReserved (s, from) {
|
|
|
328
327
|
return true;
|
|
329
328
|
}
|
|
330
329
|
|
|
331
|
-
// 8-4-4-4-12 hexadecimal digits.
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
330
|
+
// 8-4-4-4-12 hexadecimal digits. Two tables: the class of a character (1 a
|
|
331
|
+
// hex digit in either case, 2 a hyphen, 0 anything else, including anything
|
|
332
|
+
// past ASCII, which indexes past the table and reads as undefined) and the
|
|
333
|
+
// class each of the 36 positions wants. The loop ORs the mismatches and
|
|
334
|
+
// decides once at the end, so no character costs a branch: 85 to 35 ns on
|
|
335
|
+
// the order body's ids against the earlier per-run loops with two compares a
|
|
336
|
+
// character.
|
|
337
|
+
const UUID_CLASS = new Uint8Array(128);
|
|
338
|
+
for (let i = 48; i < 58; i++) UUID_CLASS[i] = 1;
|
|
339
|
+
for (let i = 97; i < 103; i++) UUID_CLASS[i] = 1;
|
|
340
|
+
for (let i = 65; i < 71; i++) UUID_CLASS[i] = 1;
|
|
341
|
+
UUID_CLASS[45] = 2;
|
|
342
|
+
const UUID_WANT = new Uint8Array(36).fill(1);
|
|
343
|
+
UUID_WANT[8] = UUID_WANT[13] = UUID_WANT[18] = UUID_WANT[23] = 2;
|
|
343
344
|
|
|
344
345
|
function uuid (s) {
|
|
345
346
|
if (s.length !== 36) return false;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
return
|
|
349
|
-
hexRun(s, 19, 23) && hexRun(s, 24, 36);
|
|
347
|
+
let bad = 0;
|
|
348
|
+
for (let i = 0; i < 36; i++) bad |= UUID_CLASS[s.charCodeAt(i)] ^ UUID_WANT[i];
|
|
349
|
+
return bad === 0;
|
|
350
350
|
}
|
|
351
351
|
|
|
352
352
|
// RFC 3339 full-time: HH:MM:SS, an optional fraction, an optional offset.
|
package/lib/interpreter-core.js
CHANGED
|
@@ -429,9 +429,15 @@ class Plan {
|
|
|
429
429
|
this.format = schema.format;
|
|
430
430
|
this.formatFn = null;
|
|
431
431
|
if (schema.format !== undefined) {
|
|
432
|
+
// Own properties only, as the generated code reads them: an inherited
|
|
433
|
+
// Object member ('hasOwnProperty', 'constructor') is not a check.
|
|
434
|
+
const own = Object.prototype.hasOwnProperty;
|
|
432
435
|
const uf = interp.userFormats;
|
|
433
|
-
const
|
|
434
|
-
|
|
436
|
+
const f = schema.format;
|
|
437
|
+
const table = require('./formats').formatChecks();
|
|
438
|
+
const fc = (uf && typeof f === 'string' && own.call(uf, f) && typeof uf[f] === 'function') ? uf[f]
|
|
439
|
+
: (typeof f === 'string' && own.call(table, f) && typeof table[f] === 'function' ? table[f] : null);
|
|
440
|
+
this.formatFn = fc;
|
|
435
441
|
}
|
|
436
442
|
this.hasString = this.minLength !== undefined || this.maxLength !== undefined || this.pattern !== null || this.formatFn !== null;
|
|
437
443
|
// A format compat registered for numbers (`type: 'number'`), checked on
|
|
@@ -917,14 +923,27 @@ class Interpreter {
|
|
|
917
923
|
if (!re) {
|
|
918
924
|
if (/\\[pP]\{/.test(src)) {
|
|
919
925
|
// Unicode property escapes need a real RegExp in unicode mode; the
|
|
920
|
-
// linear engine does not understand them
|
|
926
|
+
// linear engine does not understand them (compileSafe refuses them),
|
|
927
|
+
// so the RegExp answers whether or not it runs this pattern in linear
|
|
928
|
+
// time. Asking lib/regex-linear.js would change no answer and would
|
|
929
|
+
// add it to lite. A nested loop around a property escape can
|
|
930
|
+
// backtrack here; SECURITY.md says so.
|
|
921
931
|
try { re = new RegExp(src, 'u'); } catch { re = new RegExp(src); }
|
|
922
932
|
} else if (plainLinear(src)) {
|
|
923
933
|
re = new RegExp(src);
|
|
924
934
|
} else {
|
|
925
935
|
try {
|
|
926
936
|
re = compileSafe(src);
|
|
927
|
-
} catch {
|
|
937
|
+
} catch (e) {
|
|
938
|
+
// A parse failure means the engine cannot represent the pattern
|
|
939
|
+
// (a backreference, a lookaround, an escape it does not implement,
|
|
940
|
+
// a repeat past its instruction budget) and the platform RegExp
|
|
941
|
+
// takes it.
|
|
942
|
+
// The stub of a shared error runtime built without the engine
|
|
943
|
+
// (lib/error-runtime.js) throws too, and that one is rethrown: a
|
|
944
|
+
// module whose runtime lacks the engine must fail, not quietly
|
|
945
|
+
// match on a RegExp that can backtrack.
|
|
946
|
+
if (e && typeof e.message === 'string' && e.message.includes('built without the pattern engine')) throw e;
|
|
928
947
|
try { re = new RegExp(src, 'u'); } catch { re = new RegExp(src); }
|
|
929
948
|
}
|
|
930
949
|
}
|