@nnilky/structo 1.0.4 → 1.0.7
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/README.md +1 -1
- package/dist/datatypes/containers/array.d.ts +8 -0
- package/dist/datatypes/containers/array.js +8 -0
- package/dist/datatypes/containers/array.test.js +6 -10
- package/dist/datatypes/containers/exhuastiveArray.d.ts +9 -0
- package/dist/datatypes/containers/exhuastiveArray.js +24 -0
- package/dist/datatypes/containers/exhuastiveArray.test.js +38 -0
- package/dist/datatypes/containers/fastObject.d.ts +2 -0
- package/dist/datatypes/containers/fastObject.js +2 -0
- package/dist/datatypes/containers/fastObject.test.js +45 -0
- package/dist/datatypes/containers/list.d.ts +11 -4
- package/dist/datatypes/containers/list.js +17 -9
- package/dist/datatypes/containers/list.test.js +9 -17
- package/dist/datatypes/containers/object.js +7 -1
- package/dist/datatypes/containers/object.test.js +23 -11
- package/dist/datatypes/containers/sizedbytes.d.ts +2 -0
- package/dist/datatypes/containers/{sizedbuffer.js → sizedbytes.js} +1 -1
- package/dist/datatypes/containers/{sizedbuffer.test.js → sizedbytes.test.js} +5 -5
- package/dist/datatypes/containers/string.d.ts +2 -0
- package/dist/datatypes/containers/string.js +24 -0
- package/dist/datatypes/containers/string.test.js +24 -0
- package/dist/datatypes/containers/taggedUnion.d.ts +26 -0
- package/dist/datatypes/containers/taggedUnion.js +35 -0
- package/dist/datatypes/containers/taggedUnion.test.js +67 -0
- package/dist/datatypes/index.d.ts +5 -8
- package/dist/datatypes/index.js +5 -8
- package/dist/datatypes/numbers/bigints.d.ts +10 -0
- package/dist/datatypes/numbers/bigints.js +10 -0
- package/dist/datatypes/numbers/bigints.test.js +11 -11
- package/dist/datatypes/numbers/floats.d.ts +4 -0
- package/dist/datatypes/numbers/floats.js +18 -0
- package/dist/datatypes/numbers/floats.test.js +49 -47
- package/dist/datatypes/numbers/sints.d.ts +4 -0
- package/dist/datatypes/numbers/sints.js +4 -0
- package/dist/datatypes/numbers/sints.test.js +11 -11
- package/dist/datatypes/numbers/uints.d.ts +4 -0
- package/dist/datatypes/numbers/uints.js +4 -0
- package/dist/datatypes/numbers/uints.test.js +11 -11
- package/dist/datatypes/transforms/encode.d.ts +2 -0
- package/dist/datatypes/transforms/encode.js +7 -0
- package/dist/datatypes/transforms/fixedOffset.d.ts +2 -0
- package/dist/datatypes/transforms/{readOffset.js → fixedOffset.js} +1 -1
- package/dist/datatypes/transforms/modify.d.ts +2 -0
- package/dist/datatypes/transforms/modify.js +7 -0
- package/dist/datatypes/transforms/noAdvance.d.ts +16 -0
- package/dist/datatypes/transforms/noAdvance.js +30 -0
- package/dist/datatypes/transforms/pipe.js +12 -0
- package/dist/datatypes/utilities/remember.d.ts +11 -1
- package/dist/datatypes/utilities/remember.js +15 -5
- package/dist/datatypes/utilities/remember.test.d.ts +1 -0
- package/dist/datatypes/utilities/remember.test.js +44 -0
- package/dist/datatypes/utils.test.d.ts +2 -1
- package/dist/datatypes/utils.test.js +6 -2
- package/dist/datatypes/values/byteliteral.d.ts +1 -1
- package/dist/datatypes/values/byteliteral.js +1 -1
- package/dist/datatypes/values/bytes.d.ts +9 -0
- package/dist/datatypes/values/{buffer.js → bytes.js} +8 -1
- package/dist/datatypes/values/bytes.test.d.ts +1 -0
- package/dist/datatypes/values/{buffer.test.js → bytes.test.js} +12 -14
- package/dist/datatypes/values/json.d.ts +10 -0
- package/dist/datatypes/values/json.js +21 -0
- package/dist/datatypes/values/sizedbytes.d.ts +2 -0
- package/dist/datatypes/values/sizedbytes.js +17 -0
- package/dist/datatypes/values/sizedbytes.test.d.ts +1 -0
- package/dist/datatypes/values/sizedbytes.test.js +25 -0
- package/dist/datatypes/values/string.d.ts +12 -0
- package/dist/datatypes/values/string.js +22 -8
- package/dist/datatypes/values/string.test.js +8 -8
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/read.d.ts +1 -1
- package/dist/transforms/encode.d.ts +2 -0
- package/dist/transforms/encode.js +7 -0
- package/dist/transforms/enum.d.ts +2 -0
- package/dist/transforms/enum.js +13 -0
- package/dist/transforms/enum.test.d.ts +1 -0
- package/dist/transforms/enum.test.js +21 -0
- package/dist/transforms/fixedOffset.d.ts +2 -0
- package/dist/transforms/fixedOffset.js +18 -0
- package/dist/transforms/index.d.ts +12 -0
- package/dist/transforms/index.js +12 -0
- package/dist/transforms/literal.d.ts +1 -0
- package/dist/transforms/literal.js +12 -0
- package/dist/transforms/literal.test.d.ts +1 -0
- package/dist/transforms/literal.test.js +23 -0
- package/dist/transforms/modify.d.ts +2 -0
- package/dist/transforms/modify.js +7 -0
- package/dist/transforms/noAdvance.d.ts +16 -0
- package/dist/transforms/noAdvance.js +30 -0
- package/dist/transforms/noAdvance.test.d.ts +1 -0
- package/dist/transforms/noAdvance.test.js +17 -0
- package/dist/transforms/pipe.d.ts +11 -0
- package/dist/transforms/pipe.js +16 -0
- package/dist/transforms/toAscii.d.ts +1 -0
- package/dist/transforms/toAscii.js +11 -0
- package/dist/transforms/toAscii.test.d.ts +1 -0
- package/dist/transforms/toAscii.test.js +25 -0
- package/dist/transforms/toBase64.d.ts +1 -0
- package/dist/transforms/toBase64.js +4 -0
- package/dist/transforms/toBase64.test.d.ts +1 -0
- package/dist/transforms/toBase64.test.js +27 -0
- package/dist/transforms/toBytes.d.ts +13 -0
- package/dist/transforms/toBytes.js +21 -0
- package/dist/transforms/toBytes.test.d.ts +1 -0
- package/dist/transforms/toBytes.test.js +27 -0
- package/dist/transforms/toHex.d.ts +13 -0
- package/dist/transforms/toHex.js +16 -0
- package/dist/transforms/toHex.test.d.ts +1 -0
- package/dist/transforms/toHex.test.js +23 -0
- package/dist/transforms/toTypedArray.d.ts +16 -0
- package/dist/transforms/toTypedArray.js +14 -0
- package/dist/transforms/toTypedArray.test.d.ts +1 -0
- package/dist/transforms/toTypedArray.test.js +29 -0
- package/dist/utilities/index.d.ts +2 -0
- package/dist/utilities/index.js +2 -0
- package/dist/utilities/lazy.d.ts +2 -0
- package/dist/utilities/lazy.js +25 -0
- package/dist/utilities/lazy.test.d.ts +1 -0
- package/dist/utilities/lazy.test.js +36 -0
- package/dist/utilities/remember.d.ts +17 -0
- package/dist/utilities/remember.js +38 -0
- package/dist/utilities/remember.test.d.ts +1 -0
- package/dist/utilities/remember.test.js +76 -0
- package/dist/utils.test.d.ts +9 -1
- package/dist/utils.test.js +42 -1
- package/dist/write.d.ts +1 -1
- package/package.json +12 -7
- package/dist/datatypes/containers/sizedbuffer.d.ts +0 -2
- package/dist/datatypes/transforms/pipe.test.js +0 -13
- package/dist/datatypes/transforms/readOffset.d.ts +0 -2
- package/dist/datatypes/transforms/transform.d.ts +0 -2
- package/dist/datatypes/transforms/transform.js +0 -13
- package/dist/datatypes/utilities/constant.d.ts +0 -2
- package/dist/datatypes/utilities/constant.js +0 -13
- package/dist/datatypes/values/buffer.d.ts +0 -2
- /package/dist/datatypes/containers/{sizedbuffer.test.d.ts → exhuastiveArray.test.d.ts} +0 -0
- /package/dist/datatypes/{lazy.d.ts → containers/fastObject.test.d.ts} +0 -0
- /package/dist/datatypes/{lazy.js → containers/sizedbytes.test.d.ts} +0 -0
- /package/dist/datatypes/{transforms/pipe.test.d.ts → containers/string.test.d.ts} +0 -0
- /package/dist/datatypes/{values/buffer.test.d.ts → containers/taggedUnion.test.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { describe, it } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import { encodeTest, encodeFailTest } from "../../utils.test";
|
|
3
3
|
import * as st from "../../index";
|
|
4
4
|
describe("st.array", () => {
|
|
5
5
|
it("encode correctly", () => {
|
|
6
|
-
|
|
7
|
-
expectEncode(spec, [1, 2, 3, 4]);
|
|
6
|
+
encodeTest(st.array(4, st.u16()), [1, 2, 3, 4]);
|
|
8
7
|
});
|
|
9
8
|
it("works on empty lists", () => {
|
|
10
|
-
|
|
11
|
-
expectEncode(spec, []);
|
|
9
|
+
encodeTest(st.array(0, st.u16()), []);
|
|
12
10
|
});
|
|
13
11
|
it("throws error on invalid size", () => {
|
|
14
|
-
|
|
15
|
-
expectError(() => st.write(spec, [1]));
|
|
12
|
+
encodeFailTest(st.array(2, st.u16()), [1]);
|
|
16
13
|
});
|
|
17
14
|
it("throws error on invalid value", () => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
expectError(() => st.write(spec, [2 ** 16 + 1, 0]));
|
|
15
|
+
encodeFailTest(st.array(2, st.u16()), [1, -1]);
|
|
16
|
+
encodeFailTest(st.array(2, st.u16()), [2 ** 16 + 1, 0]);
|
|
21
17
|
});
|
|
22
18
|
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as st from "../..";
|
|
2
|
+
/**
|
|
3
|
+
* exhuastiveArray is read until the end of the data
|
|
4
|
+
*
|
|
5
|
+
* ```py
|
|
6
|
+
* exhuastiveArray(st.u32())
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export function exhuastiveArray(type) {
|
|
10
|
+
return {
|
|
11
|
+
read(ctx) {
|
|
12
|
+
let arr = [];
|
|
13
|
+
while (ctx.offset < ctx.view.byteLength) {
|
|
14
|
+
arr.push(type.read(ctx));
|
|
15
|
+
}
|
|
16
|
+
return arr;
|
|
17
|
+
},
|
|
18
|
+
write(ctx, value) {
|
|
19
|
+
for (let i = 0; i < value.length; i++) {
|
|
20
|
+
type.write(ctx, value[i]);
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, it } from "bun:test";
|
|
2
|
+
import { encodeTest, encodeSnapshotTest, encodeFailTest } from "../../utils.test";
|
|
3
|
+
import * as st from "../../index";
|
|
4
|
+
describe("st.exhuastiveArray", () => {
|
|
5
|
+
it("encode correctly", () => {
|
|
6
|
+
const spec = st.exhuastiveArray(st.u32());
|
|
7
|
+
encodeTest(spec, [1, 2, 3, 4]);
|
|
8
|
+
});
|
|
9
|
+
it("works on empty arrays", () => {
|
|
10
|
+
const spec = st.exhuastiveArray(st.u32());
|
|
11
|
+
encodeTest(spec, []);
|
|
12
|
+
});
|
|
13
|
+
it("accepts large array", () => {
|
|
14
|
+
const spec = st.exhuastiveArray(st.u32());
|
|
15
|
+
encodeTest(spec, Array.from({ length: 10000 }, (_, i) => i));
|
|
16
|
+
});
|
|
17
|
+
it("works composed", () => {
|
|
18
|
+
const spec = st.object({
|
|
19
|
+
a: st.u32(),
|
|
20
|
+
b: st.u8(),
|
|
21
|
+
c: st.exhuastiveArray(st.u32()),
|
|
22
|
+
});
|
|
23
|
+
encodeTest(spec, { a: 1, b: 2, c: [3, 4, 5, 6, 7] });
|
|
24
|
+
});
|
|
25
|
+
it("throws error on invalid value", () => {
|
|
26
|
+
encodeFailTest(st.exhuastiveArray(st.u16()), [-1]);
|
|
27
|
+
});
|
|
28
|
+
it("nested arrays encode correctly", () => {
|
|
29
|
+
encodeTest(st.exhuastiveArray(st.exhuastiveArray(st.u16())), //
|
|
30
|
+
[[1, 2]]);
|
|
31
|
+
});
|
|
32
|
+
it(`matches snapshots`, () => {
|
|
33
|
+
encodeSnapshotTest(st.exhuastiveArray(st.string(st.u16())), //
|
|
34
|
+
["foo", "bar", "baz"]);
|
|
35
|
+
encodeSnapshotTest(st.exhuastiveArray(st.u8()), //
|
|
36
|
+
[0, 1, 2, 3]);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -14,6 +14,8 @@ type InferObject<T> = T extends Record<string, Serializer<any>> ? {
|
|
|
14
14
|
* createdAt: st.f64(),
|
|
15
15
|
* })
|
|
16
16
|
* ```
|
|
17
|
+
*
|
|
18
|
+
* Note: Using st.lazy affects the performance of fastObject as it prevents discovery of subobjects, avoid when performance is a requirement
|
|
17
19
|
*/
|
|
18
20
|
export declare function fastObject<T extends Record<string, Serializer<any>>>(definition: T): Serializer<InferObject<T>>;
|
|
19
21
|
export {};
|
|
@@ -11,6 +11,8 @@ const definitionSymbol = Symbol();
|
|
|
11
11
|
* createdAt: st.f64(),
|
|
12
12
|
* })
|
|
13
13
|
* ```
|
|
14
|
+
*
|
|
15
|
+
* Note: Using st.lazy affects the performance of fastObject as it prevents discovery of subobjects, avoid when performance is a requirement
|
|
14
16
|
*/
|
|
15
17
|
export function fastObject(definition) {
|
|
16
18
|
let serializers = [];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//@ts-ignore TODO
|
|
2
|
+
import { describe, it, expect } from "bun:test";
|
|
3
|
+
import { bytes, encodeTest, encodeSnapshotTest } from "../../utils.test";
|
|
4
|
+
import * as st from "../../index";
|
|
5
|
+
describe("st.fastObject", () => {
|
|
6
|
+
const spec = st.fastObject({
|
|
7
|
+
a: st.u8(),
|
|
8
|
+
b: st.u8(),
|
|
9
|
+
});
|
|
10
|
+
it("encodes correctly", () => {
|
|
11
|
+
st.write(spec, { b: 2, a: 1 });
|
|
12
|
+
});
|
|
13
|
+
it("encodes nest correctly", () => {
|
|
14
|
+
st.write(st.fastObject({
|
|
15
|
+
a: st.fastObject({
|
|
16
|
+
cat: st.u8(),
|
|
17
|
+
puppy: st.string(st.u8()),
|
|
18
|
+
}),
|
|
19
|
+
b: st.u8(),
|
|
20
|
+
}), {
|
|
21
|
+
a: { cat: 1, puppy: "woof" },
|
|
22
|
+
b: 3,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
it("encode in correct order", () => {
|
|
26
|
+
const data = st.write(spec, { b: 2, a: 1 });
|
|
27
|
+
const arr = new Uint8Array(data);
|
|
28
|
+
expect(arr[0]).toBe(1);
|
|
29
|
+
expect(arr[1]).toBe(2);
|
|
30
|
+
});
|
|
31
|
+
it("encodes empty", () => {
|
|
32
|
+
encodeTest(st.fastObject({}), {});
|
|
33
|
+
});
|
|
34
|
+
it(`matches snapshots`, () => {
|
|
35
|
+
encodeSnapshotTest(st.fastObject({
|
|
36
|
+
number: st.u32(),
|
|
37
|
+
puppy: st.string(st.s32()),
|
|
38
|
+
buffer: st.bytes(4),
|
|
39
|
+
}), {
|
|
40
|
+
number: 1,
|
|
41
|
+
puppy: "woof woof bark bark",
|
|
42
|
+
buffer: bytes([19, 87, 19, 83]),
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { Serializer } from "../../types";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
/**
|
|
3
|
+
* `st.list` is a dynamically sized array
|
|
4
|
+
*
|
|
5
|
+
* Note: Length is read/writen first, then the values in order
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* st.list(st.u32(), User)
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
export declare function list<T>(length: Serializer<number>, type: Serializer<T>): Serializer<T[]>;
|
|
@@ -1,20 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `st.list` is a dynamically sized array
|
|
3
|
+
*
|
|
4
|
+
* Note: Length is read/writen first, then the values in order
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* st.list(st.u32(), User)
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
export function list(length, type) {
|
|
4
12
|
return {
|
|
5
13
|
write: (ctx, value) => {
|
|
6
|
-
|
|
7
|
-
if (
|
|
8
|
-
ctx.alloc(value.length *
|
|
14
|
+
length.write(ctx, value.length);
|
|
15
|
+
if (type.size)
|
|
16
|
+
ctx.alloc(value.length * type.size);
|
|
9
17
|
for (const v of value) {
|
|
10
|
-
|
|
18
|
+
type.write(ctx, v);
|
|
11
19
|
}
|
|
12
20
|
},
|
|
13
21
|
read: (ctx) => {
|
|
14
|
-
const size =
|
|
22
|
+
const size = length.read(ctx);
|
|
15
23
|
const arr = new Array(size);
|
|
16
24
|
for (let i = 0; i < size; i++) {
|
|
17
|
-
arr[i] =
|
|
25
|
+
arr[i] = type.read(ctx);
|
|
18
26
|
}
|
|
19
27
|
return arr;
|
|
20
28
|
},
|
|
@@ -1,31 +1,23 @@
|
|
|
1
1
|
import { describe, it } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import { encodeTest, encodeSnapshotTest, encodeFailTest } from "../../utils.test";
|
|
3
3
|
import * as st from "../../index";
|
|
4
4
|
describe("st.list", () => {
|
|
5
5
|
it("encode correctly", () => {
|
|
6
|
-
const spec = st.list(
|
|
7
|
-
|
|
6
|
+
const spec = st.list(st.u8(), st.u32());
|
|
7
|
+
encodeTest(spec, [1, 2, 3, 4]);
|
|
8
8
|
});
|
|
9
9
|
it("works on empty lists", () => {
|
|
10
|
-
const spec = st.list(
|
|
11
|
-
|
|
10
|
+
const spec = st.list(st.u8(), st.u32());
|
|
11
|
+
encodeTest(spec, []);
|
|
12
12
|
});
|
|
13
13
|
it("throws error on too large array", () => {
|
|
14
|
-
|
|
15
|
-
expectError(() => {
|
|
16
|
-
st.write(spec, Array.from({ length: 10000 }, () => 0));
|
|
17
|
-
});
|
|
14
|
+
encodeFailTest(st.list(st.u8(), st.u32()), Array.from({ length: 10000 }, () => 0));
|
|
18
15
|
});
|
|
19
16
|
it("throws error on invalid value", () => {
|
|
20
|
-
|
|
21
|
-
expectError(() => {
|
|
22
|
-
st.write(spec, [-1]);
|
|
23
|
-
});
|
|
17
|
+
encodeFailTest(st.list(st.u8(), st.u16()), [-1]);
|
|
24
18
|
});
|
|
25
19
|
it(`matches snapshots`, () => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
length: st.s32(),
|
|
29
|
-
}), ["foo", "bar", "baz"]);
|
|
20
|
+
encodeSnapshotTest(st.list(st.s32(), st.string(st.u16())), //
|
|
21
|
+
["foo", "bar", "baz"]);
|
|
30
22
|
});
|
|
31
23
|
});
|
|
@@ -21,7 +21,13 @@ export function object(definition) {
|
|
|
21
21
|
if (size)
|
|
22
22
|
ctx.alloc(size);
|
|
23
23
|
for (let i = 0; i < entires.length; i++) {
|
|
24
|
-
|
|
24
|
+
const key = entires[i][0];
|
|
25
|
+
try {
|
|
26
|
+
entires[i][1].write(ctx, value[key]);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
throw new Error(`Failed to encode key '${key}'`, { cause: e });
|
|
30
|
+
}
|
|
25
31
|
}
|
|
26
32
|
},
|
|
27
33
|
read: (ctx) => {
|
|
@@ -1,33 +1,45 @@
|
|
|
1
1
|
//@ts-ignore TODO
|
|
2
|
-
import { describe, it, expect } from "bun:test";
|
|
3
|
-
import { bytes,
|
|
2
|
+
import { describe, it, expect, expectTypeOf } from "bun:test";
|
|
3
|
+
import { bytes, encodeTest, encodeSnapshotTest } from "../../utils.test";
|
|
4
4
|
import * as st from "../../index";
|
|
5
5
|
describe("st.object", () => {
|
|
6
|
-
const test = st.object({
|
|
7
|
-
a: st.u8(),
|
|
8
|
-
b: st.u8(),
|
|
9
|
-
});
|
|
10
6
|
it("encodes correctly", () => {
|
|
11
|
-
st.
|
|
7
|
+
encodeTest(st.object({ a: st.u8(), b: st.u8() }), //
|
|
8
|
+
{ b: 2, a: 1 });
|
|
9
|
+
});
|
|
10
|
+
it("encodes nested correctly", () => {
|
|
11
|
+
encodeTest(st.object({
|
|
12
|
+
a: st.object({ a: st.u8() }),
|
|
13
|
+
b: st.u8(),
|
|
14
|
+
}), //
|
|
15
|
+
{ a: { a: 2 }, b: 1 });
|
|
12
16
|
});
|
|
13
17
|
it("encode in correct order", () => {
|
|
14
|
-
const data = st.write(
|
|
18
|
+
const data = st.write(st.object({
|
|
19
|
+
a: st.u8(),
|
|
20
|
+
b: st.u8(),
|
|
21
|
+
}), { b: 2, a: 1 });
|
|
15
22
|
const arr = new Uint8Array(data);
|
|
16
23
|
expect(arr[0]).toBe(1);
|
|
17
24
|
expect(arr[1]).toBe(2);
|
|
18
25
|
});
|
|
19
26
|
it("encodes empty", () => {
|
|
20
|
-
|
|
27
|
+
encodeTest(st.object({}), {});
|
|
21
28
|
});
|
|
22
29
|
it(`matches snapshots`, () => {
|
|
23
|
-
|
|
30
|
+
encodeSnapshotTest(st.object({
|
|
24
31
|
number: st.u32(),
|
|
25
32
|
puppy: st.string(st.s32()),
|
|
26
|
-
buffer: st.
|
|
33
|
+
buffer: st.bytes(4),
|
|
27
34
|
}), {
|
|
28
35
|
number: 1,
|
|
29
36
|
puppy: "woof woof bark bark",
|
|
30
37
|
buffer: bytes([19, 87, 19, 83]),
|
|
31
38
|
});
|
|
32
39
|
});
|
|
40
|
+
// Type Tests
|
|
41
|
+
expectTypeOf(st.object({
|
|
42
|
+
number: st.u32(),
|
|
43
|
+
string: st.string(st.u32()),
|
|
44
|
+
})).toEqualTypeOf();
|
|
33
45
|
});
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { describe, it } from "bun:test";
|
|
2
2
|
import { bytes, expectEncode, expectError } from "../utils.test";
|
|
3
3
|
import * as st from "../../index";
|
|
4
|
-
describe("st.
|
|
4
|
+
describe("st.sizedBytes", () => {
|
|
5
5
|
it("encode correctly", () => {
|
|
6
|
-
const buffer = st.
|
|
6
|
+
const buffer = st.sizedBytes(st.u8());
|
|
7
7
|
expectEncode(buffer, bytes([1, 2]));
|
|
8
8
|
});
|
|
9
9
|
it("encodes empty correctly", () => {
|
|
10
|
-
const spec = st.
|
|
10
|
+
const spec = st.sizedBytes(st.u8());
|
|
11
11
|
expectEncode(spec, bytes([]));
|
|
12
12
|
});
|
|
13
13
|
it("holds large data", () => {
|
|
14
14
|
const size = 1024 * 1024 * 8; // 8MB
|
|
15
|
-
const spec = st.
|
|
15
|
+
const spec = st.sizedBytes(st.u32());
|
|
16
16
|
const data = new Uint8Array(size);
|
|
17
17
|
data.set([3, 4], 1000);
|
|
18
18
|
data.set([3, 4], 2000);
|
|
19
19
|
expectEncode(spec, data.buffer);
|
|
20
20
|
});
|
|
21
21
|
it("errors on invalid length", () => {
|
|
22
|
-
const spec = st.
|
|
22
|
+
const spec = st.sizedBytes(st.u8());
|
|
23
23
|
expectError(() => {
|
|
24
24
|
st.write(spec, bytes(Array.from({ length: 256 }, () => 0)));
|
|
25
25
|
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function string(length) {
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
const decoder = new TextDecoder();
|
|
4
|
+
const lengthSize = length.size ?? 0;
|
|
5
|
+
return {
|
|
6
|
+
write: (ctx, value) => {
|
|
7
|
+
if (typeof value !== "string")
|
|
8
|
+
throw new Error("Expected String to encoder");
|
|
9
|
+
const bytes = encoder.encode(value);
|
|
10
|
+
const size = bytes.byteLength;
|
|
11
|
+
ctx.alloc(size + lengthSize);
|
|
12
|
+
length.write(ctx, size);
|
|
13
|
+
const arr = new Uint8Array(ctx.buffer, ctx.offset);
|
|
14
|
+
arr.set(bytes);
|
|
15
|
+
ctx.offset += size;
|
|
16
|
+
},
|
|
17
|
+
read: (ctx) => {
|
|
18
|
+
const size = length.read(ctx);
|
|
19
|
+
const section = ctx.buffer.slice(ctx.offset, ctx.offset + size);
|
|
20
|
+
ctx.offset += size;
|
|
21
|
+
return decoder.decode(section);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, it } from "bun:test";
|
|
2
|
+
import { expectEncode, expectError } from "../utils.test";
|
|
3
|
+
import * as st from "../../index";
|
|
4
|
+
describe("st.string", () => {
|
|
5
|
+
const string_u8 = st.string(st.u8());
|
|
6
|
+
const string_u32 = st.string(st.u32());
|
|
7
|
+
it("encode correctly", () => {
|
|
8
|
+
expectEncode(string_u8, "foo");
|
|
9
|
+
});
|
|
10
|
+
it("works on empty strings", () => {
|
|
11
|
+
expectEncode(string_u32, "");
|
|
12
|
+
});
|
|
13
|
+
it("errors on numbers", () => {
|
|
14
|
+
expectError(() => {
|
|
15
|
+
//@ts-expect-error, intentional mistake
|
|
16
|
+
st.write(st.string(st.u8()), 8);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
it("errors on too long strings", () => {
|
|
20
|
+
expectError(() => {
|
|
21
|
+
st.write(string_u8, "A".repeat(256));
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { InferInput, InferOutput, Serializer } from "../../types";
|
|
2
|
+
/**
|
|
3
|
+
* `st.taggedUnion` is a union type with a kind
|
|
4
|
+
*
|
|
5
|
+
* Note: `tag` is read/writen first, then the value
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* st.taggedUnion(st.u32(), {
|
|
9
|
+
* 0: MovementPacket,
|
|
10
|
+
* 1: ClickPacket,
|
|
11
|
+
* 2: MouseMovePacket,
|
|
12
|
+
* })
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function taggedUnion<Tag extends PropertyKey, //
|
|
16
|
+
Variants extends Record<Tag, Serializer<any>>>(tag: Serializer<Tag>, variants: Variants & Record<Exclude<keyof Variants, Tag>, never>): Serializer<{
|
|
17
|
+
[K in keyof Variants]: {
|
|
18
|
+
type: K;
|
|
19
|
+
value: InferInput<Variants[K]>;
|
|
20
|
+
};
|
|
21
|
+
}[keyof Variants], {
|
|
22
|
+
[K in keyof Variants]: {
|
|
23
|
+
type: K;
|
|
24
|
+
value: InferOutput<Variants[K]>;
|
|
25
|
+
};
|
|
26
|
+
}[keyof Variants]>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `st.taggedUnion` is a union type with a kind
|
|
3
|
+
*
|
|
4
|
+
* Note: `tag` is read/writen first, then the value
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* st.taggedUnion(st.u32(), {
|
|
8
|
+
* 0: MovementPacket,
|
|
9
|
+
* 1: ClickPacket,
|
|
10
|
+
* 2: MouseMovePacket,
|
|
11
|
+
* })
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function taggedUnion(tag, variants) {
|
|
15
|
+
return {
|
|
16
|
+
write: (ctx, value) => {
|
|
17
|
+
if (!(value.type in variants))
|
|
18
|
+
throw new Error(`Unknown type ${String(value.type)}`);
|
|
19
|
+
//@ts-expect-error
|
|
20
|
+
tag.write(ctx, value.type);
|
|
21
|
+
const variant = variants[value.type];
|
|
22
|
+
//@ts-expect-error
|
|
23
|
+
variant.write(ctx, value.value);
|
|
24
|
+
},
|
|
25
|
+
read: (ctx) => {
|
|
26
|
+
const type = tag.read(ctx);
|
|
27
|
+
if (!(type in variants))
|
|
28
|
+
throw new Error(`Unknown type ${tag}`);
|
|
29
|
+
const variant = variants[type];
|
|
30
|
+
//@ts-expect-error
|
|
31
|
+
const value = variant.read(ctx);
|
|
32
|
+
return { type, value };
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, expectTypeOf, it } from "bun:test";
|
|
2
|
+
import { encodeTest, expectEncodeSize, encodeSnapshotTest, encodeFailTest } from "../../utils.test";
|
|
3
|
+
import * as st from "../../index";
|
|
4
|
+
describe("st.taggedUnion", () => {
|
|
5
|
+
it("encode correctly", () => {
|
|
6
|
+
const spec = st.taggedUnion(st.u8(), {
|
|
7
|
+
1: st.string(st.u8()),
|
|
8
|
+
2: st.u8(),
|
|
9
|
+
});
|
|
10
|
+
encodeTest(spec, { type: 1, value: "s" });
|
|
11
|
+
encodeTest(spec, { type: 2, value: 52 });
|
|
12
|
+
});
|
|
13
|
+
it("encode with string tag", () => {
|
|
14
|
+
const spec = st.taggedUnion(st.string(st.u8()), {
|
|
15
|
+
foo: st.string(st.u8()),
|
|
16
|
+
bar: st.u8(),
|
|
17
|
+
});
|
|
18
|
+
encodeTest(spec, { type: "foo", value: "woof" });
|
|
19
|
+
encodeTest(spec, { type: "bar", value: 52 });
|
|
20
|
+
});
|
|
21
|
+
it("throws error on invalid tag", () => {
|
|
22
|
+
const spec = st.taggedUnion(st.string(st.u8()), {
|
|
23
|
+
foo: st.string(st.u8()),
|
|
24
|
+
bar: st.u8(),
|
|
25
|
+
});
|
|
26
|
+
//@ts-expect-error, intended
|
|
27
|
+
encodeFailTest(spec, { type: "unknown", value: "woof" });
|
|
28
|
+
});
|
|
29
|
+
it("throws error on invalid type", () => {
|
|
30
|
+
const spec = st.taggedUnion(st.string(st.u8()), {
|
|
31
|
+
foo: st.string(st.u8()),
|
|
32
|
+
bar: st.u8(),
|
|
33
|
+
});
|
|
34
|
+
//@ts-expect-error, intended
|
|
35
|
+
encodeFailTest(spec, { type: "unknown", value: "woof" });
|
|
36
|
+
});
|
|
37
|
+
it("throws error on invalid value", () => {
|
|
38
|
+
const spec = st.taggedUnion(st.string(st.u8()), {
|
|
39
|
+
foo: st.string(st.u8()),
|
|
40
|
+
});
|
|
41
|
+
//@ts-expect-error, intended
|
|
42
|
+
encodeFailTest(spec, { type: "foo", value: 0 });
|
|
43
|
+
});
|
|
44
|
+
it("encodes varying sizes", () => {
|
|
45
|
+
const spec = st.taggedUnion(st.u8(), {
|
|
46
|
+
1: st.string(st.u8()),
|
|
47
|
+
2: st.u8(),
|
|
48
|
+
});
|
|
49
|
+
encodeTest(spec, { type: 1, value: "s" });
|
|
50
|
+
expectEncodeSize(spec, 5, { type: 1, value: "foo" });
|
|
51
|
+
expectEncodeSize(spec, 2, { type: 2, value: 52 });
|
|
52
|
+
});
|
|
53
|
+
it("encodes snapshots", () => {
|
|
54
|
+
encodeSnapshotTest(st.taggedUnion(st.u32(), {
|
|
55
|
+
0: st.list(st.u8(), st.f64()),
|
|
56
|
+
3: st.u32(),
|
|
57
|
+
}), { type: 0, value: [1] });
|
|
58
|
+
});
|
|
59
|
+
expectTypeOf(st.taggedUnion(st.u32(), {
|
|
60
|
+
0: st.u8(),
|
|
61
|
+
1: st.string(st.u32()),
|
|
62
|
+
})).toEqualTypeOf();
|
|
63
|
+
() => {
|
|
64
|
+
//@ts-expect-error, must be same type as tags
|
|
65
|
+
st.taggedUnion(st.string(st.u8()), { 0: st.u8(), 1: st.string(st.u32()) });
|
|
66
|
+
};
|
|
67
|
+
});
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
export { u64Bigint, s64Bigint } from "./numbers/bigints";
|
|
2
|
-
export { f32, f64 } from "./numbers/floats";
|
|
2
|
+
export { f16, f32, f64 } from "./numbers/floats";
|
|
3
3
|
export { s8, s16, s32, s64 } from "./numbers/sints";
|
|
4
4
|
export { u8, u16, u32, u64 } from "./numbers/uints";
|
|
5
5
|
export { array } from "./containers/array";
|
|
6
6
|
export { fastObject } from "./containers/fastObject";
|
|
7
7
|
export { object } from "./containers/object";
|
|
8
8
|
export { list } from "./containers/list";
|
|
9
|
-
export {
|
|
9
|
+
export { taggedUnion } from "./containers/taggedUnion";
|
|
10
|
+
export { exhuastiveArray } from "./containers/exhuastiveArray";
|
|
11
|
+
export { bytes } from "./values/bytes";
|
|
12
|
+
export { sizedBytes } from "./values/sizedbytes";
|
|
10
13
|
export { string } from "./values/string";
|
|
11
|
-
export { buffer } from "./values/buffer";
|
|
12
|
-
export { byteLiteral } from "./values/byteliteral";
|
|
13
|
-
export { createRememberedValue } from "./utilities/remember";
|
|
14
|
-
export { type Transform as Pipeline, pipe } from "./transforms/pipe";
|
|
15
|
-
export { positionOffset } from "./transforms/readOffset";
|
|
16
|
-
export { transform } from "./transforms/transform";
|
package/dist/datatypes/index.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
export { u64Bigint, s64Bigint } from "./numbers/bigints";
|
|
2
|
-
export { f32, f64 } from "./numbers/floats";
|
|
2
|
+
export { f16, f32, f64 } from "./numbers/floats";
|
|
3
3
|
export { s8, s16, s32, s64 } from "./numbers/sints";
|
|
4
4
|
export { u8, u16, u32, u64 } from "./numbers/uints";
|
|
5
5
|
export { array } from "./containers/array";
|
|
6
6
|
export { fastObject } from "./containers/fastObject";
|
|
7
7
|
export { object } from "./containers/object";
|
|
8
8
|
export { list } from "./containers/list";
|
|
9
|
-
export {
|
|
9
|
+
export { taggedUnion } from "./containers/taggedUnion";
|
|
10
|
+
export { exhuastiveArray } from "./containers/exhuastiveArray";
|
|
11
|
+
export { bytes } from "./values/bytes";
|
|
12
|
+
export { sizedBytes } from "./values/sizedbytes";
|
|
10
13
|
export { string } from "./values/string";
|
|
11
|
-
export { buffer } from "./values/buffer";
|
|
12
|
-
export { byteLiteral } from "./values/byteliteral";
|
|
13
|
-
export { createRememberedValue } from "./utilities/remember";
|
|
14
|
-
export { pipe } from "./transforms/pipe";
|
|
15
|
-
export { positionOffset } from "./transforms/readOffset";
|
|
16
|
-
export { transform } from "./transforms/transform";
|