ata-validator 1.38.0 → 1.39.2

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.
@@ -156,6 +156,14 @@ function ordinalFor(rootSchema, schemaPath) {
156
156
  const map = ordinals(rootSchema);
157
157
  const hit = map.get(schemaPath);
158
158
  if (hit !== undefined) return hit;
159
+ // A path that is not a node is answered from the cache below, filled by the
160
+ // walk after it: every error behind a `$ref`, which is every error of a
161
+ // schema the interpreter answers through references, used to walk its path
162
+ // back segment by segment on every read.
163
+ let misses = _missCache.get(rootSchema);
164
+ if (misses === undefined) { misses = new Map(); _missCache.set(rootSchema, misses); }
165
+ const known = misses.get(schemaPath);
166
+ if (known !== undefined) return known;
159
167
  // Walk back to the longest prefix that exists. Paths through a keyword's
160
168
  // value that is a primitive, or through a key the schema does not have,
161
169
  // land on the nearest enclosing node.
@@ -163,16 +171,24 @@ function ordinalFor(rootSchema, schemaPath) {
163
171
  // behind a reference sort where the `$ref` is declared among its siblings;
164
172
  // rankFor orders them among themselves.
165
173
  let p = schemaPath;
174
+ let found = 0;
166
175
  while (true) {
167
176
  const cut = p.lastIndexOf('/');
168
- if (cut < 0) return 0;
177
+ if (cut < 0) break;
169
178
  p = p.slice(0, cut);
170
179
  const o = map.get(p);
171
180
  if (o !== undefined) {
172
181
  const r = map.get(p + '/$ref');
173
- return r !== undefined ? r : o;
182
+ found = r !== undefined ? r : o;
183
+ break;
174
184
  }
175
185
  }
186
+ // Bounded: behind a recursive reference the paths grow with the document's
187
+ // depth, and the cache must not grow with them.
188
+ if (misses.size < MISS_CACHE_LIMIT) misses.set(schemaPath, found);
189
+ return found;
176
190
  }
191
+ const _missCache = new WeakMap();
192
+ const MISS_CACHE_LIMIT = 4096;
177
193
 
178
194
  module.exports = { rankFor, ordinalFor, unescapePointerSegment };
@@ -292,7 +292,7 @@ function resolveSchemaByPath(rootSchema, schemaPath) {
292
292
  //
293
293
  // A failing route sees the same handful of schemaPaths over and over, which is
294
294
  // what makes the first one worth having.
295
- const { LazyRejection, RichRejection, LazyJsonRejection, _enrichLazy } = require('./rejections');
295
+ const { LazyRejection, RichRejection, LazyJsonRejection, _enrichLazy, presentErrors, needsOrdering, sortErrorsBySchemaOrder } = require('./rejections');
296
296
 
297
297
  // Text that does not parse, reported the same way on every path and platform:
298
298
  // ATA9001 with the parser's own message. The native addon used to say only
@@ -991,20 +991,28 @@ class Validator {
991
991
  };
992
992
  const _buildCombined = () => {
993
993
  if (jsCombinedFn !== undefined) return;
994
- jsCombinedFn = compileToJSCombined(schemaObj, VALID_RESULT, sm, this._userFormats) || null;
994
+ jsCombinedFn = compileToJSCombined(schemaObj, VALID_RESULT, sm, this._userFormats, { runtimeShape: true }) || null;
995
995
  _upgradeCacheEntry();
996
996
  };
997
997
  const _buildErr = () => {
998
998
  if (jsErrFn !== undefined) return;
999
- jsErrFn = compileToJSCodegenWithErrors(schemaObj, sm, this._userFormats) || null;
999
+ jsErrFn = compileToJSCodegenWithErrors(schemaObj, sm, this._userFormats, { runtimeShape: true }) || null;
1000
1000
  _upgradeCacheEntry();
1001
1001
  };
1002
1002
 
1003
+ // What validate() returns for a document already known to fail, without
1004
+ // deciding again, when the codegen paths can say (see index.js). Kept in
1005
+ // locals and on this object rather than on the instance: every call reads
1006
+ // `validate` off the instance, and three more own properties made that
1007
+ // read slower across the suite's validators.
1008
+ let _rejectBase = null, _rejectPresented = null, _rejectPlain = false;
1003
1009
  if (jsFn) {
1004
- _codegen.installPaths.call(this, {
1010
+ const _cg = {
1005
1011
  jsFn, _isCodegen, preprocess, fusedRemove, options, schemaObj, useSimdjsonForLarge, _buildCombined, _buildErr,
1006
- combined: () => jsCombinedFn, err: () => jsErrFn,
1007
- });
1012
+ combined: () => jsCombinedFn, err: () => jsErrFn, rejectBase: null,
1013
+ };
1014
+ _codegen.installPaths.call(this, _cg);
1015
+ _rejectBase = _cg.rejectBase;
1008
1016
  } else {
1009
1017
  // No JS codegen: the interpreted engine, with or without the addon. The
1010
1018
  // buffer APIs come from _installBufferApis on first use, as they do on
@@ -1030,7 +1038,9 @@ class Validator {
1030
1038
  : (preprocess
1031
1039
  ? (data) => { preprocess(data); return interp.validate(data); }
1032
1040
  : (data) => interp.validate(data));
1033
- this.validate = run;
1041
+ this.validate = this._verbose ? _verboseWrap(run, schemaObj) : run;
1042
+ // Reading errors runs the interpreter once, directly; see _rejectBase.
1043
+ if (!preprocess && !options.abortEarly && !this._verbose) _rejectBase = (data) => interp.validate(data);
1034
1044
  _bindVerdict(this, this._fastVerdict
1035
1045
  ? this._fastVerdict
1036
1046
  : (data) => run(data).valid);
@@ -1059,8 +1069,7 @@ class Validator {
1059
1069
  const enrich = this._richErrors ? _enrichLazy : null;
1060
1070
  const root = this._schemaObj;
1061
1071
  const self = this;
1062
- this.validate = (data) => {
1063
- const result = inner(data);
1072
+ const present = (result, data) => {
1064
1073
  // abortEarly returns the shared ATA9000 stub; preserve it as-is so the
1065
1074
  // perf fast path stays allocation-free and the documented code stays stable.
1066
1075
  if (result && result.valid === false && result !== ABORT_EARLY_RESULT) {
@@ -1076,6 +1085,13 @@ class Validator {
1076
1085
  }
1077
1086
  return result;
1078
1087
  };
1088
+ this.validate = (data) => present(inner(data), data);
1089
+ // The same presentation for a document already known to fail.
1090
+ const rejectBase = _rejectBase;
1091
+ _rejectPresented = rejectBase ? (data) => present(rejectBase(data), data) : null;
1092
+ // True while nothing but this layer stands between a rejection and the
1093
+ // caller; the lazy layer below can then build the rejection itself.
1094
+ _rejectPlain = !!rejectBase;
1079
1095
 
1080
1096
  // validateJSON also enriches: set _lastRawInput so the position cache
1081
1097
  // can lazily build a map for dataFrame attachment. Only validateJSON
@@ -1149,6 +1165,8 @@ class Validator {
1149
1165
  return result;
1150
1166
  };
1151
1167
  if (this.validate) this.validate = wrap(this.validate);
1168
+ if (_rejectPresented) _rejectPresented = wrap(_rejectPresented);
1169
+ _rejectPlain = false;
1152
1170
  if (this.validateJSON) this.validateJSON = wrap(this.validateJSON);
1153
1171
  // validateAndParse routes through self.validate on the codegen path, but
1154
1172
  // the native-only path returns directly from the addon — wrap it so both
@@ -1185,7 +1203,13 @@ class Validator {
1185
1203
  const _vx = this._validateTail !== null ? this._validateTail() : null;
1186
1204
  let _vxApplied = false;
1187
1205
  if (this._fastVerdict && !preprocess && !options.abortEarly && this.validate) {
1188
- const _full = this.validate;
1206
+ // The errors of a document the verdict rejected come from the rejection
1207
+ // path directly, not from validate() deciding a second time. Not with a
1208
+ // check from _extendValidate: that check can reject what the schema
1209
+ // accepts, and then the schema's own errors must come from a verdict.
1210
+ // Not with removeAdditional's fused pass either, which edits the
1211
+ // document before the errors are read.
1212
+ const _full = (_vx === null && !fusedRemove && _rejectPresented) ? _rejectPresented : this.validate;
1189
1213
  const _fast = _vx ? _fuseTail(this._fastVerdict, _vx.check) : this._fastVerdict;
1190
1214
  const _extra = _vx ? _vx.errors : null;
1191
1215
  _vxApplied = true;
@@ -1198,11 +1222,19 @@ class Validator {
1198
1222
  // verdict and say so rather than inventing a specific error.
1199
1223
  return own || _verdictFallback;
1200
1224
  };
1201
- const _buildErrors = (data) => {
1225
+ // The common case: the verdict decides, and reading `errors` runs the
1226
+ // error function and presents its list directly. Through the layers it
1227
+ // took a second verdict and a second rejection object, about 75 ns of a
1228
+ // 96 ns read on a two-error document. validate() below is the same code
1229
+ // either way, so reading only `.valid` costs what it did.
1230
+ const _plain = (_vx === null && !fusedRemove && _rejectPlain)
1231
+ ? _plainBuilders(_rejectBase, this._richErrors ? _enrichLazy : null, this._schemaObj, this, _verdictFallback)
1232
+ : null;
1233
+ const _buildErrors = _plain !== null ? _plain.errors : (data) => {
1202
1234
  const r = _full(data);
1203
1235
  return _withExtra((r && r.valid === false && r.errors && r.errors.length) ? r.errors : null, data);
1204
1236
  };
1205
- const _buildRawErrors = (data) => {
1237
+ const _buildRawErrors = _plain !== null ? _plain.raw : (data) => {
1206
1238
  const r = _full(data);
1207
1239
  let raw = null;
1208
1240
  if (r && r.valid === false) {
@@ -1700,6 +1732,60 @@ const _VERDICT_DISAGREES = Object.freeze({
1700
1732
  valid: false,
1701
1733
  errors: Object.freeze([Object.freeze({ keyword: 'validation', instancePath: '', schemaPath: '#', params: Object.freeze({}), message: 'schema validation failed' })]),
1702
1734
  });
1735
+ // The error builders of the common rejection path (see _ensureCompiled): run
1736
+ // the error function behind `reject` for a document the verdict rejected, and
1737
+ // order and present its list. Out here so the closures capture only this.
1738
+ function _plainBuilders(reject, enrich, root, self, fallback) {
1739
+ const rawOf = (data) => {
1740
+ const r = reject(data);
1741
+ const errors = r && r.errors;
1742
+ return errors && errors.length ? errors : fallback;
1743
+ };
1744
+ return {
1745
+ errors: (data) => presentErrors(rawOf(data), data, null, self, root, enrich),
1746
+ raw: (data) => {
1747
+ const raw = rawOf(data);
1748
+ return needsOrdering(raw) ? sortErrorsBySchemaOrder(root, raw) : raw;
1749
+ },
1750
+ };
1751
+ }
1752
+
1753
+ // Verbose mode: populate parentSchema, schema and data on each error, the
1754
+ // three fields the default error shape carries under the same option. `data`
1755
+ // is the value the error points at; without it a caller has to walk the
1756
+ // document by the instance path itself, which is what one migration ended up
1757
+ // writing by hand. Errors may be frozen, so they are cloned with the extra
1758
+ // fields. Both engines go through this; the interpreted one used to skip it,
1759
+ // so the same validator reported different fields depending on which engine
1760
+ // answered.
1761
+ function _verboseWrap(inner, root) {
1762
+ const { resolvePointer } = require('./pointer.js');
1763
+ return (data) => {
1764
+ const result = inner(data);
1765
+ if (result && !result.valid && result.errors) {
1766
+ const enriched = result.errors.map((err) => {
1767
+ if (!err || err.parentSchema !== undefined) return err;
1768
+ const parentSchema = resolveSchemaByPath(root, err.schemaPath);
1769
+ // The last segment of the schema path is the keyword that
1770
+ // failed, so its value on the parent is that keyword's schema.
1771
+ const sp = typeof err.schemaPath === 'string' ? err.schemaPath : '';
1772
+ const last = sp.slice(sp.lastIndexOf('/') + 1).replace(/~1/g, '/').replace(/~0/g, '~');
1773
+ const keywordSchema = (parentSchema !== null && typeof parentSchema === 'object' && last)
1774
+ ? parentSchema[last]
1775
+ : undefined;
1776
+ return {
1777
+ ...err,
1778
+ parentSchema,
1779
+ schema: keywordSchema,
1780
+ data: resolvePointer(data, err.instancePath, undefined),
1781
+ };
1782
+ });
1783
+ return { valid: false, errors: enriched };
1784
+ }
1785
+ return result;
1786
+ };
1787
+ }
1788
+
1703
1789
  function _mustReject(r) {
1704
1790
  return r && r.valid === false ? r : _VERDICT_DISAGREES;
1705
1791
  }
@@ -2063,4 +2149,4 @@ module.exports = {
2063
2149
  // surface of `require('ata-validator')` is what it was.
2064
2150
  Object.defineProperty(module.exports, '_registerCodegen', { value: _registerCodegen, enumerable: false });
2065
2151
  // What the compiled paths in index.js need from this module.
2066
- Object.defineProperty(module.exports, '_internals', { value: { _jsonSyntaxRejection, ABORT_EARLY_RESULT, HYBRID_TIER_CALLS, SIMDJSON_THRESHOLD, VALID_RESULT, _bindEntry, _bindVerdict, _compileCache, _mustReject, _rememberInstance, compileCacheKey, getNative, isV1Dialect, resolveSchemaByPath }, enumerable: false });
2152
+ Object.defineProperty(module.exports, '_internals', { value: { _jsonSyntaxRejection, ABORT_EARLY_RESULT, HYBRID_TIER_CALLS, SIMDJSON_THRESHOLD, VALID_RESULT, _bindEntry, _bindVerdict, _compileCache, _mustReject, _rememberInstance, _verboseWrap, compileCacheKey, getNative, isV1Dialect, resolveSchemaByPath }, enumerable: false });
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.38.0';
10
+ module.exports = '1.39.2';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "1.38.0",
3
+ "version": "1.39.2",
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
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -55,11 +55,11 @@
55
55
  "./lib/buffer-gate.js": "./lib/buffer-gate.browser.js"
56
56
  },
57
57
  "scripts": {
58
- "prepublishOnly": "node scripts/regen-safe-regex-source.js && node tests/test_pack_purity.js && ATA_ALLOW_DOC_PLACEHOLDERS=1 node scripts/check-doc-coverage.js && node tests/test_error_codes_lock.js && node tests/test_safe_regex_source_sync.js && node tests/test_version_sync.js",
59
- "release:check": "node scripts/regen-safe-regex-source.js && node tests/test_pack_purity.js && node scripts/check-doc-coverage.js && node tests/test_error_codes_lock.js && node tests/test_safe_regex_source_sync.js && node tests/test_version_sync.js",
58
+ "prepublishOnly": "node tests/test_pack_purity.js && ATA_ALLOW_DOC_PLACEHOLDERS=1 node scripts/check-doc-coverage.js && node tests/test_error_codes_lock.js && node tests/test_safe_regex_embed.js && node tests/test_version_sync.js",
59
+ "release:check": "node tests/test_pack_purity.js && node scripts/check-doc-coverage.js && node tests/test_error_codes_lock.js && node tests/test_safe_regex_embed.js && node tests/test_version_sync.js",
60
60
  "build": "cmake-js build --target ata",
61
61
  "rebuild": "cmake-js rebuild --target ata",
62
- "test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_text_path_errors.js && node tests/test_compiled_parity.js && node tests/test_buffer_gate.js && node tests/test_scanner_differential.js && node tests/test_validate_json_scanner_cost.js && node tests/test_buffer_reject_cost.js && node tests/test_nan_verdict.js && node tests/test_remove_additional_nested.js && node tests/test_exclusive_bounds.js && node tests/test_engine_differential.js && node tests/test_error_shape_differential.js && node tests/test_output_format.js && node tests/test_standard_json_schema.js && node tests/test_retry_message.js && node tests/test_describe_schema.js && node tests/test_aot_parse.js && node tests/test_draft7_semantics.js && node tests/test_metaschema_ref.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_esm_exports.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_native_lazy.js && node tests/test_buffer_apis_without_native.js && node tests/test_cold_start_modules.js && node tests/test_safe_regex_source_sync.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_regex_ecma_whitespace.js && node tests/test_regex_native_linear.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_format_mode.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_engine_option.js && node tests/test_compile_cache_order.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_error_accumulator_collision.js && node tests/test_uri_helper_parity.js && node tests/test_uri_fast_path.js && node tests/test_extend_verdict.js && node tests/test_ipv4_regex.js && node tests/test_ipv6_fast_path.js && node tests/test_nested_recursion_decline.js && node tests/test_generator_error_content.js && node tests/test_inherited_key_presence.js && node tests/test_proto_accessor.js && node tests/test_hybrid_tier.js && node tests/test_first_rejection_builds_one.js && node tests/test_container_local.js && node tests/test_one_shot_validate.js && node tests/test_fixed_pattern_inline.js && node tests/test_plan_source.js && node tests/test_fused_remove_additional.js && node tests/test_runtime_parse.js && node tests/test_formats_single_pass.js && node tests/test_email_format_cost.js && node tests/test_format_single_error.js && node tests/test_defs_pointer_alias.js && node tests/test_cross_doc_root_ref.js && node tests/test_codegen_entrypoint_agreement.js && node tests/test_ref_annotation_siblings.js && node tests/test_gate_wrapped_schemas.js && node tests/test_pattern_message_escape.js && node tests/test_nested_defaults_engines.js && node tests/test_nested_coercion_parity.js && node tests/test_preprocess_cache.js && node tests/test_large_schema_codegen.js && node tests/test_base_uri.js && node tests/test_hoisted_def_recursion.js && node tests/test_alias_definitions.js && node tests/test_property_names_ref.js && node tests/test_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_deferred_def_scope.js && node tests/test_node_scoped_ctx_flags.js && node tests/test_fast_slot_exhaustion.js && node tests/test_ajv_errors.js && node tests/test_custom_keywords.js && node tests/test_strict_schema.js && node tests/test_aot_external_checks.js && node tests/test_ajv_parity.js && node tests/test_user_format_error_path.js && node tests/test_unevaluated_error_path.js && node tests/test_unevaluated_error_shape.js && node tests/test_pattern_properties_errors.js && node tests/test_additional_properties_scaling.js && node tests/test_additional_props_combined_cost.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_code_lookup.js && node tests/test_error_order.js && node tests/test_error_order_ordinal.js && node tests/test_ref_error_order.js && node tests/test_rejection_shape.js && node tests/test_lazy_normalization.js && node tests/test_value_equality.js && node tests/test_vocabulary.js && node tests/test_schema_scan.js && node tests/test_lazy_errors.js && node tests/test_single_pass_errors.js && node tests/test_lazy_json_errors.js && node tests/test_lazy_instance.js && node tests/test_cyclic_input.js && node tests/test_native_error_codes.js && node tests/test_verdict_preprocess.js && node tests/test_lite_parity.js && node tests/test_lite_bundle_size.js && node tests/test_defaults_own_keys.js && node tests/test_plan_compiler.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_enrich_received.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_malformed_json_termination.js && node tests/test_data_positions.js && node tests/test_targeted_positions.js && node tests/test_position_map_cost.js && node tests/test_targeted_positions_cost.js && node tests/test_aot_staleness.js && node tests/test_file_positions.js && node tests/test_aot_positions_cost.js && node tests/test_schema_hash.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_additive_fields.js && node tests/test_diagnostic_source.js && node tests/test_diagnose.js && node tests/test_correlate.js && node tests/test_diagnostics_score.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node benchmark/bench_aot_size.mjs",
62
+ "test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_text_path_errors.js && node tests/test_compiled_parity.js && node tests/test_buffer_gate.js && node tests/test_scanner_differential.js && node tests/test_validate_json_scanner_cost.js && node tests/test_buffer_reject_cost.js && node tests/test_nan_verdict.js && node tests/test_remove_additional_nested.js && node tests/test_exclusive_bounds.js && node tests/test_engine_differential.js && node tests/test_error_shape_differential.js && node tests/test_error_list_differential.js && node tests/test_output_format.js && node tests/test_standard_json_schema.js && node tests/test_retry_message.js && node tests/test_describe_schema.js && node tests/test_aot_parse.js && node tests/test_draft7_semantics.js && node tests/test_metaschema_ref.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_esm_exports.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_native_lazy.js && node tests/test_buffer_apis_without_native.js && node tests/test_cold_start_modules.js && node tests/test_safe_regex_embed.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_regex_ecma_whitespace.js && node tests/test_regex_native_linear.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_format_mode.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_engine_option.js && node tests/test_compile_cache_order.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_error_accumulator_collision.js && node tests/test_uri_helper_parity.js && node tests/test_uri_fast_path.js && node tests/test_extend_verdict.js && node tests/test_ipv4_regex.js && node tests/test_ipv6_fast_path.js && node tests/test_nested_recursion_decline.js && node tests/test_generator_error_content.js && node tests/test_inherited_key_presence.js && node tests/test_proto_accessor.js && node tests/test_hybrid_tier.js && node tests/test_first_rejection_builds_one.js && node tests/test_container_local.js && node tests/test_one_shot_validate.js && node tests/test_fixed_pattern_inline.js && node tests/test_plan_source.js && node tests/test_fused_remove_additional.js && node tests/test_runtime_parse.js && node tests/test_formats_single_pass.js && node tests/test_email_format_cost.js && node tests/test_format_single_error.js && node tests/test_defs_pointer_alias.js && node tests/test_cross_doc_root_ref.js && node tests/test_codegen_entrypoint_agreement.js && node tests/test_ref_annotation_siblings.js && node tests/test_gate_wrapped_schemas.js && node tests/test_pattern_message_escape.js && node tests/test_nested_defaults_engines.js && node tests/test_nested_coercion_parity.js && node tests/test_preprocess_cache.js && node tests/test_large_schema_codegen.js && node tests/test_base_uri.js && node tests/test_hoisted_def_recursion.js && node tests/test_alias_definitions.js && node tests/test_property_names_ref.js && node tests/test_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_deferred_def_scope.js && node tests/test_node_scoped_ctx_flags.js && node tests/test_fast_slot_exhaustion.js && node tests/test_ajv_errors.js && node tests/test_custom_keywords.js && node tests/test_strict_schema.js && node tests/test_aot_external_checks.js && node tests/test_ajv_parity.js && node tests/test_user_format_error_path.js && node tests/test_unevaluated_error_path.js && node tests/test_unevaluated_error_shape.js && node tests/test_unevaluated_differential.js && node tests/fuzz_differential.js && node tests/test_pattern_properties_errors.js && node tests/test_additional_properties_scaling.js && node tests/test_additional_props_combined_cost.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_code_lookup.js && node tests/test_error_order.js && node tests/test_error_order_ordinal.js && node tests/test_ref_error_order.js && node tests/test_rejection_shape.js && node tests/test_lazy_normalization.js && node tests/test_value_equality.js && node tests/test_vocabulary.js && node tests/test_schema_scan.js && node tests/test_lazy_errors.js && node tests/test_single_pass_errors.js && node tests/test_lazy_json_errors.js && node tests/test_lazy_instance.js && node tests/test_cyclic_input.js && node tests/test_native_error_codes.js && node tests/test_verdict_preprocess.js && node tests/test_lite_parity.js && node tests/test_lite_bundle_size.js && node tests/test_defaults_own_keys.js && node tests/test_plan_compiler.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_enrich_received.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_malformed_json_termination.js && node tests/test_data_positions.js && node tests/test_targeted_positions.js && node tests/test_position_map_cost.js && node tests/test_targeted_positions_cost.js && node tests/test_aot_staleness.js && node tests/test_file_positions.js && node tests/test_aot_positions_cost.js && node tests/test_schema_hash.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_additive_fields.js && node tests/test_diagnostic_source.js && node tests/test_diagnose.js && node tests/test_correlate.js && node tests/test_diagnostics_score.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_aot_source_frames.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node tests/test_collect_evaluated.js && node tests/test_differential_tiers.js && node tests/test_draft7.js && node tests/test_dual_path.js && node tests/test_keywords_codegen.js && node tests/test_ref_cross.js && node tests/test_shape_classifier.js && node tests/test_tier0.js && node benchmark/bench_aot_size.mjs",
63
63
  "bench:size": "node benchmark/bench_aot_size.mjs",
64
64
  "test:suite": "node tests/run_suite.js && node tests/run_suite.js draft7 && node tests/run_suite.js v1",
65
65
  "test:compat": "node tests/test_compat.js",
@@ -135,13 +135,13 @@
135
135
  "LICENSE"
136
136
  ],
137
137
  "optionalDependencies": {
138
- "@ata-validator/native-darwin-arm64": "1.38.0",
139
- "@ata-validator/native-darwin-x64": "1.38.0",
140
- "@ata-validator/native-linux-arm64-gnu": "1.38.0",
141
- "@ata-validator/native-linux-arm64-musl": "1.38.0",
142
- "@ata-validator/native-linux-x64-gnu": "1.38.0",
143
- "@ata-validator/native-linux-x64-musl": "1.38.0",
144
- "@ata-validator/native-win32-x64": "1.38.0"
138
+ "@ata-validator/native-darwin-arm64": "1.39.2",
139
+ "@ata-validator/native-darwin-x64": "1.39.2",
140
+ "@ata-validator/native-linux-arm64-gnu": "1.39.2",
141
+ "@ata-validator/native-linux-arm64-musl": "1.39.2",
142
+ "@ata-validator/native-linux-x64-gnu": "1.39.2",
143
+ "@ata-validator/native-linux-x64-musl": "1.39.2",
144
+ "@ata-validator/native-win32-x64": "1.39.2"
145
145
  },
146
146
  "peerDependencies": {
147
147
  "yaml": "^2.0.0"
@@ -1,8 +0,0 @@
1
- 'use strict';
2
-
3
- // Auto-generated by scripts/regen-safe-regex-source.js — do not edit.
4
- // Mirrors `lib/safe-regex.js` verbatim so `lib/aot.js` can embed the engine
5
- // into standalone output without a runtime `fs` read. Kept in sync by
6
- // `tests/test_safe_regex_source_sync.js`.
7
-
8
- module.exports = "'use strict'\n\n// Linear-time regex engine for JSON Schema `pattern`, used in place of JS RegExp\n// so an adversarial input cannot trigger catastrophic backtracking (ReDoS).\n//\n// It is a Pike VM: the pattern compiles to a small instruction program, and the\n// VM simulates all NFA threads in lockstep over the input, deduping by program\n// counter. Runtime is O(input * program), with no backtracking.\n//\n// Supported (the RE2 subset, which is what ata's native path also accepts):\n// literals, ., character classes, \\d \\w \\s \\D \\W \\S, anchors ^ $, quantifiers\n// * + ? {n} {n,} {n,m} (greedy or lazy, same language for a boolean test),\n// groups ( ) (?: ), alternation |. Backreferences and lookaround are not\n// supported by linear engines; compileSafe throws on them so the caller can\n// decide (ata's codegen rejects such schemas rather than risk a hang).\n\n// ECMA-262 WhiteSpace and LineTerminator, what `\\s` matches: tab, line feed,\n// vertical tab, form feed, carriage return, space, no-break space, the\n// Unicode space separators, the line and paragraph separators, and the byte\n// order mark. The set used to stop at U+00A0, so `^\\S+$` accepted a string\n// holding U+2028 or U+3000.\nconst WS = [[9, 13], [32, 32], [160, 160], [0x1680, 0x1680], [0x2000, 0x200a], [0x2028, 0x2029], [0x202f, 0x202f], [0x205f, 0x205f], [0x3000, 0x3000], [0xfeff, 0xfeff]]\n// `.` matches anything but a line terminator: LF, CR and U+2028/U+2029. It\n// excluded only LF, so `^.$` accepted \"\\r\".\nconst isLineTerminator = (c) => c === 10 || c === 13 || c === 0x2028 || c === 0x2029\nconst DIGIT = [[48, 57]]\nconst WORD = [[48, 57], [65, 90], [97, 122], [95, 95]]\n\nfunction parse (src) {\n let i = 0\n const len = src.length\n const peek = () => src[i]\n const eof = () => i >= len\n\n function parseAlt () {\n const opts = [parseConcat()]\n while (!eof() && peek() === '|') { i++; opts.push(parseConcat()) }\n return opts.length === 1 ? opts[0] : { t: 'alt', opts }\n }\n\n function parseConcat () {\n const parts = []\n while (!eof() && peek() !== '|' && peek() !== ')') parts.push(parseRepeat())\n if (parts.length === 0) return { t: 'empty' }\n return parts.length === 1 ? parts[0] : { t: 'concat', parts }\n }\n\n function parseRepeat () {\n let node = parseAtom()\n while (!eof()) {\n const ch = peek()\n if (ch === '*') { i++; node = { t: 'star', child: node } }\n else if (ch === '+') { i++; node = { t: 'plus', child: node } }\n else if (ch === '?') { i++; node = { t: 'quest', child: node } }\n else if (ch === '{') {\n const saved = i\n const q = tryQuantifier()\n if (!q) { i = saved; break }\n node = { t: 'repeat', child: node, min: q.min, max: q.max }\n } else break\n // a trailing ? makes the quantifier lazy; same language for a boolean test\n if (!eof() && peek() === '?') i++\n }\n return node\n }\n\n function tryQuantifier () {\n // assumes current char is '{'\n i++\n let min = ''\n while (!eof() && /[0-9]/.test(peek())) { min += peek(); i++ }\n if (min === '') return null\n let max\n if (peek() === '}') { i++; return { min: +min, max: +min } }\n if (peek() === ',') {\n i++\n let m = ''\n while (!eof() && /[0-9]/.test(peek())) { m += peek(); i++ }\n if (peek() !== '}') return null\n i++\n max = m === '' ? Infinity : +m\n return { min: +min, max }\n }\n return null\n }\n\n function parseAtom () {\n const ch = peek()\n if (ch === '(') {\n i++\n if (src[i] === '?') {\n if (src[i + 1] === ':') { i += 2 }\n else throw new Error('unsupported group (lookaround/named) in pattern')\n }\n const child = parseAlt()\n if (peek() !== ')') throw new Error('unbalanced ( in pattern')\n i++\n return { t: 'group', child }\n }\n if (ch === '[') return parseClass()\n if (ch === '.') { i++; return { t: 'any' } }\n if (ch === '^') { i++; return { t: 'bol' } }\n if (ch === '$') { i++; return { t: 'eol' } }\n if (ch === '\\\\') return parseEscape(false)\n if (ch === ')' || ch === '|') return { t: 'empty' }\n i++\n return { t: 'char', c: ch.charCodeAt(0) }\n }\n\n function parseClass () {\n i++ // [\n let neg = false\n if (peek() === '^') { neg = true; i++ }\n const ranges = []\n while (!eof() && peek() !== ']') {\n let lo\n if (peek() === '\\\\') {\n const esc = parseEscape(true)\n if (esc.t === 'classpart') { for (const r of esc.ranges) ranges.push(r); continue }\n lo = esc.c\n } else { lo = peek().charCodeAt(0); i++ }\n if (peek() === '-' && src[i + 1] !== ']' && i + 1 < len) {\n i++ // -\n let hi\n if (peek() === '\\\\') { const e = parseEscape(true); hi = e.c } else { hi = peek().charCodeAt(0); i++ }\n ranges.push([lo, hi])\n } else {\n ranges.push([lo, lo])\n }\n }\n if (peek() !== ']') throw new Error('unbalanced [ in pattern')\n i++\n return { t: 'class', neg, ranges }\n }\n\n function parseEscape (inClass) {\n i++ // backslash\n if (eof()) throw new Error('trailing backslash in pattern')\n const ch = peek(); i++\n switch (ch) {\n case 'd': return inClass ? { t: 'classpart', ranges: DIGIT } : { t: 'class', neg: false, ranges: DIGIT }\n case 'w': return inClass ? { t: 'classpart', ranges: WORD } : { t: 'class', neg: false, ranges: WORD }\n case 's': return inClass ? { t: 'classpart', ranges: WS } : { t: 'class', neg: false, ranges: WS }\n case 'D': if (inClass) throw new Error('\\\\D inside a class is not supported'); return { t: 'class', neg: true, ranges: DIGIT }\n case 'W': if (inClass) throw new Error('\\\\W inside a class is not supported'); return { t: 'class', neg: true, ranges: WORD }\n case 'S': if (inClass) throw new Error('\\\\S inside a class is not supported'); return { t: 'class', neg: true, ranges: WS }\n case 'n': return { t: 'char', c: 10 }\n case 'r': return { t: 'char', c: 13 }\n case 't': return { t: 'char', c: 9 }\n case 'f': return { t: 'char', c: 12 }\n case 'v': return { t: 'char', c: 11 }\n case '0': return { t: 'char', c: 0 }\n case 'x': { const h = src.slice(i, i + 2); i += 2; return { t: 'char', c: parseInt(h, 16) } }\n case 'u': { const h = src.slice(i, i + 4); i += 4; return { t: 'char', c: parseInt(h, 16) } }\n case 'b': if (inClass) return { t: 'char', c: 8 }; throw new Error('\\\\b word boundary is not supported')\n default:\n if (/[1-9]/.test(ch)) throw new Error('backreferences are not supported in pattern')\n return { t: 'char', c: ch.charCodeAt(0) }\n }\n }\n\n const ast = parseAlt()\n if (!eof()) throw new Error('unexpected \"' + peek() + '\" in pattern')\n return ast\n}\n\nfunction compileProg (ast) {\n const prog = []\n const emit = (op, extra) => { const idx = prog.length; prog.push(Object.assign({ op }, extra)); return idx }\n\n function rec (n) {\n switch (n.t) {\n case 'empty': break\n case 'char': emit('char', { c: n.c }); break\n case 'any': emit('any'); break\n case 'class': emit('class', { neg: n.neg, ranges: n.ranges }); break\n case 'bol': emit('bol'); break\n case 'eol': emit('eol'); break\n case 'group': rec(n.child); break\n case 'concat': for (const p of n.parts) rec(p); break\n case 'alt': {\n const jmps = []\n for (let k = 0; k < n.opts.length; k++) {\n if (k < n.opts.length - 1) {\n const sp = emit('split', { x: 0, y: 0 })\n prog[sp].x = prog.length\n rec(n.opts[k])\n jmps.push(emit('jmp', { x: 0 }))\n prog[sp].y = prog.length\n } else {\n rec(n.opts[k])\n }\n }\n for (const j of jmps) prog[j].x = prog.length\n break\n }\n case 'star': {\n const sp = emit('split', { x: 0, y: 0 })\n prog[sp].x = prog.length\n rec(n.child)\n emit('jmp', { x: sp })\n prog[sp].y = prog.length\n break\n }\n case 'plus': {\n const start = prog.length\n rec(n.child)\n const sp = emit('split', { x: start, y: 0 })\n prog[sp].y = prog.length\n break\n }\n case 'quest': {\n const sp = emit('split', { x: 0, y: 0 })\n prog[sp].x = prog.length\n rec(n.child)\n prog[sp].y = prog.length\n break\n }\n case 'repeat': {\n for (let k = 0; k < n.min; k++) rec(n.child)\n if (n.max === Infinity) {\n if (n.min === 0) rec({ t: 'star', child: n.child })\n else rec({ t: 'star', child: n.child })\n } else {\n for (let k = 0; k < n.max - n.min; k++) rec({ t: 'quest', child: n.child })\n }\n break\n }\n }\n }\n\n rec(ast)\n emit('match')\n return prog\n}\n\n// Numeric opcodes for the runner. The program is compiled once into flat\n// typed arrays so the inner loop does no property lookups or string compares.\nconst OP_CHAR = 0\nconst OP_ANY = 1\nconst OP_CLASS = 2\nconst OP_SPLIT = 3\nconst OP_JMP = 4\nconst OP_BOL = 5\nconst OP_EOL = 6\nconst OP_MATCH = 7\n\nfunction classMatcher (instr) {\n // ASCII is answered from a bitmap; anything above 0x7f walks the ranges.\n const bits = new Uint8Array(128)\n const r = instr.ranges\n for (let k = 0; k < r.length; k++) {\n const hi = Math.min(r[k][1], 127)\n for (let c = r[k][0]; c <= hi; c++) bits[c] = 1\n }\n return { bits, ranges: r, neg: instr.neg }\n}\n\nfunction matchClass (cls, c) {\n let inside\n if (c < 128) {\n inside = cls.bits[c] === 1\n } else {\n inside = false\n const r = cls.ranges\n for (let k = 0; k < r.length; k++) { if (c >= r[k][0] && c <= r[k][1]) { inside = true; break } }\n }\n return cls.neg ? !inside : inside\n}\n\nfunction makeRunner (prog) {\n const n = prog.length\n const ops = new Uint8Array(n)\n const xs = new Int32Array(n)\n const ys = new Int32Array(n)\n const cs = new Int32Array(n)\n const classes = new Array(n)\n for (let i = 0; i < n; i++) {\n const I = prog[i]\n switch (I.op) {\n case 'char': ops[i] = OP_CHAR; cs[i] = I.c; break\n case 'any': ops[i] = OP_ANY; break\n case 'class': ops[i] = OP_CLASS; classes[i] = classMatcher(I); break\n case 'split': ops[i] = OP_SPLIT; xs[i] = I.x; ys[i] = I.y; break\n case 'jmp': ops[i] = OP_JMP; xs[i] = I.x; break\n case 'bol': ops[i] = OP_BOL; break\n case 'eol': ops[i] = OP_EOL; break\n case 'match': ops[i] = OP_MATCH; break\n }\n }\n\n const lastGen = new Int32Array(n).fill(-1)\n let gen = 0\n // Each unvisited instruction is popped once and pushes at most two, so the\n // stack never holds more than 2n + 1 entries.\n const stack = new Int32Array(2 * n + 2)\n // Thread lists hold at most one entry per instruction per step.\n let clist = new Int32Array(n)\n let nlist = new Int32Array(n)\n let clen = 0\n let nlen = 0\n\n // Follows epsilon edges from `pc` and records every consuming instruction\n // (or match) reached in `list`. `lastGen` dedupes per step.\n function addThread (list, len0, pc, pos, len) {\n // Most transitions land directly on a consuming instruction; skip the\n // stack walk for those.\n if (ops[pc] <= OP_CLASS || ops[pc] === OP_MATCH) {\n if (lastGen[pc] === gen) return len0\n lastGen[pc] = gen\n list[len0] = pc\n return len0 + 1\n }\n let sp = 0\n stack[sp++] = pc\n let count = len0\n while (sp > 0) {\n const p = stack[--sp]\n if (lastGen[p] === gen) continue\n lastGen[p] = gen\n switch (ops[p]) {\n case OP_JMP: stack[sp++] = xs[p]; break\n case OP_SPLIT: stack[sp++] = ys[p]; stack[sp++] = xs[p]; break\n case OP_BOL: if (pos === 0) stack[sp++] = p + 1; break\n case OP_EOL: if (pos === len) stack[sp++] = p + 1; break\n default: list[count++] = p\n }\n }\n return count\n }\n\n // A pattern is anchored when starting it anywhere but position 0 yields no\n // thread, which is the case for `^...` and its alternations. The probe sits\n // at position 1 of a length-1 string so that only `^` can fail. For anchored\n // patterns the per-position restart below is skipped, and an empty thread\n // list means the match has already failed.\n gen++\n const anchored = addThread(nlist, 0, 0, 1, 1) === 0\n\n function testNFA (s) {\n const len = s.length\n gen++\n clen = addThread(clist, 0, 0, 0, len)\n for (let pos = 0; pos <= len; pos++) {\n const c = pos < len ? s.charCodeAt(pos) : -1\n gen++\n nlen = 0\n for (let k = 0; k < clen; k++) {\n const pc = clist[k]\n switch (ops[pc]) {\n case OP_MATCH: return true\n case OP_CHAR: if (c === cs[pc]) nlen = addThread(nlist, nlen, pc + 1, pos + 1, len); break\n case OP_ANY: if (c !== -1 && !isLineTerminator(c)) nlen = addThread(nlist, nlen, pc + 1, pos + 1, len); break\n case OP_CLASS: if (c !== -1 && matchClass(classes[pc], c)) nlen = addThread(nlist, nlen, pc + 1, pos + 1, len); break\n }\n }\n if (pos < len) {\n if (!anchored) nlen = addThread(nlist, nlen, 0, pos + 1, len)\n else if (nlen === 0) return false\n }\n const tmp = clist; clist = nlist; nlist = tmp\n clen = nlen\n }\n return false\n }\n\n // Lazy DFA on top of the NFA. A DFA state is the set of consuming\n // instructions live at a position; transitions are computed on first use\n // and cached per ASCII character. `^` and `$` depend on position, so the\n // closure is taken with flags for \"at start\" and \"at end\", which gives two\n // start states and two transition tables per state. The state count is\n // capped; past the cap the matcher falls back to the NFA walk above, so the\n // time bound stays linear either way.\n const MAX_STATES = 256\n const states = []\n const stateIds = new Map()\n let overflow = false\n\n function closure (list, count, pc, atStart, atEnd) {\n // Same walk as addThread, with the position replaced by the two flags.\n let sp = 0\n stack[sp++] = pc\n while (sp > 0) {\n const p = stack[--sp]\n if (lastGen[p] === gen) continue\n lastGen[p] = gen\n switch (ops[p]) {\n case OP_JMP: stack[sp++] = xs[p]; break\n case OP_SPLIT: stack[sp++] = ys[p]; stack[sp++] = xs[p]; break\n case OP_BOL: if (atStart) stack[sp++] = p + 1; break\n case OP_EOL: if (atEnd) stack[sp++] = p + 1; break\n default: list[count++] = p\n }\n }\n return count\n }\n\n function internState (list, count) {\n const pcs = Array.from(list.subarray(0, count)).sort((a, b) => a - b)\n const key = pcs.join(',')\n let id = stateIds.get(key)\n if (id !== undefined) return id\n if (states.length >= MAX_STATES) { overflow = true; return -1 }\n id = states.length\n let isMatch = false\n for (let k = 0; k < pcs.length; k++) if (ops[pcs[k]] === OP_MATCH) { isMatch = true; break }\n states.push({ pcs: Int32Array.from(pcs), isMatch, next: new Int32Array(128).fill(-2), nextEnd: new Int32Array(128).fill(-2) })\n stateIds.set(key, id)\n return id\n }\n\n function step (state, c, atEnd) {\n gen++\n let count = 0\n const pcs = state.pcs\n for (let k = 0; k < pcs.length; k++) {\n const pc = pcs[k]\n switch (ops[pc]) {\n case OP_CHAR: if (c === cs[pc]) count = closure(nlist, count, pc + 1, false, atEnd); break\n case OP_ANY: if (!isLineTerminator(c)) count = closure(nlist, count, pc + 1, false, atEnd); break\n case OP_CLASS: if (matchClass(classes[pc], c)) count = closure(nlist, count, pc + 1, false, atEnd); break\n }\n }\n if (!anchored) count = closure(nlist, count, 0, false, atEnd)\n return internState(nlist, count)\n }\n\n let startEmpty = -2\n let startNonEmpty = -2\n\n function startState (atEnd) {\n gen++\n const count = closure(nlist, 0, 0, true, atEnd)\n return internState(nlist, count)\n }\n\n function testDFA (s) {\n const len = s.length\n let id\n if (len === 0) {\n if (startEmpty === -2) startEmpty = startState(true)\n id = startEmpty\n } else {\n if (startNonEmpty === -2) startNonEmpty = startState(false)\n id = startNonEmpty\n }\n if (id < 0) return testNFA(s)\n let state = states[id]\n for (let pos = 0; pos < len; pos++) {\n if (state.isMatch) return true\n const c = s.charCodeAt(pos)\n const atEnd = pos + 1 === len\n let nid\n if (c < 128) {\n const table = atEnd ? state.nextEnd : state.next\n nid = table[c]\n if (nid === -2) { nid = step(state, c, atEnd); table[c] = nid }\n } else {\n nid = step(state, c, atEnd)\n }\n if (nid < 0) return testNFA(s)\n state = states[nid]\n if (anchored && state.pcs.length === 0) return false\n }\n return state.isMatch\n }\n\n return function test (s) {\n return overflow ? testNFA(s) : testDFA(s)\n }\n}\n\nfunction compileSafe (pattern) {\n const prog = compileProg(parse(pattern))\n const runner = makeRunner(prog)\n // `__ataSafe` brands the result so the standalone serializer can tell a safe\n // matcher apart from a RegExp and emit `__ataSafeRe(source)` instead.\n return { test: runner, source: pattern, __ataSafe: true }\n}\n\n// True when the linear engine can represent `src`. Used by the codegen to decide\n// between the safe matcher and a JS RegExp fallback for patterns outside the\n// supported (RE2) subset (backreferences, lookaround, etc.).\nfunction patternIsSafe (src) {\n try { compileSafe(src); return true } catch { return false }\n}\n\nmodule.exports = { compileSafe, patternIsSafe, parse }\n";