@statewalker/webrun-msgpack 0.1.1 → 0.3.0

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.
@@ -0,0 +1,66 @@
1
+ import type { PortCodec, PortEnvelope } from "@statewalker/webrun-rpc";
2
+ import { deserialize, serialize } from "./msgpack-core.js";
3
+
4
+ /**
5
+ * Same shape check as `structuredCodec`'s, applied after decoding. A shared
6
+ * transport carries traffic that is not ours, and layer 1 must not mistake it
7
+ * for an envelope.
8
+ */
9
+ function isEnvelope(value: unknown): value is PortEnvelope {
10
+ if (typeof value !== "object" || value === null) return false;
11
+ const candidate = value as { type?: unknown; id?: unknown };
12
+ if (typeof candidate.id !== "number") return false;
13
+ if (!Number.isInteger(candidate.id) || candidate.id < 0) return false;
14
+ return candidate.type === "open" || candidate.type === "message" || candidate.type === "close";
15
+ }
16
+
17
+ /** Accept whatever byte shape a transport pump hands over. */
18
+ function toBytes(data: unknown): Uint8Array | undefined {
19
+ if (data instanceof Uint8Array) return data;
20
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
21
+ if (ArrayBuffer.isView(data)) {
22
+ const view = data as ArrayBufferView;
23
+ return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
24
+ }
25
+ return undefined;
26
+ }
27
+
28
+ /**
29
+ * For ports whose messages are bytes — a WebSocket, a WebRTC data channel, a
30
+ * LiveKit data packet.
31
+ *
32
+ * One envelope becomes one msgpack frame and one `postMessage`. There is no
33
+ * length prefix, because every transport this codec targets preserves message
34
+ * boundaries; adding one would be redundant framing on a transport that
35
+ * already frames. (`encodeMsgpack`/`decodeMsgpack` in this package *do* carry
36
+ * a length prefix — they are a stream codec for a transport with no
37
+ * boundaries, and are a different thing.)
38
+ *
39
+ * The transfer list is deliberately ignored: after encoding, the payload is
40
+ * inside the bytes, so there is nothing left to hand over.
41
+ *
42
+ * **Not interchangeable with `structuredCodec` in one respect:** msgpack drops
43
+ * object keys whose value is `undefined`, where structured clone preserves
44
+ * them. Nothing layer 2 sends depends on that distinction today, and it must
45
+ * not come to — see spec D16.
46
+ */
47
+ export const msgpackCodec: PortCodec = {
48
+ post(port, envelope) {
49
+ port.postMessage(serialize(envelope));
50
+ },
51
+
52
+ read(event) {
53
+ const bytes = toBytes(event.data);
54
+ if (!bytes || bytes.byteLength === 0) return undefined;
55
+ let decoded: unknown;
56
+ try {
57
+ decoded = deserialize(bytes);
58
+ } catch {
59
+ // A malformed frame is a peer bug or hostile traffic. Dropping it keeps
60
+ // layer 1's drop-never-queue posture; throwing here would escape into
61
+ // the raw port's own listener, outside any consumer's reach.
62
+ return undefined;
63
+ }
64
+ return isEnvelope(decoded) ? decoded : undefined;
65
+ },
66
+ };
package/dist/index.d.mts DELETED
@@ -1,23 +0,0 @@
1
- //#region src/msgpack.d.ts
2
- /**
3
- * Encode each value as a length-prefixed msgpack frame.
4
- * Frame format: [4-byte big-endian length][msgpack bytes]
5
- */
6
- declare function encodeMsgpack<T>(input: AsyncIterable<T>): AsyncGenerator<Uint8Array>;
7
- /**
8
- * Decode length-prefixed msgpack frames, reassembling across chunk boundaries.
9
- */
10
- declare function decodeMsgpack<T>(input: AsyncIterable<Uint8Array>): AsyncGenerator<T>;
11
- /**
12
- * Encode each Float32Array as a msgpack frame.
13
- * The Float32Array is converted to a Uint8Array view (zero-copy) before encoding as msgpack bin.
14
- */
15
- declare function encodeFloat32Arrays(input: AsyncIterable<Float32Array>): AsyncGenerator<Uint8Array>;
16
- /**
17
- * Decode msgpack frames back to Float32Array.
18
- * Each frame contains a msgpack bin value (Uint8Array), reinterpreted as Float32Array.
19
- */
20
- declare function decodeFloat32Arrays(input: AsyncIterable<Uint8Array>): AsyncGenerator<Float32Array>;
21
- //#endregion
22
- export { decodeFloat32Arrays, decodeMsgpack, encodeFloat32Arrays, encodeMsgpack };
23
- //# sourceMappingURL=index.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/msgpack.ts"],"mappings":";;;;;iBAQuB,cAAc,GAAG,OAAO,cAAc,KAAK,eAAe;;;;iBAc1D,cAAc,GAAG,OAAO,cAAc,cAAc,eAAe;;;;;iBAsBnE,oBACrB,OAAO,cAAc,gBACpB,eAAe;;;;;iBAgBK,oBACrB,OAAO,cAAc,cACpB,eAAe"}
package/dist/index.mjs DELETED
@@ -1,76 +0,0 @@
1
- import msgpack from "@ygoe/msgpack";
2
- //#region src/msgpack.ts
3
- const { serialize, deserialize } = msgpack;
4
- /**
5
- * Encode each value as a length-prefixed msgpack frame.
6
- * Frame format: [4-byte big-endian length][msgpack bytes]
7
- */
8
- async function* encodeMsgpack(input) {
9
- for await (const item of input) {
10
- const payload = serialize(item);
11
- const frame = new Uint8Array(4 + payload.length);
12
- new DataView(frame.buffer).setUint32(0, payload.length, false);
13
- frame.set(payload, 4);
14
- yield frame;
15
- }
16
- }
17
- /**
18
- * Decode length-prefixed msgpack frames, reassembling across chunk boundaries.
19
- */
20
- async function* decodeMsgpack(input) {
21
- let buffer = /* @__PURE__ */ new Uint8Array(0);
22
- for await (const chunk of input) {
23
- buffer = concat(buffer, chunk);
24
- while (buffer.length >= 4) {
25
- const frameLen = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getUint32(0, false);
26
- if (buffer.length < 4 + frameLen) break;
27
- const payload = buffer.subarray(4, 4 + frameLen);
28
- buffer = buffer.subarray(4 + frameLen);
29
- yield deserialize(Uint8Array.from(payload));
30
- }
31
- }
32
- }
33
- /**
34
- * Encode each Float32Array as a msgpack frame.
35
- * The Float32Array is converted to a Uint8Array view (zero-copy) before encoding as msgpack bin.
36
- */
37
- async function* encodeFloat32Arrays(input) {
38
- for await (const arr of input) {
39
- const bytes = new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
40
- const payload = serialize(bytes);
41
- const frame = new Uint8Array(4 + payload.length);
42
- new DataView(frame.buffer).setUint32(0, payload.length, false);
43
- frame.set(payload, 4);
44
- yield frame;
45
- }
46
- }
47
- /**
48
- * Decode msgpack frames back to Float32Array.
49
- * Each frame contains a msgpack bin value (Uint8Array), reinterpreted as Float32Array.
50
- */
51
- async function* decodeFloat32Arrays(input) {
52
- for await (const item of decodeMsgpack(wrapIterable(input))) {
53
- const aligned = alignBuffer(item);
54
- yield new Float32Array(aligned.buffer, aligned.byteOffset, aligned.byteLength / 4);
55
- }
56
- }
57
- function alignBuffer(bytes) {
58
- if (bytes.byteOffset % 4 === 0) return bytes;
59
- const aligned = new Uint8Array(bytes.length);
60
- aligned.set(bytes);
61
- return aligned;
62
- }
63
- function concat(a, b) {
64
- if (a.length === 0) return b;
65
- const result = new Uint8Array(a.length + b.length);
66
- result.set(a, 0);
67
- result.set(b, a.length);
68
- return result;
69
- }
70
- async function* wrapIterable(input) {
71
- yield* input;
72
- }
73
- //#endregion
74
- export { decodeFloat32Arrays, decodeMsgpack, encodeFloat32Arrays, encodeMsgpack };
75
-
76
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/msgpack.ts"],"sourcesContent":["import msgpack from \"@ygoe/msgpack\";\n\nconst { serialize, deserialize } = msgpack;\n\n/**\n * Encode each value as a length-prefixed msgpack frame.\n * Frame format: [4-byte big-endian length][msgpack bytes]\n */\nexport async function* encodeMsgpack<T>(input: AsyncIterable<T>): AsyncGenerator<Uint8Array> {\n for await (const item of input) {\n const payload = serialize(item);\n const frame = new Uint8Array(4 + payload.length);\n const view = new DataView(frame.buffer);\n view.setUint32(0, payload.length, false);\n frame.set(payload, 4);\n yield frame;\n }\n}\n\n/**\n * Decode length-prefixed msgpack frames, reassembling across chunk boundaries.\n */\nexport async function* decodeMsgpack<T>(input: AsyncIterable<Uint8Array>): AsyncGenerator<T> {\n let buffer: Uint8Array = new Uint8Array(0);\n\n for await (const chunk of input) {\n buffer = concat(buffer, chunk);\n\n while (buffer.length >= 4) {\n const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);\n const frameLen = view.getUint32(0, false);\n if (buffer.length < 4 + frameLen) break;\n\n const payload = buffer.subarray(4, 4 + frameLen);\n buffer = buffer.subarray(4 + frameLen);\n yield deserialize(Uint8Array.from(payload)) as T;\n }\n }\n}\n\n/**\n * Encode each Float32Array as a msgpack frame.\n * The Float32Array is converted to a Uint8Array view (zero-copy) before encoding as msgpack bin.\n */\nexport async function* encodeFloat32Arrays(\n input: AsyncIterable<Float32Array>,\n): AsyncGenerator<Uint8Array> {\n for await (const arr of input) {\n const bytes = new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);\n const payload = serialize(bytes);\n const frame = new Uint8Array(4 + payload.length);\n const view = new DataView(frame.buffer);\n view.setUint32(0, payload.length, false);\n frame.set(payload, 4);\n yield frame;\n }\n}\n\n/**\n * Decode msgpack frames back to Float32Array.\n * Each frame contains a msgpack bin value (Uint8Array), reinterpreted as Float32Array.\n */\nexport async function* decodeFloat32Arrays(\n input: AsyncIterable<Uint8Array>,\n): AsyncGenerator<Float32Array> {\n for await (const item of decodeMsgpack<Uint8Array>(wrapIterable(input))) {\n const aligned = alignBuffer(item);\n yield new Float32Array(aligned.buffer, aligned.byteOffset, aligned.byteLength / 4);\n }\n}\n\nfunction alignBuffer(bytes: Uint8Array): Uint8Array {\n if (bytes.byteOffset % 4 === 0) return bytes;\n const aligned = new Uint8Array(bytes.length);\n aligned.set(bytes);\n return aligned;\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n if (a.length === 0) return b;\n const result = new Uint8Array(a.length + b.length);\n result.set(a, 0);\n result.set(b, a.length);\n return result;\n}\n\nasync function* wrapIterable<T>(input: AsyncIterable<T>): AsyncGenerator<T> {\n yield* input;\n}\n"],"mappings":";;AAEA,MAAM,EAAE,WAAW,gBAAgB;;;;;AAMnC,gBAAuB,cAAiB,OAAqD;CAC3F,WAAW,MAAM,QAAQ,OAAO;EAC9B,MAAM,UAAU,UAAU,IAAI;EAC9B,MAAM,QAAQ,IAAI,WAAW,IAAI,QAAQ,MAAM;EAE/C,IADiB,SAAS,MAAM,MAC7B,CAAC,CAAC,UAAU,GAAG,QAAQ,QAAQ,KAAK;EACvC,MAAM,IAAI,SAAS,CAAC;EACpB,MAAM;CACR;AACF;;;;AAKA,gBAAuB,cAAiB,OAAqD;CAC3F,IAAI,yBAAqB,IAAI,WAAW,CAAC;CAEzC,WAAW,MAAM,SAAS,OAAO;EAC/B,SAAS,OAAO,QAAQ,KAAK;EAE7B,OAAO,OAAO,UAAU,GAAG;GAEzB,MAAM,WAAW,IADA,SAAS,OAAO,QAAQ,OAAO,YAAY,OAAO,UAC/C,CAAC,CAAC,UAAU,GAAG,KAAK;GACxC,IAAI,OAAO,SAAS,IAAI,UAAU;GAElC,MAAM,UAAU,OAAO,SAAS,GAAG,IAAI,QAAQ;GAC/C,SAAS,OAAO,SAAS,IAAI,QAAQ;GACrC,MAAM,YAAY,WAAW,KAAK,OAAO,CAAC;EAC5C;CACF;AACF;;;;;AAMA,gBAAuB,oBACrB,OAC4B;CAC5B,WAAW,MAAM,OAAO,OAAO;EAC7B,MAAM,QAAQ,IAAI,WAAW,IAAI,QAAQ,IAAI,YAAY,IAAI,UAAU;EACvE,MAAM,UAAU,UAAU,KAAK;EAC/B,MAAM,QAAQ,IAAI,WAAW,IAAI,QAAQ,MAAM;EAE/C,IADiB,SAAS,MAAM,MAC7B,CAAC,CAAC,UAAU,GAAG,QAAQ,QAAQ,KAAK;EACvC,MAAM,IAAI,SAAS,CAAC;EACpB,MAAM;CACR;AACF;;;;;AAMA,gBAAuB,oBACrB,OAC8B;CAC9B,WAAW,MAAM,QAAQ,cAA0B,aAAa,KAAK,CAAC,GAAG;EACvE,MAAM,UAAU,YAAY,IAAI;EAChC,MAAM,IAAI,aAAa,QAAQ,QAAQ,QAAQ,YAAY,QAAQ,aAAa,CAAC;CACnF;AACF;AAEA,SAAS,YAAY,OAA+B;CAClD,IAAI,MAAM,aAAa,MAAM,GAAG,OAAO;CACvC,MAAM,UAAU,IAAI,WAAW,MAAM,MAAM;CAC3C,QAAQ,IAAI,KAAK;CACjB,OAAO;AACT;AAEA,SAAS,OAAO,GAAe,GAA2B;CACxD,IAAI,EAAE,WAAW,GAAG,OAAO;CAC3B,MAAM,SAAS,IAAI,WAAW,EAAE,SAAS,EAAE,MAAM;CACjD,OAAO,IAAI,GAAG,CAAC;CACf,OAAO,IAAI,GAAG,EAAE,MAAM;CACtB,OAAO;AACT;AAEA,gBAAgB,aAAgB,OAA4C;CAC1E,OAAO;AACT"}