ata-validator 0.9.3 → 0.10.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.
@@ -17,6 +17,7 @@
17
17
  #include <vector>
18
18
 
19
19
  #include "ata.h"
20
+ #include <simdjson.h>
20
21
 
21
22
  // ============================================================================
22
23
  // V8 Direct Object Traversal Engine
@@ -797,6 +798,67 @@ static void validate_napi(const schema_node_ptr& node,
797
798
  // N-API Binding
798
799
  // ============================================================================
799
800
 
801
+ // ============================================================================
802
+ // simdjson DOM to V8 JS Object conversion
803
+ // ============================================================================
804
+
805
+ static Napi::Value dom_to_napi(Napi::Env env, simdjson::dom::element el) {
806
+ using namespace simdjson;
807
+ switch (el.type()) {
808
+ case dom::element_type::OBJECT: {
809
+ auto obj = Napi::Object::New(env);
810
+ for (auto [key, val] : dom::object(el)) {
811
+ obj.Set(std::string(key), dom_to_napi(env, val));
812
+ }
813
+ return obj;
814
+ }
815
+ case dom::element_type::ARRAY: {
816
+ dom::array arr = el;
817
+ auto jsArr = Napi::Array::New(env, arr.size());
818
+ uint32_t i = 0;
819
+ for (auto val : arr) {
820
+ jsArr.Set(i++, dom_to_napi(env, val));
821
+ }
822
+ return jsArr;
823
+ }
824
+ case dom::element_type::STRING: {
825
+ std::string_view sv;
826
+ el.get(sv);
827
+ return Napi::String::New(env, sv.data(), sv.length());
828
+ }
829
+ case dom::element_type::INT64: {
830
+ int64_t v;
831
+ el.get(v);
832
+ return Napi::Number::New(env, static_cast<double>(v));
833
+ }
834
+ case dom::element_type::UINT64: {
835
+ uint64_t v;
836
+ el.get(v);
837
+ return Napi::Number::New(env, static_cast<double>(v));
838
+ }
839
+ case dom::element_type::DOUBLE: {
840
+ double v;
841
+ el.get(v);
842
+ return Napi::Number::New(env, v);
843
+ }
844
+ case dom::element_type::BOOL: {
845
+ bool v;
846
+ el.get(v);
847
+ return Napi::Boolean::New(env, v);
848
+ }
849
+ case dom::element_type::NULL_VALUE:
850
+ return env.Null();
851
+ default:
852
+ return env.Undefined();
853
+ }
854
+ }
855
+
856
+ // Thread-local simdjson DOM parser for parseJSON / validateAndParse
857
+ static simdjson::dom::parser& tl_dom_parser() {
858
+ thread_local simdjson::dom::parser parser;
859
+ return parser;
860
+ }
861
+
800
862
  static Napi::Object make_result(Napi::Env env,
801
863
  const ata::validation_result& result) {
802
864
  Napi::Object obj = Napi::Object::New(env);
@@ -822,7 +884,8 @@ class CompiledSchema : public Napi::ObjectWrap<CompiledSchema> {
822
884
  {InstanceMethod("validate", &CompiledSchema::Validate),
823
885
  InstanceMethod("validateJSON", &CompiledSchema::ValidateJSON),
824
886
  InstanceMethod("validateDirect", &CompiledSchema::ValidateDirect),
825
- InstanceMethod("isValidJSON", &CompiledSchema::IsValidJSON)});
887
+ InstanceMethod("isValidJSON", &CompiledSchema::IsValidJSON),
888
+ InstanceMethod("validateAndParse", &CompiledSchema::ValidateAndParse)});
826
889
  auto* constructor = new Napi::FunctionReference();
827
890
  *constructor = Napi::Persistent(func);
828
891
  env.SetInstanceData(constructor);
@@ -944,6 +1007,77 @@ class CompiledSchema : public Napi::ObjectWrap<CompiledSchema> {
944
1007
  return ValidateDirectImpl(env, info[0]);
945
1008
  }
946
1009
 
