@nnilky/structo 1.0.5 → 1.0.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/README.md +3 -3
- package/dist/datatypes/containers/array.js +9 -0
- package/dist/datatypes/containers/array.test.js +6 -10
- package/dist/datatypes/containers/exhuastiveArray.d.ts +2 -2
- package/dist/datatypes/containers/exhuastiveArray.js +13 -2
- package/dist/datatypes/containers/exhuastiveArray.test.js +9 -12
- package/dist/datatypes/containers/fastObject.d.ts +3 -1
- package/dist/datatypes/containers/fastObject.js +3 -1
- package/dist/datatypes/containers/fastObject.test.js +18 -6
- package/dist/datatypes/containers/list.js +14 -2
- package/dist/datatypes/containers/list.test.js +6 -12
- package/dist/datatypes/containers/object.js +8 -2
- package/dist/datatypes/containers/object.test.js +22 -10
- package/dist/datatypes/containers/taggedUnion.d.ts +12 -8
- package/dist/datatypes/containers/taggedUnion.js +4 -2
- package/dist/datatypes/containers/taggedUnion.test.js +22 -18
- package/dist/datatypes/index.d.ts +2 -9
- package/dist/datatypes/index.js +2 -9
- package/dist/datatypes/numbers/bigints.test.js +11 -11
- package/dist/datatypes/numbers/floats.test.js +6 -6
- package/dist/datatypes/numbers/sints.test.js +11 -11
- package/dist/datatypes/numbers/uints.test.js +11 -11
- package/dist/datatypes/values/bytes.test.js +6 -8
- 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 +14 -0
- package/dist/datatypes/values/string.js +36 -0
- package/dist/datatypes/values/string.test.d.ts +1 -0
- package/dist/datatypes/values/string.test.js +21 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/read.d.ts +2 -6
- package/dist/read.js +9 -1
- package/dist/transforms/encode.d.ts +5 -0
- package/dist/transforms/encode.js +8 -0
- package/dist/transforms/enum.d.ts +2 -0
- package/dist/transforms/enum.js +16 -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 +11 -0
- package/dist/transforms/index.js +11 -0
- package/dist/transforms/literal.d.ts +1 -0
- package/dist/transforms/literal.js +15 -0
- package/dist/transforms/literal.test.d.ts +1 -0
- package/dist/transforms/literal.test.js +23 -0
- package/dist/transforms/modify.d.ts +20 -0
- package/dist/transforms/modify.js +27 -0
- package/dist/transforms/modify.test.d.ts +1 -0
- package/dist/transforms/modify.test.js +17 -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/offset.d.ts +2 -0
- package/dist/transforms/offset.js +20 -0
- package/dist/transforms/offset.test.d.ts +1 -0
- package/dist/transforms/offset.test.js +39 -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 +14 -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 +7 -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 +24 -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 +19 -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 +17 -0
- package/dist/transforms/toTypedArray.test.d.ts +1 -0
- package/dist/transforms/toTypedArray.test.js +29 -0
- package/dist/types.d.ts +3 -0
- package/dist/utilities/index.d.ts +3 -0
- package/dist/utilities/index.js +3 -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/reference.d.ts +18 -0
- package/dist/utilities/reference.js +91 -0
- package/dist/utilities/reference.test.d.ts +1 -0
- package/dist/utilities/reference.test.js +63 -0
- package/dist/utilities/remember.d.ts +18 -0
- package/dist/utilities/remember.js +75 -0
- package/dist/utilities/remember.test.d.ts +1 -0
- package/dist/utilities/remember.test.js +72 -0
- package/dist/utils.test.d.ts +9 -1
- package/dist/utils.test.js +42 -1
- package/dist/write.js +9 -1
- package/package.json +19 -14
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
//@ts-ignore TODO
|
|
2
2
|
import { describe, it, expect } from "bun:test";
|
|
3
|
-
import {
|
|
3
|
+
import { encodeTest, encodeSnapshotTest, expectError, randint, encodeFailTest, } from "../../utils.test";
|
|
4
4
|
import * as st from "../../index";
|
|
5
5
|
function test_sint(options) {
|
|
6
6
|
const { name, range: [start, end], serializer, size, disableRangeCheck, } = options;
|
|
7
7
|
describe(name, () => {
|
|
8
8
|
it(`works in bounds`, () => {
|
|
9
|
-
|
|
9
|
+
encodeTest(serializer, start);
|
|
10
10
|
for (let i = 0; i < 100; i++) {
|
|
11
|
-
|
|
11
|
+
encodeTest(serializer, randint(start, end));
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
encodeTest(serializer, end);
|
|
14
14
|
});
|
|
15
15
|
if (!disableRangeCheck) {
|
|
16
16
|
it(`errors outside bounds`, () => {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
encodeFailTest(serializer, start - 1);
|
|
18
|
+
encodeFailTest(serializer, end + 1);
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
it(`errors on decimal`, () => {
|
|
@@ -33,11 +33,11 @@ function test_sint(options) {
|
|
|
33
33
|
expectValueSize(end);
|
|
34
34
|
});
|
|
35
35
|
it(`matches snapshots`, () => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
encodeSnapshotTest(serializer, 0);
|
|
37
|
+
encodeSnapshotTest(serializer, start);
|
|
38
|
+
encodeSnapshotTest(serializer, start + 10);
|
|
39
|
+
encodeSnapshotTest(serializer, end - 10);
|
|
40
|
+
encodeSnapshotTest(serializer, end);
|
|
41
41
|
});
|
|
42
42
|
});
|
|
43
43
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
//@ts-ignore TODO
|
|
2
2
|
import { describe, it, expect } from "bun:test";
|
|
3
|
-
import {
|
|
3
|
+
import { encodeTest, encodeSnapshotTest, expectError, randint, encodeFailTest, } from "../../utils.test";
|
|
4
4
|
import * as st from "../../index";
|
|
5
5
|
function test_uint(options) {
|
|
6
6
|
const { name, range: [start, end], serializer, size, disableMaxCheck, } = options;
|
|
7
7
|
describe(name, () => {
|
|
8
8
|
it(`works in bounds`, () => {
|
|
9
|
-
|
|
9
|
+
encodeTest(serializer, start);
|
|
10
10
|
for (let i = 0; i < 100; i++) {
|
|
11
|
-
|
|
11
|
+
encodeTest(serializer, randint(start, end));
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
encodeTest(serializer, end);
|
|
14
14
|
});
|
|
15
15
|
it(`errors outside bounds`, () => {
|
|
16
|
-
|
|
16
|
+
encodeFailTest(serializer, start - 1);
|
|
17
17
|
if (!disableMaxCheck) {
|
|
18
|
-
|
|
18
|
+
encodeFailTest(serializer, end + 1);
|
|
19
19
|
}
|
|
20
20
|
});
|
|
21
21
|
it(`errors on decimal`, () => {
|
|
@@ -33,11 +33,11 @@ function test_uint(options) {
|
|
|
33
33
|
expectValueSize(end);
|
|
34
34
|
});
|
|
35
35
|
it(`matches snapshots`, () => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
encodeSnapshotTest(serializer, 0);
|
|
37
|
+
encodeSnapshotTest(serializer, start);
|
|
38
|
+
encodeSnapshotTest(serializer, start + 10);
|
|
39
|
+
encodeSnapshotTest(serializer, end - 10);
|
|
40
|
+
encodeSnapshotTest(serializer, end);
|
|
41
41
|
});
|
|
42
42
|
});
|
|
43
43
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { describe, it } from "bun:test";
|
|
2
|
-
import { bytes,
|
|
2
|
+
import { bytes, encodeFailTest, encodeTest } from "../../utils.test";
|
|
3
3
|
import * as st from "../../index";
|
|
4
4
|
describe("st.bytes", () => {
|
|
5
5
|
const buffer = st.bytes(2);
|
|
6
6
|
it("encode correctly", () => {
|
|
7
|
-
|
|
7
|
+
encodeTest(buffer, bytes([1, 2]));
|
|
8
8
|
});
|
|
9
9
|
it("encodes empty correctly", () => {
|
|
10
10
|
const spec = st.bytes(0);
|
|
11
|
-
|
|
11
|
+
encodeTest(spec, bytes([]));
|
|
12
12
|
});
|
|
13
13
|
it("holds large data", () => {
|
|
14
14
|
const size = 1024 * 1024 * 8; // 8MB
|
|
@@ -16,7 +16,7 @@ describe("st.bytes", () => {
|
|
|
16
16
|
const data = new Uint8Array(size);
|
|
17
17
|
data.set([3, 4], 1000);
|
|
18
18
|
data.set([3, 4], 2000);
|
|
19
|
-
|
|
19
|
+
encodeTest(spec, data.buffer);
|
|
20
20
|
});
|
|
21
21
|
it("large data writes work", () => {
|
|
22
22
|
const size = 1024 * 1024 * 8; // 8MB
|
|
@@ -28,7 +28,7 @@ describe("st.bytes", () => {
|
|
|
28
28
|
const data = new Uint8Array(size);
|
|
29
29
|
data.set([3, 4], 1000);
|
|
30
30
|
data.set([3, 4], 2000);
|
|
31
|
-
|
|
31
|
+
encodeTest(spec, {
|
|
32
32
|
before: 1,
|
|
33
33
|
data: data.buffer,
|
|
34
34
|
after: 2,
|
|
@@ -36,8 +36,6 @@ describe("st.bytes", () => {
|
|
|
36
36
|
});
|
|
37
37
|
it("errors on invalid length", () => {
|
|
38
38
|
const spec = st.bytes(5);
|
|
39
|
-
|
|
40
|
-
st.write(spec, bytes([1, 2, 3, 4]));
|
|
41
|
-
});
|
|
39
|
+
encodeFailTest(spec, bytes([1, 2, 3, 4]));
|
|
42
40
|
});
|
|
43
41
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `st.json` stores JSON data using a string serializer
|
|
3
|
+
*
|
|
4
|
+
* ```
|
|
5
|
+
* st.json(st.string(st.u32()))
|
|
6
|
+
* ```
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
export function json(type) {
|
|
10
|
+
return {
|
|
11
|
+
size: type.size,
|
|
12
|
+
write: (ctx, value) => {
|
|
13
|
+
const json = JSON.stringify(value);
|
|
14
|
+
type.write(ctx, json);
|
|
15
|
+
},
|
|
16
|
+
read: (ctx) => {
|
|
17
|
+
const value = type.read(ctx);
|
|
18
|
+
return JSON.parse(value);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function sizedBytes(length) {
|
|
2
|
+
return {
|
|
3
|
+
write: (ctx, value) => {
|
|
4
|
+
length.write(ctx, value.byteLength);
|
|
5
|
+
const bytes = new Uint8Array(value);
|
|
6
|
+
ctx.alloc(value.byteLength);
|
|
7
|
+
new Uint8Array(ctx.view.buffer).set(bytes, ctx.offset);
|
|
8
|
+
ctx.offset += value.byteLength;
|
|
9
|
+
},
|
|
10
|
+
read: (ctx) => {
|
|
11
|
+
const size = length.read(ctx);
|
|
12
|
+
const slice = ctx.buffer.slice(ctx.offset, ctx.offset + size);
|
|
13
|
+
ctx.offset += size;
|
|
14
|
+
return slice;
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, it } from "bun:test";
|
|
2
|
+
import { bytes, encodeFailTest, encodeTest } from "../../utils.test";
|
|
3
|
+
import * as st from "../../index";
|
|
4
|
+
describe("st.sizedBytes", () => {
|
|
5
|
+
it("encode correctly", () => {
|
|
6
|
+
const buffer = st.sizedBytes(st.u8());
|
|
7
|
+
encodeTest(buffer, bytes([1, 2]));
|
|
8
|
+
});
|
|
9
|
+
it("encodes empty correctly", () => {
|
|
10
|
+
const spec = st.sizedBytes(st.u8());
|
|
11
|
+
encodeTest(spec, bytes([]));
|
|
12
|
+
});
|
|
13
|
+
it("holds large data", () => {
|
|
14
|
+
const size = 1024 * 1024 * 8; // 8MB
|
|
15
|
+
const spec = st.sizedBytes(st.u32());
|
|
16
|
+
const data = new Uint8Array(size);
|
|
17
|
+
data.set([3, 4], 1000);
|
|
18
|
+
data.set([3, 4], 2000);
|
|
19
|
+
encodeTest(spec, data.buffer);
|
|
20
|
+
});
|
|
21
|
+
it("errors on invalid length", () => {
|
|
22
|
+
encodeFailTest(st.sizedBytes(st.u8()), //
|
|
23
|
+
bytes(Array.from({ length: 256 }, () => 0)));
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Serializer } from "../../types";
|
|
2
|
+
/**
|
|
3
|
+
* `st.json` stores string data by writing/reading the length and then reading that many bytes
|
|
4
|
+
*
|
|
5
|
+
* The type passed in is the type used to store it's length
|
|
6
|
+
*
|
|
7
|
+
* Note: The length stored is the byte length, not string length
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* st.string(st.u32())
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
14
|
+
export declare function string(length: Serializer<number>): Serializer<string>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `st.json` stores string data by writing/reading the length and then reading that many bytes
|
|
3
|
+
*
|
|
4
|
+
* The type passed in is the type used to store it's length
|
|
5
|
+
*
|
|
6
|
+
* Note: The length stored is the byte length, not string length
|
|
7
|
+
*
|
|
8
|
+
* ```
|
|
9
|
+
* st.string(st.u32())
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
export function string(length) {
|
|
14
|
+
const encoder = new TextEncoder();
|
|
15
|
+
const decoder = new TextDecoder();
|
|
16
|
+
const lengthSize = length.size ?? 0;
|
|
17
|
+
return {
|
|
18
|
+
write: (ctx, value) => {
|
|
19
|
+
if (typeof value !== "string")
|
|
20
|
+
throw new Error("Expected String");
|
|
21
|
+
const bytes = encoder.encode(value);
|
|
22
|
+
const size = bytes.byteLength;
|
|
23
|
+
ctx.alloc(size + lengthSize);
|
|
24
|
+
length.write(ctx, size);
|
|
25
|
+
const arr = new Uint8Array(ctx.buffer, ctx.offset);
|
|
26
|
+
arr.set(bytes);
|
|
27
|
+
ctx.offset += size;
|
|
28
|
+
},
|
|
29
|
+
read: (ctx) => {
|
|
30
|
+
const size = length.read(ctx);
|
|
31
|
+
const section = ctx.buffer.slice(ctx.offset, ctx.offset + size);
|
|
32
|
+
ctx.offset += size;
|
|
33
|
+
return decoder.decode(section);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, it } from "bun:test";
|
|
2
|
+
import { encodeFailTest, encodeTest } from "../../utils.test";
|
|
3
|
+
import * as st from "../../index";
|
|
4
|
+
describe("st.string", () => {
|
|
5
|
+
it("encode correctly", () => {
|
|
6
|
+
encodeTest(st.string(st.u8()), "foo");
|
|
7
|
+
});
|
|
8
|
+
it("works on empty strings", () => {
|
|
9
|
+
encodeTest(st.string(st.u32()), "");
|
|
10
|
+
});
|
|
11
|
+
it("errors on numbers", () => {
|
|
12
|
+
//@ts-expect-error, intentional mistake
|
|
13
|
+
encodeFailTest(st.string(st.u8()), 8);
|
|
14
|
+
});
|
|
15
|
+
it("works on long strings", () => {
|
|
16
|
+
encodeTest(st.string(st.u32()), "A".repeat(2 ** 24));
|
|
17
|
+
});
|
|
18
|
+
it("errors on too long strings", () => {
|
|
19
|
+
encodeFailTest(st.string(st.u8()), "A".repeat(256));
|
|
20
|
+
});
|
|
21
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
export * from "./datatypes/index";
|
|
1
|
+
export type { InferOutput as Infer, InferInput, InferOutput, ReaderContext, WriterContext, Serializer, } from "./types";
|
|
3
2
|
export { read, createReaderContext } from "./read";
|
|
4
3
|
export { write, createdWriterContext } from "./write";
|
|
4
|
+
export * from "./datatypes/index";
|
|
5
|
+
export * from "./transforms/index";
|
|
6
|
+
export * from "./utilities/index";
|
package/dist/index.js
CHANGED
package/dist/read.d.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import type { Serializer } from "./types";
|
|
1
|
+
import type { Serializer, ReaderContext } from "./types";
|
|
2
2
|
export declare function read<TIn, TOut>(serializer: Serializer<TIn, TOut>, buffer: ArrayBuffer): TOut;
|
|
3
|
-
export declare function createReaderContext(buffer: ArrayBuffer):
|
|
4
|
-
offset: number;
|
|
5
|
-
buffer: ArrayBuffer;
|
|
6
|
-
view: DataView<ArrayBuffer>;
|
|
7
|
-
};
|
|
3
|
+
export declare function createReaderContext(buffer: ArrayBuffer): ReaderContext;
|
package/dist/read.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
export function read(serializer, buffer) {
|
|
2
|
-
|
|
2
|
+
const ctx = createReaderContext(buffer);
|
|
3
|
+
try {
|
|
4
|
+
return serializer.read(ctx);
|
|
5
|
+
}
|
|
6
|
+
catch (e) {
|
|
7
|
+
const path = ctx.stack.join("") ?? "root";
|
|
8
|
+
throw new Error(`Deserialization error at serializer${path}`, { cause: e });
|
|
9
|
+
}
|
|
3
10
|
}
|
|
4
11
|
export function createReaderContext(buffer) {
|
|
5
12
|
return {
|
|
13
|
+
stack: [],
|
|
6
14
|
offset: 0,
|
|
7
15
|
buffer,
|
|
8
16
|
view: new DataView(buffer),
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { encode } from "./encode";
|
|
2
|
+
export function enum_(values) {
|
|
3
|
+
return encode({
|
|
4
|
+
encode: (v) => {
|
|
5
|
+
if (!values.includes(v))
|
|
6
|
+
throw new Error(`Invalid enum variant: ${v}`);
|
|
7
|
+
return v;
|
|
8
|
+
},
|
|
9
|
+
decode: (v) => {
|
|
10
|
+
if (!values.includes(v))
|
|
11
|
+
throw new Error(`Invalid enum variant: ${v}`);
|
|
12
|
+
return v;
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export { enum_ as enum };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import * as st from "../index";
|
|
3
|
+
import { encodeTest, encodeFailTest } from "../utils.test";
|
|
4
|
+
describe("st.enum", () => {
|
|
5
|
+
it("encode correctly", () => {
|
|
6
|
+
encodeTest(st.pipe(st.u8(), st.enum([0, 1, 2])), //
|
|
7
|
+
1);
|
|
8
|
+
encodeTest(st.pipe(st.string(st.u8()), st.enum(["foo", "bar", "woof"])), //
|
|
9
|
+
"foo");
|
|
10
|
+
});
|
|
11
|
+
it("invalid union variants error", () => {
|
|
12
|
+
//@ts-expect-error
|
|
13
|
+
encodeFailTest(st.pipe(st.u8(), st.enum([0, 1, 2])), 4);
|
|
14
|
+
//@ts-expect-error
|
|
15
|
+
encodeFailTest(st.pipe(st.string(st.u8()), st.enum(["A", "B", "C"])), "D");
|
|
16
|
+
});
|
|
17
|
+
() => {
|
|
18
|
+
//@ts-expect-error, invalid union variant
|
|
19
|
+
st.write(st.pipe(st.u8(), st.enum([0, 1, 2])), 4);
|
|
20
|
+
};
|
|
21
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function fixedOffset(delta) {
|
|
2
|
+
return (type) => ({
|
|
3
|
+
size: type.size,
|
|
4
|
+
read: (ctx) => {
|
|
5
|
+
let start = ctx.offset;
|
|
6
|
+
ctx.offset += delta;
|
|
7
|
+
const value = type.read(ctx);
|
|
8
|
+
ctx.offset = start;
|
|
9
|
+
return value;
|
|
10
|
+
},
|
|
11
|
+
write: (ctx, value) => {
|
|
12
|
+
let start = ctx.offset;
|
|
13
|
+
ctx.offset += delta;
|
|
14
|
+
type.write(ctx, value);
|
|
15
|
+
ctx.offset = start;
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { type Transform as Pipeline, pipe } from "./pipe";
|
|
2
|
+
export { encode } from "./encode";
|
|
3
|
+
export { modify } from "./modify";
|
|
4
|
+
export { literal } from "./literal";
|
|
5
|
+
export { enum } from "./enum";
|
|
6
|
+
export { offset } from "./offset";
|
|
7
|
+
export { toAscii } from "./toAscii";
|
|
8
|
+
export { toBytes } from "./toBytes";
|
|
9
|
+
export { toHex } from "./toHex";
|
|
10
|
+
export { toBase64 } from "./toBase64";
|
|
11
|
+
export { toTypedArray } from "./toTypedArray";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { pipe } from "./pipe";
|
|
2
|
+
export { encode } from "./encode";
|
|
3
|
+
export { modify } from "./modify";
|
|
4
|
+
export { literal } from "./literal";
|
|
5
|
+
export { enum } from "./enum";
|
|
6
|
+
export { offset } from "./offset";
|
|
7
|
+
export { toAscii } from "./toAscii";
|
|
8
|
+
export { toBytes } from "./toBytes";
|
|
9
|
+
export { toHex } from "./toHex";
|
|
10
|
+
export { toBase64 } from "./toBase64";
|
|
11
|
+
export { toTypedArray } from "./toTypedArray";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function literal<const T>(value: T): import("./pipe").Transform<T, any>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { encode } from "./encode";
|
|
2
|
+
export function literal(value) {
|
|
3
|
+
return encode({
|
|
4
|
+
encode: (v) => {
|
|
5
|
+
if (v !== value)
|
|
6
|
+
throw new Error(`Invalid literal variant: ${v}`);
|
|
7
|
+
return v;
|
|
8
|
+
},
|
|
9
|
+
decode: (v) => {
|
|
10
|
+
if (v !== value)
|
|
11
|
+
throw new Error(`Invalid literal variant: ${v}`);
|
|
12
|
+
return v;
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import * as st from "../index";
|
|
3
|
+
import { encodeTest, encodeFailTest } from "../utils.test";
|
|
4
|
+
describe("st.literal", () => {
|
|
5
|
+
it("encode correctly", () => {
|
|
6
|
+
encodeTest(st.pipe(st.u8(), st.literal(0)), //
|
|
7
|
+
0);
|
|
8
|
+
encodeTest(st.pipe(st.string(st.u8()), st.literal("foo")), //
|
|
9
|
+
"foo");
|
|
10
|
+
});
|
|
11
|
+
it("invalid literal errors", () => {
|
|
12
|
+
//@ts-expect-error
|
|
13
|
+
encodeFailTest(st.pipe(st.u8(), st.literal(0)), 2);
|
|
14
|
+
//@ts-expect-error
|
|
15
|
+
encodeFailTest(st.pipe(st.string(st.u8()), st.literal("A")), "B");
|
|
16
|
+
});
|
|
17
|
+
() => {
|
|
18
|
+
//@ts-expect-error, invalid union variant
|
|
19
|
+
st.write(st.pipe(st.u8(), st.literal(0)), 1);
|
|
20
|
+
//@ts-expect-error, invalid union variant
|
|
21
|
+
st.write(st.pipe(st.u8(), st.literal("foo")), "bar");
|
|
22
|
+
};
|
|
23
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Transform } from "./pipe";
|
|
2
|
+
/**
|
|
3
|
+
* A readonly modification function, useful for sizes or offsets.
|
|
4
|
+
*
|
|
5
|
+
* If you want a writable value, use `st.encode` instead
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* length: length.save(st.u32())
|
|
10
|
+
* st.sizedBytes(
|
|
11
|
+
* st.pipe(
|
|
12
|
+
* length.load(),
|
|
13
|
+
* st.modify(v => v - 8),
|
|
14
|
+
* )
|
|
15
|
+
* ))
|
|
16
|
+
*
|
|
17
|
+
* st.pipe()
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function modify<TIn, TOut>(callback: (value: TIn) => TOut): Transform<TOut, TIn>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A readonly modification function, useful for sizes or offsets.
|
|
3
|
+
*
|
|
4
|
+
* If you want a writable value, use `st.encode` instead
|
|
5
|
+
*
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* length: length.save(st.u32())
|
|
9
|
+
* st.sizedBytes(
|
|
10
|
+
* st.pipe(
|
|
11
|
+
* length.load(),
|
|
12
|
+
* st.modify(v => v - 8),
|
|
13
|
+
* )
|
|
14
|
+
* ))
|
|
15
|
+
*
|
|
16
|
+
* st.pipe()
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function modify(callback) {
|
|
20
|
+
return (type) => ({
|
|
21
|
+
size: type.size,
|
|
22
|
+
read: (ctx) => callback(type.read(ctx)),
|
|
23
|
+
write: () => {
|
|
24
|
+
throw new Error("Cannot write an encoded value");
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { encodeFailTest } from "../utils.test";
|
|
3
|
+
import * as st from "../index";
|
|
4
|
+
describe("st.modify", () => {
|
|
5
|
+
it("encodes correctly", () => {
|
|
6
|
+
const data = st.write(st.u32(), 10);
|
|
7
|
+
const output = st.read(st.pipe(st.u32(), st.modify((v) => v + 8)), data);
|
|
8
|
+
expect(output).toBe(18);
|
|
9
|
+
});
|
|
10
|
+
it("cannot be written", () => {
|
|
11
|
+
encodeFailTest(st.pipe(st.u32(), st.modify((v) => v + 8)), 3);
|
|
12
|
+
});
|
|
13
|
+
it("does not modify size", () => {
|
|
14
|
+
const spec = st.pipe(st.u32(), st.modify((v) => v + 8));
|
|
15
|
+
expect(spec.size).toBe(4);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Serializer } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Whilst reading this type, don't advance the offset after reading
|
|
4
|
+
*
|
|
5
|
+
* This can be useful for taggedUnions where they key is the first value in all the variants
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* st.object({
|
|
9
|
+
* stringLength: st.pipe(
|
|
10
|
+
* st.u16(),
|
|
11
|
+
* st.noAdvance(),
|
|
12
|
+
* ),
|
|
13
|
+
* text: st.string(st.u16()),
|
|
14
|
+
* })
|
|
15
|
+
*/
|
|
16
|
+
export declare function noAdvance<T>(): (type: Serializer<T>) => Serializer<T>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whilst reading this type, don't advance the offset after reading
|
|
3
|
+
*
|
|
4
|
+
* This can be useful for taggedUnions where they key is the first value in all the variants
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* st.object({
|
|
8
|
+
* stringLength: st.pipe(
|
|
9
|
+
* st.u16(),
|
|
10
|
+
* st.noAdvance(),
|
|
11
|
+
* ),
|
|
12
|
+
* text: st.string(st.u16()),
|
|
13
|
+
* })
|
|
14
|
+
*/
|
|
15
|
+
export function noAdvance() {
|
|
16
|
+
return (type) => ({
|
|
17
|
+
size: type.size,
|
|
18
|
+
read: (ctx) => {
|
|
19
|
+
let start = ctx.offset;
|
|
20
|
+
const value = type.read(ctx);
|
|
21
|
+
ctx.offset = start;
|
|
22
|
+
return value;
|
|
23
|
+
},
|
|
24
|
+
write: (ctx, value) => {
|
|
25
|
+
let start = ctx.offset;
|
|
26
|
+
type.write(ctx, value);
|
|
27
|
+
ctx.offset = start;
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import * as st from "../index";
|
|
3
|
+
import { encodeTest } from "../utils.test";
|
|
4
|
+
describe("st.noAdvance", () => {
|
|
5
|
+
it("encode correctly", () => {
|
|
6
|
+
encodeTest(st.object({
|
|
7
|
+
length: st.pipe(st.u8(), st.noAdvance()),
|
|
8
|
+
text: st.string(st.u8()),
|
|
9
|
+
}), //
|
|
10
|
+
{ length: 3, text: "foo" });
|
|
11
|
+
encodeTest(st.object({
|
|
12
|
+
a: st.pipe(st.u16(), st.noAdvance()),
|
|
13
|
+
b: st.f16(),
|
|
14
|
+
}), //
|
|
15
|
+
{ a: 0, b: 1 }, { a: 15360, b: 1 });
|
|
16
|
+
});
|
|
17
|
+
});
|