@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.
Files changed (104) hide show
  1. package/README.md +3 -3
  2. package/dist/datatypes/containers/array.js +9 -0
  3. package/dist/datatypes/containers/array.test.js +6 -10
  4. package/dist/datatypes/containers/exhuastiveArray.d.ts +2 -2
  5. package/dist/datatypes/containers/exhuastiveArray.js +13 -2
  6. package/dist/datatypes/containers/exhuastiveArray.test.js +9 -12
  7. package/dist/datatypes/containers/fastObject.d.ts +3 -1
  8. package/dist/datatypes/containers/fastObject.js +3 -1
  9. package/dist/datatypes/containers/fastObject.test.js +18 -6
  10. package/dist/datatypes/containers/list.js +14 -2
  11. package/dist/datatypes/containers/list.test.js +6 -12
  12. package/dist/datatypes/containers/object.js +8 -2
  13. package/dist/datatypes/containers/object.test.js +22 -10
  14. package/dist/datatypes/containers/taggedUnion.d.ts +12 -8
  15. package/dist/datatypes/containers/taggedUnion.js +4 -2
  16. package/dist/datatypes/containers/taggedUnion.test.js +22 -18
  17. package/dist/datatypes/index.d.ts +2 -9
  18. package/dist/datatypes/index.js +2 -9
  19. package/dist/datatypes/numbers/bigints.test.js +11 -11
  20. package/dist/datatypes/numbers/floats.test.js +6 -6
  21. package/dist/datatypes/numbers/sints.test.js +11 -11
  22. package/dist/datatypes/numbers/uints.test.js +11 -11
  23. package/dist/datatypes/values/bytes.test.js +6 -8
  24. package/dist/datatypes/values/json.d.ts +10 -0
  25. package/dist/datatypes/values/json.js +21 -0
  26. package/dist/datatypes/values/sizedbytes.d.ts +2 -0
  27. package/dist/datatypes/values/sizedbytes.js +17 -0
  28. package/dist/datatypes/values/sizedbytes.test.d.ts +1 -0
  29. package/dist/datatypes/values/sizedbytes.test.js +25 -0
  30. package/dist/datatypes/values/string.d.ts +14 -0
  31. package/dist/datatypes/values/string.js +36 -0
  32. package/dist/datatypes/values/string.test.d.ts +1 -0
  33. package/dist/datatypes/values/string.test.js +21 -0
  34. package/dist/index.d.ts +4 -2
  35. package/dist/index.js +3 -1
  36. package/dist/read.d.ts +2 -6
  37. package/dist/read.js +9 -1
  38. package/dist/transforms/encode.d.ts +5 -0
  39. package/dist/transforms/encode.js +8 -0
  40. package/dist/transforms/enum.d.ts +2 -0
  41. package/dist/transforms/enum.js +16 -0
  42. package/dist/transforms/enum.test.d.ts +1 -0
  43. package/dist/transforms/enum.test.js +21 -0
  44. package/dist/transforms/fixedOffset.d.ts +2 -0
  45. package/dist/transforms/fixedOffset.js +18 -0
  46. package/dist/transforms/index.d.ts +11 -0
  47. package/dist/transforms/index.js +11 -0
  48. package/dist/transforms/literal.d.ts +1 -0
  49. package/dist/transforms/literal.js +15 -0
  50. package/dist/transforms/literal.test.d.ts +1 -0
  51. package/dist/transforms/literal.test.js +23 -0
  52. package/dist/transforms/modify.d.ts +20 -0
  53. package/dist/transforms/modify.js +27 -0
  54. package/dist/transforms/modify.test.d.ts +1 -0
  55. package/dist/transforms/modify.test.js +17 -0
  56. package/dist/transforms/noAdvance.d.ts +16 -0
  57. package/dist/transforms/noAdvance.js +30 -0
  58. package/dist/transforms/noAdvance.test.d.ts +1 -0
  59. package/dist/transforms/noAdvance.test.js +17 -0
  60. package/dist/transforms/offset.d.ts +2 -0
  61. package/dist/transforms/offset.js +20 -0
  62. package/dist/transforms/offset.test.d.ts +1 -0
  63. package/dist/transforms/offset.test.js +39 -0
  64. package/dist/transforms/pipe.d.ts +11 -0
  65. package/dist/transforms/pipe.js +16 -0
  66. package/dist/transforms/toAscii.d.ts +1 -0
  67. package/dist/transforms/toAscii.js +14 -0
  68. package/dist/transforms/toAscii.test.d.ts +1 -0
  69. package/dist/transforms/toAscii.test.js +25 -0
  70. package/dist/transforms/toBase64.d.ts +1 -0
  71. package/dist/transforms/toBase64.js +7 -0
  72. package/dist/transforms/toBase64.test.d.ts +1 -0
  73. package/dist/transforms/toBase64.test.js +27 -0
  74. package/dist/transforms/toBytes.d.ts +13 -0
  75. package/dist/transforms/toBytes.js +24 -0
  76. package/dist/transforms/toBytes.test.d.ts +1 -0
  77. package/dist/transforms/toBytes.test.js +27 -0
  78. package/dist/transforms/toHex.d.ts +13 -0
  79. package/dist/transforms/toHex.js +19 -0
  80. package/dist/transforms/toHex.test.d.ts +1 -0
  81. package/dist/transforms/toHex.test.js +23 -0
  82. package/dist/transforms/toTypedArray.d.ts +16 -0
  83. package/dist/transforms/toTypedArray.js +17 -0
  84. package/dist/transforms/toTypedArray.test.d.ts +1 -0
  85. package/dist/transforms/toTypedArray.test.js +29 -0
  86. package/dist/types.d.ts +3 -0
  87. package/dist/utilities/index.d.ts +3 -0
  88. package/dist/utilities/index.js +3 -0
  89. package/dist/utilities/lazy.d.ts +2 -0
  90. package/dist/utilities/lazy.js +25 -0
  91. package/dist/utilities/lazy.test.d.ts +1 -0
  92. package/dist/utilities/lazy.test.js +36 -0
  93. package/dist/utilities/reference.d.ts +18 -0
  94. package/dist/utilities/reference.js +91 -0
  95. package/dist/utilities/reference.test.d.ts +1 -0
  96. package/dist/utilities/reference.test.js +63 -0
  97. package/dist/utilities/remember.d.ts +18 -0
  98. package/dist/utilities/remember.js +75 -0
  99. package/dist/utilities/remember.test.d.ts +1 -0
  100. package/dist/utilities/remember.test.js +72 -0
  101. package/dist/utils.test.d.ts +9 -1
  102. package/dist/utils.test.js +42 -1
  103. package/dist/write.js +9 -1
  104. 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 { expectEncode, expectEncodeSnapshot, expectError, randint } from "../utils.test";
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
- expectEncode(serializer, start);
9
+ encodeTest(serializer, start);
10
10
  for (let i = 0; i < 100; i++) {
11
- expectEncode(serializer, randint(start, end));
11
+ encodeTest(serializer, randint(start, end));
12
12
  }