1010
+ // Parse JSON with simdjson, validate against schema, return parsed JS object
1011
+ Napi::Value ValidateAndParse(const Napi::CallbackInfo& info) {
1012
+ Napi::Env env = info.Env();
1013
+ if (info.Length() < 1) {
1014
+ Napi::TypeError::New(env, "JSON string or Buffer expected")
1015
+ .ThrowAsJavaScriptException();
1016
+ return env.Undefined();
1017
+ }
1018
+
1019
+ const char* data;
1020
+ size_t len;
1021
+
1022
+ if (info[0].IsBuffer()) {
1023
+ auto buf = info[0].As<Napi::Buffer<char>>();
1024
+ data = buf.Data();
1025
+ len = buf.Length();
1026
+ } else if (info[0].IsString()) {
1027
+ auto [d, l] = extract_string(env, info[0]);
1028
+ data = d;
1029
+ len = l;
1030
+ } else {
1031
+ Napi::TypeError::New(env, "JSON string or Buffer expected")
1032
+ .ThrowAsJavaScriptException();
1033
+ return env.Undefined();
1034
+ }
1035
+
1036
+ // Parse with simdjson
1037
+ simdjson::padded_string padded(data, len);
1038
+ auto& parser = tl_dom_parser();
1039
+ auto doc_result = parser.parse(padded);
1040
+ if (doc_result.error()) {
1041
+ auto obj = Napi::Object::New(env);
1042
+ obj.Set("valid", false);
1043
+ obj.Set("value", env.Null());
1044
+ auto errors = Napi::Array::New(env, 1);
1045
+ auto err = Napi::Object::New(env);
1046
+ err.Set("code", Napi::Number::New(env, static_cast<int>(ata::error_code::invalid_json)));
1047
+ err.Set("path", Napi::String::New(env, ""));
1048
+ err.Set("message", Napi::String::New(env, "Invalid JSON"));
1049
+ errors[0u] = err;
1050
+ obj.Set("errors", errors);
1051
+ return obj;
1052
+ }
1053
+
1054
+ // Validate
1055
+ auto valResult = ata::validate(schema_, std::string_view(data, len));
1056
+
1057
+ // Convert DOM to JS object
1058
+ Napi::Value jsValue = dom_to_napi(env, doc_result.value());
1059
+
1060
+ // Build result
1061
+ auto obj = Napi::Object::New(env);
1062
+ obj.Set("valid", valResult.valid);
1063
+ obj.Set("value", jsValue);
1064
+ if (valResult.valid) {
1065
+ obj.Set("errors", Napi::Array::New(env, 0));
1066
+ } else {
1067
+ Napi::Array errors = Napi::Array::New(env, valResult.errors.size());
1068
+ for (size_t i = 0; i < valResult.errors.size(); ++i) {
1069
+ Napi::Object err = Napi::Object::New(env);
1070
+ err.Set("code",
1071
+ Napi::Number::New(env, static_cast<int>(valResult.errors[i].code)));
1072
+ err.Set("path", Napi::String::New(env, valResult.errors[i].path));
1073
+ err.Set("message", Napi::String::New(env, valResult.errors[i].message));
1074
+ errors[i] = err;
1075
+ }
1076
+ obj.Set("errors", errors);
1077
+ }
1078
+ return obj;
1079
+ }
1080
+
947
1081
  private:
