@nnilky/structo 1.0.7 → 1.0.9
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.d.ts +1 -1
- package/dist/datatypes/containers/array.js +9 -0
- package/dist/datatypes/containers/array.test.js +2 -2
- package/dist/datatypes/containers/exhuastiveArray.d.ts +2 -2
- package/dist/datatypes/containers/exhuastiveArray.js +13 -2
- package/dist/datatypes/containers/exhuastiveArray.test.js +2 -2
- package/dist/datatypes/containers/fastObject.d.ts +3 -3
- package/dist/datatypes/containers/fastObject.js +2 -2
- package/dist/datatypes/containers/fastObject.test.js +2 -2
- package/dist/datatypes/containers/list.d.ts +1 -1
- package/dist/datatypes/containers/list.js +14 -2
- package/dist/datatypes/containers/list.test.js +2 -2
- package/dist/datatypes/containers/object.d.ts +1 -1
- package/dist/datatypes/containers/object.js +8 -8
- package/dist/datatypes/containers/object.test.js +2 -2
- package/dist/datatypes/containers/taggedUnion.d.ts +1 -1
- package/dist/datatypes/containers/taggedUnion.test.js +2 -2
- package/dist/datatypes/index.d.ts +13 -13
- package/dist/datatypes/index.js +13 -13
- package/dist/datatypes/numbers/bigints.d.ts +1 -1
- package/dist/datatypes/numbers/bigints.test.js +2 -2
- package/dist/datatypes/numbers/floats.d.ts +1 -1
- package/dist/datatypes/numbers/floats.test.js +2 -2
- package/dist/datatypes/numbers/sints.d.ts +1 -1
- package/dist/datatypes/numbers/sints.test.js +2 -2
- package/dist/datatypes/numbers/uints.d.ts +1 -1
- package/dist/datatypes/numbers/uints.test.js +2 -2
- package/dist/datatypes/values/bytes.d.ts +1 -1
- package/dist/datatypes/values/bytes.test.js +2 -2
- package/dist/datatypes/values/sizedbytes.d.ts +1 -1
- package/dist/datatypes/values/sizedbytes.test.js +2 -2
- package/dist/datatypes/values/string.d.ts +1 -1
- package/dist/datatypes/values/string.test.js +5 -2
- package/dist/index.d.ts +6 -6
- package/dist/index.js +5 -5
- package/dist/read.d.ts +2 -6
- package/dist/read.js +9 -1
- package/dist/transforms/encode.d.ts +5 -2
- package/dist/transforms/encode.js +2 -1
- package/dist/transforms/enum.d.ts +1 -1
- package/dist/transforms/enum.js +12 -9
- package/dist/transforms/enum.test.js +2 -2
- package/dist/transforms/index.d.ts +11 -12
- package/dist/transforms/index.js +11 -12
- package/dist/transforms/literal.d.ts +1 -1
- package/dist/transforms/literal.js +12 -9
- package/dist/transforms/literal.test.js +2 -2
- package/dist/transforms/modify.d.ts +20 -2
- package/dist/transforms/modify.js +21 -1
- package/dist/transforms/modify.test.d.ts +1 -0
- package/dist/transforms/modify.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 +1 -1
- package/dist/transforms/pipe.js +1 -1
- package/dist/transforms/toAscii.d.ts +1 -1
- package/dist/transforms/toAscii.js +7 -4
- package/dist/transforms/toAscii.test.js +2 -2
- package/dist/transforms/toBase64.d.ts +1 -1
- package/dist/transforms/toBase64.js +5 -2
- package/dist/transforms/toBase64.test.js +2 -2
- package/dist/transforms/toBytes.d.ts +1 -1
- package/dist/transforms/toBytes.js +5 -2
- package/dist/transforms/toBytes.test.js +2 -2
- package/dist/transforms/toHex.d.ts +1 -1
- package/dist/transforms/toHex.js +5 -2
- package/dist/transforms/toHex.test.js +2 -2
- package/dist/transforms/toTypedArray.d.ts +1 -1
- package/dist/transforms/toTypedArray.js +5 -2
- package/dist/transforms/toTypedArray.test.js +2 -2
- package/dist/types.d.ts +3 -0
- package/dist/utilities/index.d.ts +3 -2
- package/dist/utilities/index.js +3 -2
- package/dist/utilities/lazy.d.ts +1 -1
- package/dist/utilities/lazy.js +4 -4
- package/dist/utilities/lazy.test.js +2 -2
- 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 +7 -6
- package/dist/utilities/remember.js +45 -8
- package/dist/utilities/remember.test.js +3 -7
- package/dist/utils.test.d.ts +1 -1
- package/dist/utils.test.js +1 -1
- package/dist/write.d.ts +1 -1
- package/dist/write.js +9 -1
- package/package.json +9 -9
package/dist/transforms/index.js
CHANGED
|
@@ -1,12 +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 {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export { toTypedArray } from "./toTypedArray";
|
|
1
|
+
export { pipe } from "./pipe.js";
|
|
2
|
+
export { encode } from "./encode.js";
|
|
3
|
+
export { modify } from "./modify.js";
|
|
4
|
+
export { literal } from "./literal.js";
|
|
5
|
+
export { enum } from "./enum.js";
|
|
6
|
+
export { offset } from "./offset.js";
|
|
7
|
+
export { toAscii } from "./toAscii.js";
|
|
8
|
+
export { toBytes } from "./toBytes.js";
|
|
9
|
+
export { toHex } from "./toHex.js";
|
|
10
|
+
export { toBase64 } from "./toBase64.js";
|
|
11
|
+
export { toTypedArray } from "./toTypedArray.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function literal<const T>(value: T): import("./pipe").Transform<T, any>;
|
|
1
|
+
export declare function literal<const T>(value: T): import("./pipe.js").Transform<T, any>;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { encode } from "./encode";
|
|
1
|
+
import { encode } from "./encode.js";
|
|
2
2
|
export function literal(value) {
|
|
3
|
-
return encode(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
+
},
|
|
11
14
|
});
|
|
12
15
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it } from "node:test";
|
|
2
|
-
import * as st from "../index";
|
|
3
|
-
import { encodeTest, encodeFailTest } from "../utils.test";
|
|
2
|
+
import * as st from "../index.js";
|
|
3
|
+
import { encodeTest, encodeFailTest } from "../utils.test.js";
|
|
4
4
|
describe("st.literal", () => {
|
|
5
5
|
it("encode correctly", () => {
|
|
6
6
|
encodeTest(st.pipe(st.u8(), st.literal(0)), //
|
|
@@ -1,2 +1,20 @@
|
|
|
1
|
-
import type { Transform } from "./pipe";
|
|
2
|
-
|
|
1
|
+
import type { Transform } from "./pipe.js";
|
|
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>;
|
|
@@ -1,7 +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
|
+
*/
|
|
1
19
|
export function modify(callback) {
|
|
2
20
|
return (type) => ({
|
|
3
21
|
size: type.size,
|
|
4
22
|
read: (ctx) => callback(type.read(ctx)),
|
|
5
|
-
write: (
|
|
23
|
+
write: () => {
|
|
24
|
+
throw new Error("Cannot write an encoded value");
|
|
25
|
+
},
|
|
6
26
|
});
|
|
7
27
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { encodeFailTest } from "../utils.test.js";
|
|
3
|
+
import * as st from "../index.js";
|
|
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,20 @@
|
|
|
1
|
+
export function offset(behaviour, offset) {
|
|
2
|
+
return (type) => ({
|
|
3
|
+
size: type.size,
|
|
4
|
+
read: (ctx) => {
|
|
5
|
+
let start = ctx.offset;
|
|
6
|
+
const offsetValue = typeof offset === "number" ? offset : offset.read(ctx);
|
|
7
|
+
ctx.offset = behaviour === "absolute" ? offsetValue : ctx.offset + offsetValue;
|
|
8
|
+
const value = type.read(ctx);
|
|
9
|
+
ctx.offset = start;
|
|
10
|
+
return value;
|
|
11
|
+
},
|
|
12
|
+
write: (ctx, value) => {
|
|
13
|
+
let start = ctx.offset;
|
|
14
|
+
const offsetValue = typeof offset === "number" ? offset : offset.read(ctx);
|
|
15
|
+
ctx.offset = behaviour === "absolute" ? offsetValue : ctx.offset + offsetValue;
|
|
16
|
+
type.write(ctx, value);
|
|
17
|
+
ctx.offset = start;
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, it } from "bun:test";
|
|
2
|
+
import { encodeTest } from "../utils.test.js";
|
|
3
|
+
import * as st from "../index.js";
|
|
4
|
+
describe("st.offset(relative)", () => {
|
|
5
|
+
it("encodes correctly with fixed offset", () => {
|
|
6
|
+
const spec = st.object({
|
|
7
|
+
foo: st.u32(),
|
|
8
|
+
bar: st.pipe(st.u32(), st.offset("relative", -4)),
|
|
9
|
+
});
|
|
10
|
+
encodeTest(spec, { foo: 1, bar: 5 }, { foo: 5, bar: 5 });
|
|
11
|
+
});
|
|
12
|
+
it("encodes correctly with read offset", () => {
|
|
13
|
+
const offset = st.createRememberedValue();
|
|
14
|
+
const spec = st.object({
|
|
15
|
+
foo: st.u32(),
|
|
16
|
+
offset: offset.save(st.s16()),
|
|
17
|
+
bar: st.pipe(st.u32(), st.offset("relative", offset.load())),
|
|
18
|
+
});
|
|
19
|
+
encodeTest(spec, { foo: 0, offset: -6, bar: 5 }, { foo: 5, offset: -6, bar: 5 });
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe("st.offset(absolute)", () => {
|
|
23
|
+
it("encodes correctly with fixed offset", () => {
|
|
24
|
+
const spec = st.object({
|
|
25
|
+
foo: st.u32(),
|
|
26
|
+
bar: st.pipe(st.u32(), st.offset("absolute", 0)),
|
|
27
|
+
});
|
|
28
|
+
encodeTest(spec, { foo: 0, bar: 5 }, { foo: 5, bar: 5 });
|
|
29
|
+
});
|
|
30
|
+
it("encodes correctly with read offset", () => {
|
|
31
|
+
const offset = st.createRememberedValue();
|
|
32
|
+
const spec = st.object({
|
|
33
|
+
foo: st.u32(),
|
|
34
|
+
offset: offset.save(st.u32()),
|
|
35
|
+
bar: st.pipe(st.u32(), st.offset("absolute", offset.load())),
|
|
36
|
+
});
|
|
37
|
+
encodeTest(spec, { foo: 0, offset: 0, bar: 5 }, { foo: 5, offset: 0, bar: 5 });
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Serializer } from "../types";
|
|
1
|
+
import type { Serializer } from "../types.js";
|
|
2
2
|
export type Transform<T = any, TNext = T> = (type: Serializer<TNext>) => Serializer<T>;
|
|
3
3
|
export declare function pipe<TStart, T1>(type: Serializer<TStart>, p1: Transform<T1, TStart>): Serializer<T1>;
|
|
4
4
|
export declare function pipe<TStart, T1, T2>(type: Serializer<TStart>, p1: Transform<T1, TStart>, p2: Transform<T2, T1>): Serializer<T2>;
|
package/dist/transforms/pipe.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function toAscii(): import("./pipe").Transform<string, ArrayBuffer>;
|
|
1
|
+
export declare function toAscii(): import("./pipe.js").Transform<string, ArrayBuffer>;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { encode } from "./encode";
|
|
1
|
+
import { encode } from "./encode.js";
|
|
2
2
|
export function toAscii() {
|
|
3
|
-
return encode(
|
|
4
|
-
.map((
|
|
5
|
-
.
|
|
3
|
+
return encode({
|
|
4
|
+
encode: (v) => new Uint8Array(Array.from(v).map((char) => validateAscii(char.charCodeAt(0)))).buffer,
|
|
5
|
+
decode: (v) => Array.from(new Uint8Array(v))
|
|
6
|
+
.map((v) => String.fromCharCode(v))
|
|
7
|
+
.join(""),
|
|
8
|
+
});
|
|
6
9
|
}
|
|
7
10
|
const validateAscii = (v) => {
|
|
8
11
|
if (v >= 0 && v <= 127)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it } from "bun:test";
|
|
2
|
-
import { encodeTest, encodeSnapshotTest, encodeFailTest } from "../utils.test";
|
|
3
|
-
import * as st from "../index";
|
|
2
|
+
import { encodeTest, encodeSnapshotTest, encodeFailTest } from "../utils.test.js";
|
|
3
|
+
import * as st from "../index.js";
|
|
4
4
|
describe("st.toAscii", () => {
|
|
5
5
|
it("encode correctly", () => {
|
|
6
6
|
encodeTest(st.pipe(st.bytes(5), st.toAscii()), //
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function toBase64(): import("./pipe").Transform<string, ArrayBuffer>;
|
|
1
|
+
export declare function toBase64(): import("./pipe.js").Transform<string, ArrayBuffer>;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { encode } from "./encode";
|
|
1
|
+
import { encode } from "./encode.js";
|
|
2
2
|
export function toBase64() {
|
|
3
|
-
return encode(
|
|
3
|
+
return encode({
|
|
4
|
+
encode: (v) => Uint8Array.fromBase64(v).buffer,
|
|
5
|
+
decode: (v) => new Uint8Array(v).toBase64(),
|
|
6
|
+
});
|
|
4
7
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it } from "bun:test";
|
|
2
|
-
import { encodeTest, encodeSnapshotTest, encodeFailTest } from "../utils.test";
|
|
3
|
-
import * as st from "../index";
|
|
2
|
+
import { encodeTest, encodeSnapshotTest, encodeFailTest } from "../utils.test.js";
|
|
3
|
+
import * as st from "../index.js";
|
|
4
4
|
describe("st.toBase64", () => {
|
|
5
5
|
it("encode correctly", () => {
|
|
6
6
|
encodeTest(st.pipe(st.bytes(1), st.toBase64()), //
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { encode } from "./encode";
|
|
1
|
+
import { encode } from "./encode.js";
|
|
2
2
|
/**
|
|
3
3
|
* Converts an ArrayBuffer to the byte digits
|
|
4
4
|
*
|
|
@@ -12,7 +12,10 @@ import { encode } from "./encode";
|
|
|
12
12
|
* `ArrayBuffer([0, 255, c])` => `[0, 255, ArrayBuffer([0, 255, 0])]`
|
|
13
13
|
*/
|
|
14
14
|
export function toBytes() {
|
|
15
|
-
return encode(
|
|
15
|
+
return encode({
|
|
16
|
+
encode: (v) => new Uint8Array(v.map(validateByte)).buffer,
|
|
17
|
+
decode: (v) => Array.from(new Uint8Array(v)),
|
|
18
|
+
});
|
|
16
19
|
}
|
|
17
20
|
const validateByte = (v) => {
|
|
18
21
|
if (v >= 0 && v <= 255)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it } from "bun:test";
|
|
2
|
-
import { encodeTest, encodeSnapshotTest, encodeFailTest } from "../utils.test";
|
|
3
|
-
import * as st from "../index";
|
|
2
|
+
import { encodeTest, encodeSnapshotTest, encodeFailTest } from "../utils.test.js";
|
|
3
|
+
import * as st from "../index.js";
|
|
4
4
|
describe("st.toBytes", () => {
|
|
5
5
|
it("encode correctly", () => {
|
|
6
6
|
encodeTest(st.pipe(st.bytes(3), st.toBytes()), //
|
package/dist/transforms/toHex.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { encode } from "./encode";
|
|
1
|
+
import { encode } from "./encode.js";
|
|
2
2
|
/**
|
|
3
3
|
* Converts an ArrayBuffer to uppercase hex
|
|
4
4
|
*
|
|
@@ -12,5 +12,8 @@ import { encode } from "./encode";
|
|
|
12
12
|
* `ArrayBuffer([0, 255, 0])` => `00FF00`
|
|
13
13
|
*/
|
|
14
14
|
export function toHex() {
|
|
15
|
-
return encode(
|
|
15
|
+
return encode({
|
|
16
|
+
encode: (v) => Uint8Array.fromHex(v).buffer,
|
|
17
|
+
decode: (v) => new Uint8Array(v).toHex().toUpperCase(),
|
|
18
|
+
});
|
|
16
19
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it } from "bun:test";
|
|
2
|
-
import { encodeTest, encodeSnapshotTest, encodeFailTest } from "../utils.test";
|
|
3
|
-
import * as st from "../index";
|
|
2
|
+
import { encodeTest, encodeSnapshotTest, encodeFailTest } from "../utils.test.js";
|
|
3
|
+
import * as st from "../index.js";
|
|
4
4
|
describe("st.toHex", () => {
|
|
5
5
|
it("encode correctly", () => {
|
|
6
6
|
encodeTest(st.pipe(st.bytes(3), st.toHex()), //
|
|
@@ -12,5 +12,5 @@ type TypedArrayConstructor<T extends TypedArray> = new (array: ArrayBuffer) => T
|
|
|
12
12
|
* )
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
|
-
export declare function toTypedArray<T extends TypedArray>(arrayType: TypedArrayConstructor<T>): import("./pipe").Transform<T, ArrayBuffer>;
|
|
15
|
+
export declare function toTypedArray<T extends TypedArray>(arrayType: TypedArrayConstructor<T>): import("./pipe.js").Transform<T, ArrayBuffer>;
|
|
16
16
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { encode } from "./encode";
|
|
1
|
+
import { encode } from "./encode.js";
|
|
2
2
|
/**
|
|
3
3
|
* Converts an `ArrayBuffer` to a `TypedArray`
|
|
4
4
|
*
|
|
@@ -10,5 +10,8 @@ import { encode } from "./encode";
|
|
|
10
10
|
* ```
|
|
11
11
|
*/
|
|
12
12
|
export function toTypedArray(arrayType) {
|
|
13
|
-
return encode(
|
|
13
|
+
return encode({
|
|
14
|
+
encode: (v) => v.buffer,
|
|
15
|
+
decode: (v) => new arrayType(v),
|
|
16
|
+
});
|
|
14
17
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it } from "bun:test";
|
|
2
|
-
import { encodeTest, encodeSnapshotTest, expectError } from "../utils.test";
|
|
3
|
-
import * as st from "../index";
|
|
2
|
+
import { encodeTest, encodeSnapshotTest, expectError } from "../utils.test.js";
|
|
3
|
+
import * as st from "../index.js";
|
|
4
4
|
describe("st.toArrayBuffer", () => {
|
|
5
5
|
it("encode correctly", () => {
|
|
6
6
|
encodeTest(st.pipe(st.bytes(6), //
|
package/dist/types.d.ts
CHANGED
|
@@ -3,13 +3,16 @@ export type Serializer<TIn, TOut = TIn> = {
|
|
|
3
3
|
write: (ctx: WriterContext, value: TIn) => void;
|
|
4
4
|
read: (ctx: ReaderContext) => TOut;
|
|
5
5
|
};
|
|
6
|
+
export type SerializationContext = WriterContext | ReaderContext;
|
|
6
7
|
export interface WriterContext {
|
|
8
|
+
stack: string[];
|
|
7
9
|
buffer: ArrayBuffer;
|
|
8
10
|
view: DataView;
|
|
9
11
|
offset: number;
|
|
10
12
|
alloc: (length: number) => void;
|
|
11
13
|
}
|
|
12
14
|
export interface ReaderContext {
|
|
15
|
+
stack: string[];
|
|
13
16
|
buffer: ArrayBuffer;
|
|
14
17
|
view: DataView;
|
|
15
18
|
offset: number;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { lazy } from "./lazy";
|
|
2
|
-
export {
|
|
1
|
+
export { lazy } from "./lazy.js";
|
|
2
|
+
export { createReference } from "./reference.js";
|
|
3
|
+
export { createRememberedValue } from "./remember.js";
|
package/dist/utilities/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { lazy } from "./lazy";
|
|
2
|
-
export {
|
|
1
|
+
export { lazy } from "./lazy.js";
|
|
2
|
+
export { createReference } from "./reference.js";
|
|
3
|
+
export { createRememberedValue } from "./remember.js";
|
package/dist/utilities/lazy.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { Serializer } from "../types";
|
|
1
|
+
import type { Serializer } from "../types.js";
|
|
2
2
|
export declare function lazy<TIn, TOut = TIn>(type: () => Serializer<TIn, TOut>): Serializer<TIn, TOut>;
|
package/dist/utilities/lazy.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export function lazy(type) {
|
|
2
2
|
let _size;
|
|
3
3
|
let resolve = () => {
|
|
4
|
-
const t = type();
|
|
5
|
-
_size = t.size ?? undefined;
|
|
6
|
-
serializer.read = t.read;
|
|
7
|
-
serializer.write = t.write;
|
|
8
4
|
resolve = () => { };
|
|
5
|
+
const { size, write, read } = type();
|
|
6
|
+
_size = size ?? undefined;
|
|
7
|
+
serializer.read = read;
|
|
8
|
+
serializer.write = write;
|
|
9
9
|
};
|
|
10
10
|
const serializer = {
|
|
11
11
|
get size() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import { encodeTest } from "../utils.test";
|
|
3
|
-
import * as st from "../index";
|
|
2
|
+
import { encodeTest } from "../utils.test.js";
|
|
3
|
+
import * as st from "../index.js";
|
|
4
4
|
describe("st.lazy", () => {
|
|
5
5
|
it("encodes correctly", () => {
|
|
6
6
|
const spec = st.lazy(() => st.object({
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Serializer } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* createReference lets you defer writing a value until later, useful for lengths and sizes
|
|
4
|
+
*
|
|
5
|
+
* ---
|
|
6
|
+
* ```
|
|
7
|
+
* const length = st.createReference<number>()
|
|
8
|
+
* st.object({
|
|
9
|
+
* length: length.pointer(st.u32()),
|
|
10
|
+
* type: st.u8(),
|
|
11
|
+
* data: st.sizedBytes(length.deref())
|
|
12
|
+
* })
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function createReference<T>(): {
|
|
16
|
+
pointer: (serializer: Serializer<T>) => Serializer<T>;
|
|
17
|
+
deref: () => Serializer<T>;
|
|
18
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createReference lets you defer writing a value until later, useful for lengths and sizes
|
|
3
|
+
*
|
|
4
|
+
* ---
|
|
5
|
+
* ```
|
|
6
|
+
* const length = st.createReference<number>()
|
|
7
|
+
* st.object({
|
|
8
|
+
* length: length.pointer(st.u32()),
|
|
9
|
+
* type: st.u8(),
|
|
10
|
+
* data: st.sizedBytes(length.deref())
|
|
11
|
+
* })
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function createReference() {
|
|
15
|
+
const readStack = [];
|
|
16
|
+
const writeStack = [];
|
|
17
|
+
/**
|
|
18
|
+
* When reading: reads the value normal and saves it for later
|
|
19
|
+
*
|
|
20
|
+
* When writing: omits this value, it is written by deref
|
|
21
|
+
*
|
|
22
|
+
* ---
|
|
23
|
+
* ```
|
|
24
|
+
* const length = st.createReference<number>()
|
|
25
|
+
* st.object({
|
|
26
|
+
* length: length.pointer(st.u32()),
|
|
27
|
+
* type: st.u8(),
|
|
28
|
+
* data: st.sizedBytes(length.deref())
|
|
29
|
+
* })
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function pointer(serializer) {
|
|
33
|
+
const size = serializer.size;
|
|
34
|
+
if (size === undefined)
|
|
35
|
+
throw new Error("Serializer must be sized");
|
|
36
|
+
return {
|
|
37
|
+
size,
|
|
38
|
+
read: (ctx) => {
|
|
39
|
+
const value = serializer.read(ctx);
|
|
40
|
+
readStack.push(value);
|
|
41
|
+
return value;
|
|
42
|
+
},
|
|
43
|
+
write: (ctx, value) => {
|
|
44
|
+
writeStack.push({
|
|
45
|
+
offset: ctx.offset,
|
|
46
|
+
serializer,
|
|
47
|
+
});
|
|
48
|
+
ctx.offset += size;
|
|
49
|
+
return value;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* When reading: gets the value stored in pointer
|
|
55
|
+
*
|
|
56
|
+
* When writing: saves the value into the pointer
|
|
57
|
+
*
|
|
58
|
+
* By default the value is removed from the pointer,
|
|
59
|
+
* but you can retain it to ensure the next value is
|
|
60
|
+
*
|
|
61
|
+
* ---
|
|
62
|
+
* ```
|
|
63
|
+
* const length = st.createReference<number>()
|
|
64
|
+
* st.object({
|
|
65
|
+
* length: length.pointer(st.u32()),
|
|
66
|
+
* type: st.u8(),
|
|
67
|
+
* data: st.sizedBytes(length.deref())
|
|
68
|
+
* })
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
function deref() {
|
|
72
|
+
return {
|
|
73
|
+
size: 0,
|
|
74
|
+
read: () => {
|
|
75
|
+
if (readStack.length === 0)
|
|
76
|
+
throw new Error("Read Stack Empty");
|
|
77
|
+
return readStack.pop();
|
|
78
|
+
},
|
|
79
|
+
write: (ctx, value) => {
|
|
80
|
+
if (writeStack.length === 0)
|
|
81
|
+
throw new Error("Write Stack is emtpy");
|
|
82
|
+
const { offset, serializer } = writeStack.pop();
|
|
83
|
+
const start = ctx.offset;
|
|
84
|
+
ctx.offset = offset;
|
|
85
|
+
serializer.write(ctx, value);
|
|
86
|
+
ctx.offset = start;
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return { pointer, deref };
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { encodeTest, encodeSnapshotTest, expectError } from "../utils.test.js";
|
|
3
|
+
import * as st from "../index.js";
|
|
4
|
+
describe("st.createReference", () => {
|
|
5
|
+
it("encodes correctly", () => {
|
|
6
|
+
const length = st.createReference();
|
|
7
|
+
const spec = st.object({
|
|
8
|
+
length: length.pointer(st.u32()),
|
|
9
|
+
numbers: st.list(length.deref(), st.u8()),
|
|
10
|
+
});
|
|
11
|
+
expect(spec.size).toBe(undefined);
|
|
12
|
+
encodeTest(spec, //
|
|
13
|
+
{ length: 0, numbers: [1, 3, 4] }, { length: 3, numbers: [1, 3, 4] });
|
|
14
|
+
});
|
|
15
|
+
it("cant create reference to pointer", () => {
|
|
16
|
+
const length = st.createReference();
|
|
17
|
+
expectError(() => {
|
|
18
|
+
length.pointer(st.sizedBytes(st.u8()));
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
it("multiple runs function", () => {
|
|
22
|
+
const v = st.createReference();
|
|
23
|
+
const spec = st.object({
|
|
24
|
+
a: v.pointer(st.u32()),
|
|
25
|
+
b: st.list(v.deref(), st.u8()),
|
|
26
|
+
});
|
|
27
|
+
for (let i = 0; i < 3; i++) {
|
|
28
|
+
encodeTest(spec, //
|
|
29
|
+
{ a: NaN, b: [1, 3, 4] }, { a: 3, b: [1, 3, 4] });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
it("handles recursive objects", () => {
|
|
33
|
+
const size = st.createReference();
|
|
34
|
+
//@ts-expect-error, recursive types
|
|
35
|
+
const Node = st.lazy(() => st.object({
|
|
36
|
+
size: size.pointer(st.u32()),
|
|
37
|
+
children: st.list(size.deref(), Node),
|
|
38
|
+
}));
|
|
39
|
+
encodeTest(Node, {
|
|
40
|
+
size: NaN,
|
|
41
|
+
children: [
|
|
42
|
+
{ size: NaN, children: [] },
|
|
43
|
+
{ size: NaN, children: [{ size: NaN, children: [] }] },
|
|
44
|
+
],
|
|
45
|
+
}, {
|
|
46
|
+
size: 2,
|
|
47
|
+
children: [
|
|
48
|
+
{ size: 0, children: [] },
|
|
49
|
+
{ size: 1, children: [{ size: 0, children: [] }] },
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
it("snapshots are correct", () => {
|
|
54
|
+
const v = st.createReference();
|
|
55
|
+
encodeSnapshotTest(st.object({
|
|
56
|
+
a: v.pointer(st.u32()),
|
|
57
|
+
b: st.list(v.deref(), st.u8()),
|
|
58
|
+
}), {
|
|
59
|
+
a: 3,
|
|
60
|
+
b: [1, 3, 4],
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|