diff --git a/src/state/index.ts b/src/state/index.ts index 3b68b3e4..cf7c16f3 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -122,6 +122,17 @@ function refusedUnannounced(name: string): void { } } +/** The size of one chunk of the Go side's put and get sessions, the most a call carries (design 47 G §518, A-21). */ +export const CHUNK_BYTES = 512 * 1024; + +/** A TypeScript bundle runs no put or get session in G (design 47 G §643): a value over one call's limit fails at once, naming it. */ +function withinOneCall(key: string, value: unknown): void { + const size = Buffer.byteLength(JSON.stringify(value) ?? ""); + if (size > CHUNK_BYTES) { + throw new Error(`the value of "${key}" is ${size} bytes, over the ${CHUNK_BYTES} bytes (512 KiB) one call carries: a TypeScript bundle puts no value larger than that`); + } +} + /** A state this module keeps (by its local name) or reads (`.`). */ export function state(name: string): State { const bound = (): StateChannel => { @@ -130,8 +141,10 @@ export function state(name: string): State { }; return { get: async (key) => (await bound().ask("mesh/state.get", { state: name, key })) as StateEntry | null, - put: async (key, value) => - ((await bound().ask("mesh/state.put", { state: name, key, value })) as { revision: number }).revision, + put: async (key, value) => { + withinOneCall(key, value); + return ((await bound().ask("mesh/state.put", { state: name, key, value })) as { revision: number }).revision; + }, delete: async (key) => { await bound().ask("mesh/state.delete", { state: name, key }); }, diff --git a/test/sdk.test.ts b/test/sdk.test.ts index 2ca4e66b..3db384a9 100644 --- a/test/sdk.test.ts +++ b/test/sdk.test.ts @@ -1005,3 +1005,8 @@ test("a bundle reads its tool runner's capability words once, reads an unknown c if (was === undefined) delete process.env.MESH_NODE; else process.env.MESH_NODE = was; }); + +test("a TypeScript bundle's value over one call's limit fails at once, naming the limit (design 47 G §643)", async () => { + const { state, CHUNK_BYTES } = await import("../dist/state/index.js"); + await assert.rejects(state("s").put("k", { text: "x".repeat(CHUNK_BYTES) }), /512 KiB/); +});