ata-validator 0.13.3 → 0.14.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/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  All notable changes to ata-validator are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/), and this project adheres to semantic versioning.
4
4
 
5
+ ## 0.14.0 - 2026-05-16
6
+
7
+ ### Added
8
+
9
+ - Generic `Validator<T>` with type predicate `isValidObject(data): data is T`. Pairs naturally with TypeBox, Zod-from-JSON-Schema, Valibot, or hand-written types over JSON Schema literals.
10
+ - `ValidationResult<T>` and `ValidateAndParseResult<T>` are discriminated unions. On the `valid: true` branch the parsed data is typed; on `valid: false` the `errors` array carries the diagnostic information.
11
+
12
+ ### Changed
13
+
14
+ - Type-level only: accessing `result.data` (or `result.value` on `validateAndParse`) without first checking `result.valid` is now a TypeScript compile error. Runtime behavior is unchanged. The previous shape returned `undefined` in that position, so this surfaces an existing latent bug at compile time.
15
+
16
+ ### Notes
17
+
18
+ - Pure `.d.ts` change. No JS, C++, AOT, or CLI behavior is affected. Bundle size unchanged. Runtime performance unchanged.
19
+
20
+ ## 0.13.4 — 2026-05-14
21
+
22
+ ### Fixed
23
+
24
+ - **macOS arm64 prebuild shipped with an invalid code signature.** The release workflow runs `pkg-prebuilds-copy --strip`, which on macOS runs `strip -Sx` on the addon. `strip` rewrites the Mach-O and invalidates the ad-hoc signature the linker applied, and it does not re-sign. arm64 macOS refuses to load unsigned code, so `require('ata-validator')` was killed with `SIGKILL (Code Signature Invalid)` the moment the binding loader called into the addon. Only `0.12.6` reached users this way because it was the one release published through CI rather than locally. The workflow now re-signs and verifies the macOS prebuild after `strip`, and `codesign --verify` gates the job so a broken signature cannot ship. Fixes #23.
25
+ - **macOS x64 prebuild was never produced.** The prebuild matrix used `macos-14` for the x64 leg, but `macos-14` runners are Apple Silicon only, so that leg built an arm64 binary mislabeled as x64 and no `darwin-x64` prebuild ever ended up in the tarball.
26
+
27
+ ### Removed
28
+
29
+ - **macOS x64 prebuild.** `macos-13` GitHub runners, the only ones that build x64 natively, are no longer reliably available. Since no published version ever shipped a working `darwin-x64` prebuild, this is not a regression. Intel Mac users fall back to the JS engine, which still works, only the buffer APIs are slower.
30
+
31
+ ### Changed
32
+
33
+ - **`prepublishOnly` now blocks tarballs missing platform prebuilds.** A local `npm publish` only carries the publisher's own platform, silently dropping every other prebuild. Publishing now fails unless all seven platform prebuilds are present, and when run on a Mac it also verifies the darwin code signatures.
34
+
5
35
  ## 0.13.3 — 2026-05-13
6
36
 
7
37
  ### Fixed
package/README.md CHANGED
@@ -90,6 +90,39 @@ v.isValidParallel(ndjson); // bool[]
90
90
  v.countValid(ndjson); // number
91
91
  ```
92
92
 
93
+ ### Type-safe schemas
94
+
95
+ `Validator` is generic. Pair it with any schema authoring tool, or a hand-written type, to get TypeScript narrowing in your handler code.
96
+
97
+ ```ts
98
+ import { Type, type Static } from '@sinclair/typebox'
99
+ import { Validator } from 'ata-validator'
100
+
101
+ const UserSchema = Type.Object({
102
+ id: Type.Integer({ minimum: 1 }),
103
+ name: Type.String({ minLength: 1 }),
104
+ email: Type.String({ format: 'email' }),
105
+ })
106
+
107
+ type User = Static<typeof UserSchema>
108
+
109
+ const v = new Validator<User>(UserSchema)
110
+
111
+ if (v.isValidObject(data)) {
112
+ // data is narrowed to User, no cast needed
113
+ console.log(data.name)
114
+ }
115
+
116
+ const result = v.validate(data)
117
+ if (result.valid) {
118
+ // result.data is User
119
+ } else {
120
+ // result.errors: ValidationError[]
121
+ }
122
+ ```
123
+
124
+ The same pattern works with Zod-from-JSON-Schema, Valibot, or a hand-written `type User = {...}` alongside a JSON Schema literal. `Validator<T>` makes no library-specific assumption.
125
+
93
126
  ### Cross-Schema `$ref`
94
127
 
95
128
  ```javascript
