json-as 1.1.21 โ†’ 1.1.22

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
@@ -1,5 +1,9 @@
1
1
  # Change Log
2
2
 
3
+ ## 2025-09-01 - 1.1.22
4
+
5
+ - fix: Type aliases should work across files [#154](https://github.com/JairusSW/json-as/issues/154)
6
+
3
7
  ## 2025-08-14 - 1.1.21
4
8
 
5
9
  - fix: JSON.parse on classes with enums [#155](https://github.com/JairusSW/json-as/issues/155)
package/README.md CHANGED
@@ -378,19 +378,6 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
378
378
  | Medium Object | 494 bytes | 2,395,210 ops/s | 1,381,693 ops/s | 1,183 MB/s | 682.5 MB/s |
379
379
  | Large Object | 3374 bytes | 222,222 ops/s | 117,233 ops/s | 749.7 MB/s | 395.5 MB/s |
380
380
 
381
- **๐Ÿ“Œ Insights**
382
-
383
- - JSON-AS consistently outperforms JavaScript's native implementation.
384
-
385
- - **Serialization Speed:**
386
-
387
- - JSON-AS achieves speeds up to `2,133 MB/s`, significantly faster than JavaScript's peak of `1,416 MB/s`.
388
- - Large objects see the biggest improvement, with JSON-AS at `2,074 MB/s` vs. JavaScriptโ€™s `749.7 MB/s`.
389
-
390
- - **Deserialization Speed:**
391
- - JSON-AS reaches `1,986 MB/s`, while JavaScript caps at `1,592 MB/s`.
392
- - Small and medium objects see the most significant performance boost overall.
393
-
394
381
  ### ๐Ÿ“ˆ Comparison to v0.9.x version
395
382
 
396
383
  **Table 1** - _v1.0.0_
@@ -413,12 +400,45 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
413
400
  | Medium Object | 494 bytes | 522,193 ops/s | 508,582 ops/s | 258.0 MB/s | 251.2 MB/s |
414
401
  | Large Object | 3374 bytes | 51,229 ops/s | 65,585 ops/s | 172.8 MB/s | 221.3 MB/s |
415
402
 
416
- **๐Ÿ“Œ Insights:**
403
+ ### Running benchmarks locally
404
+
405
+ Benchmarks are run directly on top of v8 for more tailored control
406
+
407
+ 1. Download JSVU off of npm
408
+
409
+ ```bash
410
+ npm install jsvu -g
411
+ ```
412
+
413
+ 2. Modify your dotfiles to add `~/.jsvu/bin` to `PATH`
414
+
415
+ ```bash
416
+ export PATH="${HOME}/.jsvu/bin:${PATH}"
417
+ ```
418
+
419
+ 3. Clone the repository
417
420
 
418
- - Massive performance improvements in JSON-AS `v1.0.0`:
419
- - Serialization is **2-12x faster** (e.g., Large Object: `2,074 MB/s` vs. `172.8 MB/s`).
420
- - Deserialization is **2-3x faster** (e.g., Large Object: `1,348 MB/s` vs. `221.3 MB/s`).
421
- - Vector3 Object serialization improved from `416 MB/s` to `1,357 MB/s`--a **3x benefit** through new code generation techniques.
421
+ ```bash
422
+ git clone https://github.com/JairusSW/json-as
423
+ ```
424
+
425
+ 4. Install dependencies
426
+
427
+ ```bash
428
+ npm i
429
+ ```
430
+
431
+ 5. Run benchmarks for either AssemblyScript or JavaScript
432
+
433
+ ```bash
434
+ ./run-bench.as.sh
435
+ ```
436
+
437
+ or
438
+
439
+ ```bash
440
+ ./run-bench.js.sh
441
+ ```
422
442
 
423
443
  ## ๐Ÿ”ญ What's Next
424
444
 
package/assembly/index.ts CHANGED
@@ -617,7 +617,7 @@ export namespace JSON {
617
617
  if (isDefined(type.__DESERIALIZE)) {
618
618
  const out = changetype<nonnull<T>>(dst || __new(offsetof<nonnull<T>>(), idof<nonnull<T>>()));
619
619
  // @ts-ignore: Defined by transform
620
- if (isNullable<T>() && isDefined(type.__INITIALIZE)) out.__INITIALIZE();
620
+ if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
621
621
  // @ts-ignore: Defined by transform
622
622
  return out.__DESERIALIZE(srcStart, srcEnd, out);
623
623
  } else if (type instanceof Map) {
@@ -1,16 +1,303 @@
1
+ import { bs } from "../lib/as-bs";
1
2
  import { JSON } from ".";
2
- const o = new JSON.Obj();
3
- o.set("schema", "http://json-schema.org/draft-07/schema#");
4
- o.set("additionalProperties", false);
5
- o.set("properties", new JSON.Obj());
6
- o.get("properties")!.as<JSON.Obj>().set("duration", new JSON.Obj());
7
- o.get("properties")!.as<JSON.Obj>().get("duration")!.as<JSON.Obj>().set("default", 10);
8
- o.get("properties")!.as<JSON.Obj>().get("duration")!.as<JSON.Obj>().set("description", "Duration of the operation in seconds");
9
- o.get("properties")!.as<JSON.Obj>().get("duration")!.as<JSON.Obj>().set("type", "number");
10
- o.get("properties")!.as<JSON.Obj>().set("steps", new JSON.Obj());
11
- o.get("properties")!.as<JSON.Obj>().get("steps")!.as<JSON.Obj>().set("default", 5);
12
- o.get("properties")!.as<JSON.Obj>().get("steps")!.as<JSON.Obj>().set("description", "Number of steps in the operation");
13
- o.get("properties")!.as<JSON.Obj>().get("steps")!.as<JSON.Obj>().set("type", "number");
14
- o.set("type", "object");
15
- console.log(o.toString());
16
- console.log((o.toString().length << 1).toString() + " == 596");
3
+
4
+ @json
5
+ class Example {
6
+ constructor(
7
+ public a: string,
8
+ public b: string,
9
+ public c: string,
10
+ public d: string,
11
+ public e: boolean,
12
+ ) {}
13
+ public f: string = "lol";
14
+ __SERIALIZE(ptr: usize): void {
15
+ bs.proposeSize(72);
16
+ store<u64>(bs.offset, 9570565822218363, 0);
17
+ store<u16>(bs.offset, 58, 8);
18
+ bs.offset += 10;
19
+ JSON.__serialize<string>(load<string>(ptr, offsetof<this>("a")));
20
+ store<u64>(bs.offset, 9570570117185580, 0);
21
+ store<u16>(bs.offset, 58, 8);
22
+ bs.offset += 10;
23
+ JSON.__serialize<string>(load<string>(ptr, offsetof<this>("b")));
24
+ store<u64>(bs.offset, 9570574412152876, 0);
25
+ store<u16>(bs.offset, 58, 8);
26
+ bs.offset += 10;
27
+ JSON.__serialize<string>(load<string>(ptr, offsetof<this>("c")));
28
+ store<u64>(bs.offset, 9570578707120172, 0);
29
+ store<u16>(bs.offset, 58, 8);
30
+ bs.offset += 10;
31
+ JSON.__serialize<string>(load<string>(ptr, offsetof<this>("d")));
32
+ store<u64>(bs.offset, 9570583002087468, 0);
33
+ store<u16>(bs.offset, 58, 8);
34
+ bs.offset += 10;
35
+ JSON.__serialize<boolean>(load<boolean>(ptr, offsetof<this>("e")));
36
+ store<u64>(bs.offset, 9570587297054764, 0);
37
+ store<u16>(bs.offset, 58, 8);
38
+ bs.offset += 10;
39
+ JSON.__serialize<string>(load<string>(ptr, offsetof<this>("f")));
40
+ store<u16>(bs.offset, 125, 0);
41
+ bs.offset += 2;
42
+ }
43
+
44
+ @inline
45
+ __INITIALIZE(): this {
46
+ store<string>(changetype<usize>(this), "", offsetof<this>("a"));
47
+ store<string>(changetype<usize>(this), "", offsetof<this>("b"));
48
+ store<string>(changetype<usize>(this), "", offsetof<this>("c"));
49
+ store<string>(changetype<usize>(this), "", offsetof<this>("d"));
50
+ store<string>(changetype<usize>(this), "lol", offsetof<this>("f"));
51
+ return this;
52
+ }
53
+ __DESERIALIZE<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): __JSON_T {
54
+ let keyStart: usize = 0;
55
+ let keyEnd: usize = 0;
56
+ let isKey = false;
57
+ let depth: i32 = 0;
58
+ let lastIndex: usize = 0;
59
+ while (srcStart < srcEnd && JSON.Util.isSpace(load<u16>(srcStart))) srcStart += 2;
60
+ while (srcEnd > srcStart && JSON.Util.isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
61
+ if (srcStart - srcEnd == 0) throw new Error("Input string had zero length or was all whitespace");
62
+ if (load<u16>(srcStart) != 123) throw new Error("Expected '{' at start of object at position " + (srcEnd - srcStart).toString());
63
+ if (load<u16>(srcEnd - 2) != 125) throw new Error("Expected '}' at end of object at position " + (srcEnd - srcStart).toString());
64
+ srcStart += 2;
65
+ while (srcStart < srcEnd) {
66
+ let code = load<u16>(srcStart);
67
+ while (JSON.Util.isSpace(code)) code = load<u16>((srcStart += 2));
68
+ if (keyStart == 0) {
69
+ if (code == 34 && load<u16>(srcStart - 2) !== 92) {
70
+ if (isKey) {
71
+ keyStart = lastIndex;
72
+ keyEnd = srcStart;
73
+ console.log("Key: " + JSON.Util.ptrToStr(keyStart, keyEnd));
74
+ while (JSON.Util.isSpace((code = load<u16>((srcStart += 2))))) {}
75
+ if (code !== 58) throw new Error("Expected ':' after key at position " + (srcEnd - srcStart).toString());
76
+ isKey = false;
77
+ } else {
78
+ isKey = true;
79
+ lastIndex = srcStart + 2;
80
+ }
81
+ }
82
+ srcStart += 2;
83
+ } else {
84
+ if (code == 34) {
85
+ lastIndex = srcStart;
86
+ srcStart += 2;
87
+ while (srcStart < srcEnd) {
88
+ const code = load<u16>(srcStart);
89
+ if (code == 34 && load<u16>(srcStart - 2) !== 92) {
90
+ console.log("Value (string, 1): " + JSON.Util.ptrToStr(lastIndex, srcStart + 2));
91
+ switch (<u32>keyEnd - <u32>keyStart) {
92
+ case 2: {
93
+ const code16 = load<u16>(keyStart);
94
+ if (code16 == 97) {
95
+ store<string>(changetype<usize>(out), JSON.__deserialize<string>(lastIndex, srcStart + 2), offsetof<this>("a"));
96
+ srcStart += 4;
97
+ keyStart = 0;
98
+ break;
99
+ } else if (code16 == 98) {
100
+ store<string>(changetype<usize>(out), JSON.__deserialize<string>(lastIndex, srcStart + 2), offsetof<this>("b"));
101
+ srcStart += 4;
102
+ keyStart = 0;
103
+ break;
104
+ } else if (code16 == 99) {
105
+ store<string>(changetype<usize>(out), JSON.__deserialize<string>(lastIndex, srcStart + 2), offsetof<this>("c"));
106
+ srcStart += 4;
107
+ keyStart = 0;
108
+ break;
109
+ } else if (code16 == 100) {
110
+ store<string>(changetype<usize>(out), JSON.__deserialize<string>(lastIndex, srcStart + 2), offsetof<this>("d"));
111
+ srcStart += 4;
112
+ keyStart = 0;
113
+ break;
114
+ } else if (code16 == 102) {
115
+ store<string>(changetype<usize>(out), JSON.__deserialize<string>(lastIndex, srcStart + 2), offsetof<this>("f"));
116
+ srcStart += 4;
117
+ keyStart = 0;
118
+ break;
119
+ } else {
120
+ srcStart += 4;
121
+ keyStart = 0;
122
+ break;
123
+ }
124
+ }
125
+
126
+ default: {
127
+ srcStart += 4;
128
+ keyStart = 0;
129
+ break;
130
+ }
131
+ }
132
+ break;
133
+ }
134
+ srcStart += 2;
135
+ }
136
+ } else if (code - 48 <= 9 || code == 45) {
137
+ lastIndex = srcStart;
138
+ srcStart += 2;
139
+ while (srcStart < srcEnd) {
140
+ const code = load<u16>(srcStart);
141
+ if (code == 44 || code == 125 || JSON.Util.isSpace(code)) {
142
+ console.log("Value (number, 2): " + JSON.Util.ptrToStr(lastIndex, srcStart));
143
+ srcStart += 2;
144
+ keyStart = 0;
145
+ break;
146
+ }
147
+ srcStart += 2;
148
+ }
149
+ } else if (code == 123) {
150
+ lastIndex = srcStart;
151
+ depth++;
152
+ srcStart += 2;
153
+ while (srcStart < srcEnd) {
154
+ const code = load<u16>(srcStart);
155
+ if (code == 34) {
156
+ srcStart += 2;
157
+ while (!(load<u16>(srcStart) == 34 && load<u16>(srcStart - 2) != 92)) srcStart += 2;
158
+ } else if (code == 125) {
159
+ if (--depth == 0) {
160
+ srcStart += 2;
161
+ console.log("Value (object, 3): " + JSON.Util.ptrToStr(lastIndex, srcStart));
162
+ switch (<u32>keyEnd - <u32>keyStart) {
163
+ case 2: {
164
+ const code16 = load<u16>(keyStart);
165
+ if (code16 == 97) {
166
+ store<string>(changetype<usize>(out), JSON.__deserialize<string>(lastIndex, srcStart), offsetof<this>("a"));
167
+ keyStart = 0;
168
+ break;
169
+ } else if (code16 == 98) {
170
+ store<string>(changetype<usize>(out), JSON.__deserialize<string>(lastIndex, srcStart), offsetof<this>("b"));
171
+ keyStart = 0;
172
+ break;
173
+ } else if (code16 == 99) {
174
+ store<string>(changetype<usize>(out), JSON.__deserialize<string>(lastIndex, srcStart), offsetof<this>("c"));
175
+ keyStart = 0;
176
+ break;
177
+ } else if (code16 == 100) {
178
+ store<string>(changetype<usize>(out), JSON.__deserialize<string>(lastIndex, srcStart), offsetof<this>("d"));
179
+ keyStart = 0;
180
+ break;
181
+ } else if (code16 == 101) {
182
+ store<boolean>(changetype<usize>(out), JSON.__deserialize<boolean>(lastIndex, srcStart), offsetof<this>("e"));
183
+ keyStart = 0;
184
+ break;
185
+ } else if (code16 == 102) {
186
+ store<string>(changetype<usize>(out), JSON.__deserialize<string>(lastIndex, srcStart), offsetof<this>("f"));
187
+ keyStart = 0;
188
+ break;
189
+ } else {
190
+ keyStart = 0;
191
+ break;
192
+ }
193
+ }
194
+
195
+ default: {
196
+ keyStart = 0;
197
+ break;
198
+ }
199
+ }
200
+ break;
201
+ }
202
+ } else if (code == 123) depth++;
203
+ srcStart += 2;
204
+ }
205
+ } else if (code == 91) {
206
+ lastIndex = srcStart;
207
+ depth++;
208
+ srcStart += 2;
209
+ while (srcStart < srcEnd) {
210
+ const code = load<u16>(srcStart);
211
+ if (code == 34) {
212
+ srcStart += 2;
213
+ while (!(load<u16>(srcStart) == 34 && load<u16>(srcStart - 2) != 92)) srcStart += 2;
214
+ } else if (code == 93) {
215
+ if (--depth == 0) {
216
+ srcStart += 2;
217
+ console.log("Value (object, 4): " + JSON.Util.ptrToStr(lastIndex, srcStart));
218
+ keyStart = 0;
219
+ break;
220
+ }
221
+ } else if (code == 91) depth++;
222
+ srcStart += 2;
223
+ }
224
+ } else if (code == 116) {
225
+ if (load<u64>(srcStart) == 28429475166421108) {
226
+ srcStart += 8;
227
+ console.log("Value (bool, 5): " + JSON.Util.ptrToStr(lastIndex, srcStart - 8));
228
+ switch (<u32>keyEnd - <u32>keyStart) {
229
+ case 2: {
230
+ const code16 = load<u16>(keyStart);
231
+ if (code16 == 101) {
232
+ store<boolean>(changetype<usize>(out), true, offsetof<this>("e"));
233
+ srcStart += 2;
234
+ keyStart = 0;
235
+ break;
236
+ } else {
237
+ srcStart += 2;
238
+ keyStart = 0;
239
+ break;
240
+ }
241
+ }
242
+
243
+ default: {
244
+ srcStart += 2;
245
+ keyStart = 0;
246
+ }
247
+ }
248
+ } else {
249
+ throw new Error("Expected to find 'true' but found '" + JSON.Util.ptrToStr(lastIndex, srcStart) + "' instead at position " + (srcEnd - srcStart).toString());
250
+ }
251
+ } else if (code == 102) {
252
+ if (load<u64>(srcStart, 2) == 28429466576093281) {
253
+ srcStart += 10;
254
+ console.log("Value (bool, 6): " + JSON.Util.ptrToStr(lastIndex, srcStart - 10));
255
+ switch (<u32>keyEnd - <u32>keyStart) {
256
+ case 2: {
257
+ const code16 = load<u16>(keyStart);
258
+ if (code16 == 101) {
259
+ store<boolean>(changetype<usize>(out), false, offsetof<this>("e"));
260
+ srcStart += 2;
261
+ keyStart = 0;
262
+ break;
263
+ } else {
264
+ srcStart += 2;
265
+ keyStart = 0;
266
+ break;
267
+ }
268
+ }
269
+
270
+ default: {
271
+ srcStart += 2;
272
+ keyStart = 0;
273
+ }
274
+ }
275
+ } else {
276
+ throw new Error("Expected to find 'false' but found '" + JSON.Util.ptrToStr(lastIndex, srcStart) + "' instead at position " + (srcEnd - srcStart).toString());
277
+ }
278
+ } else if (code == 110) {
279
+ if (load<u64>(srcStart) == 30399761348886638) {
280
+ srcStart += 8;
281
+ console.log("Value (null, 7): " + JSON.Util.ptrToStr(lastIndex, srcStart - 8));
282
+ srcStart += 2;
283
+ keyStart = 0;
284
+ }
285
+ } else {
286
+ srcStart += 2;
287
+ keyStart = 0;
288
+ }
289
+ }
290
+ }
291
+ return out;
292
+ }
293
+ }
294
+ const good = `{"a":"a","b":"b","e":true,"c":"c","d":"d"}`;
295
+ const good2 = `{"a":"a","b":"b","c":"c","d":"d","e":false}`;
296
+ const bad = `{"a":"a","b":"b","e":false,"c":"c","d":"d"}`;
297
+ const parsedGood = JSON.parse<Example>(good);
298
+ console.log("a: " + JSON.stringify(parsedGood));
299
+ const parsedGood2 = JSON.parse<Example>(good2);
300
+ console.log("b: " + JSON.stringify(parsedGood2));
301
+ const parsedBad = JSON.parse<Example>(bad);
302
+ console.log("c: " + JSON.stringify(parsedBad));
303
+ console.log(load<u64>(changetype<usize>("alse")).toString());
package/assembly/test.ts CHANGED
@@ -2,43 +2,24 @@ import { JSON } from ".";
2
2
 
3
3
 
4
4
  @json
5
- class Vec3 {
6
- x: f32 = 0.0;
7
- y: f32 = 0.0;
8
- z: f32 = 0.0;
5
+ class Example {
6
+ constructor(
7
+ public a: string,
8
+ public b: string,
9
+ public c: string,
10
+ public d: string,
11
+ public e: boolean,
12
+ ) {}
9
13
  }
10
14
 
11
-
12
- @json
13
- class Player {
14
-
15
- @alias("first name")
16
- firstName!: string | null;
17
- lastName!: string;
18
- lastActive!: i32[];
19
- // Drop in a code block, function, or expression that evaluates to a boolean
20
- @omitif((self: Player) => self.age < 18)
21
- age!: i32;
22
-
23
- pos!: Vec3 | null;
24
- isVerified!: boolean;
25
- }
26
-
27
- const player: Player = {
28
- firstName: "Jairus",
29
- lastName: "Tanaka",
30
- lastActive: [3, 9, 2025],
31
- age: 18,
32
- pos: {
33
- x: 3.4,
34
- y: 1.2,
35
- z: 8.3,
36
- },
37
- isVerified: true,
38
- };
39
-
40
- const serialized = JSON.stringify<Player>(player);
41
- const deserialized = JSON.parse<Player>(serialized);
42
-
43
- console.log("Serialized " + serialized);
44
- console.log("Deserialized " + JSON.stringify(deserialized));
15
+ const good = `{"a":"a","b":"b","e":true,"c":"c","d":"d"}`;
16
+ const good2 = `{"a":"a","b":"b","c":"c","d":"d","e":false}`;
17
+ const bad = `{"a":"a","b":"b","e":false,"c":"c","d":"d"}`;
18
+ const parsedGood = JSON.parse<Example>(good);
19
+ console.log("a: " + JSON.stringify(parsedGood));
20
+ const parsedGood2 = JSON.parse<Example>(good2);
21
+ console.log("b: " + JSON.stringify(parsedGood2));
22
+ const parsedBad = JSON.parse<Example>(bad);
23
+ console.log("c: " + JSON.stringify(parsedBad));
24
+
25
+ console.log(load<u64>(changetype<usize>("alse")).toString());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-as",
3
- "version": "1.1.21",
3
+ "version": "1.1.22",
4
4
  "author": "Jairus Tanaka",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,7 +13,7 @@
13
13
  "assemblyscript": "^0.28.4",
14
14
  "assemblyscript-prettier": "^3.0.1",
15
15
  "prettier": "^3.6.2",
16
- "tinybench": "^4.1.0",
16
+ "tinybench": "^5.0.1",
17
17
  "typescript": "^5.9.2"
18
18
  },
19
19
  "bugs": {
@@ -57,7 +57,8 @@
57
57
  "test": "bash ./run-tests.sh",
58
58
  "bench:as": "bash ./run-bench.as.sh",
59
59
  "bench:js": "bash ./run-bench.js.sh",
60
- "build:test": "rm -rf ./build/ && JSON_DEBUG=1 JSON_WRITE=assembly/test.ts asc assembly/test.ts --transform ./transform -o ./build/test.wasm --textFile ./build/test.wat --enable simd --debug --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json",
60
+ "build:test": "rm -rf ./build/ && JSON_DEBUG=2 JSON_WRITE=assembly/test.ts asc assembly/test.ts --transform ./transform -o ./build/test.wasm --textFile ./build/test.wat --enable simd --debug --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json",
61
+ "build:tmp:test": "rm -rf ./build/ && JSON_DEBUG=1 asc assembly/test.tmp.ts -o ./build/test.wasm --textFile ./build/test.wat --enable simd --debug --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json",
61
62
  "build:test:wine": "JSON_DEBUG=1 JSON_WRITE=assembly/test.ts NODE_SKIP_PLATFORM_CHECK=1 wine ~/.win-bin/node/node.exe ./node_modules/assemblyscript/bin/asc.js assembly/test.ts --transform ./transform -o ./build/test.wasm --textFile ./build/test.wat --debug --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json",
62
63
  "test:wasmtime": "wasmtime ./build/test.wasm",
63
64
  "test:wasmer": "wasmer ./build/test.wasm",