@happyvertical/json 0.74.8
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/LICENSE +7 -0
- package/README.md +184 -0
- package/dist/adapters/index.d.ts +8 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +9 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/native.d.ts +45 -0
- package/dist/adapters/native.d.ts.map +1 -0
- package/dist/adapters/sonic.d.ts +63 -0
- package/dist/adapters/sonic.d.ts.map +1 -0
- package/dist/chunks/sonic-BVfKq74R.js +332 -0
- package/dist/chunks/sonic-BVfKq74R.js.map +1 -0
- package/dist/factory.d.ts +59 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/index.d.ts +181 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +131 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +182 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +81 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright <2025> <Happy Vertical Corporation>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# @happyvertical/json
|
|
2
|
+
|
|
3
|
+
High-performance JSON parsing and serialization with Rust SIMD acceleration and automatic fallback.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Drop-in replacements** for `JSON.parse()` and `JSON.stringify()`
|
|
8
|
+
- **2-3x faster** parsing with SIMD acceleration on supported platforms
|
|
9
|
+
- **Automatic fallback** to native JavaScript when Rust bindings unavailable
|
|
10
|
+
- **Deep clone** function optimized for the `JSON.parse(JSON.stringify())` pattern
|
|
11
|
+
- **Safe variants** that don't throw on errors
|
|
12
|
+
- **TypeScript first** with full type definitions
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @happyvertical/json
|
|
18
|
+
# or
|
|
19
|
+
pnpm add @happyvertical/json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { parse, stringify, clone } from '@happyvertical/json';
|
|
26
|
+
|
|
27
|
+
// Drop-in replacements for JSON.parse/stringify
|
|
28
|
+
const data = parse<User>('{"name": "Alice", "age": 30}');
|
|
29
|
+
const json = stringify(data);
|
|
30
|
+
|
|
31
|
+
// Deep clone (optimized)
|
|
32
|
+
const copy = clone(data);
|
|
33
|
+
copy.age = 31; // Original unchanged
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
### Drop-in Replacements
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// Parse JSON string to value
|
|
42
|
+
function parse<T = unknown>(text: string, reviver?: Reviver): T;
|
|
43
|
+
|
|
44
|
+
// Stringify value to JSON
|
|
45
|
+
function stringify(value: unknown, replacer?: Replacer, space?: number | string): string;
|
|
46
|
+
|
|
47
|
+
// Deep clone via JSON round-trip (optimized)
|
|
48
|
+
function clone<T>(value: T): T;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Safe Variants (Non-throwing)
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { safeParse, safeStringify } from '@happyvertical/json';
|
|
55
|
+
|
|
56
|
+
// Returns { success: true, value: T } or { success: false, error: ParseError }
|
|
57
|
+
const result = safeParse<User>(maybeInvalidJson);
|
|
58
|
+
if (result.success) {
|
|
59
|
+
console.log(result.value.name);
|
|
60
|
+
} else {
|
|
61
|
+
console.error(`Line ${result.error.line}: ${result.error.message}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Returns { success: true, value: string } or { success: false, error: StringifyError }
|
|
65
|
+
const stringifyResult = safeStringify(data);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Validation
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { isValid } from '@happyvertical/json';
|
|
72
|
+
|
|
73
|
+
if (isValid(userInput)) {
|
|
74
|
+
const data = parse(userInput);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Adapter Control
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { JSONFactory, getAdapterInfo, isSIMDAvailable } from '@happyvertical/json';
|
|
82
|
+
|
|
83
|
+
// Check if SIMD acceleration is available
|
|
84
|
+
console.log(isSIMDAvailable()); // true on supported platforms
|
|
85
|
+
|
|
86
|
+
// Get detailed adapter info
|
|
87
|
+
const info = getAdapterInfo();
|
|
88
|
+
console.log(info.name); // 'sonic' or 'native'
|
|
89
|
+
console.log(info.simdEnabled); // true if using SIMD
|
|
90
|
+
|
|
91
|
+
// Create adapter with specific options
|
|
92
|
+
const json = JSONFactory.create({
|
|
93
|
+
adapter: 'sonic', // 'sonic' | 'native' | 'auto'
|
|
94
|
+
fallback: true, // Fall back to native if sonic unavailable
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Use adapter directly
|
|
98
|
+
const data = json.parse('{"key": "value"}');
|
|
99
|
+
const text = json.stringify(data);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Performance
|
|
103
|
+
|
|
104
|
+
On a 680KB manifest file (typical SMRT manifest):
|
|
105
|
+
|
|
106
|
+
| Operation | Native JSON | @happyvertical/json | Speedup |
|
|
107
|
+
|-----------|-------------|---------------------|---------|
|
|
108
|
+
| Parse | ~50ms | ~18ms | **2.8x** |
|
|
109
|
+
| Stringify | ~45ms | ~16ms | **2.8x** |
|
|
110
|
+
| Clone | ~95ms | ~34ms | **2.8x** |
|
|
111
|
+
|
|
112
|
+
Results measured on Apple M2 Pro. Performance varies by platform and data structure.
|
|
113
|
+
|
|
114
|
+
## Platform Support
|
|
115
|
+
|
|
116
|
+
SIMD-accelerated parsing is available on:
|
|
117
|
+
|
|
118
|
+
- ✅ macOS ARM64 (Apple Silicon)
|
|
119
|
+
- ✅ macOS x64 (Intel)
|
|
120
|
+
- ✅ Linux x64 (GNU)
|
|
121
|
+
- ✅ Linux ARM64 (GNU)
|
|
122
|
+
- ✅ Linux x64 (musl)
|
|
123
|
+
- ✅ Linux ARM64 (musl)
|
|
124
|
+
- ✅ Windows x64
|
|
125
|
+
|
|
126
|
+
When the native binary isn't available, the package automatically falls back to JavaScript's built-in `JSON` object with zero overhead.
|
|
127
|
+
|
|
128
|
+
## How It Works
|
|
129
|
+
|
|
130
|
+
This package uses [sonic-rs](https://github.com/cloudwego/sonic-rs) - a Rust JSON library that uses SIMD instructions (AVX2, SSE4.2, NEON) for vectorized parsing and serialization.
|
|
131
|
+
|
|
132
|
+
The adapter pattern allows:
|
|
133
|
+
1. Automatic selection of the fastest available implementation
|
|
134
|
+
2. Graceful fallback when native bindings can't be loaded
|
|
135
|
+
3. Consistent API regardless of the underlying implementation
|
|
136
|
+
|
|
137
|
+
## TypeScript Support
|
|
138
|
+
|
|
139
|
+
Full TypeScript definitions included:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
interface User {
|
|
143
|
+
name: string;
|
|
144
|
+
age: number;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Type-safe parsing
|
|
148
|
+
const user = parse<User>('{"name": "Alice", "age": 30}');
|
|
149
|
+
user.name; // string
|
|
150
|
+
user.age; // number
|
|
151
|
+
|
|
152
|
+
// Safe variant preserves types
|
|
153
|
+
const result = safeParse<User>(json);
|
|
154
|
+
if (result.success) {
|
|
155
|
+
result.value.name; // string
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Reviver and Replacer
|
|
160
|
+
|
|
161
|
+
Standard reviver and replacer functions work as expected:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// Reviver for custom transformations
|
|
165
|
+
const data = parse<{ date: Date }>(
|
|
166
|
+
'{"date": "2024-01-15"}',
|
|
167
|
+
(key, value) => (key === 'date' ? new Date(value) : value)
|
|
168
|
+
);
|
|
169
|
+
console.log(data.date instanceof Date); // true
|
|
170
|
+
|
|
171
|
+
// Replacer for filtering
|
|
172
|
+
const json = stringify(data, ['name', 'email']); // Only include these keys
|
|
173
|
+
|
|
174
|
+
// Pretty printing
|
|
175
|
+
const pretty = stringify(data, null, 2);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Environment Variables
|
|
179
|
+
|
|
180
|
+
- `HAPPYVERTICAL_JSON_ADAPTER`: Force adapter selection ('sonic' | 'native')
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AdapterInfo, JSONAdapter, ParseError, Replacer, Result, Reviver, StringifyError } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Native JavaScript JSON adapter implementation
|
|
4
|
+
*
|
|
5
|
+
* Provides the same interface as the Rust adapter but uses
|
|
6
|
+
* the built-in JSON object. Useful as a fallback and for
|
|
7
|
+
* environments where native bindings can't be loaded.
|
|
8
|
+
*/
|
|
9
|
+
export declare class NativeAdapter implements JSONAdapter {
|
|
10
|
+
readonly name: "native";
|
|
11
|
+
readonly isNative = true;
|
|
12
|
+
/**
|
|
13
|
+
* Parse a JSON string using native JSON.parse
|
|
14
|
+
*/
|
|
15
|
+
parse<T = unknown>(text: string, reviver?: Reviver): T;
|
|
16
|
+
/**
|
|
17
|
+
* Stringify a value using native JSON.stringify
|
|
18
|
+
*/
|
|
19
|
+
stringify(value: unknown, replacer?: Replacer, space?: number | string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Deep clone via JSON round-trip
|
|
22
|
+
*/
|
|
23
|
+
clone<T>(value: T): T;
|
|
24
|
+
/**
|
|
25
|
+
* Parse without throwing
|
|
26
|
+
*/
|
|
27
|
+
safeParse<T = unknown>(text: string): Result<T, ParseError>;
|
|
28
|
+
/**
|
|
29
|
+
* Stringify without throwing
|
|
30
|
+
*/
|
|
31
|
+
safeStringify(value: unknown): Result<string, StringifyError>;
|
|
32
|
+
/**
|
|
33
|
+
* Validate JSON string
|
|
34
|
+
*/
|
|
35
|
+
isValid(text: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Get adapter information
|
|
38
|
+
*/
|
|
39
|
+
getInfo(): AdapterInfo;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Singleton instance of the native adapter
|
|
43
|
+
*/
|
|
44
|
+
export declare const nativeAdapter: NativeAdapter;
|
|
45
|
+
//# sourceMappingURL=native.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../../src/adapters/native.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,UAAU,EACV,QAAQ,EACR,MAAM,EACN,OAAO,EACP,cAAc,EACf,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAU;IAClC,QAAQ,CAAC,QAAQ,QAAQ;IAEzB;;OAEG;IACH,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC;IAItD;;OAEG;IACH,SAAS,CACP,KAAK,EAAE,OAAO,EACd,QAAQ,CAAC,EAAE,QAAQ,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GACtB,MAAM;IAoBT;;OAEG;IACH,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC;IAIrB;;OAEG;IACH,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC;IAiC3D;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC;IAmB7D;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAS9B;;OAEG;IACH,OAAO,IAAI,WAAW;CAQvB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,eAAsB,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { AdapterInfo, JSONAdapter, NativeBindings, ParseError, Replacer, Result, Reviver, StringifyError } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sonic (Rust SIMD) JSON adapter implementation
|
|
4
|
+
*
|
|
5
|
+
* Uses sonic-rs for SIMD-accelerated parsing and serialization.
|
|
6
|
+
* On supported platforms, this can be 2-3x faster than native JSON.
|
|
7
|
+
*/
|
|
8
|
+
export declare class SonicAdapter implements JSONAdapter {
|
|
9
|
+
readonly name: "sonic";
|
|
10
|
+
readonly isNative = false;
|
|
11
|
+
private native;
|
|
12
|
+
constructor(native: NativeBindings);
|
|
13
|
+
/**
|
|
14
|
+
* Parse a JSON string using sonic-rs
|
|
15
|
+
*/
|
|
16
|
+
parse<T = unknown>(text: string, reviver?: Reviver): T;
|
|
17
|
+
/**
|
|
18
|
+
* Stringify a value using sonic-rs
|
|
19
|
+
*/
|
|
20
|
+
stringify(value: unknown, replacer?: Replacer, space?: number | string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Deep clone via sonic-rs round-trip
|
|
23
|
+
*/
|
|
24
|
+
clone<T>(value: T): T;
|
|
25
|
+
/**
|
|
26
|
+
* Parse without throwing
|
|
27
|
+
*/
|
|
28
|
+
safeParse<T = unknown>(text: string): Result<T, ParseError>;
|
|
29
|
+
/**
|
|
30
|
+
* Stringify without throwing
|
|
31
|
+
*/
|
|
32
|
+
safeStringify(value: unknown): Result<string, StringifyError>;
|
|
33
|
+
/**
|
|
34
|
+
* Validate JSON string using sonic-rs
|
|
35
|
+
*/
|
|
36
|
+
isValid(text: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Get adapter information
|
|
39
|
+
*/
|
|
40
|
+
getInfo(): AdapterInfo;
|
|
41
|
+
/**
|
|
42
|
+
* Apply reviver function to parsed value
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
private applyReviver;
|
|
46
|
+
private walkReviver;
|
|
47
|
+
/**
|
|
48
|
+
* Apply replacer to value before stringifying
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
private applyReplacer;
|
|
52
|
+
private walkReplacer;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Try to create a SonicAdapter instance
|
|
56
|
+
* Returns null if native bindings are not available
|
|
57
|
+
*/
|
|
58
|
+
export declare function createSonicAdapter(): SonicAdapter | null;
|
|
59
|
+
/**
|
|
60
|
+
* Check if sonic (Rust) adapter is available on this platform
|
|
61
|
+
*/
|
|
62
|
+
export declare function isSonicAvailable(): boolean;
|
|
63
|
+
//# sourceMappingURL=sonic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sonic.d.ts","sourceRoot":"","sources":["../../src/adapters/sonic.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,UAAU,EACV,QAAQ,EACR,MAAM,EACN,OAAO,EACP,cAAc,EACf,MAAM,aAAa,CAAC;AAiCrB;;;;;GAKG;AACH,qBAAa,YAAa,YAAW,WAAW;IAC9C,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;IACjC,QAAQ,CAAC,QAAQ,SAAS;IAE1B,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,EAAE,cAAc;IAIlC;;OAEG;IACH,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC;IAWtD;;OAEG;IACH,SAAS,CACP,KAAK,EAAE,OAAO,EACd,QAAQ,CAAC,EAAE,QAAQ,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GACtB,MAAM;IAmBT;;OAEG;IACH,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC;IAIrB;;OAEG;IACH,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC;IAmB3D;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC;IAa7D;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI9B;;OAEG;IACH,OAAO,IAAI,WAAW;IAUtB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,WAAW;IAyCnB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAwBrB,OAAO,CAAC,YAAY;CAsCrB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,GAAG,IAAI,CAMxD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
class NativeAdapter {
|
|
5
|
+
name = "native";
|
|
6
|
+
isNative = true;
|
|
7
|
+
/**
|
|
8
|
+
* Parse a JSON string using native JSON.parse
|
|
9
|
+
*/
|
|
10
|
+
parse(text, reviver) {
|
|
11
|
+
return JSON.parse(text, reviver);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Stringify a value using native JSON.stringify
|
|
15
|
+
*/
|
|
16
|
+
stringify(value, replacer, space) {
|
|
17
|
+
let result;
|
|
18
|
+
if (typeof replacer === "function") {
|
|
19
|
+
result = JSON.stringify(value, replacer, space);
|
|
20
|
+
} else if (Array.isArray(replacer)) {
|
|
21
|
+
result = JSON.stringify(value, replacer, space);
|
|
22
|
+
} else {
|
|
23
|
+
result = JSON.stringify(value, null, space);
|
|
24
|
+
}
|
|
25
|
+
if (result === void 0) {
|
|
26
|
+
throw new TypeError("Unable to stringify value");
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Deep clone via JSON round-trip
|
|
32
|
+
*/
|
|
33
|
+
clone(value) {
|
|
34
|
+
return JSON.parse(JSON.stringify(value));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse without throwing
|
|
38
|
+
*/
|
|
39
|
+
safeParse(text) {
|
|
40
|
+
try {
|
|
41
|
+
const value = JSON.parse(text);
|
|
42
|
+
return { success: true, value };
|
|
43
|
+
} catch (e) {
|
|
44
|
+
const error = e;
|
|
45
|
+
const parseError = {
|
|
46
|
+
message: error.message
|
|
47
|
+
};
|
|
48
|
+
const posMatch = error.message.match(/position (\d+)/i);
|
|
49
|
+
if (posMatch) {
|
|
50
|
+
const position = parseInt(posMatch[1], 10);
|
|
51
|
+
let line = 1;
|
|
52
|
+
let column = 1;
|
|
53
|
+
for (let i = 0; i < position && i < text.length; i++) {
|
|
54
|
+
if (text[i] === "\n") {
|
|
55
|
+
line++;
|
|
56
|
+
column = 1;
|
|
57
|
+
} else {
|
|
58
|
+
column++;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
parseError.line = line;
|
|
62
|
+
parseError.column = column;
|
|
63
|
+
}
|
|
64
|
+
return { success: false, error: parseError };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Stringify without throwing
|
|
69
|
+
*/
|
|
70
|
+
safeStringify(value) {
|
|
71
|
+
try {
|
|
72
|
+
const result = JSON.stringify(value);
|
|
73
|
+
if (result === void 0) {
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
error: { message: "Unable to stringify value" }
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return { success: true, value: result };
|
|
80
|
+
} catch (e) {
|
|
81
|
+
const error = e;
|
|
82
|
+
return {
|
|
83
|
+
success: false,
|
|
84
|
+
error: { message: error.message }
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Validate JSON string
|
|
90
|
+
*/
|
|
91
|
+
isValid(text) {
|
|
92
|
+
try {
|
|
93
|
+
JSON.parse(text);
|
|
94
|
+
return true;
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get adapter information
|
|
101
|
+
*/
|
|
102
|
+
getInfo() {
|
|
103
|
+
return {
|
|
104
|
+
name: "native",
|
|
105
|
+
isNative: true,
|
|
106
|
+
version: typeof process !== "undefined" ? process.version : "unknown",
|
|
107
|
+
simdEnabled: false
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const nativeAdapter = new NativeAdapter();
|
|
112
|
+
const require$1 = createRequire(import.meta.url);
|
|
113
|
+
function tryLoadNative() {
|
|
114
|
+
const Filename = fileURLToPath(import.meta.url);
|
|
115
|
+
const Dirname = dirname(Filename);
|
|
116
|
+
const paths = [
|
|
117
|
+
join(Dirname, "..", "..", "json-native.node"),
|
|
118
|
+
// dist/../json-native.node
|
|
119
|
+
join(Dirname, "..", "json-native.node"),
|
|
120
|
+
// dist/json-native.node
|
|
121
|
+
join(Dirname, "json-native.node")
|
|
122
|
+
// In same directory
|
|
123
|
+
];
|
|
124
|
+
for (const modulePath of paths) {
|
|
125
|
+
try {
|
|
126
|
+
const native = require$1(modulePath);
|
|
127
|
+
return native;
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
class SonicAdapter {
|
|
134
|
+
name = "sonic";
|
|
135
|
+
isNative = false;
|
|
136
|
+
native;
|
|
137
|
+
constructor(native) {
|
|
138
|
+
this.native = native;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Parse a JSON string using sonic-rs
|
|
142
|
+
*/
|
|
143
|
+
parse(text, reviver) {
|
|
144
|
+
const result = this.native.parse(text);
|
|
145
|
+
if (reviver) {
|
|
146
|
+
return this.applyReviver(result, reviver);
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Stringify a value using sonic-rs
|
|
152
|
+
*/
|
|
153
|
+
stringify(value, replacer, space) {
|
|
154
|
+
let processedValue = value;
|
|
155
|
+
if (replacer) {
|
|
156
|
+
processedValue = this.applyReplacer(value, replacer);
|
|
157
|
+
}
|
|
158
|
+
if (space !== void 0 && space !== null && space !== 0 && space !== "") {
|
|
159
|
+
const indent = typeof space === "string" ? Math.min(space.length, 10) : Math.min(Math.max(0, space), 10);
|
|
160
|
+
return this.native.stringifyPretty(processedValue, indent);
|
|
161
|
+
}
|
|
162
|
+
return this.native.stringify(processedValue);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Deep clone via sonic-rs round-trip
|
|
166
|
+
*/
|
|
167
|
+
clone(value) {
|
|
168
|
+
return this.native.clone(value);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Parse without throwing
|
|
172
|
+
*/
|
|
173
|
+
safeParse(text) {
|
|
174
|
+
const result = this.native.parseSafe(text);
|
|
175
|
+
if (result.success) {
|
|
176
|
+
return { success: true, value: result.value };
|
|
177
|
+
}
|
|
178
|
+
const parseError = {
|
|
179
|
+
message: result.error || "Unknown parse error"
|
|
180
|
+
};
|
|
181
|
+
if (result.errorPosition) {
|
|
182
|
+
parseError.line = result.errorPosition.line;
|
|
183
|
+
parseError.column = result.errorPosition.column;
|
|
184
|
+
}
|
|
185
|
+
return { success: false, error: parseError };
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Stringify without throwing
|
|
189
|
+
*/
|
|
190
|
+
safeStringify(value) {
|
|
191
|
+
const result = this.native.stringifySafe(value);
|
|
192
|
+
if (result.success && result.value !== void 0) {
|
|
193
|
+
return { success: true, value: result.value };
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
success: false,
|
|
197
|
+
error: { message: result.error || "Unknown stringify error" }
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Validate JSON string using sonic-rs
|
|
202
|
+
*/
|
|
203
|
+
isValid(text) {
|
|
204
|
+
return this.native.isValid(text);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Get adapter information
|
|
208
|
+
*/
|
|
209
|
+
getInfo() {
|
|
210
|
+
const info = this.native.getAdapterInfo();
|
|
211
|
+
return {
|
|
212
|
+
name: "sonic",
|
|
213
|
+
isNative: false,
|
|
214
|
+
version: info.version,
|
|
215
|
+
simdEnabled: info.simdEnabled
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Apply reviver function to parsed value
|
|
220
|
+
* @internal
|
|
221
|
+
*/
|
|
222
|
+
applyReviver(value, reviver) {
|
|
223
|
+
return this.walkReviver({ "": value }, "", reviver);
|
|
224
|
+
}
|
|
225
|
+
walkReviver(holder, key, reviver) {
|
|
226
|
+
const value = holder[key];
|
|
227
|
+
if (value && typeof value === "object") {
|
|
228
|
+
if (Array.isArray(value)) {
|
|
229
|
+
for (let i = 0; i < value.length; i++) {
|
|
230
|
+
const newValue = this.walkReviver(
|
|
231
|
+
value,
|
|
232
|
+
String(i),
|
|
233
|
+
reviver
|
|
234
|
+
);
|
|
235
|
+
if (newValue === void 0) {
|
|
236
|
+
value.splice(i, 1);
|
|
237
|
+
i--;
|
|
238
|
+
} else {
|
|
239
|
+
value[i] = newValue;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
for (const k of Object.keys(value)) {
|
|
244
|
+
const newValue = this.walkReviver(
|
|
245
|
+
value,
|
|
246
|
+
k,
|
|
247
|
+
reviver
|
|
248
|
+
);
|
|
249
|
+
if (newValue === void 0) {
|
|
250
|
+
delete value[k];
|
|
251
|
+
} else {
|
|
252
|
+
value[k] = newValue;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return reviver.call(holder, key, value);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Apply replacer to value before stringifying
|
|
261
|
+
* @internal
|
|
262
|
+
*/
|
|
263
|
+
applyReplacer(value, replacer) {
|
|
264
|
+
if (Array.isArray(replacer)) {
|
|
265
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
266
|
+
const filtered = {};
|
|
267
|
+
for (const key of replacer) {
|
|
268
|
+
const k = String(key);
|
|
269
|
+
if (k in value) {
|
|
270
|
+
filtered[k] = value[k];
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return filtered;
|
|
274
|
+
}
|
|
275
|
+
return value;
|
|
276
|
+
}
|
|
277
|
+
if (typeof replacer === "function") {
|
|
278
|
+
return this.walkReplacer({ "": value }, "", replacer);
|
|
279
|
+
}
|
|
280
|
+
return value;
|
|
281
|
+
}
|
|
282
|
+
walkReplacer(holder, key, replacer) {
|
|
283
|
+
let value = holder[key];
|
|
284
|
+
value = replacer.call(holder, key, value);
|
|
285
|
+
if (value && typeof value === "object") {
|
|
286
|
+
if (Array.isArray(value)) {
|
|
287
|
+
const arr = [];
|
|
288
|
+
for (let i = 0; i < value.length; i++) {
|
|
289
|
+
const item = this.walkReplacer(
|
|
290
|
+
value,
|
|
291
|
+
String(i),
|
|
292
|
+
replacer
|
|
293
|
+
);
|
|
294
|
+
arr.push(item);
|
|
295
|
+
}
|
|
296
|
+
return arr;
|
|
297
|
+
} else {
|
|
298
|
+
const obj = {};
|
|
299
|
+
for (const k of Object.keys(value)) {
|
|
300
|
+
const item = this.walkReplacer(
|
|
301
|
+
value,
|
|
302
|
+
k,
|
|
303
|
+
replacer
|
|
304
|
+
);
|
|
305
|
+
if (item !== void 0) {
|
|
306
|
+
obj[k] = item;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return obj;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return value;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function createSonicAdapter() {
|
|
316
|
+
const native = tryLoadNative();
|
|
317
|
+
if (native) {
|
|
318
|
+
return new SonicAdapter(native);
|
|
319
|
+
}
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
function isSonicAvailable() {
|
|
323
|
+
return tryLoadNative() !== null;
|
|
324
|
+
}
|
|
325
|
+
export {
|
|
326
|
+
NativeAdapter as N,
|
|
327
|
+
SonicAdapter as S,
|
|
328
|
+
createSonicAdapter as c,
|
|
329
|
+
isSonicAvailable as i,
|
|
330
|
+
nativeAdapter as n
|
|
331
|
+
};
|
|
332
|
+
//# sourceMappingURL=sonic-BVfKq74R.js.map
|