@sammorrowdrums/mcpi-protocol 0.85.0-bootstrap.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.
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/cbor/decoder.d.ts +4 -0
- package/dist/cbor/decoder.d.ts.map +1 -0
- package/dist/cbor/decoder.js +165 -0
- package/dist/cbor/decoder.js.map +1 -0
- package/dist/cbor/encoder.d.ts +4 -0
- package/dist/cbor/encoder.d.ts.map +1 -0
- package/dist/cbor/encoder.js +201 -0
- package/dist/cbor/encoder.js.map +1 -0
- package/dist/cbor/index.d.ts +4 -0
- package/dist/cbor/index.d.ts.map +1 -0
- package/dist/cbor/index.js +4 -0
- package/dist/cbor/index.js.map +1 -0
- package/dist/cbor/options.d.ts +26 -0
- package/dist/cbor/options.d.ts.map +1 -0
- package/dist/cbor/options.js +29 -0
- package/dist/cbor/options.js.map +1 -0
- package/dist/codec.d.ts +29 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +146 -0
- package/dist/codec.js.map +1 -0
- package/dist/framing.d.ts +29 -0
- package/dist/framing.d.ts.map +1 -0
- package/dist/framing.js +155 -0
- package/dist/framing.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas.d.ts +13739 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +386 -0
- package/dist/schemas.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const UINT32_BASE = 0x1_0000_0000;
|
|
2
|
+
export const MAX_UINT32 = 0xffff_ffff;
|
|
3
|
+
const MAX_CONFIGURED_DEPTH = 512;
|
|
4
|
+
/** Safe defaults for untrusted protocol payloads. */
|
|
5
|
+
export const DEFAULT_MAX_CBOR_BYTE_LENGTH = 16 * 1024 * 1024;
|
|
6
|
+
export const DEFAULT_MAX_CBOR_CONTAINER_LENGTH = 1_000_000;
|
|
7
|
+
export const DEFAULT_MAX_CBOR_DEPTH = 64;
|
|
8
|
+
export class CborError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "CborError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export const textEncoder = new TextEncoder();
|
|
15
|
+
export const textDecoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
16
|
+
function resolveLimit(name, value, maximum) {
|
|
17
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > maximum) {
|
|
18
|
+
throw new RangeError(`${name} must be an integer between 0 and ${maximum}`);
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
export function resolveOptions(options) {
|
|
23
|
+
return {
|
|
24
|
+
maxByteLength: resolveLimit("maxByteLength", options?.maxByteLength ?? DEFAULT_MAX_CBOR_BYTE_LENGTH, MAX_UINT32),
|
|
25
|
+
maxContainerLength: resolveLimit("maxContainerLength", options?.maxContainerLength ?? DEFAULT_MAX_CBOR_CONTAINER_LENGTH, MAX_UINT32),
|
|
26
|
+
maxDepth: resolveLimit("maxDepth", options?.maxDepth ?? DEFAULT_MAX_CBOR_DEPTH, MAX_CONFIGURED_DEPTH),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/cbor/options.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;AACzC,MAAM,CAAC,MAAM,UAAU,GAAG,WAAW,CAAC;AACtC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC,qDAAqD;AACrD,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAC7D,MAAM,CAAC,MAAM,iCAAiC,GAAG,SAAS,CAAC;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAiBzC,MAAM,OAAO,SAAU,SAAQ,KAAK;IACnC,YAAY,OAAe,EAAE;QAC5B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAAA,CACxB;CACD;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AAC7C,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAEtF,SAAS,YAAY,CAAC,IAAY,EAAE,KAAa,EAAE,OAAe,EAAU;IAC3E,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,OAAO,EAAE,CAAC;QAClE,MAAM,IAAI,UAAU,CAAC,GAAG,IAAI,qCAAqC,OAAO,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,MAAM,UAAU,cAAc,CAAC,OAAgC,EAAuB;IACrF,OAAO;QACN,aAAa,EAAE,YAAY,CAAC,eAAe,EAAE,OAAO,EAAE,aAAa,IAAI,4BAA4B,EAAE,UAAU,CAAC;QAChH,kBAAkB,EAAE,YAAY,CAC/B,oBAAoB,EACpB,OAAO,EAAE,kBAAkB,IAAI,iCAAiC,EAChE,UAAU,CACV;QACD,QAAQ,EAAE,YAAY,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,IAAI,sBAAsB,EAAE,oBAAoB,CAAC;KACrG,CAAC;AAAA,CACF","sourcesContent":["export const UINT32_BASE = 0x1_0000_0000;\nexport const MAX_UINT32 = 0xffff_ffff;\nconst MAX_CONFIGURED_DEPTH = 512;\n\n/** Safe defaults for untrusted protocol payloads. */\nexport const DEFAULT_MAX_CBOR_BYTE_LENGTH = 16 * 1024 * 1024;\nexport const DEFAULT_MAX_CBOR_CONTAINER_LENGTH = 1_000_000;\nexport const DEFAULT_MAX_CBOR_DEPTH = 64;\n\nexport interface CborOptions {\n\t/** Maximum encoded input/output bytes and maximum byte/text string length. */\n\tmaxByteLength?: number;\n\t/** Maximum number of elements in an array or entries in a map. */\n\tmaxContainerLength?: number;\n\t/** Maximum recursive item depth. */\n\tmaxDepth?: number;\n}\n\nexport interface ResolvedCborOptions {\n\tmaxByteLength: number;\n\tmaxContainerLength: number;\n\tmaxDepth: number;\n}\n\nexport class CborError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CborError\";\n\t}\n}\n\nexport const textEncoder = new TextEncoder();\nexport const textDecoder = new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: true });\n\nfunction resolveLimit(name: string, value: number, maximum: number): number {\n\tif (!Number.isSafeInteger(value) || value < 0 || value > maximum) {\n\t\tthrow new RangeError(`${name} must be an integer between 0 and ${maximum}`);\n\t}\n\treturn value;\n}\n\nexport function resolveOptions(options: CborOptions | undefined): ResolvedCborOptions {\n\treturn {\n\t\tmaxByteLength: resolveLimit(\"maxByteLength\", options?.maxByteLength ?? DEFAULT_MAX_CBOR_BYTE_LENGTH, MAX_UINT32),\n\t\tmaxContainerLength: resolveLimit(\n\t\t\t\"maxContainerLength\",\n\t\t\toptions?.maxContainerLength ?? DEFAULT_MAX_CBOR_CONTAINER_LENGTH,\n\t\t\tMAX_UINT32,\n\t\t),\n\t\tmaxDepth: resolveLimit(\"maxDepth\", options?.maxDepth ?? DEFAULT_MAX_CBOR_DEPTH, MAX_CONFIGURED_DEPTH),\n\t};\n}\n"]}
|
package/dist/codec.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type FrameDecoderOptions } from "./framing.ts";
|
|
2
|
+
import { type ClientMessage, PROTOCOL_VERSION, type ServerMessage } from "./schemas.ts";
|
|
3
|
+
export declare class ProtocolValidationError extends Error {
|
|
4
|
+
constructor(message: string, _value?: unknown);
|
|
5
|
+
}
|
|
6
|
+
export declare function parseClientMessage(value: unknown): ClientMessage;
|
|
7
|
+
export declare function parseServerMessage(value: unknown): ServerMessage;
|
|
8
|
+
/** Validates and encodes one complete length-prefixed client message. */
|
|
9
|
+
export declare function encodeClientMessage(message: ClientMessage, options?: FrameDecoderOptions): Uint8Array;
|
|
10
|
+
/** Validates and encodes one complete length-prefixed server message. */
|
|
11
|
+
export declare function encodeServerMessage(message: ServerMessage, options?: FrameDecoderOptions): Uint8Array;
|
|
12
|
+
/** Incrementally decodes and validates framed client messages. */
|
|
13
|
+
export declare class ClientMessageDecoder {
|
|
14
|
+
private readonly decoder;
|
|
15
|
+
constructor(options?: FrameDecoderOptions);
|
|
16
|
+
push(chunk: Uint8Array): ClientMessage[];
|
|
17
|
+
end(): void;
|
|
18
|
+
}
|
|
19
|
+
/** Incrementally decodes and validates framed server messages. */
|
|
20
|
+
export declare class ServerMessageDecoder {
|
|
21
|
+
private readonly decoder;
|
|
22
|
+
constructor(options?: FrameDecoderOptions);
|
|
23
|
+
push(chunk: Uint8Array): ServerMessage[];
|
|
24
|
+
end(): void;
|
|
25
|
+
}
|
|
26
|
+
export declare function createClientMessageDecoder(options?: FrameDecoderOptions): ClientMessageDecoder;
|
|
27
|
+
export declare function createServerMessageDecoder(options?: FrameDecoderOptions): ServerMessageDecoder;
|
|
28
|
+
export declare function isSupportedProtocolVersion(version: number): version is typeof PROTOCOL_VERSION;
|
|
29
|
+
//# sourceMappingURL=codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"AAEA,OAAO,EAKN,KAAK,mBAAmB,EACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,KAAK,aAAa,EAElB,gBAAgB,EAChB,KAAK,aAAa,EAElB,MAAM,cAAc,CAAC;AAEtB,qBAAa,uBAAwB,SAAQ,KAAK;IACjD,YAAY,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAG5C;CACD;AAkBD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAKhE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAKhE;AAyBD,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,UAAU,CAErG;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,UAAU,CAErG;AA0CD,kEAAkE;AAClE,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyC;IAEjE,YAAY,OAAO,CAAC,EAAE,mBAAmB,EAExC;IAED,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,EAAE,CAEvC;IAED,GAAG,IAAI,IAAI,CAEV;CACD;AAED,kEAAkE;AAClE,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyC;IAEjE,YAAY,OAAO,CAAC,EAAE,mBAAmB,EAExC;IAED,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,EAAE,CAEvC;IAED,GAAG,IAAI,IAAI,CAEV;CACD;AAED,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,oBAAoB,CAE9F;AAED,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,oBAAoB,CAE9F;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,IAAI,OAAO,gBAAgB,CAE9F","sourcesContent":["import { Check } from \"typebox/value\";\nimport { decodeCbor, encodeCbor } from \"./cbor/index.ts\";\nimport {\n\tassertCompleteFrame,\n\tDEFAULT_MAX_FRAME_LENGTH,\n\tencodeFrame,\n\tFrameDecoder,\n\ttype FrameDecoderOptions,\n} from \"./framing.ts\";\nimport {\n\ttype ClientMessage,\n\tClientMessageSchema,\n\tPROTOCOL_VERSION,\n\ttype ServerMessage,\n\tServerMessageSchema,\n} from \"./schemas.ts\";\n\nexport class ProtocolValidationError extends Error {\n\tconstructor(message: string, _value?: unknown) {\n\t\tsuper(message);\n\t\tthis.name = \"ProtocolValidationError\";\n\t}\n}\n\nfunction isProtocolValue(value: unknown, optionalProperty = false, ancestors = new Set<object>()): boolean {\n\tif (value === undefined) return optionalProperty;\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"number\" || typeof value === \"string\") {\n\t\treturn true;\n\t}\n\tif (typeof value !== \"object\" || ancestors.has(value)) return false;\n\tancestors.add(value);\n\ttry {\n\t\tif (Array.isArray(value)) return value.every((item) => isProtocolValue(item, false, ancestors));\n\t\tif (Object.getPrototypeOf(value) !== Object.prototype) return false;\n\t\treturn Object.values(value).every((item) => isProtocolValue(item, true, ancestors));\n\t} finally {\n\t\tancestors.delete(value);\n\t}\n}\n\nexport function parseClientMessage(value: unknown): ClientMessage {\n\tif (!isProtocolValue(value) || !Check(ClientMessageSchema, value)) {\n\t\tthrow new ProtocolValidationError(\"Invalid client protocol message\");\n\t}\n\treturn value;\n}\n\nexport function parseServerMessage(value: unknown): ServerMessage {\n\tif (!isProtocolValue(value) || !Check(ServerMessageSchema, value)) {\n\t\tthrow new ProtocolValidationError(\"Invalid server protocol message\");\n\t}\n\treturn value;\n}\n\nfunction boundedErrorMessage(error: unknown): string {\n\tif (!(error instanceof Error)) return \"Unknown codec error\";\n\treturn error.message.length <= 500 ? error.message : `${error.message.slice(0, 497)}...`;\n}\n\nfunction encodeProtocolMessage<T>(\n\tvalue: T,\n\tparse: (candidate: unknown) => T,\n\tkind: string,\n\toptions?: FrameDecoderOptions,\n): Uint8Array {\n\tconst validated = parse(value);\n\ttry {\n\t\tconst maxFrameLength = options?.maxFrameLength ?? DEFAULT_MAX_FRAME_LENGTH;\n\t\tconst frame = encodeFrame(encodeCbor(validated, { maxByteLength: maxFrameLength }));\n\t\tassertCompleteFrame(frame, { maxFrameLength });\n\t\treturn frame;\n\t} catch (error) {\n\t\tif (error instanceof ProtocolValidationError) throw error;\n\t\tthrow new ProtocolValidationError(`Unable to encode ${kind} protocol message: ${boundedErrorMessage(error)}`);\n\t}\n}\n\n/** Validates and encodes one complete length-prefixed client message. */\nexport function encodeClientMessage(message: ClientMessage, options?: FrameDecoderOptions): Uint8Array {\n\treturn encodeProtocolMessage(message, parseClientMessage, \"client\", options);\n}\n\n/** Validates and encodes one complete length-prefixed server message. */\nexport function encodeServerMessage(message: ServerMessage, options?: FrameDecoderOptions): Uint8Array {\n\treturn encodeProtocolMessage(message, parseServerMessage, \"server\", options);\n}\n\nclass ValidatedMessageDecoder<T> {\n\tprivate failed = false;\n\tprivate readonly frames: FrameDecoder;\n\tprivate readonly kind: string;\n\tprivate readonly maxFrameLength: number;\n\tprivate readonly parse: (candidate: unknown) => T;\n\n\tconstructor(kind: string, parse: (candidate: unknown) => T, options?: FrameDecoderOptions) {\n\t\tthis.frames = new FrameDecoder(options);\n\t\tthis.kind = kind;\n\t\tthis.maxFrameLength = options?.maxFrameLength ?? DEFAULT_MAX_FRAME_LENGTH;\n\t\tthis.parse = parse;\n\t}\n\n\tpush(chunk: Uint8Array): T[] {\n\t\tif (this.failed) throw new ProtocolValidationError(`${this.kind} message decoder has failed`);\n\t\ttry {\n\t\t\tconst messages: T[] = [];\n\t\t\tfor (const frame of this.frames.push(chunk)) {\n\t\t\t\tmessages.push(this.parse(decodeCbor(frame, { maxByteLength: this.maxFrameLength })));\n\t\t\t}\n\t\t\treturn messages;\n\t\t} catch (error) {\n\t\t\tthis.failed = true;\n\t\t\tif (error instanceof ProtocolValidationError) throw error;\n\t\t\tthrow new ProtocolValidationError(`Invalid ${this.kind} protocol frame: ${boundedErrorMessage(error)}`);\n\t\t}\n\t}\n\n\tend(): void {\n\t\tif (this.failed) throw new ProtocolValidationError(`${this.kind} message decoder has failed`);\n\t\ttry {\n\t\t\tthis.frames.end();\n\t\t} catch (error) {\n\t\t\tthis.failed = true;\n\t\t\tthrow new ProtocolValidationError(`Invalid ${this.kind} protocol framing: ${boundedErrorMessage(error)}`);\n\t\t}\n\t}\n}\n\n/** Incrementally decodes and validates framed client messages. */\nexport class ClientMessageDecoder {\n\tprivate readonly decoder: ValidatedMessageDecoder<ClientMessage>;\n\n\tconstructor(options?: FrameDecoderOptions) {\n\t\tthis.decoder = new ValidatedMessageDecoder(\"client\", parseClientMessage, options);\n\t}\n\n\tpush(chunk: Uint8Array): ClientMessage[] {\n\t\treturn this.decoder.push(chunk);\n\t}\n\n\tend(): void {\n\t\tthis.decoder.end();\n\t}\n}\n\n/** Incrementally decodes and validates framed server messages. */\nexport class ServerMessageDecoder {\n\tprivate readonly decoder: ValidatedMessageDecoder<ServerMessage>;\n\n\tconstructor(options?: FrameDecoderOptions) {\n\t\tthis.decoder = new ValidatedMessageDecoder(\"server\", parseServerMessage, options);\n\t}\n\n\tpush(chunk: Uint8Array): ServerMessage[] {\n\t\treturn this.decoder.push(chunk);\n\t}\n\n\tend(): void {\n\t\tthis.decoder.end();\n\t}\n}\n\nexport function createClientMessageDecoder(options?: FrameDecoderOptions): ClientMessageDecoder {\n\treturn new ClientMessageDecoder(options);\n}\n\nexport function createServerMessageDecoder(options?: FrameDecoderOptions): ServerMessageDecoder {\n\treturn new ServerMessageDecoder(options);\n}\n\nexport function isSupportedProtocolVersion(version: number): version is typeof PROTOCOL_VERSION {\n\treturn Number.isInteger(version) && version === PROTOCOL_VERSION;\n}\n"]}
|
package/dist/codec.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Check } from "typebox/value";
|
|
2
|
+
import { decodeCbor, encodeCbor } from "./cbor/index.js";
|
|
3
|
+
import { assertCompleteFrame, DEFAULT_MAX_FRAME_LENGTH, encodeFrame, FrameDecoder, } from "./framing.js";
|
|
4
|
+
import { ClientMessageSchema, PROTOCOL_VERSION, ServerMessageSchema, } from "./schemas.js";
|
|
5
|
+
export class ProtocolValidationError extends Error {
|
|
6
|
+
constructor(message, _value) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "ProtocolValidationError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function isProtocolValue(value, optionalProperty = false, ancestors = new Set()) {
|
|
12
|
+
if (value === undefined)
|
|
13
|
+
return optionalProperty;
|
|
14
|
+
if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (typeof value !== "object" || ancestors.has(value))
|
|
18
|
+
return false;
|
|
19
|
+
ancestors.add(value);
|
|
20
|
+
try {
|
|
21
|
+
if (Array.isArray(value))
|
|
22
|
+
return value.every((item) => isProtocolValue(item, false, ancestors));
|
|
23
|
+
if (Object.getPrototypeOf(value) !== Object.prototype)
|
|
24
|
+
return false;
|
|
25
|
+
return Object.values(value).every((item) => isProtocolValue(item, true, ancestors));
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
ancestors.delete(value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function parseClientMessage(value) {
|
|
32
|
+
if (!isProtocolValue(value) || !Check(ClientMessageSchema, value)) {
|
|
33
|
+
throw new ProtocolValidationError("Invalid client protocol message");
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
export function parseServerMessage(value) {
|
|
38
|
+
if (!isProtocolValue(value) || !Check(ServerMessageSchema, value)) {
|
|
39
|
+
throw new ProtocolValidationError("Invalid server protocol message");
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
function boundedErrorMessage(error) {
|
|
44
|
+
if (!(error instanceof Error))
|
|
45
|
+
return "Unknown codec error";
|
|
46
|
+
return error.message.length <= 500 ? error.message : `${error.message.slice(0, 497)}...`;
|
|
47
|
+
}
|
|
48
|
+
function encodeProtocolMessage(value, parse, kind, options) {
|
|
49
|
+
const validated = parse(value);
|
|
50
|
+
try {
|
|
51
|
+
const maxFrameLength = options?.maxFrameLength ?? DEFAULT_MAX_FRAME_LENGTH;
|
|
52
|
+
const frame = encodeFrame(encodeCbor(validated, { maxByteLength: maxFrameLength }));
|
|
53
|
+
assertCompleteFrame(frame, { maxFrameLength });
|
|
54
|
+
return frame;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error instanceof ProtocolValidationError)
|
|
58
|
+
throw error;
|
|
59
|
+
throw new ProtocolValidationError(`Unable to encode ${kind} protocol message: ${boundedErrorMessage(error)}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Validates and encodes one complete length-prefixed client message. */
|
|
63
|
+
export function encodeClientMessage(message, options) {
|
|
64
|
+
return encodeProtocolMessage(message, parseClientMessage, "client", options);
|
|
65
|
+
}
|
|
66
|
+
/** Validates and encodes one complete length-prefixed server message. */
|
|
67
|
+
export function encodeServerMessage(message, options) {
|
|
68
|
+
return encodeProtocolMessage(message, parseServerMessage, "server", options);
|
|
69
|
+
}
|
|
70
|
+
class ValidatedMessageDecoder {
|
|
71
|
+
failed = false;
|
|
72
|
+
frames;
|
|
73
|
+
kind;
|
|
74
|
+
maxFrameLength;
|
|
75
|
+
parse;
|
|
76
|
+
constructor(kind, parse, options) {
|
|
77
|
+
this.frames = new FrameDecoder(options);
|
|
78
|
+
this.kind = kind;
|
|
79
|
+
this.maxFrameLength = options?.maxFrameLength ?? DEFAULT_MAX_FRAME_LENGTH;
|
|
80
|
+
this.parse = parse;
|
|
81
|
+
}
|
|
82
|
+
push(chunk) {
|
|
83
|
+
if (this.failed)
|
|
84
|
+
throw new ProtocolValidationError(`${this.kind} message decoder has failed`);
|
|
85
|
+
try {
|
|
86
|
+
const messages = [];
|
|
87
|
+
for (const frame of this.frames.push(chunk)) {
|
|
88
|
+
messages.push(this.parse(decodeCbor(frame, { maxByteLength: this.maxFrameLength })));
|
|
89
|
+
}
|
|
90
|
+
return messages;
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
this.failed = true;
|
|
94
|
+
if (error instanceof ProtocolValidationError)
|
|
95
|
+
throw error;
|
|
96
|
+
throw new ProtocolValidationError(`Invalid ${this.kind} protocol frame: ${boundedErrorMessage(error)}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
end() {
|
|
100
|
+
if (this.failed)
|
|
101
|
+
throw new ProtocolValidationError(`${this.kind} message decoder has failed`);
|
|
102
|
+
try {
|
|
103
|
+
this.frames.end();
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
this.failed = true;
|
|
107
|
+
throw new ProtocolValidationError(`Invalid ${this.kind} protocol framing: ${boundedErrorMessage(error)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Incrementally decodes and validates framed client messages. */
|
|
112
|
+
export class ClientMessageDecoder {
|
|
113
|
+
decoder;
|
|
114
|
+
constructor(options) {
|
|
115
|
+
this.decoder = new ValidatedMessageDecoder("client", parseClientMessage, options);
|
|
116
|
+
}
|
|
117
|
+
push(chunk) {
|
|
118
|
+
return this.decoder.push(chunk);
|
|
119
|
+
}
|
|
120
|
+
end() {
|
|
121
|
+
this.decoder.end();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** Incrementally decodes and validates framed server messages. */
|
|
125
|
+
export class ServerMessageDecoder {
|
|
126
|
+
decoder;
|
|
127
|
+
constructor(options) {
|
|
128
|
+
this.decoder = new ValidatedMessageDecoder("server", parseServerMessage, options);
|
|
129
|
+
}
|
|
130
|
+
push(chunk) {
|
|
131
|
+
return this.decoder.push(chunk);
|
|
132
|
+
}
|
|
133
|
+
end() {
|
|
134
|
+
this.decoder.end();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export function createClientMessageDecoder(options) {
|
|
138
|
+
return new ClientMessageDecoder(options);
|
|
139
|
+
}
|
|
140
|
+
export function createServerMessageDecoder(options) {
|
|
141
|
+
return new ServerMessageDecoder(options);
|
|
142
|
+
}
|
|
143
|
+
export function isSupportedProtocolVersion(version) {
|
|
144
|
+
return Number.isInteger(version) && version === PROTOCOL_VERSION;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"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","sourcesContent":["import { Check } from \"typebox/value\";\nimport { decodeCbor, encodeCbor } from \"./cbor/index.ts\";\nimport {\n\tassertCompleteFrame,\n\tDEFAULT_MAX_FRAME_LENGTH,\n\tencodeFrame,\n\tFrameDecoder,\n\ttype FrameDecoderOptions,\n} from \"./framing.ts\";\nimport {\n\ttype ClientMessage,\n\tClientMessageSchema,\n\tPROTOCOL_VERSION,\n\ttype ServerMessage,\n\tServerMessageSchema,\n} from \"./schemas.ts\";\n\nexport class ProtocolValidationError extends Error {\n\tconstructor(message: string, _value?: unknown) {\n\t\tsuper(message);\n\t\tthis.name = \"ProtocolValidationError\";\n\t}\n}\n\nfunction isProtocolValue(value: unknown, optionalProperty = false, ancestors = new Set<object>()): boolean {\n\tif (value === undefined) return optionalProperty;\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"number\" || typeof value === \"string\") {\n\t\treturn true;\n\t}\n\tif (typeof value !== \"object\" || ancestors.has(value)) return false;\n\tancestors.add(value);\n\ttry {\n\t\tif (Array.isArray(value)) return value.every((item) => isProtocolValue(item, false, ancestors));\n\t\tif (Object.getPrototypeOf(value) !== Object.prototype) return false;\n\t\treturn Object.values(value).every((item) => isProtocolValue(item, true, ancestors));\n\t} finally {\n\t\tancestors.delete(value);\n\t}\n}\n\nexport function parseClientMessage(value: unknown): ClientMessage {\n\tif (!isProtocolValue(value) || !Check(ClientMessageSchema, value)) {\n\t\tthrow new ProtocolValidationError(\"Invalid client protocol message\");\n\t}\n\treturn value;\n}\n\nexport function parseServerMessage(value: unknown): ServerMessage {\n\tif (!isProtocolValue(value) || !Check(ServerMessageSchema, value)) {\n\t\tthrow new ProtocolValidationError(\"Invalid server protocol message\");\n\t}\n\treturn value;\n}\n\nfunction boundedErrorMessage(error: unknown): string {\n\tif (!(error instanceof Error)) return \"Unknown codec error\";\n\treturn error.message.length <= 500 ? error.message : `${error.message.slice(0, 497)}...`;\n}\n\nfunction encodeProtocolMessage<T>(\n\tvalue: T,\n\tparse: (candidate: unknown) => T,\n\tkind: string,\n\toptions?: FrameDecoderOptions,\n): Uint8Array {\n\tconst validated = parse(value);\n\ttry {\n\t\tconst maxFrameLength = options?.maxFrameLength ?? DEFAULT_MAX_FRAME_LENGTH;\n\t\tconst frame = encodeFrame(encodeCbor(validated, { maxByteLength: maxFrameLength }));\n\t\tassertCompleteFrame(frame, { maxFrameLength });\n\t\treturn frame;\n\t} catch (error) {\n\t\tif (error instanceof ProtocolValidationError) throw error;\n\t\tthrow new ProtocolValidationError(`Unable to encode ${kind} protocol message: ${boundedErrorMessage(error)}`);\n\t}\n}\n\n/** Validates and encodes one complete length-prefixed client message. */\nexport function encodeClientMessage(message: ClientMessage, options?: FrameDecoderOptions): Uint8Array {\n\treturn encodeProtocolMessage(message, parseClientMessage, \"client\", options);\n}\n\n/** Validates and encodes one complete length-prefixed server message. */\nexport function encodeServerMessage(message: ServerMessage, options?: FrameDecoderOptions): Uint8Array {\n\treturn encodeProtocolMessage(message, parseServerMessage, \"server\", options);\n}\n\nclass ValidatedMessageDecoder<T> {\n\tprivate failed = false;\n\tprivate readonly frames: FrameDecoder;\n\tprivate readonly kind: string;\n\tprivate readonly maxFrameLength: number;\n\tprivate readonly parse: (candidate: unknown) => T;\n\n\tconstructor(kind: string, parse: (candidate: unknown) => T, options?: FrameDecoderOptions) {\n\t\tthis.frames = new FrameDecoder(options);\n\t\tthis.kind = kind;\n\t\tthis.maxFrameLength = options?.maxFrameLength ?? DEFAULT_MAX_FRAME_LENGTH;\n\t\tthis.parse = parse;\n\t}\n\n\tpush(chunk: Uint8Array): T[] {\n\t\tif (this.failed) throw new ProtocolValidationError(`${this.kind} message decoder has failed`);\n\t\ttry {\n\t\t\tconst messages: T[] = [];\n\t\t\tfor (const frame of this.frames.push(chunk)) {\n\t\t\t\tmessages.push(this.parse(decodeCbor(frame, { maxByteLength: this.maxFrameLength })));\n\t\t\t}\n\t\t\treturn messages;\n\t\t} catch (error) {\n\t\t\tthis.failed = true;\n\t\t\tif (error instanceof ProtocolValidationError) throw error;\n\t\t\tthrow new ProtocolValidationError(`Invalid ${this.kind} protocol frame: ${boundedErrorMessage(error)}`);\n\t\t}\n\t}\n\n\tend(): void {\n\t\tif (this.failed) throw new ProtocolValidationError(`${this.kind} message decoder has failed`);\n\t\ttry {\n\t\t\tthis.frames.end();\n\t\t} catch (error) {\n\t\t\tthis.failed = true;\n\t\t\tthrow new ProtocolValidationError(`Invalid ${this.kind} protocol framing: ${boundedErrorMessage(error)}`);\n\t\t}\n\t}\n}\n\n/** Incrementally decodes and validates framed client messages. */\nexport class ClientMessageDecoder {\n\tprivate readonly decoder: ValidatedMessageDecoder<ClientMessage>;\n\n\tconstructor(options?: FrameDecoderOptions) {\n\t\tthis.decoder = new ValidatedMessageDecoder(\"client\", parseClientMessage, options);\n\t}\n\n\tpush(chunk: Uint8Array): ClientMessage[] {\n\t\treturn this.decoder.push(chunk);\n\t}\n\n\tend(): void {\n\t\tthis.decoder.end();\n\t}\n}\n\n/** Incrementally decodes and validates framed server messages. */\nexport class ServerMessageDecoder {\n\tprivate readonly decoder: ValidatedMessageDecoder<ServerMessage>;\n\n\tconstructor(options?: FrameDecoderOptions) {\n\t\tthis.decoder = new ValidatedMessageDecoder(\"server\", parseServerMessage, options);\n\t}\n\n\tpush(chunk: Uint8Array): ServerMessage[] {\n\t\treturn this.decoder.push(chunk);\n\t}\n\n\tend(): void {\n\t\tthis.decoder.end();\n\t}\n}\n\nexport function createClientMessageDecoder(options?: FrameDecoderOptions): ClientMessageDecoder {\n\treturn new ClientMessageDecoder(options);\n}\n\nexport function createServerMessageDecoder(options?: FrameDecoderOptions): ServerMessageDecoder {\n\treturn new ServerMessageDecoder(options);\n}\n\nexport function isSupportedProtocolVersion(version: number): version is typeof PROTOCOL_VERSION {\n\treturn Number.isInteger(version) && version === PROTOCOL_VERSION;\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Default upper bound for one framed CBOR payload. */
|
|
2
|
+
export declare const DEFAULT_MAX_FRAME_LENGTH: number;
|
|
3
|
+
export interface FrameDecoderOptions {
|
|
4
|
+
maxFrameLength?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class FrameError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
/** Prefixes a payload with its unsigned 32-bit big-endian byte length. */
|
|
10
|
+
export declare function encodeFrame(payload: Uint8Array): Uint8Array;
|
|
11
|
+
/** Validates that bytes contain exactly one complete frame within the configured limit. */
|
|
12
|
+
export declare function assertCompleteFrame(frame: Uint8Array, options?: FrameDecoderOptions): void;
|
|
13
|
+
/** Incrementally splits arbitrary byte chunks into length-prefixed payloads. */
|
|
14
|
+
export declare class FrameDecoder {
|
|
15
|
+
private readonly header;
|
|
16
|
+
private headerLength;
|
|
17
|
+
private readonly maxFrameLength;
|
|
18
|
+
private payloadBlocks;
|
|
19
|
+
private currentPayloadBlock;
|
|
20
|
+
private currentPayloadBlockLength;
|
|
21
|
+
private expectedPayloadLength;
|
|
22
|
+
private payloadLength;
|
|
23
|
+
private state;
|
|
24
|
+
constructor(options?: FrameDecoderOptions);
|
|
25
|
+
push(chunk: Uint8Array): Uint8Array[];
|
|
26
|
+
end(): void;
|
|
27
|
+
private fail;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=framing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"framing.d.ts","sourceRoot":"","sources":["../src/framing.ts"],"names":[],"mappings":"AAIA,uDAAuD;AACvD,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,UAAW,SAAQ,KAAK;IACpC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACD;AAUD,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CAW3D;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAW1F;AAID,gFAAgF;AAChF,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAC9D,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,mBAAmB,CAAyB;IACpD,OAAO,CAAC,yBAAyB,CAAK;IACtC,OAAO,CAAC,qBAAqB,CAAqB;IAClD,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,KAAK,CAAwB;IAErC,YAAY,OAAO,CAAC,EAAE,mBAAmB,EAExC;IAED,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,EAAE,CAuEpC;IAED,GAAG,IAAI,IAAI,CAOV;IAED,OAAO,CAAC,IAAI;CAUZ","sourcesContent":["const FRAME_HEADER_LENGTH = 4;\nconst MAX_UINT32 = 0xffff_ffff;\nconst PAYLOAD_BLOCK_SIZE = 64 * 1024;\n\n/** Default upper bound for one framed CBOR payload. */\nexport const DEFAULT_MAX_FRAME_LENGTH = 16 * 1024 * 1024;\n\nexport interface FrameDecoderOptions {\n\tmaxFrameLength?: number;\n}\n\nexport class FrameError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"FrameError\";\n\t}\n}\n\nfunction resolveMaxFrameLength(options: FrameDecoderOptions | undefined): number {\n\tconst value = options?.maxFrameLength ?? DEFAULT_MAX_FRAME_LENGTH;\n\tif (!Number.isSafeInteger(value) || value < 0 || value > MAX_UINT32) {\n\t\tthrow new RangeError(`maxFrameLength must be an integer between 0 and ${MAX_UINT32}`);\n\t}\n\treturn value;\n}\n\n/** Prefixes a payload with its unsigned 32-bit big-endian byte length. */\nexport function encodeFrame(payload: Uint8Array): Uint8Array {\n\tif (!(payload instanceof Uint8Array)) throw new TypeError(\"Frame payload must be a Uint8Array\");\n\tif (payload.byteLength > MAX_UINT32) throw new RangeError(\"Frame payload exceeds the unsigned 32-bit length limit\");\n\tconst frame = new Uint8Array(FRAME_HEADER_LENGTH + payload.byteLength);\n\tconst length = payload.byteLength;\n\tframe[0] = length >>> 24;\n\tframe[1] = length >>> 16;\n\tframe[2] = length >>> 8;\n\tframe[3] = length;\n\tframe.set(payload, FRAME_HEADER_LENGTH);\n\treturn frame;\n}\n\n/** Validates that bytes contain exactly one complete frame within the configured limit. */\nexport function assertCompleteFrame(frame: Uint8Array, options?: FrameDecoderOptions): void {\n\tif (!(frame instanceof Uint8Array)) throw new TypeError(\"Frame must be a Uint8Array\");\n\tif (frame.byteLength < FRAME_HEADER_LENGTH) throw new FrameError(\"Frame does not contain a complete length prefix\");\n\tconst length = frame[0]! * 0x1_000_000 + frame[1]! * 0x1_0000 + frame[2]! * 0x100 + frame[3]!;\n\tconst maxFrameLength = resolveMaxFrameLength(options);\n\tif (length > maxFrameLength) {\n\t\tthrow new FrameError(`Frame length ${length} exceeds configured limit of ${maxFrameLength}`);\n\t}\n\tif (frame.byteLength !== FRAME_HEADER_LENGTH + length) {\n\t\tthrow new FrameError(\"Frame must contain exactly one complete payload\");\n\t}\n}\n\ntype DecoderState = \"open\" | \"ended\" | \"failed\";\n\n/** Incrementally splits arbitrary byte chunks into length-prefixed payloads. */\nexport class FrameDecoder {\n\tprivate readonly header = new Uint8Array(FRAME_HEADER_LENGTH);\n\tprivate headerLength = 0;\n\tprivate readonly maxFrameLength: number;\n\tprivate payloadBlocks: Uint8Array[] = [];\n\tprivate currentPayloadBlock: Uint8Array | undefined;\n\tprivate currentPayloadBlockLength = 0;\n\tprivate expectedPayloadLength: number | undefined;\n\tprivate payloadLength = 0;\n\tprivate state: DecoderState = \"open\";\n\n\tconstructor(options?: FrameDecoderOptions) {\n\t\tthis.maxFrameLength = resolveMaxFrameLength(options);\n\t}\n\n\tpush(chunk: Uint8Array): Uint8Array[] {\n\t\tif (this.state === \"ended\") throw new FrameError(\"Frame decoder has ended\");\n\t\tif (this.state === \"failed\") throw new FrameError(\"Frame decoder has failed\");\n\t\tif (!(chunk instanceof Uint8Array)) throw new TypeError(\"Frame chunk must be a Uint8Array\");\n\n\t\tconst frames: Uint8Array[] = [];\n\t\tlet chunkOffset = 0;\n\t\twhile (chunkOffset < chunk.byteLength) {\n\t\t\tif (this.expectedPayloadLength === undefined) {\n\t\t\t\tconst headerBytes = Math.min(FRAME_HEADER_LENGTH - this.headerLength, chunk.byteLength - chunkOffset);\n\t\t\t\tthis.header.set(chunk.subarray(chunkOffset, chunkOffset + headerBytes), this.headerLength);\n\t\t\t\tthis.headerLength += headerBytes;\n\t\t\t\tchunkOffset += headerBytes;\n\t\t\t\tif (this.headerLength < FRAME_HEADER_LENGTH) continue;\n\n\t\t\t\tconst frameLength =\n\t\t\t\t\tthis.header[0]! * 0x1_000_000 + this.header[1]! * 0x1_0000 + this.header[2]! * 0x100 + this.header[3]!;\n\t\t\t\tthis.headerLength = 0;\n\t\t\t\tif (frameLength > this.maxFrameLength) {\n\t\t\t\t\tthis.fail(`Frame length ${frameLength} exceeds configured limit of ${this.maxFrameLength}`);\n\t\t\t\t}\n\t\t\t\tif (frameLength === 0) {\n\t\t\t\t\tframes.push(new Uint8Array());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis.expectedPayloadLength = frameLength;\n\t\t\t\tthis.payloadBlocks = [];\n\t\t\t\tthis.currentPayloadBlock = undefined;\n\t\t\t\tthis.currentPayloadBlockLength = 0;\n\t\t\t\tthis.payloadLength = 0;\n\t\t\t}\n\n\t\t\tconst expectedPayloadLength = this.expectedPayloadLength;\n\t\t\tif (expectedPayloadLength === undefined) continue;\n\t\t\twhile (chunkOffset < chunk.byteLength && this.payloadLength < expectedPayloadLength) {\n\t\t\t\tlet block = this.currentPayloadBlock;\n\t\t\t\tif (!block || this.currentPayloadBlockLength === block.byteLength) {\n\t\t\t\t\tblock = new Uint8Array(Math.min(PAYLOAD_BLOCK_SIZE, expectedPayloadLength - this.payloadLength));\n\t\t\t\t\tthis.payloadBlocks.push(block);\n\t\t\t\t\tthis.currentPayloadBlock = block;\n\t\t\t\t\tthis.currentPayloadBlockLength = 0;\n\t\t\t\t}\n\t\t\t\tconst payloadBytes = Math.min(\n\t\t\t\t\tblock.byteLength - this.currentPayloadBlockLength,\n\t\t\t\t\tchunk.byteLength - chunkOffset,\n\t\t\t\t);\n\t\t\t\tblock.set(chunk.subarray(chunkOffset, chunkOffset + payloadBytes), this.currentPayloadBlockLength);\n\t\t\t\tthis.currentPayloadBlockLength += payloadBytes;\n\t\t\t\tthis.payloadLength += payloadBytes;\n\t\t\t\tchunkOffset += payloadBytes;\n\t\t\t}\n\t\t\tif (this.payloadLength === expectedPayloadLength) {\n\t\t\t\tif (this.payloadBlocks.length === 1) {\n\t\t\t\t\tframes.push(this.payloadBlocks[0]!);\n\t\t\t\t} else {\n\t\t\t\t\tconst payload = new Uint8Array(expectedPayloadLength);\n\t\t\t\t\tlet offset = 0;\n\t\t\t\t\tfor (const payloadBlock of this.payloadBlocks) {\n\t\t\t\t\t\tpayload.set(payloadBlock, offset);\n\t\t\t\t\t\toffset += payloadBlock.byteLength;\n\t\t\t\t\t}\n\t\t\t\t\tframes.push(payload);\n\t\t\t\t}\n\t\t\t\tthis.payloadBlocks = [];\n\t\t\t\tthis.currentPayloadBlock = undefined;\n\t\t\t\tthis.currentPayloadBlockLength = 0;\n\t\t\t\tthis.expectedPayloadLength = undefined;\n\t\t\t\tthis.payloadLength = 0;\n\t\t\t}\n\t\t}\n\t\treturn frames;\n\t}\n\n\tend(): void {\n\t\tif (this.state === \"ended\") throw new FrameError(\"Frame decoder has ended\");\n\t\tif (this.state === \"failed\") throw new FrameError(\"Frame decoder has failed\");\n\t\tif (this.headerLength !== 0 || this.expectedPayloadLength !== undefined) {\n\t\t\tthis.fail(\"Truncated frame at end of stream\");\n\t\t}\n\t\tthis.state = \"ended\";\n\t}\n\n\tprivate fail(message: string): never {\n\t\tthis.state = \"failed\";\n\t\tthis.headerLength = 0;\n\t\tthis.payloadBlocks = [];\n\t\tthis.currentPayloadBlock = undefined;\n\t\tthis.currentPayloadBlockLength = 0;\n\t\tthis.expectedPayloadLength = undefined;\n\t\tthis.payloadLength = 0;\n\t\tthrow new FrameError(message);\n\t}\n}\n"]}
|
package/dist/framing.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
const FRAME_HEADER_LENGTH = 4;
|
|
2
|
+
const MAX_UINT32 = 0xffff_ffff;
|
|
3
|
+
const PAYLOAD_BLOCK_SIZE = 64 * 1024;
|
|
4
|
+
/** Default upper bound for one framed CBOR payload. */
|
|
5
|
+
export const DEFAULT_MAX_FRAME_LENGTH = 16 * 1024 * 1024;
|
|
6
|
+
export class FrameError extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "FrameError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function resolveMaxFrameLength(options) {
|
|
13
|
+
const value = options?.maxFrameLength ?? DEFAULT_MAX_FRAME_LENGTH;
|
|
14
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > MAX_UINT32) {
|
|
15
|
+
throw new RangeError(`maxFrameLength must be an integer between 0 and ${MAX_UINT32}`);
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
/** Prefixes a payload with its unsigned 32-bit big-endian byte length. */
|
|
20
|
+
export function encodeFrame(payload) {
|
|
21
|
+
if (!(payload instanceof Uint8Array))
|
|
22
|
+
throw new TypeError("Frame payload must be a Uint8Array");
|
|
23
|
+
if (payload.byteLength > MAX_UINT32)
|
|
24
|
+
throw new RangeError("Frame payload exceeds the unsigned 32-bit length limit");
|
|
25
|
+
const frame = new Uint8Array(FRAME_HEADER_LENGTH + payload.byteLength);
|
|
26
|
+
const length = payload.byteLength;
|
|
27
|
+
frame[0] = length >>> 24;
|
|
28
|
+
frame[1] = length >>> 16;
|
|
29
|
+
frame[2] = length >>> 8;
|
|
30
|
+
frame[3] = length;
|
|
31
|
+
frame.set(payload, FRAME_HEADER_LENGTH);
|
|
32
|
+
return frame;
|
|
33
|
+
}
|
|
34
|
+
/** Validates that bytes contain exactly one complete frame within the configured limit. */
|
|
35
|
+
export function assertCompleteFrame(frame, options) {
|
|
36
|
+
if (!(frame instanceof Uint8Array))
|
|
37
|
+
throw new TypeError("Frame must be a Uint8Array");
|
|
38
|
+
if (frame.byteLength < FRAME_HEADER_LENGTH)
|
|
39
|
+
throw new FrameError("Frame does not contain a complete length prefix");
|
|
40
|
+
const length = frame[0] * 0x1_000_000 + frame[1] * 0x1_0000 + frame[2] * 0x100 + frame[3];
|
|
41
|
+
const maxFrameLength = resolveMaxFrameLength(options);
|
|
42
|
+
if (length > maxFrameLength) {
|
|
43
|
+
throw new FrameError(`Frame length ${length} exceeds configured limit of ${maxFrameLength}`);
|
|
44
|
+
}
|
|
45
|
+
if (frame.byteLength !== FRAME_HEADER_LENGTH + length) {
|
|
46
|
+
throw new FrameError("Frame must contain exactly one complete payload");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Incrementally splits arbitrary byte chunks into length-prefixed payloads. */
|
|
50
|
+
export class FrameDecoder {
|
|
51
|
+
header = new Uint8Array(FRAME_HEADER_LENGTH);
|
|
52
|
+
headerLength = 0;
|
|
53
|
+
maxFrameLength;
|
|
54
|
+
payloadBlocks = [];
|
|
55
|
+
currentPayloadBlock;
|
|
56
|
+
currentPayloadBlockLength = 0;
|
|
57
|
+
expectedPayloadLength;
|
|
58
|
+
payloadLength = 0;
|
|
59
|
+
state = "open";
|
|
60
|
+
constructor(options) {
|
|
61
|
+
this.maxFrameLength = resolveMaxFrameLength(options);
|
|
62
|
+
}
|
|
63
|
+
push(chunk) {
|
|
64
|
+
if (this.state === "ended")
|
|
65
|
+
throw new FrameError("Frame decoder has ended");
|
|
66
|
+
if (this.state === "failed")
|
|
67
|
+
throw new FrameError("Frame decoder has failed");
|
|
68
|
+
if (!(chunk instanceof Uint8Array))
|
|
69
|
+
throw new TypeError("Frame chunk must be a Uint8Array");
|
|
70
|
+
const frames = [];
|
|
71
|
+
let chunkOffset = 0;
|
|
72
|
+
while (chunkOffset < chunk.byteLength) {
|
|
73
|
+
if (this.expectedPayloadLength === undefined) {
|
|
74
|
+
const headerBytes = Math.min(FRAME_HEADER_LENGTH - this.headerLength, chunk.byteLength - chunkOffset);
|
|
75
|
+
this.header.set(chunk.subarray(chunkOffset, chunkOffset + headerBytes), this.headerLength);
|
|
76
|
+
this.headerLength += headerBytes;
|
|
77
|
+
chunkOffset += headerBytes;
|
|
78
|
+
if (this.headerLength < FRAME_HEADER_LENGTH)
|
|
79
|
+
continue;
|
|
80
|
+
const frameLength = this.header[0] * 0x1_000_000 + this.header[1] * 0x1_0000 + this.header[2] * 0x100 + this.header[3];
|
|
81
|
+
this.headerLength = 0;
|
|
82
|
+
if (frameLength > this.maxFrameLength) {
|
|
83
|
+
this.fail(`Frame length ${frameLength} exceeds configured limit of ${this.maxFrameLength}`);
|
|
84
|
+
}
|
|
85
|
+
if (frameLength === 0) {
|
|
86
|
+
frames.push(new Uint8Array());
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
this.expectedPayloadLength = frameLength;
|
|
90
|
+
this.payloadBlocks = [];
|
|
91
|
+
this.currentPayloadBlock = undefined;
|
|
92
|
+
this.currentPayloadBlockLength = 0;
|
|
93
|
+
this.payloadLength = 0;
|
|
94
|
+
}
|
|
95
|
+
const expectedPayloadLength = this.expectedPayloadLength;
|
|
96
|
+
if (expectedPayloadLength === undefined)
|
|
97
|
+
continue;
|
|
98
|
+
while (chunkOffset < chunk.byteLength && this.payloadLength < expectedPayloadLength) {
|
|
99
|
+
let block = this.currentPayloadBlock;
|
|
100
|
+
if (!block || this.currentPayloadBlockLength === block.byteLength) {
|
|
101
|
+
block = new Uint8Array(Math.min(PAYLOAD_BLOCK_SIZE, expectedPayloadLength - this.payloadLength));
|
|
102
|
+
this.payloadBlocks.push(block);
|
|
103
|
+
this.currentPayloadBlock = block;
|
|
104
|
+
this.currentPayloadBlockLength = 0;
|
|
105
|
+
}
|
|
106
|
+
const payloadBytes = Math.min(block.byteLength - this.currentPayloadBlockLength, chunk.byteLength - chunkOffset);
|
|
107
|
+
block.set(chunk.subarray(chunkOffset, chunkOffset + payloadBytes), this.currentPayloadBlockLength);
|
|
108
|
+
this.currentPayloadBlockLength += payloadBytes;
|
|
109
|
+
this.payloadLength += payloadBytes;
|
|
110
|
+
chunkOffset += payloadBytes;
|
|
111
|
+
}
|
|
112
|
+
if (this.payloadLength === expectedPayloadLength) {
|
|
113
|
+
if (this.payloadBlocks.length === 1) {
|
|
114
|
+
frames.push(this.payloadBlocks[0]);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const payload = new Uint8Array(expectedPayloadLength);
|
|
118
|
+
let offset = 0;
|
|
119
|
+
for (const payloadBlock of this.payloadBlocks) {
|
|
120
|
+
payload.set(payloadBlock, offset);
|
|
121
|
+
offset += payloadBlock.byteLength;
|
|
122
|
+
}
|
|
123
|
+
frames.push(payload);
|
|
124
|
+
}
|
|
125
|
+
this.payloadBlocks = [];
|
|
126
|
+
this.currentPayloadBlock = undefined;
|
|
127
|
+
this.currentPayloadBlockLength = 0;
|
|
128
|
+
this.expectedPayloadLength = undefined;
|
|
129
|
+
this.payloadLength = 0;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return frames;
|
|
133
|
+
}
|
|
134
|
+
end() {
|
|
135
|
+
if (this.state === "ended")
|
|
136
|
+
throw new FrameError("Frame decoder has ended");
|
|
137
|
+
if (this.state === "failed")
|
|
138
|
+
throw new FrameError("Frame decoder has failed");
|
|
139
|
+
if (this.headerLength !== 0 || this.expectedPayloadLength !== undefined) {
|
|
140
|
+
this.fail("Truncated frame at end of stream");
|
|
141
|
+
}
|
|
142
|
+
this.state = "ended";
|
|
143
|
+
}
|
|
144
|
+
fail(message) {
|
|
145
|
+
this.state = "failed";
|
|
146
|
+
this.headerLength = 0;
|
|
147
|
+
this.payloadBlocks = [];
|
|
148
|
+
this.currentPayloadBlock = undefined;
|
|
149
|
+
this.currentPayloadBlockLength = 0;
|
|
150
|
+
this.expectedPayloadLength = undefined;
|
|
151
|
+
this.payloadLength = 0;
|
|
152
|
+
throw new FrameError(message);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=framing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"framing.js","sourceRoot":"","sources":["../src/framing.ts"],"names":[],"mappings":"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","sourcesContent":["const FRAME_HEADER_LENGTH = 4;\nconst MAX_UINT32 = 0xffff_ffff;\nconst PAYLOAD_BLOCK_SIZE = 64 * 1024;\n\n/** Default upper bound for one framed CBOR payload. */\nexport const DEFAULT_MAX_FRAME_LENGTH = 16 * 1024 * 1024;\n\nexport interface FrameDecoderOptions {\n\tmaxFrameLength?: number;\n}\n\nexport class FrameError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"FrameError\";\n\t}\n}\n\nfunction resolveMaxFrameLength(options: FrameDecoderOptions | undefined): number {\n\tconst value = options?.maxFrameLength ?? DEFAULT_MAX_FRAME_LENGTH;\n\tif (!Number.isSafeInteger(value) || value < 0 || value > MAX_UINT32) {\n\t\tthrow new RangeError(`maxFrameLength must be an integer between 0 and ${MAX_UINT32}`);\n\t}\n\treturn value;\n}\n\n/** Prefixes a payload with its unsigned 32-bit big-endian byte length. */\nexport function encodeFrame(payload: Uint8Array): Uint8Array {\n\tif (!(payload instanceof Uint8Array)) throw new TypeError(\"Frame payload must be a Uint8Array\");\n\tif (payload.byteLength > MAX_UINT32) throw new RangeError(\"Frame payload exceeds the unsigned 32-bit length limit\");\n\tconst frame = new Uint8Array(FRAME_HEADER_LENGTH + payload.byteLength);\n\tconst length = payload.byteLength;\n\tframe[0] = length >>> 24;\n\tframe[1] = length >>> 16;\n\tframe[2] = length >>> 8;\n\tframe[3] = length;\n\tframe.set(payload, FRAME_HEADER_LENGTH);\n\treturn frame;\n}\n\n/** Validates that bytes contain exactly one complete frame within the configured limit. */\nexport function assertCompleteFrame(frame: Uint8Array, options?: FrameDecoderOptions): void {\n\tif (!(frame instanceof Uint8Array)) throw new TypeError(\"Frame must be a Uint8Array\");\n\tif (frame.byteLength < FRAME_HEADER_LENGTH) throw new FrameError(\"Frame does not contain a complete length prefix\");\n\tconst length = frame[0]! * 0x1_000_000 + frame[1]! * 0x1_0000 + frame[2]! * 0x100 + frame[3]!;\n\tconst maxFrameLength = resolveMaxFrameLength(options);\n\tif (length > maxFrameLength) {\n\t\tthrow new FrameError(`Frame length ${length} exceeds configured limit of ${maxFrameLength}`);\n\t}\n\tif (frame.byteLength !== FRAME_HEADER_LENGTH + length) {\n\t\tthrow new FrameError(\"Frame must contain exactly one complete payload\");\n\t}\n}\n\ntype DecoderState = \"open\" | \"ended\" | \"failed\";\n\n/** Incrementally splits arbitrary byte chunks into length-prefixed payloads. */\nexport class FrameDecoder {\n\tprivate readonly header = new Uint8Array(FRAME_HEADER_LENGTH);\n\tprivate headerLength = 0;\n\tprivate readonly maxFrameLength: number;\n\tprivate payloadBlocks: Uint8Array[] = [];\n\tprivate currentPayloadBlock: Uint8Array | undefined;\n\tprivate currentPayloadBlockLength = 0;\n\tprivate expectedPayloadLength: number | undefined;\n\tprivate payloadLength = 0;\n\tprivate state: DecoderState = \"open\";\n\n\tconstructor(options?: FrameDecoderOptions) {\n\t\tthis.maxFrameLength = resolveMaxFrameLength(options);\n\t}\n\n\tpush(chunk: Uint8Array): Uint8Array[] {\n\t\tif (this.state === \"ended\") throw new FrameError(\"Frame decoder has ended\");\n\t\tif (this.state === \"failed\") throw new FrameError(\"Frame decoder has failed\");\n\t\tif (!(chunk instanceof Uint8Array)) throw new TypeError(\"Frame chunk must be a Uint8Array\");\n\n\t\tconst frames: Uint8Array[] = [];\n\t\tlet chunkOffset = 0;\n\t\twhile (chunkOffset < chunk.byteLength) {\n\t\t\tif (this.expectedPayloadLength === undefined) {\n\t\t\t\tconst headerBytes = Math.min(FRAME_HEADER_LENGTH - this.headerLength, chunk.byteLength - chunkOffset);\n\t\t\t\tthis.header.set(chunk.subarray(chunkOffset, chunkOffset + headerBytes), this.headerLength);\n\t\t\t\tthis.headerLength += headerBytes;\n\t\t\t\tchunkOffset += headerBytes;\n\t\t\t\tif (this.headerLength < FRAME_HEADER_LENGTH) continue;\n\n\t\t\t\tconst frameLength =\n\t\t\t\t\tthis.header[0]! * 0x1_000_000 + this.header[1]! * 0x1_0000 + this.header[2]! * 0x100 + this.header[3]!;\n\t\t\t\tthis.headerLength = 0;\n\t\t\t\tif (frameLength > this.maxFrameLength) {\n\t\t\t\t\tthis.fail(`Frame length ${frameLength} exceeds configured limit of ${this.maxFrameLength}`);\n\t\t\t\t}\n\t\t\t\tif (frameLength === 0) {\n\t\t\t\t\tframes.push(new Uint8Array());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis.expectedPayloadLength = frameLength;\n\t\t\t\tthis.payloadBlocks = [];\n\t\t\t\tthis.currentPayloadBlock = undefined;\n\t\t\t\tthis.currentPayloadBlockLength = 0;\n\t\t\t\tthis.payloadLength = 0;\n\t\t\t}\n\n\t\t\tconst expectedPayloadLength = this.expectedPayloadLength;\n\t\t\tif (expectedPayloadLength === undefined) continue;\n\t\t\twhile (chunkOffset < chunk.byteLength && this.payloadLength < expectedPayloadLength) {\n\t\t\t\tlet block = this.currentPayloadBlock;\n\t\t\t\tif (!block || this.currentPayloadBlockLength === block.byteLength) {\n\t\t\t\t\tblock = new Uint8Array(Math.min(PAYLOAD_BLOCK_SIZE, expectedPayloadLength - this.payloadLength));\n\t\t\t\t\tthis.payloadBlocks.push(block);\n\t\t\t\t\tthis.currentPayloadBlock = block;\n\t\t\t\t\tthis.currentPayloadBlockLength = 0;\n\t\t\t\t}\n\t\t\t\tconst payloadBytes = Math.min(\n\t\t\t\t\tblock.byteLength - this.currentPayloadBlockLength,\n\t\t\t\t\tchunk.byteLength - chunkOffset,\n\t\t\t\t);\n\t\t\t\tblock.set(chunk.subarray(chunkOffset, chunkOffset + payloadBytes), this.currentPayloadBlockLength);\n\t\t\t\tthis.currentPayloadBlockLength += payloadBytes;\n\t\t\t\tthis.payloadLength += payloadBytes;\n\t\t\t\tchunkOffset += payloadBytes;\n\t\t\t}\n\t\t\tif (this.payloadLength === expectedPayloadLength) {\n\t\t\t\tif (this.payloadBlocks.length === 1) {\n\t\t\t\t\tframes.push(this.payloadBlocks[0]!);\n\t\t\t\t} else {\n\t\t\t\t\tconst payload = new Uint8Array(expectedPayloadLength);\n\t\t\t\t\tlet offset = 0;\n\t\t\t\t\tfor (const payloadBlock of this.payloadBlocks) {\n\t\t\t\t\t\tpayload.set(payloadBlock, offset);\n\t\t\t\t\t\toffset += payloadBlock.byteLength;\n\t\t\t\t\t}\n\t\t\t\t\tframes.push(payload);\n\t\t\t\t}\n\t\t\t\tthis.payloadBlocks = [];\n\t\t\t\tthis.currentPayloadBlock = undefined;\n\t\t\t\tthis.currentPayloadBlockLength = 0;\n\t\t\t\tthis.expectedPayloadLength = undefined;\n\t\t\t\tthis.payloadLength = 0;\n\t\t\t}\n\t\t}\n\t\treturn frames;\n\t}\n\n\tend(): void {\n\t\tif (this.state === \"ended\") throw new FrameError(\"Frame decoder has ended\");\n\t\tif (this.state === \"failed\") throw new FrameError(\"Frame decoder has failed\");\n\t\tif (this.headerLength !== 0 || this.expectedPayloadLength !== undefined) {\n\t\t\tthis.fail(\"Truncated frame at end of stream\");\n\t\t}\n\t\tthis.state = \"ended\";\n\t}\n\n\tprivate fail(message: string): never {\n\t\tthis.state = \"failed\";\n\t\tthis.headerLength = 0;\n\t\tthis.payloadBlocks = [];\n\t\tthis.currentPayloadBlock = undefined;\n\t\tthis.currentPayloadBlockLength = 0;\n\t\tthis.expectedPayloadLength = undefined;\n\t\tthis.payloadLength = 0;\n\t\tthrow new FrameError(message);\n\t}\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC","sourcesContent":["export * from \"./cbor/index.ts\";\nexport * from \"./codec.ts\";\nexport * from \"./framing.ts\";\nexport * from \"./schemas.ts\";\n"]}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC","sourcesContent":["export * from \"./cbor/index.ts\";\nexport * from \"./codec.ts\";\nexport * from \"./framing.ts\";\nexport * from \"./schemas.ts\";\n"]}
|