package/index.d.ts CHANGED
@@ -15,16 +15,13 @@ export interface ValidationError {
15
15
  /** A user-supplied format checker. Receives the candidate value, returns true if valid. */
16
16
  export type FormatChecker = (value: string) => boolean;
17
17
 
18
- export interface ValidationResult {
19
- valid: boolean;
20
- errors: ValidationError[];
21
- }
18
+ export type ValidationResult<T = unknown> =
19
+ | { valid: true; data: T; errors: ValidationError[] }
20
+ | { valid: false; data?: never; errors: ValidationError[] };
22
21
 
23
- export interface ValidateAndParseResult {
24
- valid: boolean;
25
- value: unknown;
26
- errors: ValidationError[];
27
- }
22
+ export type ValidateAndParseResult<T = unknown> =
23
+ | { valid: true; value: T; errors: ValidationError[] }
24
+ | { valid: false; value: unknown; errors: ValidationError[] };
28
25
 
29
26
  export interface ValidatorOptions {
30
27
  coerceTypes?: boolean;
@@ -74,26 +71,26 @@ export interface StandaloneModule {
74
71
  errFn: ((data: unknown, allErrors?: boolean) => ValidationResult) | null;
75
72
  }
76
73
 
77
- export class Validator {
74
+ export class Validator<T = unknown> {
78
75
  constructor(schema: object | string, options?: ValidatorOptions);
79
76
 
80
77
  /** Add a schema to the registry for cross-schema $ref resolution */
81
78
  addSchema(schema: object): void;
82
79
 
83
80
  /** Validate data, returns result with errors. Applies defaults, coerceTypes, removeAdditional. */
84
- validate(data: unknown): ValidationResult;
81
+ validate(data: unknown): ValidationResult<T>;
85
82
 
86
83
  /** Fast boolean check via JS codegen or tier 0 interpreter. No error collection. */
87
- isValidObject(data: unknown): boolean;
84
+ isValidObject(data: unknown): data is T;
88
85
 
89
86
  /** Validate a JSON string. Uses simdjson fast path for large documents. */
90
- validateJSON(jsonString: string): ValidationResult;
87
+ validateJSON(jsonString: string): ValidationResult<T>;
91
88
 
92
89
  /** Fast boolean check for a JSON string */
93
90
  isValidJSON(jsonString: string): boolean;
94
91
 
95
92
  /** Parse JSON with simdjson + validate against schema. Returns parsed value and validation result. Requires native addon. */
96
- validateAndParse(jsonString: string | Buffer): ValidateAndParseResult;
93
+ validateAndParse(jsonString: string | Buffer): ValidateAndParseResult<T>;
97
94
 
98
95
  /** Ultra-fast buffer validation via native addon */
99
96
  isValid(input: Buffer | Uint8Array | string): boolean;
@@ -128,7 +125,7 @@ export class Validator {
128
125
  toStandaloneModule(options?: { format?: 'esm' | 'cjs'; abortEarly?: boolean }): string | null;
129
126
 
130
127
  /** Load a pre-compiled standalone module. Zero schema compilation at startup. */
131
- static fromStandalone(mod: StandaloneModule, schema: object | string, options?: ValidatorOptions): Validator;
128
+ static fromStandalone<T = unknown>(mod: StandaloneModule, schema: object | string, options?: ValidatorOptions): Validator<T>;
132
129
 
133
130
  /** Bundle multiple schemas into a single JS module string. Load with Validator.loadBundle(). */
134
131
  static bundle(schemas: object[], options?: ValidatorOptions): string;
@@ -155,16 +152,16 @@ export class Validator {
155
152
  }
156
153
 
157
154
  /** One-shot validate: creates a Validator, validates data, returns result. */
158
- export function validate(
155
+ export function validate<T = unknown>(
159
156
  schema: object | string,
160
157
  data: unknown
161
- ): ValidationResult;
158
+ ): ValidationResult<T>;
162
159
 
163
160
  /** Fast compile: returns a validate function directly. WeakMap cached, second call with same schema is near-zero cost. */
164
- export function compile(
161
+ export function compile<T = unknown>(
165
162
  schema: object | string,
166
163
  options?: ValidatorOptions
167
- ): (data: unknown) => ValidationResult;
164
+ ): (data: unknown) => ValidationResult<T>;
168
165
 
169
166
  /** Parse JSON using simdjson (native addon) or JSON.parse (fallback). */
170
167
  export function parseJSON(jsonString: string | Buffer): unknown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "0.13.3",
3
+ "version": "0.14.0",
4
4
  "description": "Ultra-fast JSON Schema validator. 5x faster validation, 159,000x 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",
@@ -36,11 +36,12 @@
36
36
  },
37
37
  "scripts": {
38
38
  "install": "node scripts/install.js",
39
+ "prepublishOnly": "node scripts/check-prebuilds.js",
39
40
  "build": "cmake-js build --target ata",
40
41
  "rebuild": "cmake-js rebuild --target ata",
41
42
  "prebuild": "pkg-prebuilds-copy --baseDir build/Release --source ata.node --name=ata --strip --napi_version=10",
42
43
  "prebuild-all": "npm run prebuild -- --arch x64 && npm run prebuild -- --arch arm64",
43
- "test": "node test.js && node tests/test_no_native.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",
44
+ "test": "node test.js && node tests/test_no_native.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_typed_validator_runner.js",
44
45
  "test:suite": "node tests/run_suite.js",
45
46
  "test:compat": "node tests/test_compat.js",
46
47
  "test:standard-schema": "node tests/test_standard_schema.js",
@@ -48,6 +49,7 @@
48
49
  "test:ts": "node tests/test_ts_gen.js",
49
50
  "test:ts-corpus": "node tests/test_ts_corpus.js",
50
51
  "test:ts-differential": "node tests/test_ts_differential.js",
52
+ "test:typed": "node tests/test_typed_validator_runner.js",
51
53
  "bench": "node benchmark/bench_large.js",
52
54
  "fuzz": "node tests/fuzz_differential.js",
53
55
  "fuzz:long": "FUZZ_ITERATIONS=100000 node tests/fuzz_differential.js",
@@ -118,10 +120,13 @@
118
120
  "yaml": "^2.0.0"
119
121
  },
120
122
  "peerDependenciesMeta": {
121
- "yaml": { "optional": true }
123
+ "yaml": {
124
+ "optional": true
125
+ }
122
126
  },
123
127
  "devDependencies": {
124
128
  "@sinclair/typebox": "^0.34.49",
129
+ "@types/node": "^25.8.0",
125
130
  "cmake-js": "^8.0.0",
126
131
  "mitata": "^1.0.34",
127
132
  "typebox": "^1.1.7",
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ // Refuse to publish a tarball that is missing platform prebuilds.
4
+ // Local `npm publish` only carries whatever the publisher's machine built,
5
+ // which silently drops every other platform's prebuild from the tarball.
6
+ // Publishing must go through the release workflow so all platforms are present.
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const cp = require('child_process');
11
+
12
+ const required = [
13
+ 'ata-linux-x64',
14
+ 'ata-linux-arm64',
15
+ 'ata-linux-x64-musl',
16
+ 'ata-linux-arm64-musl',
17
+ 'ata-darwin-arm64',
18
+ 'ata-win32-x64',
19
+ ];
20
+
21
+ const root = path.resolve(__dirname, '..');
22
+ const missing = required.filter(
23
+ (dir) => !fs.existsSync(path.join(root, 'prebuilds', dir, 'node-napi-v10.node')),
24
+ );
25
+
26
+ if (missing.length) {
27
+ console.error(
28
+ '\n[ata-validator] Refusing to publish, missing prebuilds:\n' +
29
+ missing.map((d) => ' - ' + d).join('\n') +
30
+ '\n\nPublish through the GitHub release workflow so every platform is built.\n',
31
+ );
32
+ process.exit(1);
33
+ }
34
+
35
+ // When publishing from a Mac, verify the darwin code signatures too. strip
36
+ // invalidates the linker signature, and an unsigned arm64 addon is killed on
37
+ // load. The release workflow re-signs and verifies, this is the local net.
38
+ if (process.platform === 'darwin') {
39
+ for (const dir of required) {
40
+ if (!dir.startsWith('ata-darwin-')) continue;
41
+ const f = path.join(root, 'prebuilds', dir, 'node-napi-v10.node');
42
+ const r = cp.spawnSync('codesign', ['--verify', f], { encoding: 'utf8' });
43
+ if (r.status !== 0) {
44
+ console.error(
45
+ '\n[ata-validator] Refusing to publish, invalid code signature:\n' +
46
+ ' ' + f + '\n' + (r.stderr || '').trim() +
47
+ '\n\nRe-sign with: codesign --force --sign - "' + f + '"\n',
48
+ );
49
+ process.exit(1);
50
+ }
51
+ }
52
+ }
53
+
54
+ console.log('[ata-validator] prebuilds present for all ' + required.length + ' platforms');