ata-validator 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,4 +1,12 @@
1
1
  const native = require("node-gyp-build")(__dirname);
2
+ const { compileToJS, compileToJSCodegen } = require("./lib/js-compiler");
3
+
4
+ const SIMDJSON_PADDING = 64;
5
+ const VALID_RESULT = Object.freeze({ valid: true, errors: Object.freeze([]) });
6
+
7
+ // Above this size, simdjson On Demand (selective field access) beats JSON.parse
8
+ // (which must materialize the full JS object tree). Buffer.from + NAPI ~2x faster.
9
+ const SIMDJSON_THRESHOLD = 8192;
2
10
 
3
11
  function parsePointerPath(path) {
4
12
  if (!path) return [];
@@ -10,11 +18,84 @@ function parsePointerPath(path) {
10
18
  }));
11
19
  }
12
20
 
21
+ function createPaddedBuffer(jsonStr) {
22
+ const jsonBuf = Buffer.from(jsonStr);
23
+ const padded = Buffer.allocUnsafe(jsonBuf.length + SIMDJSON_PADDING);
24
+ jsonBuf.copy(padded);
25
+ padded.fill(0, jsonBuf.length);
26
+ return { buffer: padded, length: jsonBuf.length };
27
+ }
28
+
13
29
  class Validator {
14
30
  constructor(schema) {
15
31
  const schemaStr =
16
32
  typeof schema === "string" ? schema : JSON.stringify(schema);
17
- this._compiled = new native.CompiledSchema(schemaStr);
33
+ const compiled = new native.CompiledSchema(schemaStr);
34
+ this._compiled = compiled;
35
+ this._fastSlot = native.fastRegister(schemaStr);
36
+
37
+ // Pure JS fast path — no NAPI, runs in V8 JIT
38
+ // Set ATA_FORCE_NAPI=1 to disable JS codegen (for correctness testing)
39
+ const schemaObj = typeof schema === "string" ? JSON.parse(schema) : schema;
40
+ const jsFn = process.env.ATA_FORCE_NAPI
41
+ ? null
42
+ : (compileToJSCodegen(schemaObj) || compileToJS(schemaObj));
43
+ this._jsFn = jsFn;
44
+
45
+ // Closure-capture: avoid `this` property lookup on every call.
46
+ // V8 keeps closure vars in registers — no hidden class traversal.
47
+ const fastSlot = this._fastSlot;
48
+
49
+ // Detect if schema is "selective" — doesn't recurse into arrays/deep objects.
50
+ // Selective schemas benefit from simdjson On Demand (seeks only needed fields).
51
+ // Non-selective schemas (items, allOf with nested) touch everything — JSON.parse + jsFn wins.
52
+ const hasArrayTraversal = schemaObj && (schemaObj.items || schemaObj.prefixItems ||
53
+ schemaObj.contains || (schemaObj.properties && Object.values(schemaObj.properties).some(
54
+ p => p && (p.items || p.prefixItems || p.contains))));
55
+ const useSimdjsonForLarge = !hasArrayTraversal;
56
+
57
+ if (jsFn) {
58
+ this.validate = (data) => jsFn(data) ? VALID_RESULT : compiled.validate(data);
59
+ this.isValidObject = jsFn;
60
+ this.validateJSON = useSimdjsonForLarge
61
+ ? (jsonStr) => {
62
+ // Selective schema: large docs use simdjson (skips irrelevant data)
63
+ if (jsonStr.length >= SIMDJSON_THRESHOLD) {
64
+ const buf = Buffer.from(jsonStr);
65
+ if (native.rawFastValidate(fastSlot, buf)) return VALID_RESULT;
66
+ return compiled.validateJSON(jsonStr);
67
+ }
68
+ try {
69
+ const obj = JSON.parse(jsonStr);
70
+ if (jsFn(obj)) return VALID_RESULT;
71
+ } catch (e) {
72
+ if (!(e instanceof SyntaxError)) throw e;
73
+ }
74
+ return compiled.validateJSON(jsonStr);
75
+ }
76
+ : (jsonStr) => {
77
+ // Non-selective schema: JSON.parse + jsFn always wins
78
+ try {
79
+ const obj = JSON.parse(jsonStr);
80
+ if (jsFn(obj)) return VALID_RESULT;
81
+ } catch (e) {
82
+ if (!(e instanceof SyntaxError)) throw e;
83
+ }
84
+ return compiled.validateJSON(jsonStr);
85
+ };
86
+ this.isValidJSON = useSimdjsonForLarge
87
+ ? (jsonStr) => {
88
+ if (jsonStr.length >= SIMDJSON_THRESHOLD) {
89
+ return native.rawFastValidate(fastSlot, Buffer.from(jsonStr));
90
+ }
91
+ try { return jsFn(JSON.parse(jsonStr)); }
92
+ catch (e) { if (!(e instanceof SyntaxError)) throw e; return false; }
93
+ }
94
+ : (jsonStr) => {
95
+ try { return jsFn(JSON.parse(jsonStr)); }
96
+ catch (e) { if (!(e instanceof SyntaxError)) throw e; return false; }
97
+ };
98
+ }
18
99
 
19
100
  const self = this;
20
101
  Object.defineProperty(this, "~standard", {
@@ -22,7 +103,7 @@ class Validator {
22
103
  version: 1,
23
104
  vendor: "ata-validator",
24
105
  validate(value) {
25
- const result = self._compiled.validate(value);
106
+ const result = self.validate(value);
26
107
  if (result.valid) {
27
108
  return { value };
28
109
  }
@@ -40,10 +121,15 @@ class Validator {
40
121
  });
41
122
  }
42
123
 
124
+ // Fallback methods — only used when JS codegen is unavailable
43
125
  validate(data) {
44
126
  return this._compiled.validate(data);
45
127
  }
46
128
 
129
+ isValidObject(data) {
130
+ return this._compiled.validate(data).valid;
131
+ }
132
+
47
133
  validateJSON(jsonStr) {
48
134
  return this._compiled.validateJSON(jsonStr);
49
135
  }
@@ -51,6 +137,31 @@ class Validator {
51
137
  isValidJSON(jsonStr) {
52
138
  return this._compiled.isValidJSON(jsonStr);
53
139
  }
140
+
141
+ // Raw NAPI fast path for Buffer/Uint8Array
142
+ isValid(input) {
143
+ return native.rawFastValidate(this._fastSlot, input);
144
+ }
145
+
146
+ // Zero-copy pre-padded path
147
+ isValidPrepadded(paddedBuffer, jsonLength) {
148
+ return native.rawFastValidate(this._fastSlot, paddedBuffer, jsonLength);
149
+ }
150
+
151
+ // Parallel NDJSON batch (multi-core)
152
+ isValidParallel(buffer) {
153
+ return native.rawParallelValidate(this._fastSlot, buffer);
154
+ }
155
+
156
+ // Parallel count (fastest — single uint32 return)
157
+ countValid(buffer) {
158
+ return native.rawParallelCount(this._fastSlot, buffer);
159
+ }
160
+
161
+ // NDJSON single-thread batch
162
+ isValidNDJSON(buffer) {
163
+ return native.rawNDJSONValidate(this._fastSlot, buffer);
164
+ }
54
165
  }
55
166
 
56
167
  function validate(schema, data) {
@@ -63,4 +174,4 @@ function version() {
63
174
  return native.version();
64
175
  }
65
176
 
66
- module.exports = { Validator, validate, version };
177
+ module.exports = { Validator, validate, version, createPaddedBuffer, SIMDJSON_PADDING };