ata-validator 0.6.1 → 0.7.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/CMakeLists.txt +132 -0
- package/compat.mjs +3 -0
- package/index.browser.mjs +4 -0
- package/index.js +7 -5
- package/index.mjs +3 -0
- package/lib/js-compiler.js +10 -7
- package/package.json +41 -10
- package/prebuilds/ata-darwin-arm64/node-napi-v10.node +0 -0
- package/prebuilds/ata-linux-arm64/node-napi-v10.node +0 -0
- package/prebuilds/ata-linux-x64/node-napi-v10.node +0 -0
- package/src/ata.cpp +8 -0
- package/binding.gyp +0 -44
- package/prebuilds/darwin-arm64/ata-validator.node +0 -0
package/CMakeLists.txt
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.28)
|
|
2
|
+
cmake_policy(SET CMP0091 NEW)
|
|
3
|
+
cmake_policy(SET CMP0042 NEW)
|
|
4
|
+
|
|
5
|
+
project(ata VERSION 0.1.0 LANGUAGES CXX)
|
|
6
|
+
|
|
7
|
+
set(CMAKE_EXPORT_COMPILE_COMMANDS True)
|
|
8
|
+
|
|
9
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
10
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
11
|
+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
12
|
+
|
|
13
|
+
if(MSVC)
|
|
14
|
+
set(ABSL_MSVC_STATIC_RUNTIME ON)
|
|
15
|
+
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>" CACHE STRING "" FORCE)
|
|
16
|
+
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
|
17
|
+
endif()
|
|
18
|
+
|
|
19
|
+
option(ATA_TESTING "Build test suite" ON)
|
|
20
|
+
option(ATA_BENCHMARKS "Build benchmarks" OFF)
|
|
21
|
+
option(ATA_SANITIZE "Enable address sanitizer" OFF)
|
|
22
|
+
|
|
23
|
+
# Fetch simdjson
|
|
24
|
+
include(FetchContent)
|
|
25
|
+
FetchContent_Declare(
|
|
26
|
+
simdjson
|
|
27
|
+
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
|
|
28
|
+
GIT_TAG v3.12.2
|
|
29
|
+
GIT_SHALLOW TRUE
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# RE2 — fast regex engine (replaces std::regex)
|
|
33
|
+
set(RE2_BUILD_TESTING OFF CACHE BOOL "" FORCE)
|
|
34
|
+
FetchContent_Declare(
|
|
35
|
+
re2
|
|
36
|
+
GIT_REPOSITORY https://github.com/google/re2.git
|
|
37
|
+
GIT_TAG 2024-07-02
|
|
38
|
+
GIT_SHALLOW TRUE
|
|
39
|
+
EXCLUDE_FROM_ALL
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Abseil — required by RE2
|
|
43
|
+
set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "" FORCE)
|
|
44
|
+
set(ABSL_BUILD_TESTING OFF CACHE BOOL "" FORCE)
|
|
45
|
+
set(ABSL_ENABLE_INSTALL ON CACHE BOOL "" FORCE)
|
|
46
|
+
FetchContent_Declare(
|
|
47
|
+
abseil-cpp
|
|
48
|
+
GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git
|
|
49
|
+
GIT_TAG 20240722.0
|
|
50
|
+
GIT_SHALLOW TRUE
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
FetchContent_MakeAvailable(abseil-cpp simdjson re2)
|
|
54
|
+
|
|
55
|
+
if (CMAKE_JS_VERSION)
|
|
56
|
+
# add_definitions(-DNAPI_VERSION=10)
|
|
57
|
+
include_directories(${CMAKE_JS_INC})
|
|
58
|
+
file(GLOB SOURCE_FILES "binding/*.cpp" "src/*.cpp" "deps/simdjson/*.cpp")
|
|
59
|
+
else()
|
|
60
|
+
file(GLOB SOURCE_FILES "src/*.cpp")
|
|
61
|
+
endif()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if (CMAKE_JS_VERSION)
|
|
65
|
+
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})
|
|
66
|
+
|
|
67
|
+
# Include Node-API wrappers
|
|
68
|
+
execute_process(
|
|
69
|
+
COMMAND node -p "require('node-addon-api').include"
|
|
70
|
+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
71
|
+
OUTPUT_VARIABLE NODE_ADDON_API_DIR
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
|
|
75
|
+
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})
|
|
76
|
+
|
|
77
|
+
# Include Node-API C headers
|
|
78
|
+
execute_process(
|
|
79
|
+
COMMAND node -p "require('node-api-headers').include_dir"
|
|
80
|
+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
81
|
+
OUTPUT_VARIABLE NODE_API_HEADERS_DIR
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
string(REGEX REPLACE "[\r\n\"]" "" NODE_API_HEADERS_DIR ${NODE_API_HEADERS_DIR})
|
|
85
|
+
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_API_HEADERS_DIR})
|
|
86
|
+
else()
|
|
87
|
+
add_library(${PROJECT_NAME} ${SOURCE_FILES})
|
|
88
|
+
endif()
|
|
89
|
+
|
|
90
|
+
target_include_directories(${PROJECT_NAME} PUBLIC include)
|
|
91
|
+
target_link_libraries(${PROJECT_NAME} PRIVATE simdjson re2)
|
|
92
|
+
|
|
93
|
+
if (CMAKE_JS_VERSION)
|
|
94
|
+
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_JS_INC})
|
|
95
|
+
target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_JS_LIB})
|
|
96
|
+
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
|
|
97
|
+
endif()
|
|
98
|
+
|
|
99
|
+
add_library(ata::ata ALIAS ata)
|
|
100
|
+
|
|
101
|
+
if(CMAKE_JS_VERSION AND UNIX AND NOT APPLE)
|
|
102
|
+
target_compile_options(${PROJECT_NAME} PRIVATE -fvisibility=hidden -fvisibility-inlines-hidden)
|
|
103
|
+
target_link_options(${PROJECT_NAME} PRIVATE -Wl,--exclude-libs,ALL)
|
|
104
|
+
endif()
|
|
105
|
+
|
|
106
|
+
if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET)
|
|
107
|
+
# Generate node.lib
|
|
108
|
+
execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS})
|
|
109
|
+
endif()
|
|
110
|
+
|
|
111
|
+
if(ATA_SANITIZE)
|
|
112
|
+
target_compile_options(ata PRIVATE -fsanitize=address -fno-omit-frame-pointer)
|
|
113
|
+
target_link_options(ata PRIVATE -fsanitize=address)
|
|
114
|
+
endif()
|
|
115
|
+
|
|
116
|
+
# Tests
|
|
117
|
+
if(ATA_TESTING)
|
|
118
|
+
enable_testing()
|
|
119
|
+
add_executable(ata_tests tests/test.cpp)
|
|
120
|
+
target_link_libraries(ata_tests PRIVATE ata simdjson)
|
|
121
|
+
if(ATA_SANITIZE)
|
|
122
|
+
target_compile_options(ata_tests PRIVATE -fsanitize=address -fno-omit-frame-pointer)
|
|
123
|
+
target_link_options(ata_tests PRIVATE -fsanitize=address)
|
|
124
|
+
endif()
|
|
125
|
+
add_test(NAME ata_tests COMMAND ata_tests)
|
|
126
|
+
endif()
|
|
127
|
+
|
|
128
|
+
# Benchmarks
|
|
129
|
+
if(ATA_BENCHMARKS)
|
|
130
|
+
add_executable(ata_bench benchmark/bench.cpp)
|
|
131
|
+
target_link_libraries(ata_bench PRIVATE ata simdjson)
|
|
132
|
+
endif()
|
package/compat.mjs
ADDED
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Native addon: optional. Core validate() uses JS codegen and works without it.
|
|
2
2
|
// Buffer APIs (isValid, countValid, isValidParallel) require native.
|
|
3
3
|
let native;
|
|
4
|
-
try { native = require("
|
|
4
|
+
try { native = require("pkg-prebuilds")(__dirname, require("./binding-options")); } catch {}
|
|
5
5
|
const {
|
|
6
6
|
compileToJS,
|
|
7
7
|
compileToJSCodegen,
|
|
@@ -282,6 +282,7 @@ function parsePointerPath(path) {
|
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
function createPaddedBuffer(jsonStr) {
|
|
285
|
+
if (typeof Buffer === 'undefined') throw new Error('createPaddedBuffer requires Node.js Buffer');
|
|
285
286
|
const jsonBuf = Buffer.from(jsonStr);
|
|
286
287
|
const padded = Buffer.allocUnsafe(jsonBuf.length + SIMDJSON_PADDING);
|
|
287
288
|
jsonBuf.copy(padded);
|
|
@@ -405,11 +406,12 @@ class Validator {
|
|
|
405
406
|
: this._schemaStr;
|
|
406
407
|
const cached = _compileCache.get(mapKey);
|
|
407
408
|
let jsFn, jsCombinedFn, jsErrFn;
|
|
408
|
-
|
|
409
|
+
var _forceNapi = typeof process !== 'undefined' && process.env && process.env.ATA_FORCE_NAPI;
|
|
410
|
+
if (cached && !_forceNapi) {
|
|
409
411
|
jsFn = cached.jsFn;
|
|
410
412
|
jsCombinedFn = cached.combined;
|
|
411
413
|
jsErrFn = cached.errFn;
|
|
412
|
-
} else if (!
|
|
414
|
+
} else if (!_forceNapi) {
|
|
413
415
|
jsFn = compileToJSCodegen(schemaObj, sm) || compileToJS(schemaObj, null, sm);
|
|
414
416
|
jsCombinedFn = compileToJSCombined(schemaObj, VALID_RESULT, sm);
|
|
415
417
|
jsErrFn = compileToJSCodegenWithErrors(schemaObj, sm);
|
|
@@ -686,7 +688,7 @@ class Validator {
|
|
|
686
688
|
|
|
687
689
|
_ensureCodegen() {
|
|
688
690
|
if (this._jsFn) return;
|
|
689
|
-
if (process.env.ATA_FORCE_NAPI) return;
|
|
691
|
+
if (typeof process !== 'undefined' && process.env && process.env.ATA_FORCE_NAPI) return;
|
|
690
692
|
const sm = this._schemaMap.size > 0 ? this._schemaMap : null;
|
|
691
693
|
const mapKey = this._schemaMap.size > 0
|
|
692
694
|
? this._schemaStr + '\0' + [...this._schemaMap.keys()].sort().join('\0')
|
|
@@ -902,7 +904,7 @@ function validate(schema, data) {
|
|
|
902
904
|
|
|
903
905
|
function version() {
|
|
904
906
|
if (native) return native.version();
|
|
905
|
-
return require("./package.json").version;
|
|
907
|
+
try { return require("./package.json").version; } catch { return "unknown"; }
|
|
906
908
|
}
|
|
907
909
|
|
|
908
910
|
// Bundle multiple validators into a single JS file for fast startup.
|
package/index.mjs
ADDED
package/lib/js-compiler.js
CHANGED
|
@@ -2331,13 +2331,16 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
2331
2331
|
if (destructKeys.length > 0) lines.push(`const{${destructKeys.join(',')}}=${v}`)
|
|
2332
2332
|
for (const key of schema.required) {
|
|
2333
2333
|
const check = hoisted[key] ? `${hoisted[key]}===undefined` : `${v}[${JSON.stringify(key)}]===undefined`
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2334
|
+
if (isStaticPath) {
|
|
2335
|
+
const ei = ctx.varCounter++
|
|
2336
|
+
const errVar = `_E${ei}`
|
|
2337
|
+
const pathVal = pathExpr ? pathExpr.slice(1, -1) : ''
|
|
2338
|
+
ctx.closureVars.push(errVar)
|
|
2339
|
+
ctx.closureVals.push(Object.freeze({keyword: 'required', instancePath: pathVal, schemaPath: `${schemaPrefix}/required`, params: Object.freeze({missingProperty: key}), message: `must have required property '${key}'`}))
|
|
2340
|
+
lines.push(`if(${check}){(_e||(_e=[])).push(${errVar})}`)
|
|
2341
|
+
} else {
|
|
2342
|
+
lines.push(`if(${check}){(_e||(_e=[])).push({keyword:'required',instancePath:${pathExpr||'""'},schemaPath:'${schemaPrefix}/required',params:{missingProperty:'${esc(key)}'},message:"must have required property '${esc(key)}'"})}`)
|
|
2343
|
+
}
|
|
2341
2344
|
}
|
|
2342
2345
|
} else if (schema.required) {
|
|
2343
2346
|
for (const key of schema.required) {
|
package/package.json
CHANGED
|
@@ -1,18 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
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
|
+
"module": "index.mjs",
|
|
6
7
|
"types": "index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./index.d.ts",
|
|
11
|
+
"browser": {
|
|
12
|
+
"import": "./index.browser.mjs",
|
|
13
|
+
"require": "./index.js"
|
|
14
|
+
},
|
|
15
|
+
"import": "./index.mjs",
|
|
16
|
+
"require": "./index.js"
|
|
17
|
+
},
|
|
18
|
+
"./compat": {
|
|
19
|
+
"types": "./compat.d.ts",
|
|
20
|
+
"import": "./compat.mjs",
|
|
21
|
+
"require": "./compat.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"browser": {
|
|
26
|
+
"pkg-prebuilds": false
|
|
27
|
+
},
|
|
7
28
|
"scripts": {
|
|
8
|
-
"install": "
|
|
9
|
-
"build": "
|
|
10
|
-
"
|
|
11
|
-
"prebuild
|
|
29
|
+
"install": "pkg-prebuilds-verify ./binding-options.js || cmake-js compile --target ata",
|
|
30
|
+
"build": "cmake-js build --target ata",
|
|
31
|
+
"rebuild": "cmake-js rebuild --target ata",
|
|
32
|
+
"prebuild": "pkg-prebuilds-copy --baseDir build/Release --source ata.node --name=ata --strip --napi_version=10",
|
|
33
|
+
"prebuild-all": "npm run prebuild -- --arch x64 && npm run prebuild -- --arch arm64",
|
|
12
34
|
"test": "node test.js",
|
|
13
35
|
"test:suite": "node tests/run_suite.js",
|
|
14
36
|
"test:compat": "node tests/test_compat.js",
|
|
15
37
|
"test:standard-schema": "node tests/test_standard_schema.js",
|
|
38
|
+
"test:browser": "node tests/test_browser.js",
|
|
16
39
|
"bench": "node benchmark/bench_large.js",
|
|
17
40
|
"fuzz": "node tests/fuzz_differential.js",
|
|
18
41
|
"fuzz:long": "FUZZ_ITERATIONS=100000 node tests/fuzz_differential.js"
|
|
@@ -47,9 +70,12 @@
|
|
|
47
70
|
},
|
|
48
71
|
"files": [
|
|
49
72
|
"index.js",
|
|
73
|
+
"index.mjs",
|
|
74
|
+
"index.browser.mjs",
|
|
50
75
|
"index.d.ts",
|
|
51
76
|
"lib/",
|
|
52
77
|
"compat.js",
|
|
78
|
+
"compat.mjs",
|
|
53
79
|
"compat.d.ts",
|
|
54
80
|
"binding.gyp",
|
|
55
81
|
"binding/",
|
|
@@ -57,21 +83,26 @@
|
|
|
57
83
|
"src/",
|
|
58
84
|
"deps/",
|
|
59
85
|
"prebuilds/",
|
|
86
|
+
"/CMakeLists.txt",
|
|
60
87
|
"README.md",
|
|
61
88
|
"LICENSE"
|
|
62
89
|
],
|
|
63
90
|
"dependencies": {
|
|
64
|
-
"node-addon-api": "^8.
|
|
65
|
-
"node-
|
|
91
|
+
"node-addon-api": "^8.7.0",
|
|
92
|
+
"node-api-headers": "^1.8.0",
|
|
93
|
+
"pkg-prebuilds": "^1.0.0"
|
|
66
94
|
},
|
|
67
95
|
"devDependencies": {
|
|
68
96
|
"@sinclair/typebox": "^0.34.49",
|
|
97
|
+
"cmake-js": "^8.0.0",
|
|
69
98
|
"mitata": "^1.0.34",
|
|
70
|
-
"node-gyp": "^11.0.0",
|
|
71
|
-
"prebuildify": "^6.0.1",
|
|
72
99
|
"typebox": "^1.1.7",
|
|
73
100
|
"valibot": "^1.3.1",
|
|
74
101
|
"zod": "^4.3.6"
|
|
75
102
|
},
|
|
76
|
-
"
|
|
103
|
+
"binary": {
|
|
104
|
+
"napi_versions": [
|
|
105
|
+
10
|
|
106
|
+
]
|
|
107
|
+
}
|
|
77
108
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/ata.cpp
CHANGED
|
@@ -19,6 +19,14 @@
|
|
|
19
19
|
#include <unistd.h>
|
|
20
20
|
#endif
|
|
21
21
|
|
|
22
|
+
// MSVC implementation by Pavel P (https://gist.github.com/pps83/3210a2f980fd02bb2ba2e5a1fc4a2ef0)
|
|
23
|
+
#if defined(_MSC_VER) && !defined(__clang__)
|
|
24
|
+
#include <intrin.h>
|
|
25
|
+
#ifndef __builtin_popcount
|
|
26
|
+
#define __builtin_popcount __popcnt
|
|
27
|
+
#endif
|
|
28
|
+
#endif // defined(_MSC_VER) && !defined(__clang__)
|
|
29
|
+
|
|
22
30
|
#include "simdjson.h"
|
|
23
31
|
|
|
24
32
|
// --- Fast format validators (no std::regex) ---
|
package/binding.gyp
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"targets": [
|
|
3
|
-
{
|
|
4
|
-
"target_name": "ata",
|
|
5
|
-
"sources": [
|
|
6
|
-
"binding/ata_napi.cpp",
|
|
7
|
-
"src/ata.cpp",
|
|
8
|
-
"deps/simdjson/simdjson.cpp"
|
|
9
|
-
],
|
|
10
|
-
"include_dirs": [
|
|
11
|
-
"<!@(node -p \"require('node-addon-api').include\")",
|
|
12
|
-
"include",
|
|
13
|
-
"deps/simdjson",
|
|
14
|
-
"<!@(node -e \"var p=process.platform,a=process.arch;if(p==='darwin'){console.log(a==='arm64'?'/opt/homebrew/opt/re2/include':'/usr/local/opt/re2/include');console.log(a==='arm64'?'/opt/homebrew/opt/abseil/include':'/usr/local/opt/abseil/include');console.log(a==='arm64'?'/opt/homebrew/opt/mimalloc/include':'/usr/local/opt/mimalloc/include')}else{console.log('/usr/include')}\")"
|
|
15
|
-
],
|
|
16
|
-
"libraries": [
|
|
17
|
-
"<!@(node -e \"var p=process.platform,a=process.arch;if(p==='darwin'){var pre=a==='arm64'?'/opt/homebrew/opt/re2':'/usr/local/opt/re2';var mi=a==='arm64'?'/opt/homebrew/opt/mimalloc':'/usr/local/opt/mimalloc';console.log('-L'+pre+'/lib -lre2 -L'+mi+'/lib -lmimalloc')}else{console.log('-lre2')}\")"
|
|
18
|
-
],
|
|
19
|
-
"dependencies": [
|
|
20
|
-
"<!(node -p \"require('node-addon-api').gyp\")"
|
|
21
|
-
],
|
|
22
|
-
"cflags!": ["-fno-exceptions"],
|
|
23
|
-
"cflags_cc!": ["-fno-exceptions"],
|
|
24
|
-
"cflags_cc": ["-std=c++20"],
|
|
25
|
-
"defines": ["NAPI_DISABLE_CPP_EXCEPTIONS", "NDEBUG"],
|
|
26
|
-
"conditions": [
|
|
27
|
-
["OS=='mac'", {
|
|
28
|
-
"xcode_settings": {
|
|
29
|
-
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
|
|
30
|
-
"CLANG_CXX_LANGUAGE_STANDARD": "c++20",
|
|
31
|
-
"MACOSX_DEPLOYMENT_TARGET": "12.0"
|
|
32
|
-
}
|
|
33
|
-
}],
|
|
34
|
-
["OS=='win'", {
|
|
35
|
-
"msvs_settings": {
|
|
36
|
-
"VCCLCompilerTool": {
|
|
37
|
-
"AdditionalOptions": ["/std:c++20", "/EHsc"]
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}]
|
|
41
|
-
]
|
|
42
|
-
}
|
|
43
|
-
]
|
|
44
|
-
}
|
|
Binary file
|