ata-validator 1.21.0 → 1.22.1
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/CHANGELOG.md +42 -0
- package/README.md +94 -12
- package/compat.js +12 -1
- package/index.d.ts +31 -0
- package/index.js +46 -17
- package/lib/data-positions.js +10 -0
- package/lib/enrich-error.js +89 -26
- package/lib/formats.js +158 -30
- package/lib/interpreter.js +20 -1
- package/lib/js-compiler.js +53 -2
- package/lib/levenshtein.js +19 -7
- package/lib/plan-compiler.js +38 -0
- package/lib/pointer.js +46 -0
- package/lib/retry-message.js +26 -2
- package/lib/suggestions.js +27 -24
- package/lib/version.js +1 -1
- package/package.json +10 -10
package/lib/suggestions.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { levenshtein } = require('./levenshtein');
|
|
4
|
+
const { resolvePointer: walk, UNRESOLVED } = require('./pointer');
|
|
4
5
|
|
|
5
6
|
// Hand-coded format hints. Keep <=60 chars per text.
|
|
6
7
|
const FORMAT_HINTS = {
|
|
@@ -46,8 +47,15 @@ function suggestEnumTypo (received, enumValues) {
|
|
|
46
47
|
return null;
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
// The widest container a typo hint is offered on. `suggestEnumTypo` caps its
|
|
51
|
+
// candidate list for the same two reasons: past a certain width the nearest
|
|
52
|
+
// key stops being evidence of a typo, and the scan is a distance computation
|
|
53
|
+
// per key on a value the caller has already rejected.
|
|
54
|
+
const MAX_TYPO_CANDIDATES = 64;
|
|
55
|
+
|
|
49
56
|
function suggestRequiredTypo (missing, presentKeys) {
|
|
50
57
|
if (!missing || !Array.isArray(presentKeys)) return null;
|
|
58
|
+
if (presentKeys.length > MAX_TYPO_CANDIDATES) return null;
|
|
51
59
|
for (const k of presentKeys) {
|
|
52
60
|
if (typeof k !== 'string') continue;
|
|
53
61
|
const d = levenshtein(missing, k, 2);
|
|
@@ -58,22 +66,14 @@ function suggestRequiredTypo (missing, presentKeys) {
|
|
|
58
66
|
return null;
|
|
59
67
|
}
|
|
60
68
|
|
|
61
|
-
function suggestFormat (format,
|
|
69
|
+
function suggestFormat (format, raw) {
|
|
62
70
|
const fn = FORMAT_HINTS[format];
|
|
63
71
|
if (!fn) return null;
|
|
64
|
-
// received is repr-truncated string like '"foo"'; unwrap for testing
|
|
65
|
-
let raw = received;
|
|
66
|
-
if (typeof raw === 'string' && raw.startsWith('"') && raw.endsWith('"')) {
|
|
67
|
-
try { raw = JSON.parse(raw); } catch {}
|
|
68
|
-
}
|
|
69
72
|
const text = fn(raw);
|
|
70
73
|
return text ? { text, kind: 'format' } : null;
|
|
71
74
|
}
|
|
72
75
|
|
|
73
|
-
function suggestCoercion (expectedType,
|
|
74
|
-
if (typeof received !== 'string' || !received.startsWith('"') || !received.endsWith('"')) return null;
|
|
75
|
-
let raw;
|
|
76
|
-
try { raw = JSON.parse(received); } catch { return null; }
|
|
76
|
+
function suggestCoercion (expectedType, raw) {
|
|
77
77
|
if (typeof raw !== 'string') return null;
|
|
78
78
|
if (expectedType === 'integer' && /^-?\d+$/.test(raw)) {
|
|
79
79
|
return { text: 'value would coerce; enable `coerceTypes` or pass an integer', kind: 'coercion' };
|
|
@@ -91,26 +91,37 @@ function suggestCoercion (expectedType, received) {
|
|
|
91
91
|
* Apply suggestion sources in priority order. Returns the first hit, or null.
|
|
92
92
|
* @param err Enriched ValidationError (with `received`, `params`, `keyword`)
|
|
93
93
|
* @param data The full input data (for required-typo)
|
|
94
|
+
* @param at The value at `err.path`, already resolved by the caller, or
|
|
95
|
+
* UNRESOLVED when the caller has none to offer
|
|
94
96
|
*/
|
|
95
|
-
function suggestFor (err, data) {
|
|
97
|
+
function suggestFor (err, data, at = UNRESOLVED) {
|
|
98
|
+
// The offending value itself. The caller resolves it once per error and
|
|
99
|
+
// passes it here rather than having each source walk the document again.
|
|
100
|
+
// A caller that passes only an enriched error falls back to un-quoting the
|
|
101
|
+
// repr, which is what every source used to read.
|
|
102
|
+
const raw = at !== UNRESOLVED ? at : parseReceived(err.received);
|
|
96
103
|
if (err.keyword === 'enum') {
|
|
97
|
-
return suggestEnumTypo(
|
|
104
|
+
return suggestEnumTypo(raw, err.params && err.params.allowedValues);
|
|
98
105
|
}
|
|
99
106
|
if (err.keyword === 'required') {
|
|
100
107
|
const missing = err.params && err.params.missingProperty;
|
|
101
|
-
|
|
108
|
+
const path = err.path || '';
|
|
109
|
+
let parentPath = path;
|
|
102
110
|
if (parentPath.endsWith('/' + missing)) parentPath = parentPath.slice(0, -missing.length - 1);
|
|
103
|
-
|
|
111
|
+
// A `required` error usually names the container it failed on, so the
|
|
112
|
+
// value the caller already resolved is the parent. Only the spelling that
|
|
113
|
+
// points at the missing key itself needs a second walk.
|
|
114
|
+
const parent = (parentPath === path && at !== UNRESOLVED) ? at : walk(data, parentPath);
|
|
104
115
|
if (parent && typeof parent === 'object') {
|
|
105
116
|
return suggestRequiredTypo(missing, Object.keys(parent));
|
|
106
117
|
}
|
|
107
118
|
return null;
|
|
108
119
|
}
|
|
109
120
|
if (err.keyword === 'format') {
|
|
110
|
-
return suggestFormat(err.params && err.params.format,
|
|
121
|
+
return suggestFormat(err.params && err.params.format, raw);
|
|
111
122
|
}
|
|
112
123
|
if (err.keyword === 'type') {
|
|
113
|
-
return suggestCoercion(err.params && err.params.type,
|
|
124
|
+
return suggestCoercion(err.params && err.params.type, raw);
|
|
114
125
|
}
|
|
115
126
|
return null;
|
|
116
127
|
}
|
|
@@ -121,12 +132,4 @@ function parseReceived (r) {
|
|
|
121
132
|
return r;
|
|
122
133
|
}
|
|
123
134
|
|
|
124
|
-
function walk (data, pointer) {
|
|
125
|
-
if (!pointer) return data;
|
|
126
|
-
const parts = pointer.replace(/^\//, '').split('/').map(s => s.replace(/~1/g, '/').replace(/~0/g, '~'));
|
|
127
|
-
let cur = data;
|
|
128
|
-
for (const p of parts) { if (cur == null) return undefined; cur = cur[p]; }
|
|
129
|
-
return cur;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
135
|
module.exports = { suggestFor, suggestEnumTypo, suggestRequiredTypo, suggestFormat, suggestCoercion };
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "JSON Schema validation
|
|
3
|
+
"version": "1.22.1",
|
|
4
|
+
"description": "JSON Schema validation that compiles for speed and still runs where code generation is blocked. Compiled and interpreted engines answer identically at 100% of the official suite. TypeScript inference, Standard Schema V1, and a build step that emits dependency-free modules.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
7
7
|
"types": "index.d.ts",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"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",
|
|
52
52
|
"build": "cmake-js build --target ata",
|
|
53
53
|
"rebuild": "cmake-js rebuild --target ata",
|
|
54
|
-
"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_buffer_gate.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_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_version_sync.js && node tests/test_native_loaded.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_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_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_formats_single_pass.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_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_ajv_errors.js && node tests/test_custom_keywords.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_pattern_properties_errors.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_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_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_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_data_positions.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",
|
|
54
|
+
"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_buffer_gate.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_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_version_sync.js && node tests/test_native_loaded.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_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_compile_cache_order.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_uri_helper_parity.js && node tests/test_formats_single_pass.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_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_ajv_errors.js && node tests/test_custom_keywords.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_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_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_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_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_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",
|
|
55
55
|
"bench:size": "node benchmark/bench_aot_size.mjs",
|
|
56
56
|
"test:suite": "node tests/run_suite.js && node tests/run_suite.js draft7 && node tests/run_suite.js v1",
|
|
57
57
|
"test:compat": "node tests/test_compat.js",
|
|
@@ -122,13 +122,13 @@
|
|
|
122
122
|
"LICENSE"
|
|
123
123
|
],
|
|
124
124
|
"optionalDependencies": {
|
|
125
|
-
"@ata-validator/native-darwin-arm64": "1.
|
|
126
|
-
"@ata-validator/native-darwin-x64": "1.
|
|
127
|
-
"@ata-validator/native-linux-arm64-gnu": "1.
|
|
128
|
-
"@ata-validator/native-linux-arm64-musl": "1.
|
|
129
|
-
"@ata-validator/native-linux-x64-gnu": "1.
|
|
130
|
-
"@ata-validator/native-linux-x64-musl": "1.
|
|
131
|
-
"@ata-validator/native-win32-x64": "1.
|
|
125
|
+
"@ata-validator/native-darwin-arm64": "1.22.1",
|
|
126
|
+
"@ata-validator/native-darwin-x64": "1.22.1",
|
|
127
|
+
"@ata-validator/native-linux-arm64-gnu": "1.22.1",
|
|
128
|
+
"@ata-validator/native-linux-arm64-musl": "1.22.1",
|
|
129
|
+
"@ata-validator/native-linux-x64-gnu": "1.22.1",
|
|
130
|
+
"@ata-validator/native-linux-x64-musl": "1.22.1",
|
|
131
|
+
"@ata-validator/native-win32-x64": "1.22.1"
|
|
132
132
|
},
|
|
133
133
|
"peerDependencies": {
|
|
134
134
|
"yaml": "^2.0.0"
|