948
1082
  Napi::Value ValidateDirectImpl(Napi::Env env, Napi::Value value) {
949
1083
  compiled_schema_internal ctx;
@@ -996,6 +1130,44 @@ Napi::Value GetVersion(const Napi::CallbackInfo& info) {
996
1130
  return Napi::String::New(info.Env(), std::string(ata::version()));
997
1131
  }
998
1132
 
1133
+ // Standalone JSON parser using simdjson — returns parsed JS object
1134
+ Napi::Value ParseJSON(const Napi::CallbackInfo& info) {
1135
+ Napi::Env env = info.Env();
1136
+ if (info.Length() < 1) {
1137
+ Napi::TypeError::New(env, "JSON string or Buffer expected")
1138
+ .ThrowAsJavaScriptException();
1139
+ return env.Undefined();
1140
+ }
1141
+
1142
+ const char* data;
1143
+ size_t len;
1144
+
1145
+ if (info[0].IsBuffer()) {
1146
+ auto buf = info[0].As<Napi::Buffer<char>>();
1147
+ data = buf.Data();
1148
+ len = buf.Length();
1149
+ } else if (info[0].IsString()) {
1150
+ auto [d, l] = CompiledSchema::extract_string(env, info[0]);
1151
+ data = d;
1152
+ len = l;
1153
+ } else {
1154
+ Napi::TypeError::New(env, "JSON string or Buffer expected")
1155
+ .ThrowAsJavaScriptException();
1156
+ return env.Undefined();
1157
+ }
1158
+
1159
+ // Parse with simdjson using thread-local parser
1160
+ simdjson::padded_string padded(data, len);
1161
+ auto& parser = tl_dom_parser();
1162
+ auto result = parser.parse(padded);
1163
+ if (result.error()) {
1164
+ Napi::Error::New(env, "Invalid JSON").ThrowAsJavaScriptException();
1165
+ return env.Undefined();
1166
+ }
1167
+
1168
+ return dom_to_napi(env, result.value());
1169
+ }
1170
+
999
1171
  // --- Thread Pool ---
1000
1172
  class ThreadPool {
1001
1173
  public:
@@ -1531,6 +1703,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
1531
1703
  CompiledSchema::Init(env, exports);
1532
1704
  exports.Set("validate", Napi::Function::New(env, ValidateOneShot));
1533
1705
  exports.Set("version", Napi::Function::New(env, GetVersion));
1706
+ exports.Set("parseJSON", Napi::Function::New(env, ParseJSON));
1534
1707
  exports.Set("fastRegister", Napi::Function::New(env, FastRegister));
1535
1708
  exports.Set("fastValidate", Napi::Function::New(env, FastValidateSlow));
1536
1709
 
package/index.js CHANGED
@@ -9,6 +9,8 @@ const {
9
9
  compileToJSCombined,
10
10
  } = require("./lib/js-compiler");
11
11
  const { normalizeDraft7 } = require("./lib/draft7");
12
+ const { classify } = require("./lib/shape-classifier");
13
+ const { buildTier0Plan, tier0Validate } = require("./lib/tier0");
12
14
 
13
15
  // Extract default values from a schema tree. Returns a function that applies
14
16
  // defaults to an object in-place (mutates), or null if no defaults exist.
@@ -413,6 +415,20 @@ class Validator {
413
415
  enumerable: false,
414
416
  configurable: false,
415
417
  });
418
+
419
+ // Tier 0 fast path: override isValidObject with a direct bound validator.
420
+ // All other methods (validate, validateJSON, etc.) stay on the lazy stubs above.
421
+ // Tier 0/1 are boolean-only; error paths continue through codegen.
422
+ const _tier = classify(schemaObj);
423
+ if (_tier.tier === 0) {
424
+ const _plan = buildTier0Plan(schemaObj);
425
+ this.isValidObject = (data) => tier0Validate(_plan, data);
426
+ }
427
+
428
+ // Populate identity cache so repeated `new Validator(sameSchema)` short-circuits.
429
+ if (!opts && typeof schema === "object" && schema !== null) {
430
+ _identityCache.set(schema, this);
431
+ }
416
432
  }
417
433
 
