diff --git a/docs/maintainers/value-contract-prototypes.md b/docs/maintainers/value-contract-prototypes.md index fc4f6af6..49ba6be9 100644 --- a/docs/maintainers/value-contract-prototypes.md +++ b/docs/maintainers/value-contract-prototypes.md @@ -6,21 +6,25 @@ import them. ## Exact integer option -The generation option is `integerMode: 'bigint'` for one callable. It maps every -Python `int` parameter and result to TypeScript `bigint`. Safe integers use the -same type. Python `bool` remains `boolean`. Calls without the option keep the -safe `number` rule. - -The current value-contract revision represents Python `int` only as a safe JSON -`number`. The compiler and return validator follow that rule. This prototype -does not generate `bigint` wrappers; that needs a deliberate contract extension. +The proposed public option is `integerMode: 'bigint'` for one callable. It would +map every Python `int` parameter and result to TypeScript `bigint`. Safe +integers use the same type. Python `bool` remains `boolean`. Calls without the +option keep the safe `number` rule. + +The default value-contract revision 2 represents Python `int` as a safe JSON +`number`. The bounded revision 3 proof selects +`EXACT_INTEGER_VALUE_CONVERSION` for an analyzed Python module. It emits +`bigint` wrappers and validators for scalar and nested results through a +test-only runtime binding. A public per-callable option and production bridge +adapter are not implemented. The proposed generated wrapper must require `exactIntegerDecimalV2` in the bridge's `meta.valueCapabilities`. It must send `params.valuePolicy.integer = 'bigint-v2'` on each call. A new bridge must reject unknown policies. An old bridge lacks the capability, so the wrapper must -reject the call before sending a tag. The prototype tests this policy in an -isolated adapter. The shipped wrapper does not implement it. +reject the call before sending a tag. The bound proof checks the capability +before its private adapter encodes arguments. That adapter sends the policy to +the Python prototype. The default bridge does not implement this mode. The version 2 integer envelope is `{"__tywrap__":"integer","codecVersion":2,"encoding":"decimal","value":"18446744073709551617"}`. @@ -46,9 +50,9 @@ rejects malformed float tags and nonfinite numbers. The Python response encoder tags every `int`, including safe values. The TypeScript response decoder returns `bigint` at declared integer nodes. The -generated return validator must require `bigint`. It must reject an ordinary -number and a raw tag. Arrow int64 keeps its current Arrow encoding and decoded -type. +generated revision 3 return validator requires `bigint`. The prototype decoder +rejects an ordinary number and a raw tag. Arrow int64 keeps its current Arrow +encoding and decoded type. Both prototypes walk nested arrays and string-keyed records within depth and node limits. They reject unsupported values and malformed envelopes. Encoders @@ -60,10 +64,16 @@ characters directly, as `JSON.stringify` does. Both paths reject unpaired surrogates. Production must check raw wire bytes before parsing; object-level checks do not prove equal byte counts for alternate numeric JSON spellings. -The bounded tests must round trip positive and negative values beyond 64 bits. -They must cover floats, booleans, nested records, malformed tags, digit limits, -byte limits, and old bridges. Production work needs separate review of -capability negotiation, generated types, and migration cost. +The bounded tests round trip positive and negative values beyond 64 bits. They +cover floats, booleans, nested records, malformed tags, digit limits, byte +limits, and old bridges. `test/value_extensions_generated_integer.test.ts` uses +analyzer IR to check `bigint` and `bigint[]` inputs, `Promise` and +`Promise` outputs, and generated bigint return validation at a +nested index. It checks signed zero, a client request limit before dispatch, +the Python raw input limit, and that an incapable bridge receives no exact +request. The same fixture under revision 2 keeps `number` types and uses an +ordinary call. Production still needs a public opt-in selector, bridge +capability negotiation, and application migration measurements. ### Exact integer migration cost @@ -77,6 +87,12 @@ Each tagged integer adds 73 JSON bytes beyond its decimal digits. For example, Nested collections multiply that cost by their integer count. Teams must measure representative call payloads against the byte limit before enabling the option. +For a compact `{"args": [...]}` payload with three integers, a boolean, and a +float, the safe-value example grows from 28 to 247 UTF-8 bytes (8.82 times). +Using one 81-bit and one 131-bit integer grows the same shape from 91 to 310 +bytes (3.41 times). These figures exclude the surrounding RPC envelope and do +not estimate application traffic. + The client must check the bridge capability before it sends tagged values. This requires a coordinated client and bridge rollout. Old bridges reject the opted call. Arrow int64 columns keep their current type and wire format. diff --git a/test/fixtures/python/value_contract_exact_integer.py b/test/fixtures/python/value_contract_exact_integer.py new file mode 100644 index 00000000..b9a2d431 --- /dev/null +++ b/test/fixtures/python/value_contract_exact_integer.py @@ -0,0 +1,14 @@ +"""Importable exact-integer fixture for the bounded v3 compiler proof.""" + +import math + + +def combine_exact(value: int, nested: list[int], flag: bool, ratio: float) -> int: + """Combine scalar and nested integers after the bridge decodes them.""" + total = value + sum(nested) + return total if flag and math.copysign(1.0, ratio) > 0 else -total + + +def echo_exact(value: int, nested: list[int]) -> list[list[int]]: + """Return nested values after the bridge decodes them.""" + return [[value], nested, [-value]] diff --git a/test/prototypes/value_contract_v3_adapter.ts b/test/prototypes/value_contract_v3_adapter.ts new file mode 100644 index 00000000..17f9eb73 --- /dev/null +++ b/test/prototypes/value_contract_v3_adapter.ts @@ -0,0 +1,19 @@ +/** Translate resolved v3 nodes into the isolated cross-language test codec. */ + +import type { ValueContractV3 } from '../../src/contracts/value-contract.js'; +import type { PrototypeContract } from './value_extensions.js'; + +export function toExactPrototypeContract(value: ValueContractV3): PrototypeContract { + switch (value.kind) { + case 'integer-exact': + return { kind: 'integer' }; + case 'boolean': + return { kind: 'boolean' }; + case 'float': + return { kind: 'float' }; + case 'sequence': + return { kind: 'array', item: toExactPrototypeContract(value.item) }; + default: + throw new Error(`The exact call fixture cannot use ${value.kind}.`); + } +} diff --git a/test/prototypes/value_exact_call.py b/test/prototypes/value_exact_call.py new file mode 100644 index 00000000..fae0cdc4 --- /dev/null +++ b/test/prototypes/value_exact_call.py @@ -0,0 +1,63 @@ +"""Private Python call adapter for the exact-integer compiler proof.""" + +from __future__ import annotations + +import json +import sys +from typing import Any + +from value_contract_exact_integer import combine_exact, echo_exact +from value_extensions import ( + MAX_PAYLOAD_BYTES, + PrototypeError, + decode_exact_integers, + encode_exact_integers, + require_capability, +) + + +def main() -> None: + trusted_contract: dict[str, Any] = json.loads(sys.argv[1]) + bridge_meta: dict[str, Any] = json.loads(sys.argv[2]) + function_name = sys.argv[3] + functions = {'combine_exact': combine_exact, 'echo_exact': echo_exact} + function = functions.get(function_name) + if function is None: + raise PrototypeError('unknown exact-integer callable') + raw = sys.stdin.buffer.read(MAX_PAYLOAD_BYTES + 1) + if len(raw) > MAX_PAYLOAD_BYTES: + raise PrototypeError('input payload exceeds byte limit') + request: dict[str, Any] = json.loads(raw) + if (request.get('module'), request.get('functionName')) != ( + 'value_contract_exact_integer', + function_name, + ): + raise PrototypeError('unexpected exact-integer callable') + params = request.get('params') + if not isinstance(params, dict): + raise PrototypeError('missing exact-integer call parameters') + value_policy = params.get('valuePolicy') + policy = value_policy.get('integer') if isinstance(value_policy, dict) else None + require_capability(bridge_meta, 'exactIntegerDecimalV2', policy) + args = params.get('args') + fields = trusted_contract.get('fields') + if ( + trusted_contract.get('kind') != 'record' + or not isinstance(fields, dict) + or not isinstance(args, list) + or len(args) != len(fields) + ): + raise PrototypeError('exact-integer arguments differ from the contract') + input_record = dict(zip(fields, args, strict=True)) + decoded = decode_exact_integers(input_record, trusted_contract) + assert isinstance(decoded, dict) + result = function(**decoded) + encoded = encode_exact_integers(result) + wire = json.dumps( + encoded, allow_nan=False, ensure_ascii=False, separators=(',', ':') + ).encode('utf-8') + sys.stdout.buffer.write(wire + b'\n') + + +if __name__ == '__main__': + main() diff --git a/test/value_extensions_generated_integer.test.ts b/test/value_extensions_generated_integer.test.ts new file mode 100644 index 00000000..3a54053f --- /dev/null +++ b/test/value_extensions_generated_integer.test.ts @@ -0,0 +1,444 @@ +import { execFileSync, spawnSync } from 'node:child_process'; +import { existsSync } from 'node:fs'; +import { mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { delimiter, join } from 'node:path'; +import { pathToFileURL } from 'node:url'; +import ts from 'typescript'; +import { describe, expect, it } from 'vitest'; +import { + compileContract, + DEFAULT_CALLABLE_CAPABILITIES, + DEFAULT_VALUE_CONVERSION, + EXACT_INTEGER_VALUE_CONVERSION, + type CompiledContract, +} from '../src/core/callable-compiler.js'; +import { CodeGenerator } from '../src/core/generator.js'; +import { validateIrContract } from '../src/core/ir-contract.js'; +import { transformIrToTsModel } from '../src/core/ir-model.js'; +import { BridgeValidationError } from '../src/runtime/errors.js'; +import { clearRuntimeBridge, setRuntimeBridge } from 'tywrap/runtime'; +import type { PythonModule } from '../src/types/index.js'; +import { PYTHON, PYTHON_AVAILABLE } from './helpers/python-probe.js'; +import { + decodeExactResponse, + encodeExactRequest, + type PrototypeContract, +} from './prototypes/value_extensions.js'; +import { toExactPrototypeContract } from './prototypes/value_contract_v3_adapter.js'; + +const fixtureDir = join(process.cwd(), 'test', 'fixtures', 'python'); +const fixturePath = join(fixtureDir, 'value_contract_exact_integer.py'); +const pythonAdapter = join(process.cwd(), 'test', 'prototypes', 'value_exact_call.py'); +const pythonEnvironment = { + ...process.env, + PYTHONPATH: [ + fixtureDir, + join(process.cwd(), 'tywrap_ir'), + join(process.cwd(), 'test', 'prototypes'), + process.env.PYTHONPATH, + ] + .filter(Boolean) + .join(delimiter), +}; + +interface ExactInvocationDescriptor { + requiredCapability: 'exactIntegerDecimalV2'; + valuePolicy: { integer: 'bigint-v2' }; + args: PrototypeContract; + result: PrototypeContract; +} + +function analyzedIr(): unknown { + return JSON.parse( + execFileSync( + PYTHON ?? 'python3', + [ + '-c', + "from tywrap_ir.ir import emit_ir_json; print(emit_ir_json('value_contract_exact_integer', pretty=False))", + ], + { env: pythonEnvironment, encoding: 'utf8' } + ) + ) as unknown; +} + +function compileInteger(ir: unknown, exact: boolean, bound = false): CompiledContract { + const validated = validateIrContract(ir, 'analyzed exact integer IR'); + expect(validated.ok).toBe(true); + if (!validated.ok) throw new Error('Exact integer IR is invalid'); + const module = transformIrToTsModel(validated.contract); + const generator = new CodeGenerator(); + return compileContract(validated.contract, { + module, + generator: bound + ? { + generateModuleDefinition(model: PythonModule, annotatedJSDoc = false) { + return generator.generateModuleBindingTemplate(model, annotatedJSDoc); + }, + } + : generator, + conversion: exact ? EXACT_INTEGER_VALUE_CONVERSION : DEFAULT_VALUE_CONVERSION, + capabilities: DEFAULT_CALLABLE_CAPABILITIES.map(capability => + capability.name === 'exact-integer-adapter' ? { ...capability, available: exact } : capability + ), + }); +} + +function descriptorFrom(compiled: CompiledContract, name: string): ExactInvocationDescriptor { + const callable = compiled.callables.find(item => item.name === name); + const functionModel = compiled.module.functions.find(item => item.name === name); + if (!callable || !functionModel || callable.result.resolution.status !== 'supported') { + throw new Error('Exact callable result is unresolved'); + } + const fields: Record = {}; + for (const [index, parameter] of functionModel.parameters.entries()) { + const resolution = callable.parameters[index]?.resolution; + if (!resolution || resolution.status !== 'supported') { + throw new Error(`Exact parameter ${parameter.name} is unresolved`); + } + fields[parameter.name] = toExactPrototypeContract(resolution.value); + } + return { + requiredCapability: 'exactIntegerDecimalV2', + valuePolicy: { integer: 'bigint-v2' }, + args: { kind: 'record', fields }, + result: toExactPrototypeContract(callable.result.resolution.value), + }; +} + +describe.skipIf(!PYTHON_AVAILABLE || !existsSync(fixturePath) || !existsSync(pythonAdapter))( + 'generated exact integer proof', + () => { + it('keeps default numeric types and fails closed without exact adapter capability', () => { + const ir = analyzedIr(); + const ordinary = compileInteger(ir, false); + expect(ordinary.generated.declaration).toContain('value: number'); + expect(ordinary.generated.declaration).toContain('nested: number[]'); + expect(ordinary.generated.declaration).toContain('Promise'); + expect(ordinary.generated.declaration).toContain('Promise'); + + const validated = validateIrContract(ir, 'exact integer capability control'); + expect(validated.ok).toBe(true); + if (!validated.ok) throw new Error('Exact integer IR is invalid'); + const module = transformIrToTsModel(validated.contract); + expect(() => + compileContract(validated.contract, { + module, + generator: new CodeGenerator(), + conversion: EXACT_INTEGER_VALUE_CONVERSION, + capabilities: DEFAULT_CALLABLE_CAPABILITIES, + }) + ).toThrow(/exact-integer-adapter is unavailable/); + }); + + it('executes a bound bigint wrapper with pre-send capability checks', async () => { + const compiled = compileInteger(analyzedIr(), true, true); + const descriptors = { + combine_exact: descriptorFrom(compiled, 'combine_exact'), + echo_exact: descriptorFrom(compiled, 'echo_exact'), + }; + for (const callable of compiled.callables) { + expect(callable.requiredCapabilities).toContain('exact-integer-adapter'); + } + expect(compiled.generated.declaration).toContain('value: bigint'); + expect(compiled.generated.declaration).toContain('nested: bigint[]'); + expect(compiled.generated.declaration).toContain('flag: boolean'); + expect(compiled.generated.declaration).toContain('ratio: number'); + expect(compiled.generated.declaration).toContain('Promise'); + expect(compiled.generated.declaration).toContain('Promise'); + expect(compiled.generated.typescript).toContain('"kind":"primitive","type":"bigint"'); + expect(compiled.generated.typescript).toContain('__tywrapRuntimeProvider().call'); + + const temporary = await mkdtemp(join(process.cwd(), 'test', '.tywrap-integer-')); + try { + const outputPath = join(temporary, 'value_contract_exact_integer.generated.mjs'); + const declarationPath = join(temporary, 'value_contract_exact_integer.generated.d.ts'); + const consumerPath = join(temporary, 'consumer.ts'); + const binding = ` +import { getRuntimeBridge } from 'tywrap/runtime'; +const __tywrapExactDescriptors = ${JSON.stringify(descriptors)} as Record; +type PolicyCall = ( + module: string, + functionName: string, + args: unknown[], + kwargs: Record | undefined, + validate: ((result: T) => void) | undefined, + descriptor: unknown +) => Promise; +function __tywrapRuntimeProvider() { + const bridge = getRuntimeBridge() as ReturnType & { + meta?: { valueCapabilities?: unknown }; + callWithValuePolicy?: PolicyCall; + }; + const capabilities = bridge.meta?.valueCapabilities; + if (!Array.isArray(capabilities) || !capabilities.includes('exactIntegerDecimalV2')) { + throw new Error('bridge lacks exactIntegerDecimalV2 capability'); + } + if (typeof bridge.callWithValuePolicy !== 'function') { + throw new Error('bridge lacks the exact integer value-policy adapter'); + } + return { + call( + module: string, + functionName: string, + args: unknown[], + kwargs?: Record, + validate?: (result: T) => void + ): Promise { + const descriptor = __tywrapExactDescriptors[functionName]; + if (!descriptor) { + throw new Error('exact integer descriptor is missing'); + } + return bridge.callWithValuePolicy!( + module, functionName, args, kwargs, validate, descriptor + ); + }, + }; +} +`; + const javascript = ts.transpileModule(compiled.generated.typescript + binding, { + compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext }, + }).outputText; + await writeFile(outputPath, javascript, 'utf8'); + await writeFile(declarationPath, compiled.generated.declaration, 'utf8'); + await writeFile( + consumerPath, + `import { combineExact, echoExact } from './value_contract_exact_integer.generated.js'; +const result: Promise = combineExact(1n, [2n], true, 1.5); +const nested: Promise = echoExact(1n, [2n]); +result.then(value => { + const valid: bigint = value; + // @ts-expect-error Exact result is bigint. + const wrong: number = value; + return valid + BigInt(wrong); +}); +nested.then(rows => { + const valid: bigint = rows[0]![0]!; + // @ts-expect-error Nested exact result is bigint. + const wrong: number = rows[0]![0]!; + return valid + BigInt(wrong); +}); +// @ts-expect-error Exact input is bigint. +combineExact(1, [2n], true, 1.5); +// @ts-expect-error Nested exact input is bigint[]. +combineExact(1n, [2], true, 1.5); +// @ts-expect-error Nested exact input is bigint[]. +echoExact(1n, [2]);`, + 'utf8' + ); + const program = ts.createProgram([consumerPath], { + module: ts.ModuleKind.NodeNext, + moduleResolution: ts.ModuleResolutionKind.NodeNext, + target: ts.ScriptTarget.ES2022, + strict: true, + noEmit: true, + skipLibCheck: true, + types: [], + }); + expect( + ts + .getPreEmitDiagnostics(program) + .map(diagnostic => ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')) + ).toEqual([]); + + let sent = 0; + let responseOverride: unknown; + let maxRequestBytes = 10 * 1024 * 1024; + const bridge = { + meta: { valueCapabilities: ['exactIntegerDecimalV2'] }, + async call(): Promise { + throw new Error('the exact wrapper must use its policy adapter'); + }, + async callWithValuePolicy( + moduleName: string, + functionName: string, + args: unknown[], + _kwargs?: Record, + validate?: (result: T) => void, + invocation?: ExactInvocationDescriptor + ): Promise { + expect(moduleName).toBe('value_contract_exact_integer'); + expect(['combine_exact', 'echo_exact']).toContain(functionName); + expect(invocation).toEqual(descriptors[functionName as keyof typeof descriptors]); + if (!invocation || invocation.args.kind !== 'record') { + throw new Error('Exact invocation descriptor is missing'); + } + const names = Object.keys(invocation.args.fields); + const input = Object.fromEntries(names.map((name, index) => [name, args[index]])); + const encoded = encodeExactRequest(input, invocation.args) as Record; + const request = { + module: moduleName, + functionName, + params: { + valuePolicy: invocation.valuePolicy, + args: names.map(name => encoded[name]), + }, + }; + const raw = JSON.stringify(request); + if (new TextEncoder().encode(raw).length > maxRequestBytes) { + throw new Error('exact request exceeds byte limit'); + } + sent += 1; + const response = JSON.parse( + execFileSync( + PYTHON ?? 'python3', + [ + pythonAdapter, + JSON.stringify(invocation.args), + JSON.stringify(this.meta), + functionName, + ], + { input: raw, env: pythonEnvironment, encoding: 'utf8', maxBuffer: 1024 * 1024 } + ) + ) as unknown; + const decoded = decodeExactResponse( + responseOverride === undefined ? response : responseOverride, + invocation.result + ); + validate?.(decoded as T); + return decoded as T; + }, + async dispose(): Promise {}, + }; + setRuntimeBridge(bridge); + const generated = (await import(pathToFileURL(outputPath).href)) as { + combineExact: ( + value: bigint, + nested: bigint[], + flag: boolean, + ratio: number + ) => Promise; + echoExact: (value: bigint, nested: bigint[]) => Promise; + }; + + const positive = 2n ** 80n + 1n; + const negative = -(2n ** 130n + 7n); + await expect(generated.combineExact(positive, [7n, negative], true, 1.5)).resolves.toBe( + positive + 7n + negative + ); + await expect(generated.combineExact(7n, [2n], true, 0)).resolves.toBe(9n); + await expect(generated.combineExact(7n, [2n], true, -0)).resolves.toBe(-9n); + await expect(generated.combineExact(7n, [2n], false, 1.5)).resolves.toBe(-9n); + await expect(generated.echoExact(positive, [negative, 7n])).resolves.toEqual([ + [positive], + [negative, 7n], + [-positive], + ]); + + const beforeOversize = sent; + maxRequestBytes = 100; + await expect(generated.echoExact(7n, [2n])).rejects.toThrow( + /exact request exceeds byte limit/ + ); + expect(sent).toBe(beforeOversize); + maxRequestBytes = 10 * 1024 * 1024; + + const beforeMissing = sent; + bridge.meta = { valueCapabilities: [] }; + await expect(generated.combineExact(7n, [2n], true, 1.5)).rejects.toThrow( + /bridge lacks exactIntegerDecimalV2 capability/ + ); + expect(sent).toBe(beforeMissing); + bridge.meta = { valueCapabilities: ['exactIntegerDecimalV2'] }; + responseOverride = { + __tywrap__: 'integer', + codecVersion: 1, + encoding: 'decimal', + value: '9', + }; + await expect(generated.combineExact(7n, [2n], true, 1.5)).rejects.toThrow( + /invalid integer envelope/ + ); + responseOverride = 9; + await expect(generated.combineExact(7n, [2n], true, 1.5)).rejects.toThrow( + /expected integer envelope/ + ); + responseOverride = [ + [{ __tywrap__: 'integer', codecVersion: 2, encoding: 'decimal', value: '7' }], + [{ __tywrap__: 'integer', codecVersion: 1, encoding: 'decimal', value: '2' }], + ]; + await expect(generated.echoExact(7n, [2n])).rejects.toThrow(/invalid integer envelope/); + + const bypassBridge = { + meta: { valueCapabilities: ['exactIntegerDecimalV2'] }, + async call(): Promise { + throw new Error('ordinary call must not run'); + }, + async callWithValuePolicy( + _module: string, + functionName: string, + _args: unknown[], + _kwargs?: Record, + validate?: (result: T) => void + ): Promise { + const wrong = + functionName === 'echo_exact' ? ([[7n], [2, 3n]] as unknown as T) : (9 as T); + validate?.(wrong); + return wrong; + }, + async dispose(): Promise {}, + }; + setRuntimeBridge(bypassBridge); + await expect(generated.combineExact(7n, [2n], true, 1.5)).rejects.toThrow( + BridgeValidationError + ); + await expect(generated.echoExact(7n, [2n])).rejects.toThrow(BridgeValidationError); + + const ordinary = compileInteger(analyzedIr(), false); + const ordinaryPath = join(temporary, 'value_contract_integer_default.generated.mjs'); + await writeFile( + ordinaryPath, + ts.transpileModule(ordinary.generated.typescript, { + compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext }, + }).outputText, + 'utf8' + ); + let ordinaryCalls = 0; + let exactCalls = 0; + const compatibleBridge = { + async call(): Promise { + ordinaryCalls += 1; + return 9 as T; + }, + async callWithValuePolicy(): Promise { + exactCalls += 1; + throw new Error('default calls must not select the exact policy'); + }, + async dispose(): Promise {}, + }; + setRuntimeBridge(compatibleBridge); + const defaultGenerated = (await import(pathToFileURL(ordinaryPath).href)) as { + combineExact: ( + value: number, + nested: number[], + flag: boolean, + ratio: number + ) => Promise; + }; + await expect(defaultGenerated.combineExact(7, [2], true, 1.5)).resolves.toBe(9); + expect(ordinaryCalls).toBe(1); + expect(exactCalls).toBe(0); + + const serverGuard = spawnSync( + PYTHON ?? 'python3', + [ + pythonAdapter, + JSON.stringify(descriptors.echo_exact.args), + JSON.stringify(bridge.meta), + 'echo_exact', + ], + { + input: Buffer.alloc(10 * 1024 * 1024 + 1, 0x20), + env: pythonEnvironment, + encoding: 'utf8', + maxBuffer: 1024 * 1024, + } + ); + expect(serverGuard.status).not.toBe(0); + expect(serverGuard.stderr).toContain('input payload exceeds byte limit'); + } finally { + clearRuntimeBridge(); + await rm(temporary, { recursive: true, force: true }); + } + }); + } +);