13
- expectEncode(serializer, end);
13
+ encodeTest(serializer, end);
14
14
  });
15
15
  if (!disableRangeCheck) {
16
16
  it(`errors outside bounds`, () => {
17
- expectError(() => st.write(serializer, start - 1));
18
- expectError(() => st.write(serializer, end + 1));
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
- expectEncodeSnapshot(serializer, 0);
37
- expectEncodeSnapshot(serializer, start);
38
- expectEncodeSnapshot(serializer, start + 10);
39
- expectEncodeSnapshot(serializer, end - 10);
40
- expectEncodeSnapshot(serializer, end);
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 { expectEncode, expectEncodeSnapshot, expectError, randint } from "../utils.test";
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
- expectEncode(serializer, start);
9
+ encodeTest(serializer, start);
10
10
  for (let i = 0; i < 100; i++) {
11
- expectEncode(serializer, randint(start, end));
11
+ encodeTest(serializer, randint(start, end));
12
12
  }
13
- expectEncode(serializer, end);
13
+ encodeTest(serializer, end);
14
14
  });
15
15
  it(`errors outside bounds`, () => {
16
- expectError(() => st.write(serializer, start - 1));
16
+ encodeFailTest(serializer, start - 1);
17
17
  if (!disableMaxCheck) {
18
- expectError(() => st.write(serializer, end + 1));
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
- expectEncodeSnapshot(serializer, 0);
37
- expectEncodeSnapshot(serializer, start);
38
- expectEncodeSnapshot(serializer, start + 10);
39
- expectEncodeSnapshot(serializer, end - 10);
40
- expectEncodeSnapshot(serializer, end);
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, expectEncode, expectError } from "../utils.test";
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
- expectEncode(buffer, bytes([1, 2]));
7
+ encodeTest(buffer, bytes([1, 2]));
8
8
  });
9
9
  it("encodes empty correctly", () => {
10
10
  const spec = st.bytes(0);
11
- expectEncode(spec, bytes([]));
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
- expectEncode(spec, data.buffer);
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
- expectEncode(spec, {
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
- expectError(() => {
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,10 @@
1
+ import type { Serializer } from "../../types";
2
+ /**
3
+ * `st.json` stores JSON data using a string serializer
4
+ *
5
+ * ```
6
+ * st.json(st.string(st.u32()))
7
+ * ```
8
+ *
9
+ */
10
+ export declare function json(type: Serializer<string>): Serializer<any>;
@@ -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,2 @@
1
+ import type { Serializer } from "../../types";
2
+ export declare function sizedBytes(length: Serializer<number>): Serializer<ArrayBuffer>;
@@ -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 { InferInput as Infer, InferInput, InferOutput, ReaderContext, WriterContext, Serializer, } from "./types";
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
@@ -1,3 +1,5 @@
1
- export * from "./datatypes/index";
2
1
  export { read, createReaderContext } from "./read";
3
2
  export { write, createdWriterContext } from "./write";
3
+ export * from "./datatypes/index";
4
+ export * from "./transforms/index";
5
+ export * from "./utilities/index";
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
- return serializer.read(createReaderContext(buffer));
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,5 @@
1
+ import type { Transform } from "./pipe";
2
+ export declare function encode<TIn, TOut>(options: {
3
+ encode: (value: TOut) => TIn;
4
+ decode: (value: TIn) => TOut;
5
+ }): Transform<TOut, TIn>;
@@ -0,0 +1,8 @@
1
+ export function encode(options) {
2
+ const { encode, decode } = options;
3
+ return (type) => ({
4
+ size: type.size,
5
+ read: (ctx) => decode(type.read(ctx)),
6
+ write: (ctx, value) => type.write(ctx, encode(value)),
7
+ });
8
+ }
@@ -0,0 +1,2 @@
1
+ export declare function enum_<const T>(values: T[]): import("./pipe").Transform<T, any>;
2
+ export { enum_ as enum };
@@ -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,2 @@
1
+ import type { Serializer } from "../types";
2
+ export declare function fixedOffset<T>(delta: number): (type: Serializer<T>) => Serializer<T>;
@@ -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
+ });
@@ -0,0 +1,2 @@
1
+ import type { Serializer } from "../types";
2
+ export declare function offset<TIn, TOut>(behaviour: "relative" | "absolute", offset: Serializer<number> | number): (type: Serializer<TIn, TOut>) => Serializer<TIn, TOut>;