418
434
  _ensureCompiled() {
@@ -0,0 +1,96 @@
1
+ 'use strict';
2
+
3
+ // Classifies a JSON Schema into one of three execution tiers:
4
+ // 0 - simple object or top-level primitive, fast-path validator
5
+ // 1 - nested objects/arrays, no composition, generic interpreter
6
+ // 2 - composition, $ref, dynamic, etc. -> existing codegen
7
+ //
8
+ // Tier 0/1 validators are BOOLEAN only. Error-returning paths stay on codegen.
9
+
10
+ const PRIMITIVE_TYPES = new Set(['string', 'number', 'integer', 'boolean']);
11
+
12
+ // Meta keywords that are always safe to see at any node (annotations, no validation impact)
13
+ const META_KEYS = new Set([
14
+ '$schema', '$id', '$comment',
15
+ 'title', 'description', 'default', 'examples', 'deprecated', 'readOnly', 'writeOnly',
16
+ ]);
17
+
18
+ const TIER0_OBJECT_ALLOWED = new Set([
19
+ 'type', 'properties', 'required', 'additionalProperties',
20
+ ...META_KEYS,
21
+ ]);
22
+
23
+ const TIER0_PRIMITIVE_ALLOWED = new Set([
24
+ 'type', 'enum', 'const',
25
+ 'minLength', 'maxLength',
26
+ 'minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum',
27
+ 'multipleOf', 'format',
28
+ ...META_KEYS,
29
+ ]);
30
+
31
+ const MAX_TIER0_PROPS = 10;
32
+ const MAX_TIER0_ENUM = 256;
33
+
34
+ function isPrimitiveType(t) {
35
+ return typeof t === 'string' && PRIMITIVE_TYPES.has(t);
36
+ }
37
+
38
+ function isPrimitiveEnumValue(v) {
39
+ const t = typeof v;
40
+ return v === null || t === 'string' || t === 'number' || t === 'boolean';
41
+ }
42
+
43
+ function isTier0Primitive(schema) {
44
+ if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) return false;
45
+ if (!isPrimitiveType(schema.type)) return false;
46
+ for (const k of Object.keys(schema)) {
47
+ if (!TIER0_PRIMITIVE_ALLOWED.has(k)) return false;
48
+ }
49
+ if (schema.enum !== undefined) {
50
+ if (!Array.isArray(schema.enum)) return false;
51
+ if (schema.enum.length === 0 || schema.enum.length > MAX_TIER0_ENUM) return false;
52
+ for (const v of schema.enum) {
53
+ if (!isPrimitiveEnumValue(v)) return false;
54
+ }
55
+ }
56
+ if (schema.const !== undefined && !isPrimitiveEnumValue(schema.const)) return false;
57
+ return true;
58
+ }
59
+
60
+ function isTier0Object(schema) {
61
+ if (schema.type !== 'object') return false;
62
+ for (const k of Object.keys(schema)) {
63
+ if (!TIER0_OBJECT_ALLOWED.has(k)) return false;
64
+ }
65
+ const ap = schema.additionalProperties;
66
+ if (ap !== undefined && ap !== true && ap !== false) return false;
67
+ if (schema.required !== undefined) {
68
+ if (!Array.isArray(schema.required)) return false;
69
+ for (const r of schema.required) if (typeof r !== 'string') return false;
70
+ }
71
+ const props = schema.properties;
72
+ if (props === undefined) return true;
73
+ if (typeof props !== 'object' || props === null || Array.isArray(props)) return false;
74
+ const keys = Object.keys(props);
75
+ if (keys.length > MAX_TIER0_PROPS) return false;
76
+ for (const k of keys) {
77
+ if (!isTier0Primitive(props[k])) return false;
78
+ }
79
+ return true;
80
+ }
81
+
82
+ function classify(schema) {
83
+ if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) {
84
+ return { tier: 2, plan: null };
85
+ }
86
+ if (isTier0Primitive(schema)) return { tier: 0, plan: null };
87
+ if (isTier0Object(schema)) return { tier: 0, plan: null };
88
+ return { tier: 2, plan: null };
89
+ }
90
+
91
+ module.exports = {
92
+ classify,
93
+ MAX_TIER0_PROPS,
94
+ MAX_TIER0_ENUM,
95
+ PRIMITIVE_TYPES,
96
+ };
package/lib/tier0.js ADDED
@@ -0,0 +1,203 @@
1
+ 'use strict';
2
+
3
+ // Tier 0 fast path: shared parametric validator for simple schemas.
4
+ // All tier-0 Validators call the same tier0Validate() function;
5
+ // the per-instance difference is the plan object.
6
+ // V8 sees one function with monomorphic hidden classes and JIT-compiles it once.
7
+
8
+ const TYPE_MASK = {
9
+ string: 1,
10
+ number: 2,
11
+ integer: 4,
12
+ boolean: 8,
13
+ };
14
+
15
+ const T_STRING = TYPE_MASK.string;
16
+ const T_NUMBER = TYPE_MASK.number;
17
+ const T_INTEGER = TYPE_MASK.integer;
18
+ const T_BOOLEAN = TYPE_MASK.boolean;
19
+
20
+ // Numeric constraint flags, packed into constraint.numFlags.
21
+ // Using bit flags means the validator does a cheap bitwise-and instead of
22
+ // five Number.isNaN() calls per numeric property when only one bound is set.
23
+ const F_MIN = 1;
24
+ const F_MAX = 2;
25
+ const F_EXCL_MIN = 4;
26
+ const F_EXCL_MAX = 8;
27
+ const F_MULT = 16;
28
+
29
+ // Build a constraint tuple for one primitive-typed property.
30
+ // All fields have the same layout so every constraint shares one hidden class.
31
+ function primConstraint(key, propSchema) {
32
+ const t = propSchema.type;
33
+ const hasEnum = Array.isArray(propSchema.enum);
34
+ const hasConst = propSchema.const !== undefined;
35
+ let numFlags = 0;
36
+ if (typeof propSchema.minimum === 'number') numFlags |= F_MIN;
37
+ if (typeof propSchema.maximum === 'number') numFlags |= F_MAX;
38
+ if (typeof propSchema.exclusiveMinimum === 'number') numFlags |= F_EXCL_MIN;
39
+ if (typeof propSchema.exclusiveMaximum === 'number') numFlags |= F_EXCL_MAX;
40
+ if (typeof propSchema.multipleOf === 'number') numFlags |= F_MULT;
41
+ return {
42
+ key,
43
+ typeMask: TYPE_MASK[t] | 0,
44
+ numFlags,
45
+ hasEnum,
46
+ hasConst,
47
+ enumSet: hasEnum ? new Set(propSchema.enum) : null,
48
+ constVal: hasConst ? propSchema.const : undefined,
49
+ minLen: typeof propSchema.minLength === 'number' ? propSchema.minLength : -1,
50
+ maxLen: typeof propSchema.maxLength === 'number' ? propSchema.maxLength : -1,
51
+ min: typeof propSchema.minimum === 'number' ? propSchema.minimum : 0,
52
+ max: typeof propSchema.maximum === 'number' ? propSchema.maximum : 0,
53
+ exclMin: typeof propSchema.exclusiveMinimum === 'number' ? propSchema.exclusiveMinimum : 0,
54
+ exclMax: typeof propSchema.exclusiveMaximum === 'number' ? propSchema.exclusiveMaximum : 0,
55
+ multipleOf: typeof propSchema.multipleOf === 'number' ? propSchema.multipleOf : 0,
56
+ };
57
+ }
58
+
59
+ function buildTier0Plan(schema) {
60
+ if (schema.type !== 'object') {
61
+ return {
62
+ isPrimitive: true,
63
+ constraints: [primConstraint('__root__', schema)],
64
+ requiredMask: 0,
65
+ additionalAllowed: true,
66
+ knownKeys: null,
67
+ };
68
+ }
69
+ const props = schema.properties || {};
70
+ const keys = Object.keys(props);
71
+ const required = schema.required ? new Set(schema.required) : null;
72
+ const constraints = new Array(keys.length);
73
+ const knownKeys = new Set();
74
+ let requiredMask = 0;
75
+ for (let i = 0; i < keys.length; i++) {
76
+ const k = keys[i];
77
+ constraints[i] = primConstraint(k, props[k]);
78
+ knownKeys.add(k);
79
+ if (required && required.has(k)) requiredMask |= (1 << i);
80
+ }
81
+ return {
82
+ isPrimitive: false,
83
+ constraints,
84
+ requiredMask,
85
+ additionalAllowed: schema.additionalProperties !== false,
86
+ knownKeys,
87
+ };
88
+ }
89
+
90
+ // checkPrimitive stays exported for Tier 1 reuse.
91
+ function checkPrimitive(c, v) {
92
+ const m = c.typeMask;
93
+ if (m === T_STRING) {
94
+ if (typeof v !== 'string') return false;
95
+ const minLen = c.minLen;
96
+ const maxLen = c.maxLen;
97
+ if (minLen >= 0 && v.length < minLen) return false;
98
+ if (maxLen >= 0 && v.length > maxLen) return false;
99
+ } else if (m === T_INTEGER) {
100
+ if (typeof v !== 'number' || !Number.isInteger(v)) return false;
101
+ const f = c.numFlags;
102
+ if (f !== 0) {
103
+ if ((f & F_MIN) && v < c.min) return false;
104
+ if ((f & F_MAX) && v > c.max) return false;
105
+ if ((f & F_EXCL_MIN) && v <= c.exclMin) return false;
106
+ if ((f & F_EXCL_MAX) && v >= c.exclMax) return false;
107
+ if ((f & F_MULT) && v % c.multipleOf !== 0) return false;
108
+ }
109
+ } else if (m === T_NUMBER) {
110
+ if (typeof v !== 'number') return false;
111
+ const f = c.numFlags;
112
+ if (f !== 0) {
113
+ if ((f & F_MIN) && v < c.min) return false;
114
+ if ((f & F_MAX) && v > c.max) return false;
115
+ if ((f & F_EXCL_MIN) && v <= c.exclMin) return false;
116
+ if ((f & F_EXCL_MAX) && v >= c.exclMax) return false;
117
+ if ((f & F_MULT) && v % c.multipleOf !== 0) return false;
118
+ }
119
+ } else if (m === T_BOOLEAN) {
120
+ if (typeof v !== 'boolean') return false;
121
+ } else {
122
+ return false;
123
+ }
124
+ if (c.hasEnum && !c.enumSet.has(v)) return false;
125
+ if (c.hasConst && v !== c.constVal) return false;
126
+ return true;
127
+ }
128
+
129
+ // Inlined object validator. Separating the primitive path removes a dead
130
+ // branch from the hot object path.
131
+ function tier0ValidateObject(plan, data) {
132
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) return false;
133
+ const cs = plan.constraints;
134
+ const n = cs.length;
135
+ const reqMask = plan.requiredMask;
136
+ let seenMask = 0;
137
+ for (let i = 0; i < n; i++) {
138
+ const c = cs[i];
139
+ const v = data[c.key];
140
+ if (v === undefined) {
141
+ if (reqMask & (1 << i)) return false;
142
+ continue;
143
+ }
144
+ seenMask |= (1 << i);
145
+ // Inlined type + constraint check
146
+ const m = c.typeMask;
147
+ if (m === T_STRING) {
148
+ if (typeof v !== 'string') return false;
149
+ const minLen = c.minLen;
150
+ const maxLen = c.maxLen;
151
+ if (minLen >= 0 && v.length < minLen) return false;
152
+ if (maxLen >= 0 && v.length > maxLen) return false;
153
+ } else if (m === T_INTEGER) {
154
+ if (typeof v !== 'number' || !Number.isInteger(v)) return false;
155
+ const f = c.numFlags;
156
+ if (f !== 0) {
157
+ if ((f & F_MIN) && v < c.min) return false;
158
+ if ((f & F_MAX) && v > c.max) return false;
159
+ if ((f & F_EXCL_MIN) && v <= c.exclMin) return false;
160
+ if ((f & F_EXCL_MAX) && v >= c.exclMax) return false;
161
+ if ((f & F_MULT) && v % c.multipleOf !== 0) return false;
162
+ }
163
+ } else if (m === T_NUMBER) {
164
+ if (typeof v !== 'number') return false;
165
+ const f = c.numFlags;
166
+ if (f !== 0) {
167
+ if ((f & F_MIN) && v < c.min) return false;
168
+ if ((f & F_MAX) && v > c.max) return false;
169
+ if ((f & F_EXCL_MIN) && v <= c.exclMin) return false;
170
+ if ((f & F_EXCL_MAX) && v >= c.exclMax) return false;
171
+ if ((f & F_MULT) && v % c.multipleOf !== 0) return false;
172
+ }
173
+ } else if (m === T_BOOLEAN) {
174
+ if (typeof v !== 'boolean') return false;
175
+ } else {
176
+ return false;
177
+ }
178
+ if (c.hasEnum && !c.enumSet.has(v)) return false;
179
+ if (c.hasConst && v !== c.constVal) return false;
180
+ }
181
+ if ((seenMask & reqMask) !== reqMask) return false;
182
+ if (!plan.additionalAllowed) {
183
+ const known = plan.knownKeys;
184
+ for (const k in data) {
185
+ if (!Object.prototype.hasOwnProperty.call(data, k)) continue;
186
+ if (!known.has(k)) return false;
187
+ }
188
+ }
189
+ return true;
190
+ }
191
+
192
+ function tier0Validate(plan, data) {
193
+ if (plan.isPrimitive) return checkPrimitive(plan.constraints[0], data);
194
+ return tier0ValidateObject(plan, data);
195
+ }
196
+
197
+ module.exports = {
198
+ buildTier0Plan,
199
+ tier0Validate,
200
+ tier0ValidateObject,
201
+ checkPrimitive,
202
+ TYPE_MASK,
203
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "0.9.3",
3
+ "version": "0.10.0",
4
4
  "description": "Ultra-fast JSON Schema validator. 4.7x faster validation, 1,800x faster compilation. Works without native addon. Cross-schema $ref, Draft 2020-12 + Draft 7, V8-optimized JS codegen, simdjson, RE2, multi-core. Standard Schema V1 compatible.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",