@tickernelz/paperclip-pro-adapter-utils 2026.925.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/README.md +37 -0
- package/dist/acpx-engine/cli.d.ts +2 -0
- package/dist/acpx-engine/cli.d.ts.map +1 -0
- package/dist/acpx-engine/cli.js +128 -0
- package/dist/acpx-engine/cli.js.map +1 -0
- package/dist/acpx-engine/composed-run-characterization.test.d.ts +2 -0
- package/dist/acpx-engine/composed-run-characterization.test.d.ts.map +1 -0
- package/dist/acpx-engine/composed-run-characterization.test.js +739 -0
- package/dist/acpx-engine/composed-run-characterization.test.js.map +1 -0
- package/dist/acpx-engine/constants.d.ts +20 -0
- package/dist/acpx-engine/constants.d.ts.map +1 -0
- package/dist/acpx-engine/constants.js +37 -0
- package/dist/acpx-engine/constants.js.map +1 -0
- package/dist/acpx-engine/execute-identity.test.d.ts +2 -0
- package/dist/acpx-engine/execute-identity.test.d.ts.map +1 -0
- package/dist/acpx-engine/execute-identity.test.js +218 -0
- package/dist/acpx-engine/execute-identity.test.js.map +1 -0
- package/dist/acpx-engine/execute.d.ts +325 -0
- package/dist/acpx-engine/execute.d.ts.map +1 -0
- package/dist/acpx-engine/execute.js +4448 -0
- package/dist/acpx-engine/execute.js.map +1 -0
- package/dist/acpx-engine/execute.test.d.ts +2 -0
- package/dist/acpx-engine/execute.test.d.ts.map +1 -0
- package/dist/acpx-engine/execute.test.js +6694 -0
- package/dist/acpx-engine/execute.test.js.map +1 -0
- package/dist/acpx-engine/index.d.ts +6 -0
- package/dist/acpx-engine/index.d.ts.map +1 -0
- package/dist/acpx-engine/index.js +6 -0
- package/dist/acpx-engine/index.js.map +1 -0
- package/dist/acpx-engine/local-process-control.d.ts +5 -0
- package/dist/acpx-engine/local-process-control.d.ts.map +1 -0
- package/dist/acpx-engine/local-process-control.js +39 -0
- package/dist/acpx-engine/local-process-control.js.map +1 -0
- package/dist/acpx-engine/local-process-control.test.d.ts +2 -0
- package/dist/acpx-engine/local-process-control.test.d.ts.map +1 -0
- package/dist/acpx-engine/local-process-control.test.js +38 -0
- package/dist/acpx-engine/local-process-control.test.js.map +1 -0
- package/dist/acpx-engine/operator-stop.test.d.ts +2 -0
- package/dist/acpx-engine/operator-stop.test.d.ts.map +1 -0
- package/dist/acpx-engine/operator-stop.test.js +107 -0
- package/dist/acpx-engine/operator-stop.test.js.map +1 -0
- package/dist/acpx-engine/remote-spawn-smoke.test.d.ts +2 -0
- package/dist/acpx-engine/remote-spawn-smoke.test.d.ts.map +1 -0
- package/dist/acpx-engine/remote-spawn-smoke.test.js +151 -0
- package/dist/acpx-engine/remote-spawn-smoke.test.js.map +1 -0
- package/dist/acpx-engine/run-contracts.d.ts +413 -0
- package/dist/acpx-engine/run-contracts.d.ts.map +1 -0
- package/dist/acpx-engine/run-contracts.js +20 -0
- package/dist/acpx-engine/run-contracts.js.map +1 -0
- package/dist/acpx-engine/run-contracts.test.d.ts +2 -0
- package/dist/acpx-engine/run-contracts.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-contracts.test.js +57 -0
- package/dist/acpx-engine/run-contracts.test.js.map +1 -0
- package/dist/acpx-engine/run-coordinator.d.ts +57 -0
- package/dist/acpx-engine/run-coordinator.d.ts.map +1 -0
- package/dist/acpx-engine/run-coordinator.js +63 -0
- package/dist/acpx-engine/run-coordinator.js.map +1 -0
- package/dist/acpx-engine/run-coordinator.test.d.ts +2 -0
- package/dist/acpx-engine/run-coordinator.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-coordinator.test.js +160 -0
- package/dist/acpx-engine/run-coordinator.test.js.map +1 -0
- package/dist/acpx-engine/run-fault-matrix.test.d.ts +2 -0
- package/dist/acpx-engine/run-fault-matrix.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-fault-matrix.test.js +829 -0
- package/dist/acpx-engine/run-fault-matrix.test.js.map +1 -0
- package/dist/acpx-engine/run-resource-ledger.d.ts +30 -0
- package/dist/acpx-engine/run-resource-ledger.d.ts.map +1 -0
- package/dist/acpx-engine/run-resource-ledger.js +148 -0
- package/dist/acpx-engine/run-resource-ledger.js.map +1 -0
- package/dist/acpx-engine/run-resource-ledger.test.d.ts +2 -0
- package/dist/acpx-engine/run-resource-ledger.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-resource-ledger.test.js +154 -0
- package/dist/acpx-engine/run-resource-ledger.test.js.map +1 -0
- package/dist/acpx-engine/run-site-host.d.ts +73 -0
- package/dist/acpx-engine/run-site-host.d.ts.map +1 -0
- package/dist/acpx-engine/run-site-host.js +65 -0
- package/dist/acpx-engine/run-site-host.js.map +1 -0
- package/dist/acpx-engine/run-site-host.test.d.ts +2 -0
- package/dist/acpx-engine/run-site-host.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-site-host.test.js +98 -0
- package/dist/acpx-engine/run-site-host.test.js.map +1 -0
- package/dist/acpx-engine/run-site-sandbox.d.ts +142 -0
- package/dist/acpx-engine/run-site-sandbox.d.ts.map +1 -0
- package/dist/acpx-engine/run-site-sandbox.js +278 -0
- package/dist/acpx-engine/run-site-sandbox.js.map +1 -0
- package/dist/acpx-engine/run-site-sandbox.test.d.ts +2 -0
- package/dist/acpx-engine/run-site-sandbox.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-site-sandbox.test.js +275 -0
- package/dist/acpx-engine/run-site-sandbox.test.js.map +1 -0
- package/dist/acpx-engine/session-codec.d.ts +3 -0
- package/dist/acpx-engine/session-codec.d.ts.map +1 -0
- package/dist/acpx-engine/session-codec.js +49 -0
- package/dist/acpx-engine/session-codec.js.map +1 -0
- package/dist/acpx-engine/session-reuse-store.d.ts +62 -0
- package/dist/acpx-engine/session-reuse-store.d.ts.map +1 -0
- package/dist/acpx-engine/session-reuse-store.js +134 -0
- package/dist/acpx-engine/session-reuse-store.js.map +1 -0
- package/dist/acpx-engine/session-reuse-store.test.d.ts +2 -0
- package/dist/acpx-engine/session-reuse-store.test.d.ts.map +1 -0
- package/dist/acpx-engine/session-reuse-store.test.js +134 -0
- package/dist/acpx-engine/session-reuse-store.test.js.map +1 -0
- package/dist/acpx-engine/settlement-characterization.test.d.ts +2 -0
- package/dist/acpx-engine/settlement-characterization.test.d.ts.map +1 -0
- package/dist/acpx-engine/settlement-characterization.test.js +880 -0
- package/dist/acpx-engine/settlement-characterization.test.js.map +1 -0
- package/dist/acpx-engine/settlement-sequence.d.ts +77 -0
- package/dist/acpx-engine/settlement-sequence.d.ts.map +1 -0
- package/dist/acpx-engine/settlement-sequence.js +119 -0
- package/dist/acpx-engine/settlement-sequence.js.map +1 -0
- package/dist/acpx-engine/settlement-sequence.test.d.ts +2 -0
- package/dist/acpx-engine/settlement-sequence.test.d.ts.map +1 -0
- package/dist/acpx-engine/settlement-sequence.test.js +272 -0
- package/dist/acpx-engine/settlement-sequence.test.js.map +1 -0
- package/dist/acpx-engine/spawn-smoke.test.d.ts +2 -0
- package/dist/acpx-engine/spawn-smoke.test.d.ts.map +1 -0
- package/dist/acpx-engine/spawn-smoke.test.js +184 -0
- package/dist/acpx-engine/spawn-smoke.test.js.map +1 -0
- package/dist/acpx-engine/startup-cancellation.d.ts +8 -0
- package/dist/acpx-engine/startup-cancellation.d.ts.map +1 -0
- package/dist/acpx-engine/startup-cancellation.js +92 -0
- package/dist/acpx-engine/startup-cancellation.js.map +1 -0
- package/dist/acpx-engine/startup-cancellation.test.d.ts +2 -0
- package/dist/acpx-engine/startup-cancellation.test.d.ts.map +1 -0
- package/dist/acpx-engine/startup-cancellation.test.js +115 -0
- package/dist/acpx-engine/startup-cancellation.test.js.map +1 -0
- package/dist/acpx-engine/startup-characterization.test.d.ts +2 -0
- package/dist/acpx-engine/startup-characterization.test.d.ts.map +1 -0
- package/dist/acpx-engine/startup-characterization.test.js +742 -0
- package/dist/acpx-engine/startup-characterization.test.js.map +1 -0
- package/dist/acpx-engine/startup-timing.d.ts +419 -0
- package/dist/acpx-engine/startup-timing.d.ts.map +1 -0
- package/dist/acpx-engine/startup-timing.js +616 -0
- package/dist/acpx-engine/startup-timing.js.map +1 -0
- package/dist/acpx-engine/startup-timing.test.d.ts +2 -0
- package/dist/acpx-engine/startup-timing.test.d.ts.map +1 -0
- package/dist/acpx-engine/startup-timing.test.js +619 -0
- package/dist/acpx-engine/startup-timing.test.js.map +1 -0
- package/dist/acpx-engine/turn-characterization.test.d.ts +2 -0
- package/dist/acpx-engine/turn-characterization.test.d.ts.map +1 -0
- package/dist/acpx-engine/turn-characterization.test.js +566 -0
- package/dist/acpx-engine/turn-characterization.test.js.map +1 -0
- package/dist/acpx-engine/turn-sequence.d.ts +54 -0
- package/dist/acpx-engine/turn-sequence.d.ts.map +1 -0
- package/dist/acpx-engine/turn-sequence.js +75 -0
- package/dist/acpx-engine/turn-sequence.js.map +1 -0
- package/dist/acpx-engine/turn-sequence.test.d.ts +2 -0
- package/dist/acpx-engine/turn-sequence.test.d.ts.map +1 -0
- package/dist/acpx-engine/turn-sequence.test.js +177 -0
- package/dist/acpx-engine/turn-sequence.test.js.map +1 -0
- package/dist/acpx-engine/ui.d.ts +3 -0
- package/dist/acpx-engine/ui.d.ts.map +1 -0
- package/dist/acpx-engine/ui.js +162 -0
- package/dist/acpx-engine/ui.js.map +1 -0
- package/dist/billing.d.ts +2 -0
- package/dist/billing.d.ts.map +1 -0
- package/dist/billing.js +16 -0
- package/dist/billing.js.map +1 -0
- package/dist/billing.test.d.ts +2 -0
- package/dist/billing.test.d.ts.map +1 -0
- package/dist/billing.test.js +14 -0
- package/dist/billing.test.js.map +1 -0
- package/dist/bridge-transport-contract.d.ts +43 -0
- package/dist/bridge-transport-contract.d.ts.map +1 -0
- package/dist/bridge-transport-contract.js +33 -0
- package/dist/bridge-transport-contract.js.map +1 -0
- package/dist/chat-file-delivery.d.ts +10 -0
- package/dist/chat-file-delivery.d.ts.map +1 -0
- package/dist/chat-file-delivery.js +38 -0
- package/dist/chat-file-delivery.js.map +1 -0
- package/dist/chat-file-delivery.test.d.ts +2 -0
- package/dist/chat-file-delivery.test.d.ts.map +1 -0
- package/dist/chat-file-delivery.test.js +92 -0
- package/dist/chat-file-delivery.test.js.map +1 -0
- package/dist/command-managed-runtime.d.ts +162 -0
- package/dist/command-managed-runtime.d.ts.map +1 -0
- package/dist/command-managed-runtime.js +456 -0
- package/dist/command-managed-runtime.js.map +1 -0
- package/dist/command-managed-runtime.test.d.ts +2 -0
- package/dist/command-managed-runtime.test.d.ts.map +1 -0
- package/dist/command-managed-runtime.test.js +908 -0
- package/dist/command-managed-runtime.test.js.map +1 -0
- package/dist/command-redaction.d.ts +15 -0
- package/dist/command-redaction.d.ts.map +1 -0
- package/dist/command-redaction.js +97 -0
- package/dist/command-redaction.js.map +1 -0
- package/dist/command-redaction.test.d.ts +2 -0
- package/dist/command-redaction.test.d.ts.map +1 -0
- package/dist/command-redaction.test.js +80 -0
- package/dist/command-redaction.test.js.map +1 -0
- package/dist/duplex-frame-codec.d.ts +124 -0
- package/dist/duplex-frame-codec.d.ts.map +1 -0
- package/dist/duplex-frame-codec.js +134 -0
- package/dist/duplex-frame-codec.js.map +1 -0
- package/dist/duplex-frame-codec.test.d.ts +2 -0
- package/dist/duplex-frame-codec.test.d.ts.map +1 -0
- package/dist/duplex-frame-codec.test.js +172 -0
- package/dist/duplex-frame-codec.test.js.map +1 -0
- package/dist/duplex-observability.d.ts +220 -0
- package/dist/duplex-observability.d.ts.map +1 -0
- package/dist/duplex-observability.js +248 -0
- package/dist/duplex-observability.js.map +1 -0
- package/dist/duplex-observability.test.d.ts +2 -0
- package/dist/duplex-observability.test.d.ts.map +1 -0
- package/dist/duplex-observability.test.js +55 -0
- package/dist/duplex-observability.test.js.map +1 -0
- package/dist/env-bindings.d.ts +17 -0
- package/dist/env-bindings.d.ts.map +1 -0
- package/dist/env-bindings.js +98 -0
- package/dist/env-bindings.js.map +1 -0
- package/dist/env-bindings.test.d.ts +2 -0
- package/dist/env-bindings.test.d.ts.map +1 -0
- package/dist/env-bindings.test.js +66 -0
- package/dist/env-bindings.test.js.map +1 -0
- package/dist/exclude-patterns.d.ts +4 -0
- package/dist/exclude-patterns.d.ts.map +1 -0
- package/dist/exclude-patterns.js +26 -0
- package/dist/exclude-patterns.js.map +1 -0
- package/dist/execution-target-sandbox.test.d.ts +2 -0
- package/dist/execution-target-sandbox.test.d.ts.map +1 -0
- package/dist/execution-target-sandbox.test.js +6522 -0
- package/dist/execution-target-sandbox.test.js.map +1 -0
- package/dist/execution-target-stdin-race.test.d.ts +2 -0
- package/dist/execution-target-stdin-race.test.d.ts.map +1 -0
- package/dist/execution-target-stdin-race.test.js +1842 -0
- package/dist/execution-target-stdin-race.test.js.map +1 -0
- package/dist/execution-target.d.ts +537 -0
- package/dist/execution-target.d.ts.map +1 -0
- package/dist/execution-target.js +3877 -0
- package/dist/execution-target.js.map +1 -0
- package/dist/execution-target.test.d.ts +2 -0
- package/dist/execution-target.test.d.ts.map +1 -0
- package/dist/execution-target.test.js +346 -0
- package/dist/execution-target.test.js.map +1 -0
- package/dist/git-workspace-sync.d.ts +193 -0
- package/dist/git-workspace-sync.d.ts.map +1 -0
- package/dist/git-workspace-sync.js +786 -0
- package/dist/git-workspace-sync.js.map +1 -0
- package/dist/git-workspace-sync.test.d.ts +2 -0
- package/dist/git-workspace-sync.test.d.ts.map +1 -0
- package/dist/git-workspace-sync.test.js +830 -0
- package/dist/git-workspace-sync.test.js.map +1 -0
- package/dist/github-launcher-environment.test.d.ts +2 -0
- package/dist/github-launcher-environment.test.d.ts.map +1 -0
- package/dist/github-launcher-environment.test.js +285 -0
- package/dist/github-launcher-environment.test.js.map +1 -0
- package/dist/github-launcher.d.ts +8 -0
- package/dist/github-launcher.d.ts.map +1 -0
- package/dist/github-launcher.js +127 -0
- package/dist/github-launcher.js.map +1 -0
- package/dist/github-launcher.test.d.ts +2 -0
- package/dist/github-launcher.test.d.ts.map +1 -0
- package/dist/github-launcher.test.js +166 -0
- package/dist/github-launcher.test.js.map +1 -0
- package/dist/http2-bridge-server.d.ts +465 -0
- package/dist/http2-bridge-server.d.ts.map +1 -0
- package/dist/http2-bridge-server.js +1087 -0
- package/dist/http2-bridge-server.js.map +1 -0
- package/dist/http2-bridge-server.test.d.ts +2 -0
- package/dist/http2-bridge-server.test.d.ts.map +1 -0
- package/dist/http2-bridge-server.test.js +2129 -0
- package/dist/http2-bridge-server.test.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/local-process-sandbox.d.ts +41 -0
- package/dist/local-process-sandbox.d.ts.map +1 -0
- package/dist/local-process-sandbox.js +432 -0
- package/dist/local-process-sandbox.js.map +1 -0
- package/dist/local-process-sandbox.test.d.ts +2 -0
- package/dist/local-process-sandbox.test.d.ts.map +1 -0
- package/dist/local-process-sandbox.test.js +399 -0
- package/dist/local-process-sandbox.test.js.map +1 -0
- package/dist/log-redaction.d.ts +9 -0
- package/dist/log-redaction.d.ts.map +1 -0
- package/dist/log-redaction.js +88 -0
- package/dist/log-redaction.js.map +1 -0
- package/dist/login-capability.d.ts +88 -0
- package/dist/login-capability.d.ts.map +1 -0
- package/dist/login-capability.js +78 -0
- package/dist/login-capability.js.map +1 -0
- package/dist/login-capability.test.d.ts +2 -0
- package/dist/login-capability.test.d.ts.map +1 -0
- package/dist/login-capability.test.js +78 -0
- package/dist/login-capability.test.js.map +1 -0
- package/dist/login-pty-transport.d.ts +80 -0
- package/dist/login-pty-transport.d.ts.map +1 -0
- package/dist/login-pty-transport.js +75 -0
- package/dist/login-pty-transport.js.map +1 -0
- package/dist/login-pty-transport.test.d.ts +2 -0
- package/dist/login-pty-transport.test.d.ts.map +1 -0
- package/dist/login-pty-transport.test.js +185 -0
- package/dist/login-pty-transport.test.js.map +1 -0
- package/dist/login-runner-lifecycle.d.ts +70 -0
- package/dist/login-runner-lifecycle.d.ts.map +1 -0
- package/dist/login-runner-lifecycle.js +52 -0
- package/dist/login-runner-lifecycle.js.map +1 -0
- package/dist/login-runner-lifecycle.test.d.ts +2 -0
- package/dist/login-runner-lifecycle.test.d.ts.map +1 -0
- package/dist/login-runner-lifecycle.test.js +122 -0
- package/dist/login-runner-lifecycle.test.js.map +1 -0
- package/dist/mcp-isolation.integration.test.d.ts +2 -0
- package/dist/mcp-isolation.integration.test.d.ts.map +1 -0
- package/dist/mcp-isolation.integration.test.js +202 -0
- package/dist/mcp-isolation.integration.test.js.map +1 -0
- package/dist/paperclip-runner-permissions.d.ts +110 -0
- package/dist/paperclip-runner-permissions.d.ts.map +1 -0
- package/dist/paperclip-runner-permissions.js +150 -0
- package/dist/paperclip-runner-permissions.js.map +1 -0
- package/dist/paperclip-runner-permissions.test.d.ts +2 -0
- package/dist/paperclip-runner-permissions.test.d.ts.map +1 -0
- package/dist/paperclip-runner-permissions.test.js +47 -0
- package/dist/paperclip-runner-permissions.test.js.map +1 -0
- package/dist/published-exports.test.d.ts +2 -0
- package/dist/published-exports.test.d.ts.map +1 -0
- package/dist/published-exports.test.js +17 -0
- package/dist/published-exports.test.js.map +1 -0
- package/dist/remote-execution-env.d.ts +2 -0
- package/dist/remote-execution-env.d.ts.map +1 -0
- package/dist/remote-execution-env.js +46 -0
- package/dist/remote-execution-env.js.map +1 -0
- package/dist/remote-managed-runtime.d.ts +45 -0
- package/dist/remote-managed-runtime.d.ts.map +1 -0
- package/dist/remote-managed-runtime.js +183 -0
- package/dist/remote-managed-runtime.js.map +1 -0
- package/dist/remote-managed-runtime.test.d.ts +2 -0
- package/dist/remote-managed-runtime.test.d.ts.map +1 -0
- package/dist/remote-managed-runtime.test.js +294 -0
- package/dist/remote-managed-runtime.test.js.map +1 -0
- package/dist/runner-connectivity.d.ts +61 -0
- package/dist/runner-connectivity.d.ts.map +1 -0
- package/dist/runner-connectivity.js +81 -0
- package/dist/runner-connectivity.js.map +1 -0
- package/dist/runner-connectivity.test.d.ts +2 -0
- package/dist/runner-connectivity.test.d.ts.map +1 -0
- package/dist/runner-connectivity.test.js +183 -0
- package/dist/runner-connectivity.test.js.map +1 -0
- package/dist/runtime-progress.d.ts +48 -0
- package/dist/runtime-progress.d.ts.map +1 -0
- package/dist/runtime-progress.js +99 -0
- package/dist/runtime-progress.js.map +1 -0
- package/dist/runtime-progress.test.d.ts +2 -0
- package/dist/runtime-progress.test.d.ts.map +1 -0
- package/dist/runtime-progress.test.js +240 -0
- package/dist/runtime-progress.test.js.map +1 -0
- package/dist/sandbox-callback-bridge-body.d.ts +12 -0
- package/dist/sandbox-callback-bridge-body.d.ts.map +1 -0
- package/dist/sandbox-callback-bridge-body.js +39 -0
- package/dist/sandbox-callback-bridge-body.js.map +1 -0
- package/dist/sandbox-callback-bridge-body.test.d.ts +2 -0
- package/dist/sandbox-callback-bridge-body.test.d.ts.map +1 -0
- package/dist/sandbox-callback-bridge-body.test.js +31 -0
- package/dist/sandbox-callback-bridge-body.test.js.map +1 -0
- package/dist/sandbox-callback-bridge.d.ts +268 -0
- package/dist/sandbox-callback-bridge.d.ts.map +1 -0
- package/dist/sandbox-callback-bridge.js +2357 -0
- package/dist/sandbox-callback-bridge.js.map +1 -0
- package/dist/sandbox-callback-bridge.test.d.ts +2 -0
- package/dist/sandbox-callback-bridge.test.d.ts.map +1 -0
- package/dist/sandbox-callback-bridge.test.js +3253 -0
- package/dist/sandbox-callback-bridge.test.js.map +1 -0
- package/dist/sandbox-file-sync.test.d.ts +2 -0
- package/dist/sandbox-file-sync.test.d.ts.map +1 -0
- package/dist/sandbox-file-sync.test.js +419 -0
- package/dist/sandbox-file-sync.test.js.map +1 -0
- package/dist/sandbox-install-command.d.ts +2 -0
- package/dist/sandbox-install-command.d.ts.map +1 -0
- package/dist/sandbox-install-command.js +47 -0
- package/dist/sandbox-install-command.js.map +1 -0
- package/dist/sandbox-install-command.test.d.ts +2 -0
- package/dist/sandbox-install-command.test.d.ts.map +1 -0
- package/dist/sandbox-install-command.test.js +22 -0
- package/dist/sandbox-install-command.test.js.map +1 -0
- package/dist/sandbox-managed-runtime.d.ts +417 -0
- package/dist/sandbox-managed-runtime.d.ts.map +1 -0
- package/dist/sandbox-managed-runtime.js +1337 -0
- package/dist/sandbox-managed-runtime.js.map +1 -0
- package/dist/sandbox-managed-runtime.test.d.ts +2 -0
- package/dist/sandbox-managed-runtime.test.d.ts.map +1 -0
- package/dist/sandbox-managed-runtime.test.js +3746 -0
- package/dist/sandbox-managed-runtime.test.js.map +1 -0
- package/dist/sandbox-run-log-stream.d.ts +44 -0
- package/dist/sandbox-run-log-stream.d.ts.map +1 -0
- package/dist/sandbox-run-log-stream.js +203 -0
- package/dist/sandbox-run-log-stream.js.map +1 -0
- package/dist/sandbox-shell.d.ts +3 -0
- package/dist/sandbox-shell.d.ts.map +1 -0
- package/dist/sandbox-shell.js +7 -0
- package/dist/sandbox-shell.js.map +1 -0
- package/dist/server-utils-env.test.d.ts +2 -0
- package/dist/server-utils-env.test.d.ts.map +1 -0
- package/dist/server-utils-env.test.js +15 -0
- package/dist/server-utils-env.test.js.map +1 -0
- package/dist/server-utils.d.ts +617 -0
- package/dist/server-utils.d.ts.map +1 -0
- package/dist/server-utils.js +3258 -0
- package/dist/server-utils.js.map +1 -0
- package/dist/server-utils.test.d.ts +2 -0
- package/dist/server-utils.test.d.ts.map +1 -0
- package/dist/server-utils.test.js +3047 -0
- package/dist/server-utils.test.js.map +1 -0
- package/dist/session-compaction.d.ts +25 -0
- package/dist/session-compaction.d.ts.map +1 -0
- package/dist/session-compaction.js +165 -0
- package/dist/session-compaction.js.map +1 -0
- package/dist/skill-library-manifest.d.ts +6 -0
- package/dist/skill-library-manifest.d.ts.map +1 -0
- package/dist/skill-library-manifest.js +53 -0
- package/dist/skill-library-manifest.js.map +1 -0
- package/dist/skill-library-manifest.test.d.ts +2 -0
- package/dist/skill-library-manifest.test.d.ts.map +1 -0
- package/dist/skill-library-manifest.test.js +84 -0
- package/dist/skill-library-manifest.test.js.map +1 -0
- package/dist/skills-staging-follow-symlinks.test.d.ts +2 -0
- package/dist/skills-staging-follow-symlinks.test.d.ts.map +1 -0
- package/dist/skills-staging-follow-symlinks.test.js +126 -0
- package/dist/skills-staging-follow-symlinks.test.js.map +1 -0
- package/dist/ssh-fixture.test.d.ts +2 -0
- package/dist/ssh-fixture.test.d.ts.map +1 -0
- package/dist/ssh-fixture.test.js +832 -0
- package/dist/ssh-fixture.test.js.map +1 -0
- package/dist/ssh.d.ts +124 -0
- package/dist/ssh.d.ts.map +1 -0
- package/dist/ssh.js +1667 -0
- package/dist/ssh.js.map +1 -0
- package/dist/sync-operation-schedule.d.ts +17 -0
- package/dist/sync-operation-schedule.d.ts.map +1 -0
- package/dist/sync-operation-schedule.js +66 -0
- package/dist/sync-operation-schedule.js.map +1 -0
- package/dist/sync-operation-schedule.test.d.ts +2 -0
- package/dist/sync-operation-schedule.test.d.ts.map +1 -0
- package/dist/sync-operation-schedule.test.js +130 -0
- package/dist/sync-operation-schedule.test.js.map +1 -0
- package/dist/test-support/mcp-isolation-harness.d.ts +23 -0
- package/dist/test-support/mcp-isolation-harness.d.ts.map +1 -0
- package/dist/test-support/mcp-isolation-harness.js +67 -0
- package/dist/test-support/mcp-isolation-harness.js.map +1 -0
- package/dist/types.d.ts +780 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/workspace-restore-merge.d.ts +77 -0
- package/dist/workspace-restore-merge.d.ts.map +1 -0
- package/dist/workspace-restore-merge.js +396 -0
- package/dist/workspace-restore-merge.js.map +1 -0
- package/dist/workspace-restore-merge.test.d.ts +2 -0
- package/dist/workspace-restore-merge.test.d.ts.map +1 -0
- package/dist/workspace-restore-merge.test.js +438 -0
- package/dist/workspace-restore-merge.test.js.map +1 -0
- package/dist/workspace-restore-teardown.d.ts +29 -0
- package/dist/workspace-restore-teardown.d.ts.map +1 -0
- package/dist/workspace-restore-teardown.js +34 -0
- package/dist/workspace-restore-teardown.js.map +1 -0
- package/dist/workspace-restore-teardown.test.d.ts +2 -0
- package/dist/workspace-restore-teardown.test.d.ts.map +1 -0
- package/dist/workspace-restore-teardown.test.js +71 -0
- package/dist/workspace-restore-teardown.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/LICENSE +191 -0
- package/node_modules/@agentclientprotocol/sdk/README.md +70 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.d.ts +1803 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.js +1869 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.test.js +5036 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.d.ts +89 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.js +490 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.test.js +507 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.d.ts +26 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.js +96 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.test.js +77 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/agent.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/agent.js +232 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/agent.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/client.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/client.js +132 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/client.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/dual-version-agent.d.ts +9 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/dual-version-agent.js +221 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/dual-version-agent.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-client.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-client.js +86 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-client.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-server.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-server.js +112 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-server.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/ws-client.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/ws-client.js +88 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/ws-client.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.d.ts +33 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.js +435 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.test.js +1380 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.d.ts +603 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js +1076 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.test.js +917 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.d.ts +22 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.js +64 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.test.js +45 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.d.ts +17 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.js +426 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.test.js +499 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.d.ts +37 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.js +474 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.test.js +535 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.d.ts +11 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.js +69 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.test.js +82 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.d.ts +183 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.js +207 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/index.d.ts +51 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/index.js +52 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/index.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.d.ts +6219 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.js +3 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.d.ts +15215 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.js +3084 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.d.ts +30 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.js +98 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.test.js +93 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-permission.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-permission.test.js +261 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-permission.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-session-sse.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-session-sse.test.js +536 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-session-sse.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.js +88 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.test.js +53 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-websocket-upgrade.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-websocket-upgrade.test.js +992 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-websocket-upgrade.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.d.ts +81 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.js +386 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.test.js +1116 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.d.ts +4 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.js +91 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.test.js +126 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.d.ts +35 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.js +115 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.test.js +195 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/streams.d.ts +8 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/streams.js +51 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/streams.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-agent.d.ts +24 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-agent.js +163 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-agent.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-http-server.d.ts +9 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-http-server.js +74 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-http-server.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.d.ts +957 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.js +1484 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.test.js +1287 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/guards.gen.d.ts +946 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/guards.gen.js +1046 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/guards.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/index.d.ts +42 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/index.js +43 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/index.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/types.gen.d.ts +6208 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/types.gen.js +3 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/types.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/zod.gen.d.ts +33267 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/zod.gen.js +3199 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/zod.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-server.d.ts +14 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-server.js +357 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-server.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.d.ts +54 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.js +253 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.test.js +836 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.d.ts +13 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.js +78 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.test.js +42 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/package.json +104 -0
- package/node_modules/@agentclientprotocol/sdk/schema/schema.json +8967 -0
- package/node_modules/@agentclientprotocol/sdk/schema/v2/schema.unstable.json +10242 -0
- package/node_modules/@clack/core/LICENSE +9 -0
- package/node_modules/@clack/core/README.md +22 -0
- package/node_modules/@clack/core/dist/index.d.mts +503 -0
- package/node_modules/@clack/core/dist/index.mjs +1003 -0
- package/node_modules/@clack/core/package.json +63 -0
- package/node_modules/@clack/prompts/LICENSE +9 -0
- package/node_modules/@clack/prompts/README.md +396 -0
- package/node_modules/@clack/prompts/dist/index.d.mts +949 -0
- package/node_modules/@clack/prompts/dist/index.mjs +1423 -0
- package/node_modules/@clack/prompts/package.json +67 -0
- package/node_modules/@esbuild/linux-x64/README.md +3 -0
- package/node_modules/@esbuild/linux-x64/bin/esbuild +0 -0
- package/node_modules/@esbuild/linux-x64/package.json +20 -0
- package/node_modules/acpx/LICENSE +21 -0
- package/node_modules/acpx/README.md +424 -0
- package/node_modules/acpx/dist/cli-CxsS1XQp.js +197 -0
- package/node_modules/acpx/dist/cli-CxsS1XQp.js.map +1 -0
- package/node_modules/acpx/dist/cli.d.ts +14 -0
- package/node_modules/acpx/dist/cli.d.ts.map +1 -0
- package/node_modules/acpx/dist/cli.js +2683 -0
- package/node_modules/acpx/dist/cli.js.map +1 -0
- package/node_modules/acpx/dist/client-DwNyFauq.d.ts +192 -0
- package/node_modules/acpx/dist/client-DwNyFauq.d.ts.map +1 -0
- package/node_modules/acpx/dist/flags-Bjq_XQ4-.js +260 -0
- package/node_modules/acpx/dist/flags-Bjq_XQ4-.js.map +1 -0
- package/node_modules/acpx/dist/flows-Db_LZKu9.js +1920 -0
- package/node_modules/acpx/dist/flows-Db_LZKu9.js.map +1 -0
- package/node_modules/acpx/dist/flows.d.ts +318 -0
- package/node_modules/acpx/dist/flows.d.ts.map +1 -0
- package/node_modules/acpx/dist/flows.js +2 -0
- package/node_modules/acpx/dist/live-checkpoint-ClPCSdrW.js +6210 -0
- package/node_modules/acpx/dist/live-checkpoint-ClPCSdrW.js.map +1 -0
- package/node_modules/acpx/dist/output-CjdF5rHk.js +4593 -0
- package/node_modules/acpx/dist/output-CjdF5rHk.js.map +1 -0
- package/node_modules/acpx/dist/runtime.d.ts +472 -0
- package/node_modules/acpx/dist/runtime.d.ts.map +1 -0
- package/node_modules/acpx/dist/runtime.js +1711 -0
- package/node_modules/acpx/dist/runtime.js.map +1 -0
- package/node_modules/acpx/dist/session-options-jkYbBxGE.d.ts +278 -0
- package/node_modules/acpx/dist/session-options-jkYbBxGE.d.ts.map +1 -0
- package/node_modules/acpx/package.json +129 -0
- package/node_modules/acpx/skills/acpx/SKILL.md +617 -0
- package/node_modules/b4a/LICENSE +201 -0
- package/node_modules/b4a/README.md +153 -0
- package/node_modules/b4a/browser.js +546 -0
- package/node_modules/b4a/index.d.ts +144 -0
- package/node_modules/b4a/index.js +193 -0
- package/node_modules/b4a/lib/ascii.js +31 -0
- package/node_modules/b4a/lib/base64.js +64 -0
- package/node_modules/b4a/lib/hex.js +51 -0
- package/node_modules/b4a/lib/latin1.js +31 -0
- package/node_modules/b4a/lib/utf16le.js +40 -0
- package/node_modules/b4a/lib/utf8.js +144 -0
- package/node_modules/b4a/package.json +54 -0
- package/node_modules/b4a/react-native.js +5 -0
- package/node_modules/b4a/runtime.d.ts +14 -0
- package/node_modules/bare-events/LICENSE +201 -0
- package/node_modules/bare-events/README.md +25 -0
- package/node_modules/bare-events/global.d.ts +15 -0
- package/node_modules/bare-events/global.js +5 -0
- package/node_modules/bare-events/index.d.ts +89 -0
- package/node_modules/bare-events/index.js +397 -0
- package/node_modules/bare-events/lib/errors.js +23 -0
- package/node_modules/bare-events/package.json +63 -0
- package/node_modules/bare-events/web.d.ts +79 -0
- package/node_modules/bare-events/web.js +417 -0
- package/node_modules/bare-fs/CMakeLists.txt +13 -0
- package/node_modules/bare-fs/LICENSE +201 -0
- package/node_modules/bare-fs/README.md +33 -0
- package/node_modules/bare-fs/binding.c +2913 -0
- package/node_modules/bare-fs/binding.js +1 -0
- package/node_modules/bare-fs/index.d.ts +1502 -0
- package/node_modules/bare-fs/index.js +2855 -0
- package/node_modules/bare-fs/lib/constants.d.ts +50 -0
- package/node_modules/bare-fs/lib/constants.js +49 -0
- package/node_modules/bare-fs/lib/errors.d.ts +13 -0
- package/node_modules/bare-fs/lib/errors.js +55 -0
- package/node_modules/bare-fs/package.json +74 -0
- package/node_modules/bare-fs/prebuilds/android-arm/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/android-arm64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/android-ia32/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/android-x64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/darwin-arm64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/darwin-x64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/ios-arm64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/ios-arm64-simulator/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/ios-x64-simulator/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/linux-arm64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/linux-x64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/win32-arm64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/win32-x64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/promises.d.ts +480 -0
- package/node_modules/bare-fs/promises.js +119 -0
- package/node_modules/bare-path/CMakeLists.txt +13 -0
- package/node_modules/bare-path/LICENSE +201 -0
- package/node_modules/bare-path/NOTICE +28 -0
- package/node_modules/bare-path/README.md +19 -0
- package/node_modules/bare-path/binding.c +106 -0
- package/node_modules/bare-path/binding.js +1 -0
- package/node_modules/bare-path/index.d.ts +27 -0
- package/node_modules/bare-path/index.js +9 -0
- package/node_modules/bare-path/lib/constants.js +11 -0
- package/node_modules/bare-path/lib/posix.js +263 -0
- package/node_modules/bare-path/lib/shared.js +81 -0
- package/node_modules/bare-path/lib/win32.js +564 -0
- package/node_modules/bare-path/package.json +47 -0
- package/node_modules/bare-path/prebuilds/android-arm/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/android-arm64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/android-ia32/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/android-x64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/darwin-arm64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/darwin-x64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/ios-arm64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/ios-arm64-simulator/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/ios-x64-simulator/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/linux-arm64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/linux-x64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/win32-arm64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/win32-x64/bare-path.bare +0 -0
- package/node_modules/bare-stream/LICENSE +201 -0
- package/node_modules/bare-stream/README.md +354 -0
- package/node_modules/bare-stream/global.d.ts +31 -0
- package/node_modules/bare-stream/global.js +15 -0
- package/node_modules/bare-stream/index.d.ts +235 -0
- package/node_modules/bare-stream/index.js +493 -0
- package/node_modules/bare-stream/package.json +75 -0
- package/node_modules/bare-stream/promises.js +3 -0
- package/node_modules/bare-stream/web.d.ts +152 -0
- package/node_modules/bare-stream/web.js +607 -0
- package/node_modules/bare-url/CMakeLists.txt +36 -0
- package/node_modules/bare-url/LICENSE +201 -0
- package/node_modules/bare-url/README.md +33 -0
- package/node_modules/bare-url/binding.c +422 -0
- package/node_modules/bare-url/binding.js +1 -0
- package/node_modules/bare-url/global.d.ts +24 -0
- package/node_modules/bare-url/global.js +2 -0
- package/node_modules/bare-url/index.d.ts +96 -0
- package/node_modules/bare-url/index.js +676 -0
- package/node_modules/bare-url/lib/errors.d.ts +10 -0
- package/node_modules/bare-url/lib/errors.js +33 -0
- package/node_modules/bare-url/lib/url-search-params.d.ts +66 -0
- package/node_modules/bare-url/lib/url-search-params.js +276 -0
- package/node_modules/bare-url/package.json +54 -0
- package/node_modules/bare-url/prebuilds/android-arm/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/android-arm64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/android-ia32/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/android-x64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/darwin-arm64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/darwin-x64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/ios-arm64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/ios-arm64-simulator/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/ios-x64-simulator/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/linux-arm64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/linux-x64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/win32-arm64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/win32-x64/bare-url.bare +0 -0
- package/node_modules/commander/LICENSE +22 -0
- package/node_modules/commander/Readme.md +1172 -0
- package/node_modules/commander/index.js +21 -0
- package/node_modules/commander/lib/argument.js +147 -0
- package/node_modules/commander/lib/command.js +2790 -0
- package/node_modules/commander/lib/error.js +36 -0
- package/node_modules/commander/lib/help.js +731 -0
- package/node_modules/commander/lib/option.js +377 -0
- package/node_modules/commander/lib/suggestSimilar.js +99 -0
- package/node_modules/commander/package-support.json +19 -0
- package/node_modules/commander/package.json +64 -0
- package/node_modules/commander/typings/index.d.ts +1113 -0
- package/node_modules/esbuild/LICENSE.md +21 -0
- package/node_modules/esbuild/README.md +3 -0
- package/node_modules/esbuild/bin/esbuild +223 -0
- package/node_modules/esbuild/install.js +300 -0
- package/node_modules/esbuild/lib/main.d.ts +720 -0
- package/node_modules/esbuild/lib/main.js +2536 -0
- package/node_modules/esbuild/package.json +74 -0
- package/node_modules/events-universal/LICENSE +201 -0
- package/node_modules/events-universal/README.md +17 -0
- package/node_modules/events-universal/bare.js +1 -0
- package/node_modules/events-universal/default.js +1 -0
- package/node_modules/events-universal/index.js +1 -0
- package/node_modules/events-universal/package.json +39 -0
- package/node_modules/events-universal/react-native.js +1 -0
- package/node_modules/fast-fifo/LICENSE +21 -0
- package/node_modules/fast-fifo/README.md +78 -0
- package/node_modules/fast-fifo/fixed-size.js +39 -0
- package/node_modules/fast-fifo/index.js +48 -0
- package/node_modules/fast-fifo/package.json +28 -0
- package/node_modules/fast-string-truncated-width/dist/index.d.ts +4 -0
- package/node_modules/fast-string-truncated-width/dist/index.js +111 -0
- package/node_modules/fast-string-truncated-width/dist/types.d.ts +19 -0
- package/node_modules/fast-string-truncated-width/dist/types.js +2 -0
- package/node_modules/fast-string-truncated-width/dist/utils.d.ts +4 -0
- package/node_modules/fast-string-truncated-width/dist/utils.js +20 -0
- package/node_modules/fast-string-truncated-width/license +21 -0
- package/node_modules/fast-string-truncated-width/package.json +35 -0
- package/node_modules/fast-string-truncated-width/readme.md +59 -0
- package/node_modules/fast-string-width/dist/index.d.ts +4 -0
- package/node_modules/fast-string-width/dist/index.js +14 -0
- package/node_modules/fast-string-width/license +21 -0
- package/node_modules/fast-string-width/package.json +34 -0
- package/node_modules/fast-string-width/readme.md +42 -0
- package/node_modules/fast-wrap-ansi/LICENSE +23 -0
- package/node_modules/fast-wrap-ansi/README.md +26 -0
- package/node_modules/fast-wrap-ansi/lib/main.d.ts +6 -0
- package/node_modules/fast-wrap-ansi/lib/main.js +218 -0
- package/node_modules/fast-wrap-ansi/package.json +51 -0
- package/node_modules/sisteransi/license +21 -0
- package/node_modules/sisteransi/package.json +34 -0
- package/node_modules/sisteransi/readme.md +113 -0
- package/node_modules/sisteransi/src/index.js +58 -0
- package/node_modules/sisteransi/src/sisteransi.d.ts +35 -0
- package/node_modules/skillflag/LICENSE +21 -0
- package/node_modules/skillflag/README.md +44 -0
- package/node_modules/skillflag/dist/bin/skill-install.d.ts +3 -0
- package/node_modules/skillflag/dist/bin/skill-install.d.ts.map +1 -0
- package/node_modules/skillflag/dist/bin/skill-install.js +6 -0
- package/node_modules/skillflag/dist/bin/skill-install.js.map +1 -0
- package/node_modules/skillflag/dist/bin/skillflag.d.ts +3 -0
- package/node_modules/skillflag/dist/bin/skillflag.d.ts.map +1 -0
- package/node_modules/skillflag/dist/bin/skillflag.js +25 -0
- package/node_modules/skillflag/dist/bin/skillflag.js.map +1 -0
- package/node_modules/skillflag/dist/core/digest.d.ts +2 -0
- package/node_modules/skillflag/dist/core/digest.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/digest.js +15 -0
- package/node_modules/skillflag/dist/core/digest.js.map +1 -0
- package/node_modules/skillflag/dist/core/errors.d.ts +6 -0
- package/node_modules/skillflag/dist/core/errors.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/errors.js +13 -0
- package/node_modules/skillflag/dist/core/errors.js.map +1 -0
- package/node_modules/skillflag/dist/core/export.d.ts +2 -0
- package/node_modules/skillflag/dist/core/export.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/export.js +28 -0
- package/node_modules/skillflag/dist/core/export.js.map +1 -0
- package/node_modules/skillflag/dist/core/list.d.ts +21 -0
- package/node_modules/skillflag/dist/core/list.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/list.js +71 -0
- package/node_modules/skillflag/dist/core/list.js.map +1 -0
- package/node_modules/skillflag/dist/core/paths.d.ts +15 -0
- package/node_modules/skillflag/dist/core/paths.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/paths.js +149 -0
- package/node_modules/skillflag/dist/core/paths.js.map +1 -0
- package/node_modules/skillflag/dist/core/show.d.ts +2 -0
- package/node_modules/skillflag/dist/core/show.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/show.js +8 -0
- package/node_modules/skillflag/dist/core/show.js.map +1 -0
- package/node_modules/skillflag/dist/core/skillflag.d.ts +3 -0
- package/node_modules/skillflag/dist/core/skillflag.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/skillflag.js +2 -0
- package/node_modules/skillflag/dist/core/skillflag.js.map +1 -0
- package/node_modules/skillflag/dist/core/tar.d.ts +16 -0
- package/node_modules/skillflag/dist/core/tar.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/tar.js +140 -0
- package/node_modules/skillflag/dist/core/tar.js.map +1 -0
- package/node_modules/skillflag/dist/index.d.ts +5 -0
- package/node_modules/skillflag/dist/index.d.ts.map +1 -0
- package/node_modules/skillflag/dist/index.js +3 -0
- package/node_modules/skillflag/dist/index.js.map +1 -0
- package/node_modules/skillflag/dist/install/cli.d.ts +57 -0
- package/node_modules/skillflag/dist/install/cli.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/cli.js +653 -0
- package/node_modules/skillflag/dist/install/cli.js.map +1 -0
- package/node_modules/skillflag/dist/install/copy.d.ts +2 -0
- package/node_modules/skillflag/dist/install/copy.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/copy.js +19 -0
- package/node_modules/skillflag/dist/install/copy.js.map +1 -0
- package/node_modules/skillflag/dist/install/errors.d.ts +6 -0
- package/node_modules/skillflag/dist/install/errors.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/errors.js +13 -0
- package/node_modules/skillflag/dist/install/errors.js.map +1 -0
- package/node_modules/skillflag/dist/install/extract.d.ts +3 -0
- package/node_modules/skillflag/dist/install/extract.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/extract.js +76 -0
- package/node_modules/skillflag/dist/install/extract.js.map +1 -0
- package/node_modules/skillflag/dist/install/install.d.ts +21 -0
- package/node_modules/skillflag/dist/install/install.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/install.js +35 -0
- package/node_modules/skillflag/dist/install/install.js.map +1 -0
- package/node_modules/skillflag/dist/install/resolve.d.ts +12 -0
- package/node_modules/skillflag/dist/install/resolve.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/resolve.js +119 -0
- package/node_modules/skillflag/dist/install/resolve.js.map +1 -0
- package/node_modules/skillflag/dist/install/validate.d.ts +7 -0
- package/node_modules/skillflag/dist/install/validate.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/validate.js +28 -0
- package/node_modules/skillflag/dist/install/validate.js.map +1 -0
- package/node_modules/skillflag/dist/shared/frontmatter.d.ts +2 -0
- package/node_modules/skillflag/dist/shared/frontmatter.d.ts.map +1 -0
- package/node_modules/skillflag/dist/shared/frontmatter.js +28 -0
- package/node_modules/skillflag/dist/shared/frontmatter.js.map +1 -0
- package/node_modules/skillflag/dist/skillflag.d.ts +29 -0
- package/node_modules/skillflag/dist/skillflag.d.ts.map +1 -0
- package/node_modules/skillflag/dist/skillflag.js +230 -0
- package/node_modules/skillflag/dist/skillflag.js.map +1 -0
- package/node_modules/skillflag/dist/utils/collections.d.ts +3 -0
- package/node_modules/skillflag/dist/utils/collections.d.ts.map +1 -0
- package/node_modules/skillflag/dist/utils/collections.js +18 -0
- package/node_modules/skillflag/dist/utils/collections.js.map +1 -0
- package/node_modules/skillflag/package.json +78 -0
- package/node_modules/skillflag/skills/skillflag/SKILL.md +144 -0
- package/node_modules/streamx/LICENSE +21 -0
- package/node_modules/streamx/README.md +550 -0
- package/node_modules/streamx/index.d.ts +198 -0
- package/node_modules/streamx/index.js +1314 -0
- package/node_modules/streamx/lib/errors.d.ts +13 -0
- package/node_modules/streamx/lib/errors.js +47 -0
- package/node_modules/streamx/package.json +51 -0
- package/node_modules/streamx/runtime.d.ts +55 -0
- package/node_modules/tar-stream/LICENSE +21 -0
- package/node_modules/tar-stream/README.md +183 -0
- package/node_modules/tar-stream/constants.js +14 -0
- package/node_modules/tar-stream/extract.js +418 -0
- package/node_modules/tar-stream/headers.js +322 -0
- package/node_modules/tar-stream/index.d.ts +60 -0
- package/node_modules/tar-stream/index.js +2 -0
- package/node_modules/tar-stream/pack.js +287 -0
- package/node_modules/tar-stream/package.json +50 -0
- package/node_modules/teex/LICENSE +21 -0
- package/node_modules/teex/README.md +31 -0
- package/node_modules/teex/example.js +16 -0
- package/node_modules/teex/index.js +52 -0
- package/node_modules/teex/package.json +26 -0
- package/node_modules/teex/test.js +68 -0
- package/node_modules/text-decoder/LICENSE +201 -0
- package/node_modules/text-decoder/README.md +36 -0
- package/node_modules/text-decoder/index.js +64 -0
- package/node_modules/text-decoder/lib/pass-through-decoder.js +19 -0
- package/node_modules/text-decoder/lib/utf8-decoder.js +185 -0
- package/node_modules/text-decoder/package.json +39 -0
- package/node_modules/tsx/LICENSE +21 -0
- package/node_modules/tsx/README.md +32 -0
- package/node_modules/tsx/dist/cjs/api/index.cjs +2 -0
- package/node_modules/tsx/dist/cjs/api/index.d.cts +35 -0
- package/node_modules/tsx/dist/cjs/api/index.d.mts +35 -0
- package/node_modules/tsx/dist/cjs/api/index.mjs +2 -0
- package/node_modules/tsx/dist/cjs/index.cjs +1 -0
- package/node_modules/tsx/dist/cli.mjs +55 -0
- package/node_modules/tsx/dist/client-XItNFmsq.cjs +1 -0
- package/node_modules/tsx/dist/esm/api/index.cjs +2 -0
- package/node_modules/tsx/dist/esm/api/index.d.cts +37 -0
- package/node_modules/tsx/dist/esm/api/index.d.mts +37 -0
- package/node_modules/tsx/dist/esm/api/index.mjs +2 -0
- package/node_modules/tsx/dist/esm/index.mjs +2 -0
- package/node_modules/tsx/dist/index-Bqjv9TxC.mjs +1 -0
- package/node_modules/tsx/dist/index-DE3OBZuV.mjs +14 -0
- package/node_modules/tsx/dist/lexer-CGfpMDSb.cjs +3 -0
- package/node_modules/tsx/dist/lexer-CanA6ArK.mjs +3 -0
- package/node_modules/tsx/dist/loader.mjs +2 -0
- package/node_modules/tsx/dist/package-Dj0mHsMt.mjs +1 -0
- package/node_modules/tsx/dist/patch-repl.cjs +1 -0
- package/node_modules/tsx/dist/preflight.cjs +1 -0
- package/node_modules/tsx/dist/register-B1c7OH6V.cjs +23 -0
- package/node_modules/tsx/dist/register-nyXW-TH3.mjs +2 -0
- package/node_modules/tsx/dist/repl.mjs +4 -0
- package/node_modules/tsx/dist/require-CBjy4Foe.mjs +10 -0
- package/node_modules/tsx/dist/require-CVaYn9z3.cjs +1 -0
- package/node_modules/tsx/dist/suppress-warnings.cjs +1 -0
- package/node_modules/tsx/dist/temporary-directory-Du7LpLp9.mjs +1 -0
- package/node_modules/tsx/package.json +70 -0
- package/node_modules/zod/LICENSE +21 -0
- package/node_modules/zod/README.md +218 -0
- package/node_modules/zod/compile.cjs +96 -0
- package/node_modules/zod/compile.d.cts +1 -0
- package/node_modules/zod/compile.d.ts +1 -0
- package/node_modules/zod/compile.js +52 -0
- package/node_modules/zod/index.cjs +52 -0
- package/node_modules/zod/index.d.cts +3 -0
- package/node_modules/zod/index.d.ts +3 -0
- package/node_modules/zod/index.js +7 -0
- package/node_modules/zod/locales/index.cjs +36 -0
- package/node_modules/zod/locales/index.d.cts +1 -0
- package/node_modules/zod/locales/index.d.ts +1 -0
- package/node_modules/zod/locales/index.js +1 -0
- package/node_modules/zod/locales/package.json +7 -0
- package/node_modules/zod/mini/index.cjs +51 -0
- package/node_modules/zod/mini/index.d.cts +3 -0
- package/node_modules/zod/mini/index.d.ts +3 -0
- package/node_modules/zod/mini/index.js +3 -0
- package/node_modules/zod/mini/package.json +7 -0
- package/node_modules/zod/package.json +147 -0
- package/node_modules/zod/src/compile.ts +56 -0
- package/node_modules/zod/src/index.ts +7 -0
- package/node_modules/zod/src/locales/index.ts +1 -0
- package/node_modules/zod/src/mini/index.ts +3 -0
- package/node_modules/zod/src/v3/ZodError.ts +340 -0
- package/node_modules/zod/src/v3/benchmarks/datetime.ts +58 -0
- package/node_modules/zod/src/v3/benchmarks/discriminatedUnion.ts +80 -0
- package/node_modules/zod/src/v3/benchmarks/index.ts +59 -0
- package/node_modules/zod/src/v3/benchmarks/ipv4.ts +57 -0
- package/node_modules/zod/src/v3/benchmarks/object.ts +69 -0
- package/node_modules/zod/src/v3/benchmarks/primitives.ts +162 -0
- package/node_modules/zod/src/v3/benchmarks/realworld.ts +63 -0
- package/node_modules/zod/src/v3/benchmarks/string.ts +55 -0
- package/node_modules/zod/src/v3/benchmarks/union.ts +80 -0
- package/node_modules/zod/src/v3/errors.ts +13 -0
- package/node_modules/zod/src/v3/external.ts +6 -0
- package/node_modules/zod/src/v3/helpers/enumUtil.ts +17 -0
- package/node_modules/zod/src/v3/helpers/errorUtil.ts +8 -0
- package/node_modules/zod/src/v3/helpers/parseUtil.ts +176 -0
- package/node_modules/zod/src/v3/helpers/partialUtil.ts +34 -0
- package/node_modules/zod/src/v3/helpers/typeAliases.ts +2 -0
- package/node_modules/zod/src/v3/helpers/util.ts +224 -0
- package/node_modules/zod/src/v3/index.ts +4 -0
- package/node_modules/zod/src/v3/locales/en.ts +124 -0
- package/node_modules/zod/src/v3/standard-schema.ts +113 -0
- package/node_modules/zod/src/v3/tests/Mocker.ts +54 -0
- package/node_modules/zod/src/v3/tests/all-errors.test.ts +157 -0
- package/node_modules/zod/src/v3/tests/anyunknown.test.ts +28 -0
- package/node_modules/zod/src/v3/tests/array.test.ts +71 -0
- package/node_modules/zod/src/v3/tests/async-parsing.test.ts +388 -0
- package/node_modules/zod/src/v3/tests/async-refinements.test.ts +46 -0
- package/node_modules/zod/src/v3/tests/base.test.ts +29 -0
- package/node_modules/zod/src/v3/tests/bigint.test.ts +55 -0
- package/node_modules/zod/src/v3/tests/branded.test.ts +53 -0
- package/node_modules/zod/src/v3/tests/catch.test.ts +220 -0
- package/node_modules/zod/src/v3/tests/coerce.test.ts +133 -0
- package/node_modules/zod/src/v3/tests/complex.test.ts +70 -0
- package/node_modules/zod/src/v3/tests/custom.test.ts +31 -0
- package/node_modules/zod/src/v3/tests/date.test.ts +32 -0
- package/node_modules/zod/src/v3/tests/deepmasking.test.ts +186 -0
- package/node_modules/zod/src/v3/tests/default.test.ts +112 -0
- package/node_modules/zod/src/v3/tests/description.test.ts +33 -0
- package/node_modules/zod/src/v3/tests/discriminated-unions.test.ts +315 -0
- package/node_modules/zod/src/v3/tests/enum.test.ts +80 -0
- package/node_modules/zod/src/v3/tests/error.test.ts +586 -0
- package/node_modules/zod/src/v3/tests/firstparty.test.ts +87 -0
- package/node_modules/zod/src/v3/tests/firstpartyschematypes.test.ts +21 -0
- package/node_modules/zod/src/v3/tests/function.test.ts +261 -0
- package/node_modules/zod/src/v3/tests/generics.test.ts +48 -0
- package/node_modules/zod/src/v3/tests/instanceof.test.ts +37 -0
- package/node_modules/zod/src/v3/tests/intersection.test.ts +128 -0
- package/node_modules/zod/src/v3/tests/language-server.source.ts +76 -0
- package/node_modules/zod/src/v3/tests/language-server.test.ts +206 -0
- package/node_modules/zod/src/v3/tests/literal.test.ts +36 -0
- package/node_modules/zod/src/v3/tests/map.test.ts +110 -0
- package/node_modules/zod/src/v3/tests/masking.test.ts +4 -0
- package/node_modules/zod/src/v3/tests/mocker.test.ts +19 -0
- package/node_modules/zod/src/v3/tests/nan.test.ts +24 -0
- package/node_modules/zod/src/v3/tests/nativeEnum.test.ts +87 -0
- package/node_modules/zod/src/v3/tests/nullable.test.ts +42 -0
- package/node_modules/zod/src/v3/tests/number.test.ts +176 -0
- package/node_modules/zod/src/v3/tests/object-augmentation.test.ts +29 -0
- package/node_modules/zod/src/v3/tests/object-in-es5-env.test.ts +29 -0
- package/node_modules/zod/src/v3/tests/object.test.ts +431 -0
- package/node_modules/zod/src/v3/tests/optional.test.ts +42 -0
- package/node_modules/zod/src/v3/tests/parseUtil.test.ts +23 -0
- package/node_modules/zod/src/v3/tests/parser.test.ts +41 -0
- package/node_modules/zod/src/v3/tests/partials.test.ts +243 -0
- package/node_modules/zod/src/v3/tests/pickomit.test.ts +111 -0
- package/node_modules/zod/src/v3/tests/pipeline.test.ts +29 -0
- package/node_modules/zod/src/v3/tests/preprocess.test.ts +186 -0
- package/node_modules/zod/src/v3/tests/primitive.test.ts +440 -0
- package/node_modules/zod/src/v3/tests/promise.test.ts +90 -0
- package/node_modules/zod/src/v3/tests/readonly.test.ts +114 -0
- package/node_modules/zod/src/v3/tests/record.test.ts +171 -0
- package/node_modules/zod/src/v3/tests/recursive.test.ts +197 -0
- package/node_modules/zod/src/v3/tests/refine.test.ts +313 -0
- package/node_modules/zod/src/v3/tests/safeparse.test.ts +27 -0
- package/node_modules/zod/src/v3/tests/set.test.ts +142 -0
- package/node_modules/zod/src/v3/tests/standard-schema.test.ts +83 -0
- package/node_modules/zod/src/v3/tests/string.test.ts +922 -0
- package/node_modules/zod/src/v3/tests/transformer.test.ts +233 -0
- package/node_modules/zod/src/v3/tests/tuple.test.ts +90 -0
- package/node_modules/zod/src/v3/tests/unions.test.ts +57 -0
- package/node_modules/zod/src/v3/tests/validations.test.ts +133 -0
- package/node_modules/zod/src/v3/tests/void.test.ts +15 -0
- package/node_modules/zod/src/v3/types.ts +5138 -0
- package/node_modules/zod/src/v4/classic/checks.ts +33 -0
- package/node_modules/zod/src/v4/classic/coerce.ts +27 -0
- package/node_modules/zod/src/v4/classic/compat.ts +78 -0
- package/node_modules/zod/src/v4/classic/deep-partial.ts +76 -0
- package/node_modules/zod/src/v4/classic/errors.ts +97 -0
- package/node_modules/zod/src/v4/classic/external.ts +57 -0
- package/node_modules/zod/src/v4/classic/from-json-schema.ts +944 -0
- package/node_modules/zod/src/v4/classic/in-out.ts +45 -0
- package/node_modules/zod/src/v4/classic/index.ts +5 -0
- package/node_modules/zod/src/v4/classic/iso.ts +20 -0
- package/node_modules/zod/src/v4/classic/parse.ts +88 -0
- package/node_modules/zod/src/v4/classic/schemas.ts +2937 -0
- package/node_modules/zod/src/v4/classic/tests/anyunknown.test.ts +26 -0
- package/node_modules/zod/src/v4/classic/tests/apply.test.ts +86 -0
- package/node_modules/zod/src/v4/classic/tests/array.test.ts +264 -0
- package/node_modules/zod/src/v4/classic/tests/assignability.test.ts +402 -0
- package/node_modules/zod/src/v4/classic/tests/async-parsing.test.ts +381 -0
- package/node_modules/zod/src/v4/classic/tests/async-refinements.test.ts +68 -0
- package/node_modules/zod/src/v4/classic/tests/base.test.ts +7 -0
- package/node_modules/zod/src/v4/classic/tests/bigint.test.ts +89 -0
- package/node_modules/zod/src/v4/classic/tests/brand.test.ts +106 -0
- package/node_modules/zod/src/v4/classic/tests/catch.test.ts +604 -0
- package/node_modules/zod/src/v4/classic/tests/coalesce.test.ts +20 -0
- package/node_modules/zod/src/v4/classic/tests/codec-examples.test.ts +572 -0
- package/node_modules/zod/src/v4/classic/tests/codec.test.ts +704 -0
- package/node_modules/zod/src/v4/classic/tests/coerce.test.ts +160 -0
- package/node_modules/zod/src/v4/classic/tests/continuability.test.ts +374 -0
- package/node_modules/zod/src/v4/classic/tests/credit-card.test.ts +76 -0
- package/node_modules/zod/src/v4/classic/tests/custom.test.ts +40 -0
- package/node_modules/zod/src/v4/classic/tests/cyclic-data.test.ts +985 -0
- package/node_modules/zod/src/v4/classic/tests/date.test.ts +92 -0
- package/node_modules/zod/src/v4/classic/tests/datetime.test.ts +316 -0
- package/node_modules/zod/src/v4/classic/tests/deep-partial.test.ts +380 -0
- package/node_modules/zod/src/v4/classic/tests/default-locale.test.ts +32 -0
- package/node_modules/zod/src/v4/classic/tests/default.test.ts +455 -0
- package/node_modules/zod/src/v4/classic/tests/describe-meta-checks.test.ts +27 -0
- package/node_modules/zod/src/v4/classic/tests/description.test.ts +32 -0
- package/node_modules/zod/src/v4/classic/tests/detached-methods.test.ts +221 -0
- package/node_modules/zod/src/v4/classic/tests/discriminated-unions.test.ts +942 -0
- package/node_modules/zod/src/v4/classic/tests/enum.test.ts +305 -0
- package/node_modules/zod/src/v4/classic/tests/error-utils.test.ts +917 -0
- package/node_modules/zod/src/v4/classic/tests/error.test.ts +1108 -0
- package/node_modules/zod/src/v4/classic/tests/file.test.ts +96 -0
- package/node_modules/zod/src/v4/classic/tests/firstparty.test.ts +179 -0
- package/node_modules/zod/src/v4/classic/tests/fix-json-issue.test.ts +26 -0
- package/node_modules/zod/src/v4/classic/tests/from-json-schema.test.ts +1467 -0
- package/node_modules/zod/src/v4/classic/tests/function.test.ts +391 -0
- package/node_modules/zod/src/v4/classic/tests/generics.test.ts +72 -0
- package/node_modules/zod/src/v4/classic/tests/global-config.test.ts +32 -0
- package/node_modules/zod/src/v4/classic/tests/hash.test.ts +67 -0
- package/node_modules/zod/src/v4/classic/tests/iban.test.ts +81 -0
- package/node_modules/zod/src/v4/classic/tests/in-out.test.ts +159 -0
- package/node_modules/zod/src/v4/classic/tests/index.test.ts +958 -0
- package/node_modules/zod/src/v4/classic/tests/instance-footprint.test.ts +319 -0
- package/node_modules/zod/src/v4/classic/tests/instanceof.test.ts +127 -0
- package/node_modules/zod/src/v4/classic/tests/intersection.test.ts +334 -0
- package/node_modules/zod/src/v4/classic/tests/issue-schema.test.ts +215 -0
- package/node_modules/zod/src/v4/classic/tests/jitless-allows-eval.test.ts +39 -0
- package/node_modules/zod/src/v4/classic/tests/json.test.ts +109 -0
- package/node_modules/zod/src/v4/classic/tests/lazy.test.ts +262 -0
- package/node_modules/zod/src/v4/classic/tests/literal.test.ts +128 -0
- package/node_modules/zod/src/v4/classic/tests/locales_ka.test.ts +29 -0
- package/node_modules/zod/src/v4/classic/tests/locales_ro.test.ts +24 -0
- package/node_modules/zod/src/v4/classic/tests/map.test.ts +342 -0
- package/node_modules/zod/src/v4/classic/tests/nan.test.ts +21 -0
- package/node_modules/zod/src/v4/classic/tests/nested-refine.test.ts +168 -0
- package/node_modules/zod/src/v4/classic/tests/nonoptional.test.ts +101 -0
- package/node_modules/zod/src/v4/classic/tests/nullable.test.ts +22 -0
- package/node_modules/zod/src/v4/classic/tests/number.test.ts +401 -0
- package/node_modules/zod/src/v4/classic/tests/object.test.ts +1006 -0
- package/node_modules/zod/src/v4/classic/tests/optin-ladder.test.ts +235 -0
- package/node_modules/zod/src/v4/classic/tests/optional.test.ts +366 -0
- package/node_modules/zod/src/v4/classic/tests/partial.test.ts +497 -0
- package/node_modules/zod/src/v4/classic/tests/pickomit.test.ts +211 -0
- package/node_modules/zod/src/v4/classic/tests/pipe.test.ts +101 -0
- package/node_modules/zod/src/v4/classic/tests/prefault.test.ts +144 -0
- package/node_modules/zod/src/v4/classic/tests/preprocess-types.test.ts +44 -0
- package/node_modules/zod/src/v4/classic/tests/preprocess.test.ts +351 -0
- package/node_modules/zod/src/v4/classic/tests/primitive.test.ts +175 -0
- package/node_modules/zod/src/v4/classic/tests/promise.test.ts +81 -0
- package/node_modules/zod/src/v4/classic/tests/properties.test.ts +141 -0
- package/node_modules/zod/src/v4/classic/tests/prototypes.test.ts +23 -0
- package/node_modules/zod/src/v4/classic/tests/readonly.test.ts +184 -0
- package/node_modules/zod/src/v4/classic/tests/record.test.ts +885 -0
- package/node_modules/zod/src/v4/classic/tests/recursive-types.test.ts +707 -0
- package/node_modules/zod/src/v4/classic/tests/redos.test.ts +68 -0
- package/node_modules/zod/src/v4/classic/tests/refine.test.ts +693 -0
- package/node_modules/zod/src/v4/classic/tests/registries.test.ts +243 -0
- package/node_modules/zod/src/v4/classic/tests/set.test.ts +190 -0
- package/node_modules/zod/src/v4/classic/tests/standard-schema.test.ts +153 -0
- package/node_modules/zod/src/v4/classic/tests/string-formats.test.ts +125 -0
- package/node_modules/zod/src/v4/classic/tests/string.test.ts +1440 -0
- package/node_modules/zod/src/v4/classic/tests/stringbool.test.ts +124 -0
- package/node_modules/zod/src/v4/classic/tests/template-literal.test.ts +812 -0
- package/node_modules/zod/src/v4/classic/tests/to-json-schema-methods.test.ts +438 -0
- package/node_modules/zod/src/v4/classic/tests/to-json-schema.test.ts +4733 -0
- package/node_modules/zod/src/v4/classic/tests/transform.test.ts +378 -0
- package/node_modules/zod/src/v4/classic/tests/tuple.test.ts +511 -0
- package/node_modules/zod/src/v4/classic/tests/union.test.ts +310 -0
- package/node_modules/zod/src/v4/classic/tests/url.test.ts +13 -0
- package/node_modules/zod/src/v4/classic/tests/validate.test.ts +645 -0
- package/node_modules/zod/src/v4/classic/tests/validations.test.ts +283 -0
- package/node_modules/zod/src/v4/classic/tests/void.test.ts +12 -0
- package/node_modules/zod/src/v4/core/api.ts +1875 -0
- package/node_modules/zod/src/v4/core/checks.ts +1266 -0
- package/node_modules/zod/src/v4/core/compile.ts +2313 -0
- package/node_modules/zod/src/v4/core/core.ts +230 -0
- package/node_modules/zod/src/v4/core/doc.ts +52 -0
- package/node_modules/zod/src/v4/core/errors.ts +543 -0
- package/node_modules/zod/src/v4/core/index.ts +19 -0
- package/node_modules/zod/src/v4/core/json-schema-generator.ts +131 -0
- package/node_modules/zod/src/v4/core/json-schema-processors.ts +934 -0
- package/node_modules/zod/src/v4/core/json-schema.ts +148 -0
- package/node_modules/zod/src/v4/core/memoizer.ts +331 -0
- package/node_modules/zod/src/v4/core/parse.ts +309 -0
- package/node_modules/zod/src/v4/core/regexes.ts +217 -0
- package/node_modules/zod/src/v4/core/registries.ts +105 -0
- package/node_modules/zod/src/v4/core/schemas.ts +5192 -0
- package/node_modules/zod/src/v4/core/standard-schema.ts +159 -0
- package/node_modules/zod/src/v4/core/tests/cached.test.ts +54 -0
- package/node_modules/zod/src/v4/core/tests/compile-differential.test.ts +1004 -0
- package/node_modules/zod/src/v4/core/tests/compile-generative.test.ts +445 -0
- package/node_modules/zod/src/v4/core/tests/compile-global.test.ts +89 -0
- package/node_modules/zod/src/v4/core/tests/compile-off-by-default.test.ts +26 -0
- package/node_modules/zod/src/v4/core/tests/compile-stats.test.ts +34 -0
- package/node_modules/zod/src/v4/core/tests/compile.test.ts +2036 -0
- package/node_modules/zod/src/v4/core/tests/email-regex.test.ts +140 -0
- package/node_modules/zod/src/v4/core/tests/extend.test.ts +59 -0
- package/node_modules/zod/src/v4/core/tests/index.test.ts +46 -0
- package/node_modules/zod/src/v4/core/tests/locales/be.test.ts +124 -0
- package/node_modules/zod/src/v4/core/tests/locales/el.test.ts +214 -0
- package/node_modules/zod/src/v4/core/tests/locales/en.test.ts +22 -0
- package/node_modules/zod/src/v4/core/tests/locales/es.test.ts +180 -0
- package/node_modules/zod/src/v4/core/tests/locales/fr.test.ts +72 -0
- package/node_modules/zod/src/v4/core/tests/locales/he.test.ts +379 -0
- package/node_modules/zod/src/v4/core/tests/locales/hr.test.ts +163 -0
- package/node_modules/zod/src/v4/core/tests/locales/nl.test.ts +46 -0
- package/node_modules/zod/src/v4/core/tests/locales/parity.test.ts +42 -0
- package/node_modules/zod/src/v4/core/tests/locales/ru.test.ts +128 -0
- package/node_modules/zod/src/v4/core/tests/locales/tg.test.ts +85 -0
- package/node_modules/zod/src/v4/core/tests/locales/tk.test.ts +69 -0
- package/node_modules/zod/src/v4/core/tests/locales/tr.test.ts +69 -0
- package/node_modules/zod/src/v4/core/tests/locales/uz.test.ts +105 -0
- package/node_modules/zod/src/v4/core/tests/polyfill-collision.test.ts +46 -0
- package/node_modules/zod/src/v4/core/tests/record-constructor.test.ts +125 -0
- package/node_modules/zod/src/v4/core/tests/recursive-tuples.test.ts +45 -0
- package/node_modules/zod/src/v4/core/tests/treeshake.test.ts +51 -0
- package/node_modules/zod/src/v4/core/tests/url-no-canparse.test.ts +133 -0
- package/node_modules/zod/src/v4/core/to-json-schema.ts +857 -0
- package/node_modules/zod/src/v4/core/util.ts +1280 -0
- package/node_modules/zod/src/v4/core/versions.ts +5 -0
- package/node_modules/zod/src/v4/core/visit.ts +193 -0
- package/node_modules/zod/src/v4/core/zsf.ts +323 -0
- package/node_modules/zod/src/v4/index.ts +4 -0
- package/node_modules/zod/src/v4/locales/ar.ts +120 -0
- package/node_modules/zod/src/v4/locales/az.ts +116 -0
- package/node_modules/zod/src/v4/locales/be.ts +188 -0
- package/node_modules/zod/src/v4/locales/bg.ts +133 -0
- package/node_modules/zod/src/v4/locales/bn.ts +123 -0
- package/node_modules/zod/src/v4/locales/ca.ts +121 -0
- package/node_modules/zod/src/v4/locales/ckb.ts +140 -0
- package/node_modules/zod/src/v4/locales/cs.ts +123 -0
- package/node_modules/zod/src/v4/locales/da.ts +128 -0
- package/node_modules/zod/src/v4/locales/de.ts +121 -0
- package/node_modules/zod/src/v4/locales/el.ts +124 -0
- package/node_modules/zod/src/v4/locales/en.ts +136 -0
- package/node_modules/zod/src/v4/locales/eo.ts +123 -0
- package/node_modules/zod/src/v4/locales/es.ts +145 -0
- package/node_modules/zod/src/v4/locales/fa.ts +131 -0
- package/node_modules/zod/src/v4/locales/fi.ts +126 -0
- package/node_modules/zod/src/v4/locales/fr-CA.ts +121 -0
- package/node_modules/zod/src/v4/locales/fr.ts +139 -0
- package/node_modules/zod/src/v4/locales/gu.ts +123 -0
- package/node_modules/zod/src/v4/locales/he.ts +253 -0
- package/node_modules/zod/src/v4/locales/hi.ts +126 -0
- package/node_modules/zod/src/v4/locales/hr.ts +135 -0
- package/node_modules/zod/src/v4/locales/hu.ts +122 -0
- package/node_modules/zod/src/v4/locales/hy.ts +175 -0
- package/node_modules/zod/src/v4/locales/id.ts +120 -0
- package/node_modules/zod/src/v4/locales/index.ts +63 -0
- package/node_modules/zod/src/v4/locales/is.ts +124 -0
- package/node_modules/zod/src/v4/locales/it.ts +121 -0
- package/node_modules/zod/src/v4/locales/ja.ts +119 -0
- package/node_modules/zod/src/v4/locales/ka.ts +128 -0
- package/node_modules/zod/src/v4/locales/kh.ts +7 -0
- package/node_modules/zod/src/v4/locales/km.ts +124 -0
- package/node_modules/zod/src/v4/locales/kn.ts +126 -0
- package/node_modules/zod/src/v4/locales/ko.ts +126 -0
- package/node_modules/zod/src/v4/locales/lt.ts +243 -0
- package/node_modules/zod/src/v4/locales/mk.ts +123 -0
- package/node_modules/zod/src/v4/locales/ms.ts +120 -0
- package/node_modules/zod/src/v4/locales/ne.ts +128 -0
- package/node_modules/zod/src/v4/locales/nl.ts +126 -0
- package/node_modules/zod/src/v4/locales/nn.ts +121 -0
- package/node_modules/zod/src/v4/locales/no.ts +121 -0
- package/node_modules/zod/src/v4/locales/ota.ts +122 -0
- package/node_modules/zod/src/v4/locales/pl.ts +123 -0
- package/node_modules/zod/src/v4/locales/ps.ts +131 -0
- package/node_modules/zod/src/v4/locales/pt-BR.ts +161 -0
- package/node_modules/zod/src/v4/locales/pt.ts +159 -0
- package/node_modules/zod/src/v4/locales/ro.ts +132 -0
- package/node_modules/zod/src/v4/locales/ru.ts +188 -0
- package/node_modules/zod/src/v4/locales/sk.ts +123 -0
- package/node_modules/zod/src/v4/locales/sl.ts +123 -0
- package/node_modules/zod/src/v4/locales/sv.ts +124 -0
- package/node_modules/zod/src/v4/locales/ta.ts +123 -0
- package/node_modules/zod/src/v4/locales/tg.ts +135 -0
- package/node_modules/zod/src/v4/locales/th.ts +124 -0
- package/node_modules/zod/src/v4/locales/tk.ts +113 -0
- package/node_modules/zod/src/v4/locales/tr.ts +116 -0
- package/node_modules/zod/src/v4/locales/ua.ts +7 -0
- package/node_modules/zod/src/v4/locales/uk.ts +122 -0
- package/node_modules/zod/src/v4/locales/ur.ts +124 -0
- package/node_modules/zod/src/v4/locales/uz.ts +120 -0
- package/node_modules/zod/src/v4/locales/vi.ts +122 -0
- package/node_modules/zod/src/v4/locales/yo.ts +129 -0
- package/node_modules/zod/src/v4/locales/zh-CN.ts +121 -0
- package/node_modules/zod/src/v4/locales/zh-TW.ts +120 -0
- package/node_modules/zod/src/v4/mini/checks.ts +33 -0
- package/node_modules/zod/src/v4/mini/coerce.ts +27 -0
- package/node_modules/zod/src/v4/mini/deep-partial.ts +73 -0
- package/node_modules/zod/src/v4/mini/external.ts +52 -0
- package/node_modules/zod/src/v4/mini/in-out.ts +45 -0
- package/node_modules/zod/src/v4/mini/index.ts +3 -0
- package/node_modules/zod/src/v4/mini/iso.ts +66 -0
- package/node_modules/zod/src/v4/mini/parse.ts +16 -0
- package/node_modules/zod/src/v4/mini/schemas.ts +2051 -0
- package/node_modules/zod/src/v4/mini/tests/apply.test.ts +57 -0
- package/node_modules/zod/src/v4/mini/tests/assignability.test.ts +190 -0
- package/node_modules/zod/src/v4/mini/tests/brand.test.ts +94 -0
- package/node_modules/zod/src/v4/mini/tests/checks.test.ts +158 -0
- package/node_modules/zod/src/v4/mini/tests/codec.test.ts +582 -0
- package/node_modules/zod/src/v4/mini/tests/computed.test.ts +30 -0
- package/node_modules/zod/src/v4/mini/tests/cyclic-data.test.ts +84 -0
- package/node_modules/zod/src/v4/mini/tests/deep-partial.test.ts +54 -0
- package/node_modules/zod/src/v4/mini/tests/error.test.ts +41 -0
- package/node_modules/zod/src/v4/mini/tests/functions.test.ts +14 -0
- package/node_modules/zod/src/v4/mini/tests/index.test.ts +1146 -0
- package/node_modules/zod/src/v4/mini/tests/number.test.ts +109 -0
- package/node_modules/zod/src/v4/mini/tests/object.test.ts +292 -0
- package/node_modules/zod/src/v4/mini/tests/prototypes.test.ts +43 -0
- package/node_modules/zod/src/v4/mini/tests/recursive-types.test.ts +341 -0
- package/node_modules/zod/src/v4/mini/tests/standard-schema.test.ts +50 -0
- package/node_modules/zod/src/v4/mini/tests/string.test.ts +396 -0
- package/node_modules/zod/src/v4-mini/index.ts +3 -0
- package/node_modules/zod/v3/ZodError.cjs +167 -0
- package/node_modules/zod/v3/ZodError.d.cts +164 -0
- package/node_modules/zod/v3/ZodError.d.ts +164 -0
- package/node_modules/zod/v3/ZodError.js +143 -0
- package/node_modules/zod/v3/errors.cjs +36 -0
- package/node_modules/zod/v3/errors.d.cts +5 -0
- package/node_modules/zod/v3/errors.d.ts +5 -0
- package/node_modules/zod/v3/errors.js +9 -0
- package/node_modules/zod/v3/external.cjs +41 -0
- package/node_modules/zod/v3/external.d.cts +6 -0
- package/node_modules/zod/v3/external.d.ts +6 -0
- package/node_modules/zod/v3/external.js +6 -0
- package/node_modules/zod/v3/helpers/enumUtil.cjs +21 -0
- package/node_modules/zod/v3/helpers/enumUtil.d.cts +8 -0
- package/node_modules/zod/v3/helpers/enumUtil.d.ts +8 -0
- package/node_modules/zod/v3/helpers/enumUtil.js +1 -0
- package/node_modules/zod/v3/helpers/errorUtil.cjs +28 -0
- package/node_modules/zod/v3/helpers/errorUtil.d.cts +9 -0
- package/node_modules/zod/v3/helpers/errorUtil.d.ts +9 -0
- package/node_modules/zod/v3/helpers/errorUtil.js +6 -0
- package/node_modules/zod/v3/helpers/parseUtil.cjs +143 -0
- package/node_modules/zod/v3/helpers/parseUtil.d.cts +78 -0
- package/node_modules/zod/v3/helpers/parseUtil.d.ts +78 -0
- package/node_modules/zod/v3/helpers/parseUtil.js +109 -0
- package/node_modules/zod/v3/helpers/partialUtil.cjs +21 -0
- package/node_modules/zod/v3/helpers/partialUtil.d.cts +8 -0
- package/node_modules/zod/v3/helpers/partialUtil.d.ts +8 -0
- package/node_modules/zod/v3/helpers/partialUtil.js +1 -0
- package/node_modules/zod/v3/helpers/typeAliases.cjs +21 -0
- package/node_modules/zod/v3/helpers/typeAliases.d.cts +2 -0
- package/node_modules/zod/v3/helpers/typeAliases.d.ts +2 -0
- package/node_modules/zod/v3/helpers/typeAliases.js +1 -0
- package/node_modules/zod/v3/helpers/util.cjs +156 -0
- package/node_modules/zod/v3/helpers/util.d.cts +85 -0
- package/node_modules/zod/v3/helpers/util.d.ts +85 -0
- package/node_modules/zod/v3/helpers/util.js +133 -0
- package/node_modules/zod/v3/index.cjs +52 -0
- package/node_modules/zod/v3/index.d.cts +4 -0
- package/node_modules/zod/v3/index.d.ts +4 -0
- package/node_modules/zod/v3/index.js +4 -0
- package/node_modules/zod/v3/locales/en.cjs +112 -0
- package/node_modules/zod/v3/locales/en.d.cts +3 -0
- package/node_modules/zod/v3/locales/en.d.ts +3 -0
- package/node_modules/zod/v3/locales/en.js +109 -0
- package/node_modules/zod/v3/package.json +7 -0
- package/node_modules/zod/v3/standard-schema.cjs +21 -0
- package/node_modules/zod/v3/standard-schema.d.cts +102 -0
- package/node_modules/zod/v3/standard-schema.d.ts +102 -0
- package/node_modules/zod/v3/standard-schema.js +1 -0
- package/node_modules/zod/v3/types.cjs +3798 -0
- package/node_modules/zod/v3/types.d.cts +1034 -0
- package/node_modules/zod/v3/types.d.ts +1034 -0
- package/node_modules/zod/v3/types.js +3697 -0
- package/node_modules/zod/v4/classic/checks.cjs +53 -0
- package/node_modules/zod/v4/classic/checks.d.cts +1 -0
- package/node_modules/zod/v4/classic/checks.d.ts +1 -0
- package/node_modules/zod/v4/classic/checks.js +1 -0
- package/node_modules/zod/v4/classic/coerce.cjs +66 -0
- package/node_modules/zod/v4/classic/coerce.d.cts +17 -0
- package/node_modules/zod/v4/classic/coerce.d.ts +17 -0
- package/node_modules/zod/v4/classic/coerce.js +17 -0
- package/node_modules/zod/v4/classic/compat.cjs +80 -0
- package/node_modules/zod/v4/classic/compat.d.cts +48 -0
- package/node_modules/zod/v4/classic/compat.d.ts +48 -0
- package/node_modules/zod/v4/classic/compat.js +31 -0
- package/node_modules/zod/v4/classic/deep-partial.cjs +58 -0
- package/node_modules/zod/v4/classic/deep-partial.d.cts +14 -0
- package/node_modules/zod/v4/classic/deep-partial.d.ts +14 -0
- package/node_modules/zod/v4/classic/deep-partial.js +13 -0
- package/node_modules/zod/v4/classic/errors.cjs +102 -0
- package/node_modules/zod/v4/classic/errors.d.cts +29 -0
- package/node_modules/zod/v4/classic/errors.d.ts +29 -0
- package/node_modules/zod/v4/classic/errors.js +57 -0
- package/node_modules/zod/v4/classic/external.cjs +97 -0
- package/node_modules/zod/v4/classic/external.d.cts +19 -0
- package/node_modules/zod/v4/classic/external.d.ts +19 -0
- package/node_modules/zod/v4/classic/external.js +17 -0
- package/node_modules/zod/v4/classic/from-json-schema.cjs +926 -0
- package/node_modules/zod/v4/classic/from-json-schema.d.cts +12 -0
- package/node_modules/zod/v4/classic/from-json-schema.d.ts +12 -0
- package/node_modules/zod/v4/classic/from-json-schema.js +881 -0
- package/node_modules/zod/v4/classic/in-out.cjs +82 -0
- package/node_modules/zod/v4/classic/in-out.d.cts +8 -0
- package/node_modules/zod/v4/classic/in-out.d.ts +8 -0
- package/node_modules/zod/v4/classic/in-out.js +36 -0
- package/node_modules/zod/v4/classic/index.cjs +52 -0
- package/node_modules/zod/v4/classic/index.d.cts +3 -0
- package/node_modules/zod/v4/classic/index.d.ts +3 -0
- package/node_modules/zod/v4/classic/index.js +4 -0
- package/node_modules/zod/v4/classic/iso.cjs +68 -0
- package/node_modules/zod/v4/classic/iso.d.cts +7 -0
- package/node_modules/zod/v4/classic/iso.d.ts +7 -0
- package/node_modules/zod/v4/classic/iso.js +15 -0
- package/node_modules/zod/v4/classic/package.json +7 -0
- package/node_modules/zod/v4/classic/parse.cjs +63 -0
- package/node_modules/zod/v4/classic/parse.d.cts +44 -0
- package/node_modules/zod/v4/classic/parse.d.ts +44 -0
- package/node_modules/zod/v4/classic/parse.js +16 -0
- package/node_modules/zod/v4/classic/schemas.cjs +1676 -0
- package/node_modules/zod/v4/classic/schemas.d.cts +816 -0
- package/node_modules/zod/v4/classic/schemas.d.ts +816 -0
- package/node_modules/zod/v4/classic/schemas.js +1535 -0
- package/node_modules/zod/v4/core/api.cjs +1269 -0
- package/node_modules/zod/v4/core/api.d.cts +335 -0
- package/node_modules/zod/v4/core/api.d.ts +335 -0
- package/node_modules/zod/v4/core/api.js +1107 -0
- package/node_modules/zod/v4/core/checks.cjs +561 -0
- package/node_modules/zod/v4/core/checks.d.cts +294 -0
- package/node_modules/zod/v4/core/checks.d.ts +294 -0
- package/node_modules/zod/v4/core/checks.js +516 -0
- package/node_modules/zod/v4/core/compile.cjs +1908 -0
- package/node_modules/zod/v4/core/compile.d.cts +65 -0
- package/node_modules/zod/v4/core/compile.d.ts +65 -0
- package/node_modules/zod/v4/core/compile.js +1858 -0
- package/node_modules/zod/v4/core/core.cjs +167 -0
- package/node_modules/zod/v4/core/core.d.cts +82 -0
- package/node_modules/zod/v4/core/core.d.ts +82 -0
- package/node_modules/zod/v4/core/core.js +141 -0
- package/node_modules/zod/v4/core/doc.cjs +61 -0
- package/node_modules/zod/v4/core/doc.d.cts +16 -0
- package/node_modules/zod/v4/core/doc.d.ts +16 -0
- package/node_modules/zod/v4/core/doc.js +38 -0
- package/node_modules/zod/v4/core/errors.cjs +317 -0
- package/node_modules/zod/v4/core/errors.d.cts +225 -0
- package/node_modules/zod/v4/core/errors.d.ts +225 -0
- package/node_modules/zod/v4/core/errors.js +267 -0
- package/node_modules/zod/v4/core/index.cjs +70 -0
- package/node_modules/zod/v4/core/index.d.cts +19 -0
- package/node_modules/zod/v4/core/index.d.ts +19 -0
- package/node_modules/zod/v4/core/index.js +19 -0
- package/node_modules/zod/v4/core/json-schema-generator.cjs +122 -0
- package/node_modules/zod/v4/core/json-schema-generator.d.cts +65 -0
- package/node_modules/zod/v4/core/json-schema-generator.d.ts +65 -0
- package/node_modules/zod/v4/core/json-schema-generator.js +99 -0
- package/node_modules/zod/v4/core/json-schema-processors.cjs +929 -0
- package/node_modules/zod/v4/core/json-schema-processors.d.cts +65 -0
- package/node_modules/zod/v4/core/json-schema-processors.d.ts +65 -0
- package/node_modules/zod/v4/core/json-schema-processors.js +843 -0
- package/node_modules/zod/v4/core/json-schema.cjs +21 -0
- package/node_modules/zod/v4/core/json-schema.d.cts +89 -0
- package/node_modules/zod/v4/core/json-schema.d.ts +89 -0
- package/node_modules/zod/v4/core/json-schema.js +1 -0
- package/node_modules/zod/v4/core/memoizer.cjs +360 -0
- package/node_modules/zod/v4/core/memoizer.d.cts +15 -0
- package/node_modules/zod/v4/core/memoizer.d.ts +15 -0
- package/node_modules/zod/v4/core/memoizer.js +311 -0
- package/node_modules/zod/v4/core/package.json +7 -0
- package/node_modules/zod/v4/core/parse.cjs +233 -0
- package/node_modules/zod/v4/core/parse.d.cts +52 -0
- package/node_modules/zod/v4/core/parse.d.ts +52 -0
- package/node_modules/zod/v4/core/parse.js +175 -0
- package/node_modules/zod/v4/core/regexes.cjs +209 -0
- package/node_modules/zod/v4/core/regexes.d.cts +90 -0
- package/node_modules/zod/v4/core/regexes.d.ts +90 -0
- package/node_modules/zod/v4/core/regexes.js +156 -0
- package/node_modules/zod/v4/core/registries.cjs +75 -0
- package/node_modules/zod/v4/core/registries.d.cts +35 -0
- package/node_modules/zod/v4/core/registries.d.ts +35 -0
- package/node_modules/zod/v4/core/registries.js +51 -0
- package/node_modules/zod/v4/core/schemas.cjs +2669 -0
- package/node_modules/zod/v4/core/schemas.d.cts +1273 -0
- package/node_modules/zod/v4/core/schemas.d.ts +1273 -0
- package/node_modules/zod/v4/core/schemas.js +2606 -0
- package/node_modules/zod/v4/core/standard-schema.cjs +21 -0
- package/node_modules/zod/v4/core/standard-schema.d.cts +126 -0
- package/node_modules/zod/v4/core/standard-schema.d.ts +126 -0
- package/node_modules/zod/v4/core/standard-schema.js +1 -0
- package/node_modules/zod/v4/core/to-json-schema.cjs +681 -0
- package/node_modules/zod/v4/core/to-json-schema.d.cts +154 -0
- package/node_modules/zod/v4/core/to-json-schema.d.ts +154 -0
- package/node_modules/zod/v4/core/to-json-schema.js +651 -0
- package/node_modules/zod/v4/core/util.cjs +949 -0
- package/node_modules/zod/v4/core/util.d.cts +278 -0
- package/node_modules/zod/v4/core/util.d.ts +278 -0
- package/node_modules/zod/v4/core/util.js +859 -0
- package/node_modules/zod/v4/core/versions.cjs +27 -0
- package/node_modules/zod/v4/core/versions.d.cts +5 -0
- package/node_modules/zod/v4/core/versions.d.ts +5 -0
- package/node_modules/zod/v4/core/versions.js +5 -0
- package/node_modules/zod/v4/core/visit.cjs +214 -0
- package/node_modules/zod/v4/core/visit.d.cts +30 -0
- package/node_modules/zod/v4/core/visit.d.ts +30 -0
- package/node_modules/zod/v4/core/visit.js +169 -0
- package/node_modules/zod/v4/index.cjs +51 -0
- package/node_modules/zod/v4/index.d.cts +3 -0
- package/node_modules/zod/v4/index.d.ts +3 -0
- package/node_modules/zod/v4/index.js +4 -0
- package/node_modules/zod/v4/locales/ar.cjs +138 -0
- package/node_modules/zod/v4/locales/ar.d.cts +5 -0
- package/node_modules/zod/v4/locales/ar.d.ts +4 -0
- package/node_modules/zod/v4/locales/ar.js +111 -0
- package/node_modules/zod/v4/locales/az.cjs +137 -0
- package/node_modules/zod/v4/locales/az.d.cts +5 -0
- package/node_modules/zod/v4/locales/az.d.ts +4 -0
- package/node_modules/zod/v4/locales/az.js +110 -0
- package/node_modules/zod/v4/locales/be.cjs +195 -0
- package/node_modules/zod/v4/locales/be.d.cts +5 -0
- package/node_modules/zod/v4/locales/be.d.ts +4 -0
- package/node_modules/zod/v4/locales/be.js +168 -0
- package/node_modules/zod/v4/locales/bg.cjs +152 -0
- package/node_modules/zod/v4/locales/bg.d.cts +5 -0
- package/node_modules/zod/v4/locales/bg.d.ts +4 -0
- package/node_modules/zod/v4/locales/bg.js +125 -0
- package/node_modules/zod/v4/locales/bn.cjs +140 -0
- package/node_modules/zod/v4/locales/bn.d.cts +5 -0
- package/node_modules/zod/v4/locales/bn.d.ts +4 -0
- package/node_modules/zod/v4/locales/bn.js +113 -0
- package/node_modules/zod/v4/locales/ca.cjs +139 -0
- package/node_modules/zod/v4/locales/ca.d.cts +5 -0
- package/node_modules/zod/v4/locales/ca.d.ts +4 -0
- package/node_modules/zod/v4/locales/ca.js +112 -0
- package/node_modules/zod/v4/locales/ckb.cjs +160 -0
- package/node_modules/zod/v4/locales/ckb.d.cts +5 -0
- package/node_modules/zod/v4/locales/ckb.d.ts +4 -0
- package/node_modules/zod/v4/locales/ckb.js +133 -0
- package/node_modules/zod/v4/locales/cs.cjs +143 -0
- package/node_modules/zod/v4/locales/cs.d.cts +5 -0
- package/node_modules/zod/v4/locales/cs.d.ts +4 -0
- package/node_modules/zod/v4/locales/cs.js +116 -0
- package/node_modules/zod/v4/locales/da.cjs +147 -0
- package/node_modules/zod/v4/locales/da.d.cts +5 -0
- package/node_modules/zod/v4/locales/da.d.ts +4 -0
- package/node_modules/zod/v4/locales/da.js +120 -0
- package/node_modules/zod/v4/locales/de.cjs +140 -0
- package/node_modules/zod/v4/locales/de.d.cts +5 -0
- package/node_modules/zod/v4/locales/de.d.ts +4 -0
- package/node_modules/zod/v4/locales/de.js +113 -0
- package/node_modules/zod/v4/locales/el.cjs +139 -0
- package/node_modules/zod/v4/locales/el.d.cts +5 -0
- package/node_modules/zod/v4/locales/el.d.ts +4 -0
- package/node_modules/zod/v4/locales/el.js +112 -0
- package/node_modules/zod/v4/locales/en.cjs +152 -0
- package/node_modules/zod/v4/locales/en.d.cts +5 -0
- package/node_modules/zod/v4/locales/en.d.ts +4 -0
- package/node_modules/zod/v4/locales/en.js +125 -0
- package/node_modules/zod/v4/locales/eo.cjs +141 -0
- package/node_modules/zod/v4/locales/eo.d.cts +5 -0
- package/node_modules/zod/v4/locales/eo.d.ts +4 -0
- package/node_modules/zod/v4/locales/eo.js +114 -0
- package/node_modules/zod/v4/locales/es.cjs +163 -0
- package/node_modules/zod/v4/locales/es.d.cts +5 -0
- package/node_modules/zod/v4/locales/es.d.ts +4 -0
- package/node_modules/zod/v4/locales/es.js +136 -0
- package/node_modules/zod/v4/locales/fa.cjs +146 -0
- package/node_modules/zod/v4/locales/fa.d.cts +5 -0
- package/node_modules/zod/v4/locales/fa.d.ts +4 -0
- package/node_modules/zod/v4/locales/fa.js +119 -0
- package/node_modules/zod/v4/locales/fi.cjs +144 -0
- package/node_modules/zod/v4/locales/fi.d.cts +5 -0
- package/node_modules/zod/v4/locales/fi.d.ts +4 -0
- package/node_modules/zod/v4/locales/fi.js +117 -0
- package/node_modules/zod/v4/locales/fr-CA.cjs +139 -0
- package/node_modules/zod/v4/locales/fr-CA.d.cts +5 -0
- package/node_modules/zod/v4/locales/fr-CA.d.ts +4 -0
- package/node_modules/zod/v4/locales/fr-CA.js +112 -0
- package/node_modules/zod/v4/locales/fr.cjs +156 -0
- package/node_modules/zod/v4/locales/fr.d.cts +5 -0
- package/node_modules/zod/v4/locales/fr.d.ts +4 -0
- package/node_modules/zod/v4/locales/fr.js +129 -0
- package/node_modules/zod/v4/locales/gu.cjs +140 -0
- package/node_modules/zod/v4/locales/gu.d.cts +5 -0
- package/node_modules/zod/v4/locales/gu.d.ts +4 -0
- package/node_modules/zod/v4/locales/gu.js +113 -0
- package/node_modules/zod/v4/locales/he.cjs +248 -0
- package/node_modules/zod/v4/locales/he.d.cts +5 -0
- package/node_modules/zod/v4/locales/he.d.ts +4 -0
- package/node_modules/zod/v4/locales/he.js +221 -0
- package/node_modules/zod/v4/locales/hi.cjs +138 -0
- package/node_modules/zod/v4/locales/hi.d.cts +5 -0
- package/node_modules/zod/v4/locales/hi.d.ts +4 -0
- package/node_modules/zod/v4/locales/hi.js +111 -0
- package/node_modules/zod/v4/locales/hr.cjs +153 -0
- package/node_modules/zod/v4/locales/hr.d.cts +5 -0
- package/node_modules/zod/v4/locales/hr.d.ts +4 -0
- package/node_modules/zod/v4/locales/hr.js +126 -0
- package/node_modules/zod/v4/locales/hu.cjs +140 -0
- package/node_modules/zod/v4/locales/hu.d.cts +5 -0
- package/node_modules/zod/v4/locales/hu.d.ts +4 -0
- package/node_modules/zod/v4/locales/hu.js +113 -0
- package/node_modules/zod/v4/locales/hy.cjs +185 -0
- package/node_modules/zod/v4/locales/hy.d.cts +5 -0
- package/node_modules/zod/v4/locales/hy.d.ts +4 -0
- package/node_modules/zod/v4/locales/hy.js +158 -0
- package/node_modules/zod/v4/locales/id.cjs +138 -0
- package/node_modules/zod/v4/locales/id.d.cts +5 -0
- package/node_modules/zod/v4/locales/id.d.ts +4 -0
- package/node_modules/zod/v4/locales/id.js +111 -0
- package/node_modules/zod/v4/locales/index.cjs +152 -0
- package/node_modules/zod/v4/locales/index.d.cts +63 -0
- package/node_modules/zod/v4/locales/index.d.ts +63 -0
- package/node_modules/zod/v4/locales/index.js +63 -0
- package/node_modules/zod/v4/locales/is.cjs +141 -0
- package/node_modules/zod/v4/locales/is.d.cts +5 -0
- package/node_modules/zod/v4/locales/is.d.ts +4 -0
- package/node_modules/zod/v4/locales/is.js +114 -0
- package/node_modules/zod/v4/locales/it.cjs +140 -0
- package/node_modules/zod/v4/locales/it.d.cts +5 -0
- package/node_modules/zod/v4/locales/it.d.ts +4 -0
- package/node_modules/zod/v4/locales/it.js +113 -0
- package/node_modules/zod/v4/locales/ja.cjs +139 -0
- package/node_modules/zod/v4/locales/ja.d.cts +5 -0
- package/node_modules/zod/v4/locales/ja.d.ts +4 -0
- package/node_modules/zod/v4/locales/ja.js +112 -0
- package/node_modules/zod/v4/locales/ka.cjs +144 -0
- package/node_modules/zod/v4/locales/ka.d.cts +5 -0
- package/node_modules/zod/v4/locales/ka.d.ts +4 -0
- package/node_modules/zod/v4/locales/ka.js +117 -0
- package/node_modules/zod/v4/locales/kh.cjs +12 -0
- package/node_modules/zod/v4/locales/kh.d.cts +5 -0
- package/node_modules/zod/v4/locales/kh.d.ts +5 -0
- package/node_modules/zod/v4/locales/kh.js +5 -0
- package/node_modules/zod/v4/locales/km.cjs +142 -0
- package/node_modules/zod/v4/locales/km.d.cts +5 -0
- package/node_modules/zod/v4/locales/km.d.ts +4 -0
- package/node_modules/zod/v4/locales/km.js +115 -0
- package/node_modules/zod/v4/locales/kn.cjs +143 -0
- package/node_modules/zod/v4/locales/kn.d.cts +5 -0
- package/node_modules/zod/v4/locales/kn.d.ts +4 -0
- package/node_modules/zod/v4/locales/kn.js +116 -0
- package/node_modules/zod/v4/locales/ko.cjs +143 -0
- package/node_modules/zod/v4/locales/ko.d.cts +5 -0
- package/node_modules/zod/v4/locales/ko.d.ts +4 -0
- package/node_modules/zod/v4/locales/ko.js +116 -0
- package/node_modules/zod/v4/locales/lt.cjs +234 -0
- package/node_modules/zod/v4/locales/lt.d.cts +5 -0
- package/node_modules/zod/v4/locales/lt.d.ts +4 -0
- package/node_modules/zod/v4/locales/lt.js +207 -0
- package/node_modules/zod/v4/locales/mk.cjs +141 -0
- package/node_modules/zod/v4/locales/mk.d.cts +5 -0
- package/node_modules/zod/v4/locales/mk.d.ts +4 -0
- package/node_modules/zod/v4/locales/mk.js +114 -0
- package/node_modules/zod/v4/locales/ms.cjs +139 -0
- package/node_modules/zod/v4/locales/ms.d.cts +5 -0
- package/node_modules/zod/v4/locales/ms.d.ts +4 -0
- package/node_modules/zod/v4/locales/ms.js +112 -0
- package/node_modules/zod/v4/locales/ne.cjs +138 -0
- package/node_modules/zod/v4/locales/ne.d.cts +5 -0
- package/node_modules/zod/v4/locales/ne.d.ts +4 -0
- package/node_modules/zod/v4/locales/ne.js +111 -0
- package/node_modules/zod/v4/locales/nl.cjs +142 -0
- package/node_modules/zod/v4/locales/nl.d.cts +5 -0
- package/node_modules/zod/v4/locales/nl.d.ts +4 -0
- package/node_modules/zod/v4/locales/nl.js +115 -0
- package/node_modules/zod/v4/locales/nn.cjs +140 -0
- package/node_modules/zod/v4/locales/nn.d.cts +5 -0
- package/node_modules/zod/v4/locales/nn.d.ts +4 -0
- package/node_modules/zod/v4/locales/nn.js +113 -0
- package/node_modules/zod/v4/locales/no.cjs +140 -0
- package/node_modules/zod/v4/locales/no.d.cts +5 -0
- package/node_modules/zod/v4/locales/no.d.ts +4 -0
- package/node_modules/zod/v4/locales/no.js +113 -0
- package/node_modules/zod/v4/locales/ota.cjs +141 -0
- package/node_modules/zod/v4/locales/ota.d.cts +5 -0
- package/node_modules/zod/v4/locales/ota.d.ts +4 -0
- package/node_modules/zod/v4/locales/ota.js +114 -0
- package/node_modules/zod/v4/locales/package.json +7 -0
- package/node_modules/zod/v4/locales/pl.cjs +141 -0
- package/node_modules/zod/v4/locales/pl.d.cts +5 -0
- package/node_modules/zod/v4/locales/pl.d.ts +4 -0
- package/node_modules/zod/v4/locales/pl.js +114 -0
- package/node_modules/zod/v4/locales/ps.cjs +146 -0
- package/node_modules/zod/v4/locales/ps.d.cts +5 -0
- package/node_modules/zod/v4/locales/ps.d.ts +4 -0
- package/node_modules/zod/v4/locales/ps.js +119 -0
- package/node_modules/zod/v4/locales/pt-BR.cjs +170 -0
- package/node_modules/zod/v4/locales/pt-BR.d.cts +5 -0
- package/node_modules/zod/v4/locales/pt-BR.d.ts +4 -0
- package/node_modules/zod/v4/locales/pt-BR.js +143 -0
- package/node_modules/zod/v4/locales/pt.cjs +169 -0
- package/node_modules/zod/v4/locales/pt.d.cts +5 -0
- package/node_modules/zod/v4/locales/pt.d.ts +4 -0
- package/node_modules/zod/v4/locales/pt.js +142 -0
- package/node_modules/zod/v4/locales/ro.cjs +149 -0
- package/node_modules/zod/v4/locales/ro.d.cts +5 -0
- package/node_modules/zod/v4/locales/ro.d.ts +4 -0
- package/node_modules/zod/v4/locales/ro.js +122 -0
- package/node_modules/zod/v4/locales/ru.cjs +195 -0
- package/node_modules/zod/v4/locales/ru.d.cts +5 -0
- package/node_modules/zod/v4/locales/ru.d.ts +4 -0
- package/node_modules/zod/v4/locales/ru.js +168 -0
- package/node_modules/zod/v4/locales/sk.cjs +143 -0
- package/node_modules/zod/v4/locales/sk.d.cts +5 -0
- package/node_modules/zod/v4/locales/sk.d.ts +4 -0
- package/node_modules/zod/v4/locales/sk.js +116 -0
- package/node_modules/zod/v4/locales/sl.cjs +141 -0
- package/node_modules/zod/v4/locales/sl.d.cts +5 -0
- package/node_modules/zod/v4/locales/sl.d.ts +4 -0
- package/node_modules/zod/v4/locales/sl.js +114 -0
- package/node_modules/zod/v4/locales/sv.cjs +142 -0
- package/node_modules/zod/v4/locales/sv.d.cts +5 -0
- package/node_modules/zod/v4/locales/sv.d.ts +4 -0
- package/node_modules/zod/v4/locales/sv.js +115 -0
- package/node_modules/zod/v4/locales/ta.cjs +142 -0
- package/node_modules/zod/v4/locales/ta.d.cts +5 -0
- package/node_modules/zod/v4/locales/ta.d.ts +4 -0
- package/node_modules/zod/v4/locales/ta.js +115 -0
- package/node_modules/zod/v4/locales/tg.cjs +144 -0
- package/node_modules/zod/v4/locales/tg.d.cts +5 -0
- package/node_modules/zod/v4/locales/tg.d.ts +4 -0
- package/node_modules/zod/v4/locales/tg.js +117 -0
- package/node_modules/zod/v4/locales/th.cjs +142 -0
- package/node_modules/zod/v4/locales/th.d.cts +5 -0
- package/node_modules/zod/v4/locales/th.d.ts +4 -0
- package/node_modules/zod/v4/locales/th.js +115 -0
- package/node_modules/zod/v4/locales/tk.cjs +134 -0
- package/node_modules/zod/v4/locales/tk.d.cts +5 -0
- package/node_modules/zod/v4/locales/tk.d.ts +4 -0
- package/node_modules/zod/v4/locales/tk.js +107 -0
- package/node_modules/zod/v4/locales/tr.cjs +137 -0
- package/node_modules/zod/v4/locales/tr.d.cts +5 -0
- package/node_modules/zod/v4/locales/tr.d.ts +4 -0
- package/node_modules/zod/v4/locales/tr.js +110 -0
- package/node_modules/zod/v4/locales/ua.cjs +12 -0
- package/node_modules/zod/v4/locales/ua.d.cts +5 -0
- package/node_modules/zod/v4/locales/ua.d.ts +5 -0
- package/node_modules/zod/v4/locales/ua.js +5 -0
- package/node_modules/zod/v4/locales/uk.cjs +140 -0
- package/node_modules/zod/v4/locales/uk.d.cts +5 -0
- package/node_modules/zod/v4/locales/uk.d.ts +4 -0
- package/node_modules/zod/v4/locales/uk.js +113 -0
- package/node_modules/zod/v4/locales/ur.cjs +142 -0
- package/node_modules/zod/v4/locales/ur.d.cts +5 -0
- package/node_modules/zod/v4/locales/ur.d.ts +4 -0
- package/node_modules/zod/v4/locales/ur.js +115 -0
- package/node_modules/zod/v4/locales/uz.cjs +140 -0
- package/node_modules/zod/v4/locales/uz.d.cts +5 -0
- package/node_modules/zod/v4/locales/uz.d.ts +4 -0
- package/node_modules/zod/v4/locales/uz.js +113 -0
- package/node_modules/zod/v4/locales/vi.cjs +140 -0
- package/node_modules/zod/v4/locales/vi.d.cts +5 -0
- package/node_modules/zod/v4/locales/vi.d.ts +4 -0
- package/node_modules/zod/v4/locales/vi.js +113 -0
- package/node_modules/zod/v4/locales/yo.cjs +139 -0
- package/node_modules/zod/v4/locales/yo.d.cts +5 -0
- package/node_modules/zod/v4/locales/yo.d.ts +4 -0
- package/node_modules/zod/v4/locales/yo.js +112 -0
- package/node_modules/zod/v4/locales/zh-CN.cjs +141 -0
- package/node_modules/zod/v4/locales/zh-CN.d.cts +5 -0
- package/node_modules/zod/v4/locales/zh-CN.d.ts +4 -0
- package/node_modules/zod/v4/locales/zh-CN.js +114 -0
- package/node_modules/zod/v4/locales/zh-TW.cjs +139 -0
- package/node_modules/zod/v4/locales/zh-TW.d.cts +5 -0
- package/node_modules/zod/v4/locales/zh-TW.d.ts +4 -0
- package/node_modules/zod/v4/locales/zh-TW.js +112 -0
- package/node_modules/zod/v4/mini/checks.cjs +54 -0
- package/node_modules/zod/v4/mini/checks.d.cts +1 -0
- package/node_modules/zod/v4/mini/checks.d.ts +1 -0
- package/node_modules/zod/v4/mini/checks.js +1 -0
- package/node_modules/zod/v4/mini/coerce.cjs +71 -0
- package/node_modules/zod/v4/mini/coerce.d.cts +7 -0
- package/node_modules/zod/v4/mini/coerce.d.ts +7 -0
- package/node_modules/zod/v4/mini/coerce.js +22 -0
- package/node_modules/zod/v4/mini/deep-partial.cjs +58 -0
- package/node_modules/zod/v4/mini/deep-partial.d.cts +14 -0
- package/node_modules/zod/v4/mini/deep-partial.d.ts +14 -0
- package/node_modules/zod/v4/mini/deep-partial.js +13 -0
- package/node_modules/zod/v4/mini/external.cjs +95 -0
- package/node_modules/zod/v4/mini/external.d.cts +16 -0
- package/node_modules/zod/v4/mini/external.d.ts +16 -0
- package/node_modules/zod/v4/mini/external.js +16 -0
- package/node_modules/zod/v4/mini/in-out.cjs +82 -0
- package/node_modules/zod/v4/mini/in-out.d.cts +8 -0
- package/node_modules/zod/v4/mini/in-out.d.ts +8 -0
- package/node_modules/zod/v4/mini/in-out.js +36 -0
- package/node_modules/zod/v4/mini/index.cjs +51 -0
- package/node_modules/zod/v4/mini/index.d.cts +3 -0
- package/node_modules/zod/v4/mini/index.d.ts +3 -0
- package/node_modules/zod/v4/mini/index.js +3 -0
- package/node_modules/zod/v4/mini/iso.cjs +83 -0
- package/node_modules/zod/v4/mini/iso.d.cts +22 -0
- package/node_modules/zod/v4/mini/iso.d.ts +22 -0
- package/node_modules/zod/v4/mini/iso.js +34 -0
- package/node_modules/zod/v4/mini/package.json +7 -0
- package/node_modules/zod/v4/mini/parse.cjs +37 -0
- package/node_modules/zod/v4/mini/parse.d.cts +1 -0
- package/node_modules/zod/v4/mini/parse.d.ts +1 -0
- package/node_modules/zod/v4/mini/parse.js +1 -0
- package/node_modules/zod/v4/mini/schemas.cjs +1147 -0
- package/node_modules/zod/v4/mini/schemas.d.cts +480 -0
- package/node_modules/zod/v4/mini/schemas.d.ts +480 -0
- package/node_modules/zod/v4/mini/schemas.js +1002 -0
- package/node_modules/zod/v4/package.json +7 -0
- package/node_modules/zod/v4-mini/index.cjs +51 -0
- package/node_modules/zod/v4-mini/index.d.cts +3 -0
- package/node_modules/zod/v4-mini/index.d.ts +3 -0
- package/node_modules/zod/v4-mini/index.js +3 -0
- package/node_modules/zod/v4-mini/package.json +7 -0
- package/package.json +50 -0
|
@@ -0,0 +1,2129 @@
|
|
|
1
|
+
import { duplexPair } from "node:stream";
|
|
2
|
+
import http2 from "node:http2";
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { buildHttp2BridgeForwardUrl, classifyStreamAgainstGoaway, createBridgeBodyReservation, createBridgeRouteBodyLedger, createHttp2BridgeServer, getBridgeBodyReservedBytesForTest, parseCanonicalBridgeRequestPath, resetBridgeBodyReservationsForTest, wrapDuplexChannelAsNodeDuplex, BridgeProcessCapacityError, DEFAULT_HTTP2_BRIDGE_MAX_BUFFERED_READ_BYTES, DEFAULT_HTTP2_BRIDGE_PING_INTERVAL_MS, DEFAULT_HTTP2_BRIDGE_PING_STALL_MS, HTTP2_BRIDGE_ENABLE_PUSH, HTTP2_BRIDGE_HEADER_TABLE_SIZE, HTTP2_BRIDGE_MAX_CONCURRENT_STREAMS, HTTP2_BRIDGE_MAX_DEFLATE_DYNAMIC_TABLE_SIZE, HTTP2_BRIDGE_MAX_HEADER_LIST_PAIRS, HTTP2_BRIDGE_MAX_HEADER_LIST_SIZE, HTTP2_BRIDGE_MAX_PROCESS_BODY_BYTES, HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES, HTTP2_BRIDGE_MAX_SESSION_INVALID_FRAMES, HTTP2_BRIDGE_MAX_SESSION_MEMORY, HTTP2_BRIDGE_MAX_SESSION_REJECTED_STREAMS, HTTP2_BRIDGE_SERVER_OPTIONS, HTTP2_BRIDGE_STREAM_RESET_BURST, HTTP2_BRIDGE_STREAM_RESET_RATE, } from "./http2-bridge-server.js";
|
|
5
|
+
import { createSandboxHttp2BridgeGateway, DEFAULT_SANDBOX_CALLBACK_BRIDGE_MAX_BODY_BYTES, HTTP2_SANDBOX_CALLBACK_BRIDGE_ROUTE_ALLOWLIST, } from "./sandbox-callback-bridge.js";
|
|
6
|
+
/**
|
|
7
|
+
* Unit harness for the host HTTP/2 server and the sandbox HTTP/2 client
|
|
8
|
+
* gateway. Every test connects the pair over one paired in-memory `Duplex`
|
|
9
|
+
* (`node:stream`'s `duplexPair`) — no real TCP socket and no spawned sandbox
|
|
10
|
+
* process. This proves the two halves speak one wire-compatible HTTP/2
|
|
11
|
+
* session with no network in between.
|
|
12
|
+
*/
|
|
13
|
+
const BRIDGE_TOKEN = "test-bridge-token-fixed-length-32";
|
|
14
|
+
/** Wrap one side of a paired `Duplex` as a minimal fake `CommandManagedDuplexChannel`. */
|
|
15
|
+
function fakeChannelFromDuplex(duplex) {
|
|
16
|
+
const dataListeners = [];
|
|
17
|
+
const exitListeners = [];
|
|
18
|
+
duplex.on("data", (chunk) => {
|
|
19
|
+
for (const listener of dataListeners)
|
|
20
|
+
listener(chunk);
|
|
21
|
+
});
|
|
22
|
+
duplex.on("end", () => {
|
|
23
|
+
for (const listener of exitListeners)
|
|
24
|
+
listener({ exitCode: null, transportClosed: true });
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
write: (data) => {
|
|
28
|
+
duplex.write(Buffer.from(data));
|
|
29
|
+
},
|
|
30
|
+
onData: (listener) => {
|
|
31
|
+
dataListeners.push(listener);
|
|
32
|
+
},
|
|
33
|
+
onExit: (listener) => {
|
|
34
|
+
exitListeners.push(listener);
|
|
35
|
+
},
|
|
36
|
+
stop: () => {
|
|
37
|
+
duplex.destroy();
|
|
38
|
+
},
|
|
39
|
+
close: async () => {
|
|
40
|
+
duplex.end();
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Bind the host server to one side of a fresh paired in-memory `Duplex`.
|
|
45
|
+
* Returns the other side, unbound, for a caller to connect a client to. */
|
|
46
|
+
function bindTestServer(options = {}) {
|
|
47
|
+
const bridgeToken = options.bridgeToken ?? BRIDGE_TOKEN;
|
|
48
|
+
const [serverSide, clientSide] = duplexPair();
|
|
49
|
+
const forwardRequest = options.forwardRequest ??
|
|
50
|
+
(async (request) => ({
|
|
51
|
+
status: 200,
|
|
52
|
+
headers: { "content-type": "application/json" },
|
|
53
|
+
body: Buffer.from(JSON.stringify({ echoedMethod: request.method, echoedPath: request.pathname }), "utf8"),
|
|
54
|
+
}));
|
|
55
|
+
const handle = createHttp2BridgeServer({
|
|
56
|
+
bridgeToken,
|
|
57
|
+
forwardRequest,
|
|
58
|
+
pingIntervalMs: options.pingIntervalMs,
|
|
59
|
+
pingStallMs: options.pingStallMs,
|
|
60
|
+
requestBodyTimeoutMs: options.requestBodyTimeoutMs,
|
|
61
|
+
requestBodyLifetimeCeilingMs: options.requestBodyLifetimeCeilingMs,
|
|
62
|
+
closeGraceMs: options.closeGraceMs,
|
|
63
|
+
capacityDenialSettleDeadlineMs: options.capacityDenialSettleDeadlineMs,
|
|
64
|
+
responseWriteSettleDeadlineMs: options.responseWriteSettleDeadlineMs,
|
|
65
|
+
maxBodyBytes: options.maxBodyBytes,
|
|
66
|
+
routes: options.routes,
|
|
67
|
+
onGoaway: options.onGoaway,
|
|
68
|
+
onSessionError: options.onSessionError,
|
|
69
|
+
onSession: options.onSession,
|
|
70
|
+
});
|
|
71
|
+
const channel = fakeChannelFromDuplex(serverSide);
|
|
72
|
+
handle.bindChannel(channel);
|
|
73
|
+
return { handle, bridgeToken, clientSide, serverSide };
|
|
74
|
+
}
|
|
75
|
+
/** Bind the server, then connect the sandbox HTTP/2 client gateway to the
|
|
76
|
+
* other side of the same paired in-memory `Duplex`. */
|
|
77
|
+
function createTestPair(options = {}) {
|
|
78
|
+
const { handle, bridgeToken, clientSide, serverSide } = bindTestServer(options);
|
|
79
|
+
const gateway = createSandboxHttp2BridgeGateway({
|
|
80
|
+
bridgeToken,
|
|
81
|
+
createConnection: () => clientSide,
|
|
82
|
+
});
|
|
83
|
+
return { handle, gateway, bridgeToken, clientSide, serverSide };
|
|
84
|
+
}
|
|
85
|
+
/** Open a raw HTTP/2 client session directly against one side of the pair,
|
|
86
|
+
* bypassing the sandbox gateway. Some tests need direct stream control (an
|
|
87
|
+
* explicit RST_STREAM, an explicit GOAWAY) the gateway's `forwardRequest`
|
|
88
|
+
* abstraction does not expose. `settings`, when given, rides the client's
|
|
89
|
+
* own initial SETTINGS frame — for example `{ initialWindowSize: 0 }` to
|
|
90
|
+
* deny the server any flow-control credit to send response bytes with, a
|
|
91
|
+
* deterministic stall independent of the fake transport's own buffering. */
|
|
92
|
+
function connectRawClient(clientSide, settings) {
|
|
93
|
+
return http2.connect("http://bridge.internal", { createConnection: () => clientSide, settings });
|
|
94
|
+
}
|
|
95
|
+
/** Track whether `forwardRequest` ran, so a test can prove a denied or
|
|
96
|
+
* destroyed stream never reached it. Call `markCalled()` from inside
|
|
97
|
+
* `forwardRequest`, then assert on `.called`. */
|
|
98
|
+
function createForwarderCallTracker() {
|
|
99
|
+
const tracker = {
|
|
100
|
+
called: false,
|
|
101
|
+
markCalled() {
|
|
102
|
+
tracker.called = true;
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
return tracker;
|
|
106
|
+
}
|
|
107
|
+
/** Send one more request over `rawClient` and wait for it to complete, so a
|
|
108
|
+
* test can prove the session survived a prior faulted, stalled, or denied
|
|
109
|
+
* stream. */
|
|
110
|
+
async function expectSessionStillServesARequest(rawClient, request) {
|
|
111
|
+
return new Promise((resolve, reject) => {
|
|
112
|
+
const req = rawClient.request({
|
|
113
|
+
":method": request.method,
|
|
114
|
+
":path": request.path,
|
|
115
|
+
authorization: `Bearer ${request.token}`,
|
|
116
|
+
});
|
|
117
|
+
let status = 0;
|
|
118
|
+
let body = "";
|
|
119
|
+
req.setEncoding("utf8");
|
|
120
|
+
req.on("response", (headers) => {
|
|
121
|
+
status = Number(headers[":status"]) || 0;
|
|
122
|
+
});
|
|
123
|
+
req.on("data", (chunk) => (body += chunk));
|
|
124
|
+
req.on("end", () => resolve({ status, body }));
|
|
125
|
+
req.on("error", reject);
|
|
126
|
+
req.end();
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
describe("createHttp2BridgeServer + createSandboxHttp2BridgeGateway", () => {
|
|
130
|
+
// Every test in this file shares the module-scope process reservation
|
|
131
|
+
// total. A reservation one test leaves unreleased would otherwise lower
|
|
132
|
+
// the ceiling every later test sees, so each test starts from zero.
|
|
133
|
+
beforeEach(() => {
|
|
134
|
+
resetBridgeBodyReservationsForTest();
|
|
135
|
+
});
|
|
136
|
+
it("test_one_session_over_a_fake_channel_forwards_a_request", async () => {
|
|
137
|
+
const { handle, gateway } = createTestPair();
|
|
138
|
+
try {
|
|
139
|
+
const response = await gateway.forwardRequest({
|
|
140
|
+
method: "GET",
|
|
141
|
+
path: "/api/agents/me",
|
|
142
|
+
query: "",
|
|
143
|
+
headers: {},
|
|
144
|
+
body: Buffer.alloc(0),
|
|
145
|
+
receivedToken: BRIDGE_TOKEN,
|
|
146
|
+
});
|
|
147
|
+
expect(response.status).toBe(200);
|
|
148
|
+
expect(JSON.parse(response.body.toString("utf8"))).toEqual({
|
|
149
|
+
echoedMethod: "GET",
|
|
150
|
+
echoedPath: "/api/agents/me",
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
await gateway.close();
|
|
155
|
+
await handle.close();
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
it("test_sixty_four_concurrent_streams_all_complete", async () => {
|
|
159
|
+
const seenPaths = new Set();
|
|
160
|
+
const { handle, gateway } = createTestPair({
|
|
161
|
+
forwardRequest: async (request) => {
|
|
162
|
+
// Force real overlap: every forward call waits one macrotask before it
|
|
163
|
+
// resolves, so 64 concurrent streams are genuinely in flight together.
|
|
164
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
165
|
+
seenPaths.add(request.pathname);
|
|
166
|
+
return {
|
|
167
|
+
status: 200,
|
|
168
|
+
headers: {},
|
|
169
|
+
body: Buffer.from(JSON.stringify({ echoedPath: request.pathname }), "utf8"),
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
try {
|
|
174
|
+
const requests = Array.from({ length: 64 }, (_, index) => gateway.forwardRequest({
|
|
175
|
+
method: "GET",
|
|
176
|
+
path: `/api/issues/${index}`,
|
|
177
|
+
query: "",
|
|
178
|
+
headers: {},
|
|
179
|
+
body: Buffer.alloc(0),
|
|
180
|
+
receivedToken: BRIDGE_TOKEN,
|
|
181
|
+
}));
|
|
182
|
+
const responses = await Promise.all(requests);
|
|
183
|
+
expect(responses).toHaveLength(64);
|
|
184
|
+
for (const [index, response] of responses.entries()) {
|
|
185
|
+
expect(response.status).toBe(200);
|
|
186
|
+
expect(JSON.parse(response.body.toString("utf8"))).toEqual({
|
|
187
|
+
echoedPath: `/api/issues/${index}`,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
expect(seenPaths.size).toBe(64);
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
await gateway.close();
|
|
194
|
+
await handle.close();
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
it("test_ping_detects_a_silent_stall_within_twenty_seconds", async () => {
|
|
198
|
+
// The production defaults name the twenty-second bound this test name
|
|
199
|
+
// promises. The mechanism test below exercises the same code path with
|
|
200
|
+
// small overrides, so the suite stays fast.
|
|
201
|
+
expect(DEFAULT_HTTP2_BRIDGE_PING_STALL_MS).toBe(20_000);
|
|
202
|
+
expect(DEFAULT_HTTP2_BRIDGE_PING_INTERVAL_MS).toBeLessThan(DEFAULT_HTTP2_BRIDGE_PING_STALL_MS);
|
|
203
|
+
const [serverSide] = duplexPair();
|
|
204
|
+
// Nothing consumes the other side of the pair, so every PING frame the
|
|
205
|
+
// server sends goes unacknowledged: a silent stall.
|
|
206
|
+
const stallErrors = [];
|
|
207
|
+
const handle = createHttp2BridgeServer({
|
|
208
|
+
bridgeToken: BRIDGE_TOKEN,
|
|
209
|
+
forwardRequest: async () => ({ status: 200 }),
|
|
210
|
+
pingIntervalMs: 15,
|
|
211
|
+
pingStallMs: 40,
|
|
212
|
+
onSessionError: (error) => stallErrors.push(error),
|
|
213
|
+
});
|
|
214
|
+
handle.bindChannel(fakeChannelFromDuplex(serverSide));
|
|
215
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
216
|
+
expect(stallErrors).toHaveLength(1);
|
|
217
|
+
expect(stallErrors[0]?.message).toMatch(/stall/i);
|
|
218
|
+
await handle.close();
|
|
219
|
+
});
|
|
220
|
+
it("test_goaway_reports_the_last_processed_stream_identifier", async () => {
|
|
221
|
+
// GOAWAY's Last-Stream-ID names the highest stream the SENDER processed
|
|
222
|
+
// for the peer's own initiated streams. Every request stream in this
|
|
223
|
+
// transport originates from the sandbox, so the meaningful, valid
|
|
224
|
+
// direction is the host naming the last client stream it processed — not
|
|
225
|
+
// the reverse (a client-sent GOAWAY only ever carries 0 here, since
|
|
226
|
+
// server push is disabled). The sandbox gateway observes it and can
|
|
227
|
+
// classify its own dispatched stream IDs with `classifyStreamAgainstGoaway`.
|
|
228
|
+
let hostSession;
|
|
229
|
+
const goawayRecords = [];
|
|
230
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
231
|
+
onSession: (session) => {
|
|
232
|
+
hostSession = session;
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
const gateway = createSandboxHttp2BridgeGateway({
|
|
236
|
+
bridgeToken,
|
|
237
|
+
createConnection: () => clientSide,
|
|
238
|
+
onGoaway: (record) => goawayRecords.push(record),
|
|
239
|
+
});
|
|
240
|
+
try {
|
|
241
|
+
const response = await gateway.forwardRequest({
|
|
242
|
+
method: "GET",
|
|
243
|
+
path: "/api/agents/me",
|
|
244
|
+
query: "",
|
|
245
|
+
headers: {},
|
|
246
|
+
body: Buffer.alloc(0),
|
|
247
|
+
receivedToken: bridgeToken,
|
|
248
|
+
});
|
|
249
|
+
expect(response.status).toBe(200);
|
|
250
|
+
await new Promise((resolve) => {
|
|
251
|
+
hostSession?.goaway(http2.constants.NGHTTP2_NO_ERROR, 1);
|
|
252
|
+
setTimeout(resolve, 50);
|
|
253
|
+
});
|
|
254
|
+
expect(goawayRecords).toHaveLength(1);
|
|
255
|
+
expect(goawayRecords[0]?.lastStreamId).toBe(1);
|
|
256
|
+
expect(classifyStreamAgainstGoaway(1, goawayRecords[0].lastStreamId)).toBe("accepted");
|
|
257
|
+
expect(classifyStreamAgainstGoaway(3, goawayRecords[0].lastStreamId)).toBe("not_accepted");
|
|
258
|
+
}
|
|
259
|
+
finally {
|
|
260
|
+
await gateway.close();
|
|
261
|
+
await handle.close();
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
it("test_rst_stream_fails_one_request_and_keeps_the_session", async () => {
|
|
265
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
266
|
+
forwardRequest: async (request) => {
|
|
267
|
+
if (request.pathname === "/api/agents/me") {
|
|
268
|
+
// Hold this one open long enough for the test to RST it.
|
|
269
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
270
|
+
}
|
|
271
|
+
return { status: 200, headers: {}, body: Buffer.from(JSON.stringify({ path: request.pathname }), "utf8") };
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
const rawClient = connectRawClient(clientSide);
|
|
275
|
+
try {
|
|
276
|
+
const abortedStream = rawClient.request({
|
|
277
|
+
":method": "GET",
|
|
278
|
+
":path": "/api/agents/me",
|
|
279
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
280
|
+
});
|
|
281
|
+
const abortedOutcome = new Promise((resolve) => {
|
|
282
|
+
abortedStream.on("error", () => resolve("aborted"));
|
|
283
|
+
abortedStream.on("close", () => resolve("closed"));
|
|
284
|
+
});
|
|
285
|
+
abortedStream.end();
|
|
286
|
+
// Give the request time to reach the server before the reset.
|
|
287
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
288
|
+
abortedStream.close(http2.constants.NGHTTP2_CANCEL);
|
|
289
|
+
await abortedOutcome;
|
|
290
|
+
// The session survives: a second request completes normally.
|
|
291
|
+
const survivingResponse = await expectSessionStillServesARequest(rawClient, {
|
|
292
|
+
method: "GET",
|
|
293
|
+
path: "/api/companies/co1",
|
|
294
|
+
token: bridgeToken,
|
|
295
|
+
});
|
|
296
|
+
expect(survivingResponse.status).toBe(200);
|
|
297
|
+
expect(JSON.parse(survivingResponse.body)).toEqual({ path: "/api/companies/co1" });
|
|
298
|
+
}
|
|
299
|
+
finally {
|
|
300
|
+
rawClient.close();
|
|
301
|
+
await handle.close();
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
it("test_the_server_aborts_the_forward_when_the_client_closes_the_stream", async () => {
|
|
305
|
+
let capturedRequest;
|
|
306
|
+
let releaseForward;
|
|
307
|
+
const forwardHeld = new Promise((resolve) => {
|
|
308
|
+
releaseForward = resolve;
|
|
309
|
+
});
|
|
310
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
311
|
+
forwardRequest: async (request) => {
|
|
312
|
+
capturedRequest = request;
|
|
313
|
+
// Hold the handler open, so the test controls exactly when the
|
|
314
|
+
// client-side stream close happens relative to the forward.
|
|
315
|
+
await forwardHeld;
|
|
316
|
+
return { status: 200, body: Buffer.from("{}", "utf8") };
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
const rawClient = connectRawClient(clientSide);
|
|
320
|
+
try {
|
|
321
|
+
const stream = rawClient.request({
|
|
322
|
+
":method": "GET",
|
|
323
|
+
":path": "/api/agents/me",
|
|
324
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
325
|
+
});
|
|
326
|
+
const streamClosed = new Promise((resolve) => {
|
|
327
|
+
stream.on("error", () => resolve());
|
|
328
|
+
stream.on("close", () => resolve());
|
|
329
|
+
});
|
|
330
|
+
stream.end();
|
|
331
|
+
// Give the request time to reach the server and enter forwardRequest.
|
|
332
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
333
|
+
expect(capturedRequest).toBeDefined();
|
|
334
|
+
expect(capturedRequest.signal.aborted).toBe(false);
|
|
335
|
+
stream.close(http2.constants.NGHTTP2_CANCEL);
|
|
336
|
+
await streamClosed;
|
|
337
|
+
// Give the server's own stream `close` listener a turn to run.
|
|
338
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
339
|
+
expect(capturedRequest.signal.aborted).toBe(true);
|
|
340
|
+
releaseForward();
|
|
341
|
+
}
|
|
342
|
+
finally {
|
|
343
|
+
rawClient.close();
|
|
344
|
+
await handle.close();
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
it("test_one_aborted_stream_leaves_the_session_and_the_other_streams_open", async () => {
|
|
348
|
+
const signalsByPath = new Map();
|
|
349
|
+
let releaseSurvivor;
|
|
350
|
+
const survivorHeld = new Promise((resolve) => {
|
|
351
|
+
releaseSurvivor = resolve;
|
|
352
|
+
});
|
|
353
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
354
|
+
forwardRequest: async (request) => {
|
|
355
|
+
signalsByPath.set(request.pathname, request.signal);
|
|
356
|
+
if (request.pathname === "/api/agents/aborted") {
|
|
357
|
+
// The RST — not a manual release — is what must end this forward:
|
|
358
|
+
// it settles only when the stream's own signal fires, so the test
|
|
359
|
+
// proves the signal itself unblocks the handler.
|
|
360
|
+
await new Promise((_resolve, reject) => {
|
|
361
|
+
request.signal.addEventListener("abort", () => reject(new Error("aborted")), {
|
|
362
|
+
once: true,
|
|
363
|
+
});
|
|
364
|
+
}).catch(() => undefined);
|
|
365
|
+
}
|
|
366
|
+
else if (request.pathname === "/api/agents/survivor") {
|
|
367
|
+
await survivorHeld;
|
|
368
|
+
}
|
|
369
|
+
return { status: 200, body: Buffer.from(JSON.stringify({ path: request.pathname }), "utf8") };
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
const rawClient = connectRawClient(clientSide);
|
|
373
|
+
try {
|
|
374
|
+
const abortedStream = rawClient.request({
|
|
375
|
+
":method": "GET",
|
|
376
|
+
":path": "/api/agents/aborted",
|
|
377
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
378
|
+
});
|
|
379
|
+
const survivorStream = rawClient.request({
|
|
380
|
+
":method": "GET",
|
|
381
|
+
":path": "/api/agents/survivor",
|
|
382
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
383
|
+
});
|
|
384
|
+
const abortedStreamClosed = new Promise((resolve) => {
|
|
385
|
+
abortedStream.on("error", () => resolve());
|
|
386
|
+
abortedStream.on("close", () => resolve());
|
|
387
|
+
});
|
|
388
|
+
// Capture the survivor stream's own response, so the test proves this
|
|
389
|
+
// exact stream — the sibling of the aborted one, on the same session —
|
|
390
|
+
// completes normally, not merely that the session admits a fresh one.
|
|
391
|
+
const survivorResponse = new Promise((resolve, reject) => {
|
|
392
|
+
let status = 0;
|
|
393
|
+
let body = "";
|
|
394
|
+
survivorStream.setEncoding("utf8");
|
|
395
|
+
survivorStream.on("response", (h) => {
|
|
396
|
+
status = Number(h[":status"]) || 0;
|
|
397
|
+
});
|
|
398
|
+
survivorStream.on("data", (chunk) => (body += chunk));
|
|
399
|
+
survivorStream.on("end", () => resolve({ status, body }));
|
|
400
|
+
survivorStream.on("error", reject);
|
|
401
|
+
});
|
|
402
|
+
abortedStream.end();
|
|
403
|
+
survivorStream.end();
|
|
404
|
+
// Give both requests time to reach the server and enter forwardRequest.
|
|
405
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
406
|
+
abortedStream.close(http2.constants.NGHTTP2_CANCEL);
|
|
407
|
+
await abortedStreamClosed;
|
|
408
|
+
// Give the server's own stream `close` listener a turn to run.
|
|
409
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
410
|
+
// The one aborted stream's signal fires. The other, still-open stream's
|
|
411
|
+
// signal stays untouched: the abort is local to its own stream.
|
|
412
|
+
expect(signalsByPath.get("/api/agents/aborted")?.aborted).toBe(true);
|
|
413
|
+
expect(signalsByPath.get("/api/agents/survivor")?.aborted).toBe(false);
|
|
414
|
+
releaseSurvivor();
|
|
415
|
+
const response = await survivorResponse;
|
|
416
|
+
expect(response.status).toBe(200);
|
|
417
|
+
expect(JSON.parse(response.body)).toEqual({ path: "/api/agents/survivor" });
|
|
418
|
+
}
|
|
419
|
+
finally {
|
|
420
|
+
rawClient.close();
|
|
421
|
+
await handle.close();
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
it("test_live_forward_work_never_passes_the_stream_limit", async () => {
|
|
425
|
+
// A forward that is never aborted holds open until the caller aborts it
|
|
426
|
+
// — this stands in for a forward stuck at its own long timeout. Only the
|
|
427
|
+
// per-stream abort binding this file adds can free such a forward before
|
|
428
|
+
// that timeout, so this count proves the binding, not merely the HTTP/2
|
|
429
|
+
// session's own stream-slot accounting (a stream's protocol slot frees on
|
|
430
|
+
// RST regardless of whether its forward call ever settles).
|
|
431
|
+
let liveForwards = 0;
|
|
432
|
+
let maxLiveForwards = 0;
|
|
433
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
434
|
+
forwardRequest: async (request) => {
|
|
435
|
+
liveForwards += 1;
|
|
436
|
+
maxLiveForwards = Math.max(maxLiveForwards, liveForwards);
|
|
437
|
+
try {
|
|
438
|
+
await new Promise((resolve, reject) => {
|
|
439
|
+
if (request.signal.aborted) {
|
|
440
|
+
reject(new Error("aborted"));
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
request.signal.addEventListener("abort", () => reject(new Error("aborted")), { once: true });
|
|
444
|
+
});
|
|
445
|
+
return { status: 200, body: Buffer.from(JSON.stringify({ path: request.pathname }), "utf8") };
|
|
446
|
+
}
|
|
447
|
+
finally {
|
|
448
|
+
liveForwards -= 1;
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
const rawClient = connectRawClient(clientSide);
|
|
453
|
+
try {
|
|
454
|
+
const openAndCancelOneBatch = async (label) => {
|
|
455
|
+
const streams = Array.from({ length: HTTP2_BRIDGE_MAX_CONCURRENT_STREAMS }, (_, index) => rawClient.request({
|
|
456
|
+
":method": "GET",
|
|
457
|
+
":path": `/api/issues/${label}-${index}`,
|
|
458
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
459
|
+
}));
|
|
460
|
+
for (const stream of streams)
|
|
461
|
+
stream.end();
|
|
462
|
+
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
463
|
+
for (const stream of streams)
|
|
464
|
+
stream.close(http2.constants.NGHTTP2_CANCEL);
|
|
465
|
+
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
466
|
+
};
|
|
467
|
+
// One full batch of maximum-concurrency streams, closed while their
|
|
468
|
+
// forwards are still held open, then a second full batch opened right
|
|
469
|
+
// after. Without the abort binding, the first batch's forwards would
|
|
470
|
+
// still be alive when the second batch dispatches, doubling the live-
|
|
471
|
+
// forward count past the stream limit.
|
|
472
|
+
await openAndCancelOneBatch("first");
|
|
473
|
+
await openAndCancelOneBatch("second");
|
|
474
|
+
expect(maxLiveForwards).toBeLessThanOrEqual(HTTP2_BRIDGE_MAX_CONCURRENT_STREAMS);
|
|
475
|
+
expect(liveForwards).toBe(0);
|
|
476
|
+
}
|
|
477
|
+
finally {
|
|
478
|
+
rawClient.close();
|
|
479
|
+
await handle.close();
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
it("test_a_stalled_request_body_settles_instead_of_hanging_forever", async () => {
|
|
483
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
484
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
485
|
+
requestBodyTimeoutMs: 30,
|
|
486
|
+
forwardRequest: async () => {
|
|
487
|
+
forwarderTracker.markCalled();
|
|
488
|
+
return { status: 200 };
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
const rawClient = connectRawClient(clientSide);
|
|
492
|
+
try {
|
|
493
|
+
const startMs = Date.now();
|
|
494
|
+
// A partial, never-ended body stalls the read. The timeout destroys the
|
|
495
|
+
// stream, so the request settles (through an `error` or a `close`, not
|
|
496
|
+
// a clean response) well inside the bound, instead of hanging forever.
|
|
497
|
+
await new Promise((resolve) => {
|
|
498
|
+
const req = rawClient.request({
|
|
499
|
+
":method": "POST",
|
|
500
|
+
":path": "/api/issues/abc/comments",
|
|
501
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
502
|
+
});
|
|
503
|
+
req.on("error", () => resolve());
|
|
504
|
+
req.on("close", () => resolve());
|
|
505
|
+
req.write("partial-body");
|
|
506
|
+
});
|
|
507
|
+
expect(Date.now() - startMs).toBeLessThan(5_000);
|
|
508
|
+
expect(forwarderTracker.called).toBe(false);
|
|
509
|
+
// The session survives the timed-out stream: a second, complete request
|
|
510
|
+
// still succeeds.
|
|
511
|
+
const survivingResponse = await expectSessionStillServesARequest(rawClient, {
|
|
512
|
+
method: "POST",
|
|
513
|
+
path: "/api/issues/abc/comments",
|
|
514
|
+
token: bridgeToken,
|
|
515
|
+
});
|
|
516
|
+
expect(survivingResponse.status).toBe(200);
|
|
517
|
+
}
|
|
518
|
+
finally {
|
|
519
|
+
rawClient.close();
|
|
520
|
+
await handle.close();
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
it("test_a_slow_but_progressing_request_body_completes_instead_of_timing_out", async () => {
|
|
524
|
+
// The idle bound is well under the total time the whole body takes, so a
|
|
525
|
+
// one-shot bound over the full read would trip. Each chunk resets the
|
|
526
|
+
// bound, so the request still completes.
|
|
527
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
528
|
+
requestBodyTimeoutMs: 80,
|
|
529
|
+
forwardRequest: async (request) => ({
|
|
530
|
+
status: 200,
|
|
531
|
+
body: Buffer.from(JSON.stringify({ bodyLength: request.body.byteLength }), "utf8"),
|
|
532
|
+
}),
|
|
533
|
+
});
|
|
534
|
+
const rawClient = connectRawClient(clientSide);
|
|
535
|
+
try {
|
|
536
|
+
const response = await new Promise((resolve, reject) => {
|
|
537
|
+
const req = rawClient.request({
|
|
538
|
+
":method": "POST",
|
|
539
|
+
":path": "/api/issues/abc/comments",
|
|
540
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
541
|
+
});
|
|
542
|
+
let status = 0;
|
|
543
|
+
let body = "";
|
|
544
|
+
req.setEncoding("utf8");
|
|
545
|
+
req.on("response", (headers) => {
|
|
546
|
+
status = Number(headers[":status"]) || 0;
|
|
547
|
+
});
|
|
548
|
+
req.on("data", (chunk) => (body += chunk));
|
|
549
|
+
req.on("end", () => resolve({ status, body }));
|
|
550
|
+
req.on("error", reject);
|
|
551
|
+
// Five chunks, each inside the idle bound, summing past it.
|
|
552
|
+
let sent = 0;
|
|
553
|
+
const sendNext = () => {
|
|
554
|
+
if (sent >= 5) {
|
|
555
|
+
req.end();
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
sent += 1;
|
|
559
|
+
req.write("chunk");
|
|
560
|
+
setTimeout(sendNext, 40);
|
|
561
|
+
};
|
|
562
|
+
sendNext();
|
|
563
|
+
});
|
|
564
|
+
expect(response.status).toBe(200);
|
|
565
|
+
expect(JSON.parse(response.body)).toEqual({ bodyLength: "chunk".length * 5 });
|
|
566
|
+
}
|
|
567
|
+
finally {
|
|
568
|
+
rawClient.close();
|
|
569
|
+
await handle.close();
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
it("test_a_slow_trickle_that_never_ends_stops_at_the_one_shot_ceiling", async () => {
|
|
573
|
+
// The idle bound is generous, so a chunk every 40ms never lets it
|
|
574
|
+
// expire. The ceiling (150ms) arms once, when the read starts, and
|
|
575
|
+
// never renews on a chunk: this proves a peer that keeps every chunk
|
|
576
|
+
// gap under the idle bound, but never finishes the body, still loses
|
|
577
|
+
// the stream once the read's total age passes the ceiling. This is the
|
|
578
|
+
// failure mode an authenticated sandbox could otherwise use to hold one
|
|
579
|
+
// of the concurrent-stream slots open indefinitely: keep sending just
|
|
580
|
+
// enough to dodge the idle bound, and never finish the body.
|
|
581
|
+
//
|
|
582
|
+
// Every write below lands well before the ceiling, and the test makes
|
|
583
|
+
// no further call on the stream after the last one: nothing races the
|
|
584
|
+
// server's own reset of it once the ceiling passes, so this proves the
|
|
585
|
+
// bound from the server's own, deterministic effect (the forward
|
|
586
|
+
// handler never runs) instead of from a raw client-stream event whose
|
|
587
|
+
// timing Node does not guarantee here.
|
|
588
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
589
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
590
|
+
requestBodyTimeoutMs: 5_000,
|
|
591
|
+
requestBodyLifetimeCeilingMs: 150,
|
|
592
|
+
forwardRequest: async () => {
|
|
593
|
+
forwarderTracker.markCalled();
|
|
594
|
+
return { status: 200 };
|
|
595
|
+
},
|
|
596
|
+
});
|
|
597
|
+
const rawClient = connectRawClient(clientSide);
|
|
598
|
+
try {
|
|
599
|
+
const req = rawClient.request({
|
|
600
|
+
":method": "POST",
|
|
601
|
+
":path": "/api/issues/abc/comments",
|
|
602
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
603
|
+
});
|
|
604
|
+
req.on("error", () => undefined);
|
|
605
|
+
// Four chunks, 40ms apart, well inside the idle bound. The total send
|
|
606
|
+
// time, about 160ms, passes the 150ms ceiling. The body never ends:
|
|
607
|
+
// this request never calls `.end()`.
|
|
608
|
+
for (let chunkIndex = 0; chunkIndex < 4; chunkIndex += 1) {
|
|
609
|
+
req.write("chunk");
|
|
610
|
+
await new Promise((resolve) => setTimeout(resolve, 40));
|
|
611
|
+
}
|
|
612
|
+
// Wait past the ceiling with no further write, so the server has time
|
|
613
|
+
// to destroy the stalled stream on its own.
|
|
614
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
615
|
+
expect(forwarderTracker.called).toBe(false);
|
|
616
|
+
// The session survives the stopped stream: a second, complete request
|
|
617
|
+
// still succeeds.
|
|
618
|
+
const survivingResponse = await expectSessionStillServesARequest(rawClient, {
|
|
619
|
+
method: "POST",
|
|
620
|
+
path: "/api/issues/abc/comments",
|
|
621
|
+
token: bridgeToken,
|
|
622
|
+
});
|
|
623
|
+
expect(survivingResponse.status).toBe(200);
|
|
624
|
+
}
|
|
625
|
+
finally {
|
|
626
|
+
rawClient.close();
|
|
627
|
+
await handle.close();
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
it("test_a_request_body_within_the_lifetime_bound_still_completes", async () => {
|
|
631
|
+
// A generous ceiling must not interfere with an ordinary request that
|
|
632
|
+
// finishes well inside it.
|
|
633
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
634
|
+
requestBodyTimeoutMs: 5_000,
|
|
635
|
+
requestBodyLifetimeCeilingMs: 5_000,
|
|
636
|
+
forwardRequest: async (request) => ({
|
|
637
|
+
status: 200,
|
|
638
|
+
body: Buffer.from(JSON.stringify({ bodyLength: request.body.byteLength }), "utf8"),
|
|
639
|
+
}),
|
|
640
|
+
});
|
|
641
|
+
const rawClient = connectRawClient(clientSide);
|
|
642
|
+
try {
|
|
643
|
+
const response = await new Promise((resolve, reject) => {
|
|
644
|
+
const req = rawClient.request({
|
|
645
|
+
":method": "POST",
|
|
646
|
+
":path": "/api/issues/abc/comments",
|
|
647
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
648
|
+
});
|
|
649
|
+
let status = 0;
|
|
650
|
+
let body = "";
|
|
651
|
+
req.setEncoding("utf8");
|
|
652
|
+
req.on("response", (headers) => {
|
|
653
|
+
status = Number(headers[":status"]) || 0;
|
|
654
|
+
});
|
|
655
|
+
req.on("data", (chunk) => (body += chunk));
|
|
656
|
+
req.on("end", () => resolve({ status, body }));
|
|
657
|
+
req.on("error", reject);
|
|
658
|
+
req.write("chunk");
|
|
659
|
+
req.end();
|
|
660
|
+
});
|
|
661
|
+
expect(response.status).toBe(200);
|
|
662
|
+
expect(JSON.parse(response.body)).toEqual({ bodyLength: "chunk".length });
|
|
663
|
+
}
|
|
664
|
+
finally {
|
|
665
|
+
rawClient.close();
|
|
666
|
+
await handle.close();
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
it("test_a_slow_but_progressing_upload_completes_within_the_ceiling", async () => {
|
|
670
|
+
// The idle bound and the ceiling are both generous, so a slow but
|
|
671
|
+
// genuinely progressing upload completes normally, instead of losing
|
|
672
|
+
// its stream mid-upload.
|
|
673
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
674
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
675
|
+
requestBodyTimeoutMs: 5_000,
|
|
676
|
+
requestBodyLifetimeCeilingMs: 5_000,
|
|
677
|
+
forwardRequest: async (request) => {
|
|
678
|
+
forwarderTracker.markCalled();
|
|
679
|
+
return { status: 200, body: Buffer.from(JSON.stringify({ bodyLength: request.body.byteLength }), "utf8") };
|
|
680
|
+
},
|
|
681
|
+
});
|
|
682
|
+
const rawClient = connectRawClient(clientSide);
|
|
683
|
+
try {
|
|
684
|
+
const response = await new Promise((resolve, reject) => {
|
|
685
|
+
const req = rawClient.request({
|
|
686
|
+
":method": "POST",
|
|
687
|
+
":path": "/api/issues/abc/comments",
|
|
688
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
689
|
+
});
|
|
690
|
+
let status = 0;
|
|
691
|
+
let body = "";
|
|
692
|
+
req.setEncoding("utf8");
|
|
693
|
+
req.on("response", (headers) => {
|
|
694
|
+
status = Number(headers[":status"]) || 0;
|
|
695
|
+
});
|
|
696
|
+
req.on("data", (chunk) => (body += chunk));
|
|
697
|
+
req.on("end", () => resolve({ status, body }));
|
|
698
|
+
req.on("error", reject);
|
|
699
|
+
void (async () => {
|
|
700
|
+
// Six chunks, 40ms apart (well inside the idle bound), each real
|
|
701
|
+
// progress.
|
|
702
|
+
for (let sent = 0; sent < 6; sent += 1) {
|
|
703
|
+
req.write("chunk");
|
|
704
|
+
await new Promise((r) => setTimeout(r, 40));
|
|
705
|
+
}
|
|
706
|
+
req.end();
|
|
707
|
+
})();
|
|
708
|
+
});
|
|
709
|
+
expect(forwarderTracker.called).toBe(true);
|
|
710
|
+
expect(response.status).toBe(200);
|
|
711
|
+
expect(JSON.parse(response.body)).toEqual({ bodyLength: "chunk".length * 6 });
|
|
712
|
+
}
|
|
713
|
+
finally {
|
|
714
|
+
rawClient.close();
|
|
715
|
+
await handle.close();
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
it("test_close_force_destroys_a_session_with_a_stalled_stream_instead_of_waiting_forever", async () => {
|
|
719
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
720
|
+
// A body timeout far longer than the close grace, so `close()` is the
|
|
721
|
+
// only thing that bounds the wait in this test.
|
|
722
|
+
requestBodyTimeoutMs: 60_000,
|
|
723
|
+
closeGraceMs: 30,
|
|
724
|
+
});
|
|
725
|
+
const rawClient = connectRawClient(clientSide);
|
|
726
|
+
try {
|
|
727
|
+
const req = rawClient.request({
|
|
728
|
+
":method": "POST",
|
|
729
|
+
":path": "/api/issues/abc/comments",
|
|
730
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
731
|
+
});
|
|
732
|
+
req.write("partial-body");
|
|
733
|
+
// Give the request time to reach the server before close() runs.
|
|
734
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
735
|
+
const closeStart = Date.now();
|
|
736
|
+
await handle.close();
|
|
737
|
+
const closeElapsedMs = Date.now() - closeStart;
|
|
738
|
+
// `close()` force-destroyed the stalled session at the grace bound,
|
|
739
|
+
// instead of waiting on `session.close()` forever.
|
|
740
|
+
expect(closeElapsedMs).toBeLessThan(5_000);
|
|
741
|
+
}
|
|
742
|
+
finally {
|
|
743
|
+
rawClient.close();
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
it("test_the_server_sets_every_bounded_option", async () => {
|
|
747
|
+
expect(HTTP2_BRIDGE_SERVER_OPTIONS).toEqual({
|
|
748
|
+
settings: {
|
|
749
|
+
enablePush: HTTP2_BRIDGE_ENABLE_PUSH,
|
|
750
|
+
maxConcurrentStreams: HTTP2_BRIDGE_MAX_CONCURRENT_STREAMS,
|
|
751
|
+
maxHeaderListSize: HTTP2_BRIDGE_MAX_HEADER_LIST_SIZE,
|
|
752
|
+
headerTableSize: HTTP2_BRIDGE_HEADER_TABLE_SIZE,
|
|
753
|
+
},
|
|
754
|
+
maxSessionMemory: HTTP2_BRIDGE_MAX_SESSION_MEMORY,
|
|
755
|
+
maxHeaderListPairs: HTTP2_BRIDGE_MAX_HEADER_LIST_PAIRS,
|
|
756
|
+
maxDeflateDynamicTableSize: HTTP2_BRIDGE_MAX_DEFLATE_DYNAMIC_TABLE_SIZE,
|
|
757
|
+
maxSessionInvalidFrames: HTTP2_BRIDGE_MAX_SESSION_INVALID_FRAMES,
|
|
758
|
+
maxSessionRejectedStreams: HTTP2_BRIDGE_MAX_SESSION_REJECTED_STREAMS,
|
|
759
|
+
streamResetRate: HTTP2_BRIDGE_STREAM_RESET_RATE,
|
|
760
|
+
streamResetBurst: HTTP2_BRIDGE_STREAM_RESET_BURST,
|
|
761
|
+
});
|
|
762
|
+
expect(HTTP2_BRIDGE_ENABLE_PUSH).toBe(false);
|
|
763
|
+
expect(HTTP2_BRIDGE_MAX_CONCURRENT_STREAMS).toBe(4);
|
|
764
|
+
expect(HTTP2_BRIDGE_MAX_HEADER_LIST_SIZE).toBe(16384);
|
|
765
|
+
expect(HTTP2_BRIDGE_HEADER_TABLE_SIZE).toBe(4096);
|
|
766
|
+
expect(HTTP2_BRIDGE_MAX_SESSION_MEMORY).toBe(16);
|
|
767
|
+
expect(HTTP2_BRIDGE_MAX_HEADER_LIST_PAIRS).toBe(128);
|
|
768
|
+
expect(HTTP2_BRIDGE_MAX_DEFLATE_DYNAMIC_TABLE_SIZE).toBe(4096);
|
|
769
|
+
expect(HTTP2_BRIDGE_MAX_SESSION_INVALID_FRAMES).toBe(100);
|
|
770
|
+
expect(HTTP2_BRIDGE_MAX_SESSION_REJECTED_STREAMS).toBe(100);
|
|
771
|
+
expect(HTTP2_BRIDGE_STREAM_RESET_RATE).toBe(10);
|
|
772
|
+
expect(HTTP2_BRIDGE_STREAM_RESET_BURST).toBe(100);
|
|
773
|
+
// The running server actually carries the bound, not only the constant:
|
|
774
|
+
// the four `settings` values ride the server's outbound SETTINGS frame,
|
|
775
|
+
// so the connected client's `remoteSettings` reflects them after the
|
|
776
|
+
// handshake completes.
|
|
777
|
+
const { handle, clientSide } = bindTestServer();
|
|
778
|
+
const rawClient = connectRawClient(clientSide);
|
|
779
|
+
try {
|
|
780
|
+
const remoteSettings = await new Promise((resolve) => {
|
|
781
|
+
rawClient.once("remoteSettings", resolve);
|
|
782
|
+
});
|
|
783
|
+
expect(remoteSettings.enablePush).toBe(HTTP2_BRIDGE_ENABLE_PUSH);
|
|
784
|
+
expect(remoteSettings.maxConcurrentStreams).toBe(HTTP2_BRIDGE_MAX_CONCURRENT_STREAMS);
|
|
785
|
+
expect(remoteSettings.maxHeaderListSize).toBe(HTTP2_BRIDGE_MAX_HEADER_LIST_SIZE);
|
|
786
|
+
expect(remoteSettings.headerTableSize).toBe(HTTP2_BRIDGE_HEADER_TABLE_SIZE);
|
|
787
|
+
}
|
|
788
|
+
finally {
|
|
789
|
+
rawClient.close();
|
|
790
|
+
await handle.close();
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
it("test_the_host_body_budget_matches_the_stream_limit", () => {
|
|
794
|
+
// The multiplier counts every retained `Buffer` copy of one live
|
|
795
|
+
// forward's request and response body: four exact `Buffer` rows. The
|
|
796
|
+
// forward path decodes no body to a string, so no row applies the
|
|
797
|
+
// two-bytes-per-UTF-16-code-unit string overhead any more.
|
|
798
|
+
// `test_live_forward_work_never_passes_the_stream_limit` proves the
|
|
799
|
+
// count of live forwards never passes `HTTP2_BRIDGE_MAX_CONCURRENT_STREAMS`,
|
|
800
|
+
// so this multiplier bounds live forwards, not merely open streams.
|
|
801
|
+
// `HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES` uses this exact same formula to
|
|
802
|
+
// enforce it as a real per-route cap, not merely a derived figure.
|
|
803
|
+
const expectedRouteBudget = HTTP2_BRIDGE_MAX_CONCURRENT_STREAMS * 4 * DEFAULT_SANDBOX_CALLBACK_BRIDGE_MAX_BODY_BYTES;
|
|
804
|
+
expect(expectedRouteBudget).toBe(168_820_736);
|
|
805
|
+
expect(HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES).toBe(expectedRouteBudget);
|
|
806
|
+
});
|
|
807
|
+
describe("createBridgeBodyReservation", () => {
|
|
808
|
+
it("test_a_reservation_denies_a_chunk_that_passes_the_process_ceiling", () => {
|
|
809
|
+
const owner = createBridgeBodyReservation();
|
|
810
|
+
try {
|
|
811
|
+
expect(owner.reserve(HTTP2_BRIDGE_MAX_PROCESS_BODY_BYTES)).toBe(true);
|
|
812
|
+
expect(owner.heldBytes).toBe(HTTP2_BRIDGE_MAX_PROCESS_BODY_BYTES);
|
|
813
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(HTTP2_BRIDGE_MAX_PROCESS_BODY_BYTES);
|
|
814
|
+
// One more byte passes the ceiling: denied, and it holds no bytes.
|
|
815
|
+
expect(owner.reserve(1)).toBe(false);
|
|
816
|
+
expect(owner.heldBytes).toBe(HTTP2_BRIDGE_MAX_PROCESS_BODY_BYTES);
|
|
817
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(HTTP2_BRIDGE_MAX_PROCESS_BODY_BYTES);
|
|
818
|
+
}
|
|
819
|
+
finally {
|
|
820
|
+
owner.release();
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
it("test_a_reservation_denies_the_concatenated_copy_that_passes_the_process_ceiling", () => {
|
|
824
|
+
const owner = createBridgeBodyReservation();
|
|
825
|
+
try {
|
|
826
|
+
// The chunk-array reservation alone passes comfortably.
|
|
827
|
+
const chunkArrayBytes = Math.floor(HTTP2_BRIDGE_MAX_PROCESS_BODY_BYTES * 0.6);
|
|
828
|
+
expect(owner.reserve(chunkArrayBytes)).toBe(true);
|
|
829
|
+
// The concatenated `Buffer.concat` copy is a second, separate live
|
|
830
|
+
// copy of the same bytes: reserving it on top of the chunk array
|
|
831
|
+
// this owner already holds passes the ceiling, even though the
|
|
832
|
+
// chunk-array reservation alone did not.
|
|
833
|
+
expect(owner.reserve(chunkArrayBytes)).toBe(false);
|
|
834
|
+
expect(owner.heldBytes).toBe(chunkArrayBytes);
|
|
835
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(chunkArrayBytes);
|
|
836
|
+
}
|
|
837
|
+
finally {
|
|
838
|
+
owner.release();
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
it("test_a_reservation_releases_every_held_byte_one_time_only", () => {
|
|
842
|
+
const owner = createBridgeBodyReservation();
|
|
843
|
+
expect(owner.reserve(1_000)).toBe(true);
|
|
844
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(1_000);
|
|
845
|
+
owner.release();
|
|
846
|
+
expect(owner.heldBytes).toBe(0);
|
|
847
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
848
|
+
// A second release must not double-subtract: the total must not go
|
|
849
|
+
// below zero.
|
|
850
|
+
owner.release();
|
|
851
|
+
expect(owner.heldBytes).toBe(0);
|
|
852
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
853
|
+
});
|
|
854
|
+
});
|
|
855
|
+
describe("route isolation", () => {
|
|
856
|
+
it("test_a_route_ledger_denies_a_reservation_that_passes_its_own_ceiling_with_the_process_ceiling_still_open", () => {
|
|
857
|
+
// The process-wide ceiling has ample room (1 GiB); only this one
|
|
858
|
+
// route's own share is tight. A route-scoped owner must still deny
|
|
859
|
+
// the second reservation, proving the route ceiling is a real,
|
|
860
|
+
// independent check, not merely a reflection of the process total.
|
|
861
|
+
const routeLedger = createBridgeRouteBodyLedger();
|
|
862
|
+
const owner = createBridgeBodyReservation(routeLedger);
|
|
863
|
+
try {
|
|
864
|
+
expect(owner.reserve(HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES)).toBe(true);
|
|
865
|
+
expect(routeLedger.reservedBytes).toBe(HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES);
|
|
866
|
+
expect(owner.reserve(1)).toBe(false);
|
|
867
|
+
expect(owner.heldBytes).toBe(HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES);
|
|
868
|
+
expect(routeLedger.reservedBytes).toBe(HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES);
|
|
869
|
+
// The process-wide total only ever grew by what this owner actually
|
|
870
|
+
// holds: the denied byte reserved against neither total.
|
|
871
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES);
|
|
872
|
+
}
|
|
873
|
+
finally {
|
|
874
|
+
owner.release();
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
it("test_one_route_at_its_own_ceiling_never_blocks_a_sibling_routes_reservation", () => {
|
|
878
|
+
// Two routes, two ledgers. Route A spends its own entire ceiling.
|
|
879
|
+
// Route B's reservation, against its own separate ledger, must still
|
|
880
|
+
// succeed: the process-wide total (1 GiB) has room for both routes'
|
|
881
|
+
// ceilings many times over, so only route isolation — not the shared
|
|
882
|
+
// total — could explain a denial here.
|
|
883
|
+
const routeLedgerA = createBridgeRouteBodyLedger();
|
|
884
|
+
const routeLedgerB = createBridgeRouteBodyLedger();
|
|
885
|
+
const ownerA = createBridgeBodyReservation(routeLedgerA);
|
|
886
|
+
const ownerB = createBridgeBodyReservation(routeLedgerB);
|
|
887
|
+
try {
|
|
888
|
+
expect(ownerA.reserve(HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES)).toBe(true);
|
|
889
|
+
expect(ownerA.reserve(1)).toBe(false);
|
|
890
|
+
expect(ownerB.reserve(HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES)).toBe(true);
|
|
891
|
+
}
|
|
892
|
+
finally {
|
|
893
|
+
ownerA.release();
|
|
894
|
+
ownerB.release();
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
it("test_two_bridge_server_routes_isolate_their_own_reservations_end_to_end", async () => {
|
|
898
|
+
// The end-to-end proof: while route A's own forward holds its own
|
|
899
|
+
// entire per-route ceiling in flight, a second, independent route's
|
|
900
|
+
// request must still succeed. Each `bindTestServer` call is its own
|
|
901
|
+
// route (its own `createHttp2BridgeServer` call, so its own route
|
|
902
|
+
// ledger). Route A's forward parks on `releaseRouteAHold` after it
|
|
903
|
+
// reserves, so its reservation stays live — not merely reserved and
|
|
904
|
+
// immediately released — for the whole time route B's request runs.
|
|
905
|
+
let markRouteAReserved;
|
|
906
|
+
const routeAReserved = new Promise((resolve) => {
|
|
907
|
+
markRouteAReserved = resolve;
|
|
908
|
+
});
|
|
909
|
+
let releaseRouteAHold;
|
|
910
|
+
const routeAHold = new Promise((resolve) => {
|
|
911
|
+
releaseRouteAHold = resolve;
|
|
912
|
+
});
|
|
913
|
+
const routeA = bindTestServer({
|
|
914
|
+
forwardRequest: async (request) => {
|
|
915
|
+
// Reserve this route's own entire ceiling against its own ledger,
|
|
916
|
+
// simulating route A at its own documented peak.
|
|
917
|
+
if (!request.reservation.reserve(HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES)) {
|
|
918
|
+
throw new BridgeProcessCapacityError();
|
|
919
|
+
}
|
|
920
|
+
markRouteAReserved();
|
|
921
|
+
await routeAHold;
|
|
922
|
+
return { status: 200 };
|
|
923
|
+
},
|
|
924
|
+
});
|
|
925
|
+
const routeB = bindTestServer({
|
|
926
|
+
forwardRequest: async () => ({ status: 200 }),
|
|
927
|
+
});
|
|
928
|
+
const rawClientA = connectRawClient(routeA.clientSide);
|
|
929
|
+
const rawClientB = connectRawClient(routeB.clientSide);
|
|
930
|
+
try {
|
|
931
|
+
const pendingResponseA = expectSessionStillServesARequest(rawClientA, {
|
|
932
|
+
method: "GET",
|
|
933
|
+
path: "/api/agents/me",
|
|
934
|
+
token: routeA.bridgeToken,
|
|
935
|
+
});
|
|
936
|
+
// Wait until route A's forward actually holds its own ceiling —
|
|
937
|
+
// not merely until the request was sent — before checking route B.
|
|
938
|
+
await routeAReserved;
|
|
939
|
+
expect(getBridgeBodyReservedBytesForTest()).toBeGreaterThanOrEqual(HTTP2_BRIDGE_MAX_ROUTE_BODY_BYTES);
|
|
940
|
+
// Route A now holds its own entire per-route ceiling, live. The
|
|
941
|
+
// process-wide total still has headroom (1 GiB minus one 161 MiB
|
|
942
|
+
// route), so route B's independent request succeeds only if its own
|
|
943
|
+
// ledger is genuinely separate from route A's.
|
|
944
|
+
const responseB = await expectSessionStillServesARequest(rawClientB, {
|
|
945
|
+
method: "GET",
|
|
946
|
+
path: "/api/agents/me",
|
|
947
|
+
token: routeB.bridgeToken,
|
|
948
|
+
});
|
|
949
|
+
expect(responseB.status).toBe(200);
|
|
950
|
+
releaseRouteAHold();
|
|
951
|
+
const responseA = await pendingResponseA;
|
|
952
|
+
expect(responseA.status).toBe(200);
|
|
953
|
+
}
|
|
954
|
+
finally {
|
|
955
|
+
rawClientA.close();
|
|
956
|
+
rawClientB.close();
|
|
957
|
+
await routeA.handle.close();
|
|
958
|
+
await routeB.handle.close();
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
});
|
|
962
|
+
it("test_the_stream_handler_releases_its_reservation_on_completion_error_abort_timeout_and_close", async () => {
|
|
963
|
+
// Every stream, no matter how it ends, must leave the process-wide
|
|
964
|
+
// reservation total at zero: `handleStream`'s `finally` block releases
|
|
965
|
+
// exactly one owner exactly one time on every exit path.
|
|
966
|
+
async function runCompletionCase() {
|
|
967
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
968
|
+
forwardRequest: async (request) => {
|
|
969
|
+
request.reservation.reserve(1_000);
|
|
970
|
+
return { status: 200, body: Buffer.from("{}", "utf8") };
|
|
971
|
+
},
|
|
972
|
+
});
|
|
973
|
+
const rawClient = connectRawClient(clientSide);
|
|
974
|
+
try {
|
|
975
|
+
const response = await expectSessionStillServesARequest(rawClient, {
|
|
976
|
+
method: "GET",
|
|
977
|
+
path: "/api/agents/me",
|
|
978
|
+
token: bridgeToken,
|
|
979
|
+
});
|
|
980
|
+
expect(response.status).toBe(200);
|
|
981
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
982
|
+
}
|
|
983
|
+
finally {
|
|
984
|
+
rawClient.close();
|
|
985
|
+
await handle.close();
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
async function runErrorCase() {
|
|
989
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
990
|
+
forwardRequest: async (request) => {
|
|
991
|
+
request.reservation.reserve(1_000);
|
|
992
|
+
throw new Error("forward handler fault");
|
|
993
|
+
},
|
|
994
|
+
});
|
|
995
|
+
const rawClient = connectRawClient(clientSide);
|
|
996
|
+
try {
|
|
997
|
+
const response = await expectSessionStillServesARequest(rawClient, {
|
|
998
|
+
method: "GET",
|
|
999
|
+
path: "/api/agents/me",
|
|
1000
|
+
token: bridgeToken,
|
|
1001
|
+
});
|
|
1002
|
+
expect(response.status).toBe(502);
|
|
1003
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
1004
|
+
}
|
|
1005
|
+
finally {
|
|
1006
|
+
rawClient.close();
|
|
1007
|
+
await handle.close();
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
async function runAbortCase() {
|
|
1011
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1012
|
+
forwardRequest: async (request) => {
|
|
1013
|
+
request.reservation.reserve(1_000);
|
|
1014
|
+
await new Promise((resolve) => {
|
|
1015
|
+
if (request.signal.aborted) {
|
|
1016
|
+
resolve();
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
request.signal.addEventListener("abort", () => resolve(), { once: true });
|
|
1020
|
+
});
|
|
1021
|
+
return { status: 200 };
|
|
1022
|
+
},
|
|
1023
|
+
});
|
|
1024
|
+
const rawClient = connectRawClient(clientSide);
|
|
1025
|
+
try {
|
|
1026
|
+
const stream = rawClient.request({
|
|
1027
|
+
":method": "GET",
|
|
1028
|
+
":path": "/api/agents/me",
|
|
1029
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1030
|
+
});
|
|
1031
|
+
const streamClosed = new Promise((resolve) => {
|
|
1032
|
+
stream.on("error", () => resolve());
|
|
1033
|
+
stream.on("close", () => resolve());
|
|
1034
|
+
});
|
|
1035
|
+
stream.end();
|
|
1036
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1037
|
+
stream.close(http2.constants.NGHTTP2_CANCEL);
|
|
1038
|
+
await streamClosed;
|
|
1039
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1040
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
1041
|
+
}
|
|
1042
|
+
finally {
|
|
1043
|
+
rawClient.close();
|
|
1044
|
+
await handle.close();
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
async function runTimeoutCase() {
|
|
1048
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1049
|
+
requestBodyTimeoutMs: 30,
|
|
1050
|
+
forwardRequest: async () => ({ status: 200 }),
|
|
1051
|
+
});
|
|
1052
|
+
const rawClient = connectRawClient(clientSide);
|
|
1053
|
+
try {
|
|
1054
|
+
await new Promise((resolve) => {
|
|
1055
|
+
const req = rawClient.request({
|
|
1056
|
+
":method": "POST",
|
|
1057
|
+
":path": "/api/issues/abc/comments",
|
|
1058
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1059
|
+
});
|
|
1060
|
+
req.on("error", () => resolve());
|
|
1061
|
+
req.on("close", () => resolve());
|
|
1062
|
+
req.write("partial-body");
|
|
1063
|
+
});
|
|
1064
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1065
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
1066
|
+
}
|
|
1067
|
+
finally {
|
|
1068
|
+
rawClient.close();
|
|
1069
|
+
await handle.close();
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
async function runCloseCase() {
|
|
1073
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1074
|
+
requestBodyTimeoutMs: 60_000,
|
|
1075
|
+
closeGraceMs: 30,
|
|
1076
|
+
});
|
|
1077
|
+
const rawClient = connectRawClient(clientSide);
|
|
1078
|
+
try {
|
|
1079
|
+
const req = rawClient.request({
|
|
1080
|
+
":method": "POST",
|
|
1081
|
+
":path": "/api/issues/abc/comments",
|
|
1082
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1083
|
+
});
|
|
1084
|
+
req.write("partial-body");
|
|
1085
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1086
|
+
await handle.close();
|
|
1087
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1088
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
1089
|
+
}
|
|
1090
|
+
finally {
|
|
1091
|
+
rawClient.close();
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
await runCompletionCase();
|
|
1095
|
+
await runErrorCase();
|
|
1096
|
+
await runAbortCase();
|
|
1097
|
+
await runTimeoutCase();
|
|
1098
|
+
await runCloseCase();
|
|
1099
|
+
}, 10_000);
|
|
1100
|
+
it("test_a_backpressured_response_holds_its_reservation_until_the_write_settles", async () => {
|
|
1101
|
+
// `stream.end(body)` only queues `body` for asynchronous transmission.
|
|
1102
|
+
// A client that never reads its response leaves those bytes in process
|
|
1103
|
+
// memory well after `end()` returns, so the reservation covering them
|
|
1104
|
+
// must stay held until the write actually settles, not merely until the
|
|
1105
|
+
// call to `end()` returns.
|
|
1106
|
+
const responseBytes = 4 * 1024 * 1024;
|
|
1107
|
+
const responseBody = Buffer.alloc(responseBytes, 0x61);
|
|
1108
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1109
|
+
forwardRequest: async (request) => {
|
|
1110
|
+
// Mirror the real forward path: the handler reserves the response
|
|
1111
|
+
// body it read from the sandbox target before returning it.
|
|
1112
|
+
if (!request.reservation.reserve(responseBytes)) {
|
|
1113
|
+
throw new BridgeProcessCapacityError();
|
|
1114
|
+
}
|
|
1115
|
+
return { status: 200, body: responseBody };
|
|
1116
|
+
},
|
|
1117
|
+
});
|
|
1118
|
+
const rawClient = connectRawClient(clientSide);
|
|
1119
|
+
try {
|
|
1120
|
+
const req = rawClient.request({
|
|
1121
|
+
":method": "GET",
|
|
1122
|
+
":path": "/api/agents/me",
|
|
1123
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1124
|
+
});
|
|
1125
|
+
// Deliberately paused: no `resume()` and no `data` listener yet, so
|
|
1126
|
+
// the client never issues the flow-control credit the host needs to
|
|
1127
|
+
// finish writing a response this size.
|
|
1128
|
+
const headersReceived = new Promise((resolve) => {
|
|
1129
|
+
req.once("response", () => resolve());
|
|
1130
|
+
});
|
|
1131
|
+
req.end();
|
|
1132
|
+
await headersReceived;
|
|
1133
|
+
// Give the host's `stream.end()` call, and its microtask queue, a
|
|
1134
|
+
// turn to run — the write is now queued but the client still is not
|
|
1135
|
+
// draining it.
|
|
1136
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
1137
|
+
expect(getBridgeBodyReservedBytesForTest()).toBeGreaterThan(0);
|
|
1138
|
+
const drained = new Promise((resolve) => {
|
|
1139
|
+
req.on("data", () => {
|
|
1140
|
+
// Discard: this test only cares that the bytes left the host.
|
|
1141
|
+
});
|
|
1142
|
+
req.once("end", () => resolve());
|
|
1143
|
+
});
|
|
1144
|
+
req.resume();
|
|
1145
|
+
await drained;
|
|
1146
|
+
// Give the host's `finally` block a turn to run after the write
|
|
1147
|
+
// settles.
|
|
1148
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
1149
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
1150
|
+
}
|
|
1151
|
+
finally {
|
|
1152
|
+
rawClient.close();
|
|
1153
|
+
await handle.close();
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
it("test_a_peer_reset_holds_the_reservation_until_the_active_forward_settles", async () => {
|
|
1157
|
+
let capturedRequest;
|
|
1158
|
+
let releaseForward;
|
|
1159
|
+
const forwardHeld = new Promise((resolve) => {
|
|
1160
|
+
releaseForward = resolve;
|
|
1161
|
+
});
|
|
1162
|
+
let forwardSettled = false;
|
|
1163
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1164
|
+
forwardRequest: async (request) => {
|
|
1165
|
+
capturedRequest = request;
|
|
1166
|
+
// Stand in for a live response-body copy `readBridgeForwardResponseBody`
|
|
1167
|
+
// (`execution-target.ts`) would reserve against this same owner.
|
|
1168
|
+
request.reservation.reserve(10_000);
|
|
1169
|
+
// The forward stays active past the peer's own reset: it settles
|
|
1170
|
+
// only when the test releases it below, not merely when the abort
|
|
1171
|
+
// signal fires — the same shape a real outbound `fetch` bound to
|
|
1172
|
+
// `request.signal` has, since an abort does not settle the fetch
|
|
1173
|
+
// promise synchronously.
|
|
1174
|
+
await new Promise((resolve) => {
|
|
1175
|
+
if (request.signal.aborted) {
|
|
1176
|
+
resolve();
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
request.signal.addEventListener("abort", () => resolve(), { once: true });
|
|
1180
|
+
});
|
|
1181
|
+
await forwardHeld;
|
|
1182
|
+
forwardSettled = true;
|
|
1183
|
+
return { status: 200, body: Buffer.from("{}", "utf8") };
|
|
1184
|
+
},
|
|
1185
|
+
});
|
|
1186
|
+
const rawClient = connectRawClient(clientSide);
|
|
1187
|
+
try {
|
|
1188
|
+
const stream = rawClient.request({
|
|
1189
|
+
":method": "GET",
|
|
1190
|
+
":path": "/api/agents/me",
|
|
1191
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1192
|
+
});
|
|
1193
|
+
const streamClosed = new Promise((resolve) => {
|
|
1194
|
+
stream.on("error", () => resolve());
|
|
1195
|
+
stream.on("close", () => resolve());
|
|
1196
|
+
});
|
|
1197
|
+
stream.end();
|
|
1198
|
+
// Give the request time to reach the server, enter `forwardRequest`,
|
|
1199
|
+
// and land its reservation.
|
|
1200
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1201
|
+
expect(capturedRequest).toBeDefined();
|
|
1202
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(10_000);
|
|
1203
|
+
// The peer resets the stream. The forward call is still pending.
|
|
1204
|
+
stream.close(http2.constants.NGHTTP2_CANCEL);
|
|
1205
|
+
await streamClosed;
|
|
1206
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1207
|
+
expect(capturedRequest.signal.aborted).toBe(true);
|
|
1208
|
+
// The owner still holds its bytes: the active forward has not
|
|
1209
|
+
// settled yet, so `handleStream`'s `finally` has not released it.
|
|
1210
|
+
expect(forwardSettled).toBe(false);
|
|
1211
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(10_000);
|
|
1212
|
+
// Let the forward settle. The `finally` block releases the owner
|
|
1213
|
+
// exactly one time, and the process total returns to zero.
|
|
1214
|
+
releaseForward();
|
|
1215
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1216
|
+
expect(forwardSettled).toBe(true);
|
|
1217
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
1218
|
+
}
|
|
1219
|
+
finally {
|
|
1220
|
+
rawClient.close();
|
|
1221
|
+
await handle.close();
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1224
|
+
it("test_a_stalled_body_releases_its_reservation_while_other_streams_hold_theirs", async () => {
|
|
1225
|
+
const releaseByPath = new Map();
|
|
1226
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1227
|
+
requestBodyTimeoutMs: 60,
|
|
1228
|
+
forwardRequest: async (request) => {
|
|
1229
|
+
// Stand in for a live response-body buffer: reserve, then hold the
|
|
1230
|
+
// forward open until the test releases this exact path.
|
|
1231
|
+
request.reservation.reserve(20_000);
|
|
1232
|
+
await new Promise((resolve) => {
|
|
1233
|
+
releaseByPath.set(request.pathname, resolve);
|
|
1234
|
+
});
|
|
1235
|
+
return { status: 200, body: Buffer.from("{}", "utf8") };
|
|
1236
|
+
},
|
|
1237
|
+
});
|
|
1238
|
+
const rawClient = connectRawClient(clientSide);
|
|
1239
|
+
try {
|
|
1240
|
+
const streamA = rawClient.request({
|
|
1241
|
+
":method": "GET",
|
|
1242
|
+
":path": "/api/agents/a",
|
|
1243
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1244
|
+
});
|
|
1245
|
+
const streamB = rawClient.request({
|
|
1246
|
+
":method": "GET",
|
|
1247
|
+
":path": "/api/agents/b",
|
|
1248
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1249
|
+
});
|
|
1250
|
+
// Drain each response as it arrives: `session.close()` later in this
|
|
1251
|
+
// test's teardown waits for every stream to fully end on both sides,
|
|
1252
|
+
// and an unread response can hold a stream open past that point.
|
|
1253
|
+
streamA.resume();
|
|
1254
|
+
streamB.resume();
|
|
1255
|
+
streamA.end();
|
|
1256
|
+
streamB.end();
|
|
1257
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1258
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(40_000);
|
|
1259
|
+
// The third stream's request body stalls past the idle bound. It
|
|
1260
|
+
// never reaches `forwardRequest`, so it never reserves anything of
|
|
1261
|
+
// its own; its owner still holds zero bytes at the moment it releases.
|
|
1262
|
+
const streamC = rawClient.request({
|
|
1263
|
+
":method": "POST",
|
|
1264
|
+
":path": "/api/issues/abc/comments",
|
|
1265
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1266
|
+
});
|
|
1267
|
+
const streamCClosed = new Promise((resolve) => {
|
|
1268
|
+
streamC.on("error", () => resolve());
|
|
1269
|
+
streamC.on("close", () => resolve());
|
|
1270
|
+
});
|
|
1271
|
+
streamC.write("partial-body");
|
|
1272
|
+
await streamCClosed;
|
|
1273
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1274
|
+
// The stalled stream's release changed nothing: the other two streams
|
|
1275
|
+
// keep their reservations.
|
|
1276
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(40_000);
|
|
1277
|
+
releaseByPath.get("/api/agents/a")?.();
|
|
1278
|
+
releaseByPath.get("/api/agents/b")?.();
|
|
1279
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1280
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
1281
|
+
}
|
|
1282
|
+
finally {
|
|
1283
|
+
rawClient.close();
|
|
1284
|
+
await handle.close();
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
it("test_a_denied_stream_drains_its_body_and_holds_no_bytes", async () => {
|
|
1288
|
+
const { handle, clientSide } = bindTestServer({
|
|
1289
|
+
forwardRequest: async () => ({ status: 200 }),
|
|
1290
|
+
});
|
|
1291
|
+
const rawClient = connectRawClient(clientSide);
|
|
1292
|
+
try {
|
|
1293
|
+
const response = await new Promise((resolve, reject) => {
|
|
1294
|
+
// No `authorization` header: the token check denies this stream
|
|
1295
|
+
// before its body is ever read.
|
|
1296
|
+
const req = rawClient.request({ ":method": "POST", ":path": "/api/issues/abc/comments" });
|
|
1297
|
+
let status = 0;
|
|
1298
|
+
req.on("response", (headers) => {
|
|
1299
|
+
status = Number(headers[":status"]) || 0;
|
|
1300
|
+
});
|
|
1301
|
+
req.on("data", () => undefined);
|
|
1302
|
+
req.on("end", () => resolve({ status }));
|
|
1303
|
+
req.on("error", reject);
|
|
1304
|
+
req.write(Buffer.alloc(50_000, "a"));
|
|
1305
|
+
req.end();
|
|
1306
|
+
});
|
|
1307
|
+
expect(response.status).toBe(401);
|
|
1308
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1309
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
1310
|
+
}
|
|
1311
|
+
finally {
|
|
1312
|
+
rawClient.close();
|
|
1313
|
+
await handle.close();
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
it("test_a_request_body_reservation_denial_answers_503_on_the_wire", async () => {
|
|
1317
|
+
// A denied request-body chunk must leave the stream alive long enough
|
|
1318
|
+
// to answer 503 for real, on the wire — not merely destroy the stream
|
|
1319
|
+
// and leave the caller with a bare reset. `respondJson` only queues the
|
|
1320
|
+
// 503 write; the handler must wait for that write to settle before it
|
|
1321
|
+
// destroys the stream, or the client can see a reset instead of the
|
|
1322
|
+
// full response body asserted below.
|
|
1323
|
+
const filler = createBridgeBodyReservation();
|
|
1324
|
+
expect(filler.reserve(HTTP2_BRIDGE_MAX_PROCESS_BODY_BYTES - 100)).toBe(true);
|
|
1325
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
1326
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1327
|
+
forwardRequest: async () => {
|
|
1328
|
+
forwarderTracker.markCalled();
|
|
1329
|
+
return { status: 200 };
|
|
1330
|
+
},
|
|
1331
|
+
});
|
|
1332
|
+
const rawClient = connectRawClient(clientSide);
|
|
1333
|
+
try {
|
|
1334
|
+
const response = await new Promise((resolve, reject) => {
|
|
1335
|
+
const req = rawClient.request({
|
|
1336
|
+
":method": "POST",
|
|
1337
|
+
":path": "/api/issues/abc/comments",
|
|
1338
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1339
|
+
});
|
|
1340
|
+
let status = 0;
|
|
1341
|
+
let body = "";
|
|
1342
|
+
req.setEncoding("utf8");
|
|
1343
|
+
req.on("response", (headers) => {
|
|
1344
|
+
status = Number(headers[":status"]) || 0;
|
|
1345
|
+
});
|
|
1346
|
+
req.on("data", (chunk) => (body += chunk));
|
|
1347
|
+
req.on("end", () => resolve({ status, body }));
|
|
1348
|
+
req.on("error", reject);
|
|
1349
|
+
// Ten times the 100 bytes of headroom the filler above left.
|
|
1350
|
+
req.end(Buffer.alloc(1_000, "a"));
|
|
1351
|
+
});
|
|
1352
|
+
expect(response.status).toBe(503);
|
|
1353
|
+
expect(forwarderTracker.called).toBe(false);
|
|
1354
|
+
// A raw reset delivers no body at all: parsing it here proves the
|
|
1355
|
+
// full JSON response actually reached the client, not merely the
|
|
1356
|
+
// status line.
|
|
1357
|
+
expect(JSON.parse(response.body)).toEqual({
|
|
1358
|
+
error: "The bridge host reached its reserved process body byte ceiling. Retry later.",
|
|
1359
|
+
});
|
|
1360
|
+
// The stream ended with a real response, not a raw reset: the session
|
|
1361
|
+
// stays healthy, so a second, complete request still succeeds.
|
|
1362
|
+
const survivingResponse = await expectSessionStillServesARequest(rawClient, {
|
|
1363
|
+
method: "GET",
|
|
1364
|
+
path: "/api/agents/me",
|
|
1365
|
+
token: bridgeToken,
|
|
1366
|
+
});
|
|
1367
|
+
expect(survivingResponse.status).toBe(200);
|
|
1368
|
+
}
|
|
1369
|
+
finally {
|
|
1370
|
+
rawClient.close();
|
|
1371
|
+
await handle.close();
|
|
1372
|
+
filler.release();
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1375
|
+
it("test_a_stalled_client_still_frees_the_capacity_denial_reservation_and_slot", async () => {
|
|
1376
|
+
// The capacity-denial (503) path must not wait forever for its queued
|
|
1377
|
+
// write to settle: a client that grants no flow-control credit for the
|
|
1378
|
+
// response would otherwise hold this stream's reservation and
|
|
1379
|
+
// concurrent-stream slot open indefinitely. A zero initial window
|
|
1380
|
+
// denies the server any credit to send the 503 body with, at the
|
|
1381
|
+
// protocol layer, regardless of the fake transport's own buffering —
|
|
1382
|
+
// the same deterministic stall a real stalled peer produces.
|
|
1383
|
+
const filler = createBridgeBodyReservation();
|
|
1384
|
+
expect(filler.reserve(HTTP2_BRIDGE_MAX_PROCESS_BODY_BYTES - 100)).toBe(true);
|
|
1385
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
1386
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1387
|
+
capacityDenialSettleDeadlineMs: 30,
|
|
1388
|
+
forwardRequest: async () => {
|
|
1389
|
+
forwarderTracker.markCalled();
|
|
1390
|
+
return { status: 200 };
|
|
1391
|
+
},
|
|
1392
|
+
});
|
|
1393
|
+
const rawClient = connectRawClient(clientSide, { initialWindowSize: 0 });
|
|
1394
|
+
try {
|
|
1395
|
+
const startMs = Date.now();
|
|
1396
|
+
const closed = new Promise((resolve) => {
|
|
1397
|
+
const req = rawClient.request({
|
|
1398
|
+
":method": "POST",
|
|
1399
|
+
":path": "/api/issues/abc/comments",
|
|
1400
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1401
|
+
});
|
|
1402
|
+
req.on("error", () => undefined);
|
|
1403
|
+
req.on("close", () => resolve());
|
|
1404
|
+
// Ten times the 100 bytes of headroom the filler above left: the
|
|
1405
|
+
// request body itself denies the reservation and enters the 503
|
|
1406
|
+
// path.
|
|
1407
|
+
req.end(Buffer.alloc(1_000, "a"));
|
|
1408
|
+
});
|
|
1409
|
+
await closed;
|
|
1410
|
+
// The deadline bounded the wait: the stream closed near the deadline
|
|
1411
|
+
// bound, not after some much longer natural settle that never comes.
|
|
1412
|
+
expect(Date.now() - startMs).toBeLessThan(5_000);
|
|
1413
|
+
expect(forwarderTracker.called).toBe(false);
|
|
1414
|
+
// The stream's own reservation released even though the client never
|
|
1415
|
+
// drained the 503 body — only the filler's own bytes remain held.
|
|
1416
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1417
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(filler.heldBytes);
|
|
1418
|
+
// The stream's slot freed too. Restore a normal flow-control window
|
|
1419
|
+
// first: only the denied stream above needs to stall.
|
|
1420
|
+
await new Promise((resolve) => rawClient.settings({ initialWindowSize: 65_535 }, () => resolve()));
|
|
1421
|
+
const survivingResponse = await expectSessionStillServesARequest(rawClient, {
|
|
1422
|
+
method: "GET",
|
|
1423
|
+
path: "/api/agents/me",
|
|
1424
|
+
token: bridgeToken,
|
|
1425
|
+
});
|
|
1426
|
+
expect(survivingResponse.status).toBe(200);
|
|
1427
|
+
}
|
|
1428
|
+
finally {
|
|
1429
|
+
rawClient.close();
|
|
1430
|
+
await handle.close();
|
|
1431
|
+
filler.release();
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
it("test_a_stalled_normal_response_still_frees_its_reservation_and_slot", async () => {
|
|
1435
|
+
// The completed-response (200) write path must not wait forever for its
|
|
1436
|
+
// queued write to settle either: a client that grants no flow-control
|
|
1437
|
+
// credit for the response would otherwise hold this stream's
|
|
1438
|
+
// reservation and concurrent-stream slot open indefinitely, the same
|
|
1439
|
+
// failure mode the capacity-denial path already guards against. A zero
|
|
1440
|
+
// initial window denies the server any credit to send the response body
|
|
1441
|
+
// with, at the protocol layer, regardless of the fake transport's own
|
|
1442
|
+
// buffering.
|
|
1443
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
1444
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1445
|
+
responseWriteSettleDeadlineMs: 30,
|
|
1446
|
+
forwardRequest: async () => {
|
|
1447
|
+
forwarderTracker.markCalled();
|
|
1448
|
+
return { status: 200, headers: {}, body: Buffer.alloc(1_000, "a") };
|
|
1449
|
+
},
|
|
1450
|
+
});
|
|
1451
|
+
const rawClient = connectRawClient(clientSide, { initialWindowSize: 0 });
|
|
1452
|
+
try {
|
|
1453
|
+
const startMs = Date.now();
|
|
1454
|
+
const closed = new Promise((resolve) => {
|
|
1455
|
+
const req = rawClient.request({
|
|
1456
|
+
":method": "GET",
|
|
1457
|
+
":path": "/api/agents/me",
|
|
1458
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1459
|
+
});
|
|
1460
|
+
req.on("error", () => undefined);
|
|
1461
|
+
req.on("close", () => resolve());
|
|
1462
|
+
req.end();
|
|
1463
|
+
});
|
|
1464
|
+
await closed;
|
|
1465
|
+
expect(forwarderTracker.called).toBe(true);
|
|
1466
|
+
// The deadline bounded the wait: the stream closed near the deadline
|
|
1467
|
+
// bound, not after some much longer natural settle that never comes.
|
|
1468
|
+
expect(Date.now() - startMs).toBeLessThan(5_000);
|
|
1469
|
+
// This stream's own reservation released even though the client never
|
|
1470
|
+
// drained the response body.
|
|
1471
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1472
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
1473
|
+
// The stream's slot freed too. Restore a normal flow-control window
|
|
1474
|
+
// first: only the stalled stream above needs to stall.
|
|
1475
|
+
await new Promise((resolve) => rawClient.settings({ initialWindowSize: 65_535 }, () => resolve()));
|
|
1476
|
+
const survivingResponse = await expectSessionStillServesARequest(rawClient, {
|
|
1477
|
+
method: "GET",
|
|
1478
|
+
path: "/api/agents/me",
|
|
1479
|
+
token: bridgeToken,
|
|
1480
|
+
});
|
|
1481
|
+
expect(survivingResponse.status).toBe(200);
|
|
1482
|
+
}
|
|
1483
|
+
finally {
|
|
1484
|
+
rawClient.close();
|
|
1485
|
+
await handle.close();
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1488
|
+
it("test_a_normal_response_that_settles_in_time_is_left_alone", async () => {
|
|
1489
|
+
// A response that genuinely settles inside the deadline must not be
|
|
1490
|
+
// force-destroyed: the bounded wait exists only for a stream that never
|
|
1491
|
+
// settles on its own.
|
|
1492
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1493
|
+
responseWriteSettleDeadlineMs: 5_000,
|
|
1494
|
+
forwardRequest: async () => ({
|
|
1495
|
+
status: 200,
|
|
1496
|
+
headers: { "content-type": "application/json" },
|
|
1497
|
+
body: Buffer.from(JSON.stringify({ ok: true }), "utf8"),
|
|
1498
|
+
}),
|
|
1499
|
+
});
|
|
1500
|
+
const rawClient = connectRawClient(clientSide);
|
|
1501
|
+
try {
|
|
1502
|
+
const response = await expectSessionStillServesARequest(rawClient, {
|
|
1503
|
+
method: "GET",
|
|
1504
|
+
path: "/api/agents/me",
|
|
1505
|
+
token: bridgeToken,
|
|
1506
|
+
});
|
|
1507
|
+
expect(response.status).toBe(200);
|
|
1508
|
+
expect(JSON.parse(response.body)).toEqual({ ok: true });
|
|
1509
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(0);
|
|
1510
|
+
}
|
|
1511
|
+
finally {
|
|
1512
|
+
rawClient.close();
|
|
1513
|
+
await handle.close();
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
it("test_a_response_reservation_denial_answers_503_on_the_wire", async () => {
|
|
1517
|
+
// The response reader on the sandbox target side
|
|
1518
|
+
// (`readBridgeForwardResponseBody`) denies its own reservation and
|
|
1519
|
+
// throws `BridgeProcessCapacityError` from inside `forwardRequest`,
|
|
1520
|
+
// after the forward handler already ran — a different call site than
|
|
1521
|
+
// the request-body denial above, but the same 503-before-destroy
|
|
1522
|
+
// contract must hold: the stream must stay alive long enough to answer
|
|
1523
|
+
// 503 for real, on the wire, not merely destroy and leave the caller
|
|
1524
|
+
// with a bare reset.
|
|
1525
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
1526
|
+
// Only the first call denies: the surviving-request check below reuses
|
|
1527
|
+
// the same session for a second, independent request, which must
|
|
1528
|
+
// succeed once this first stream's reservation released.
|
|
1529
|
+
let forwardCalls = 0;
|
|
1530
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1531
|
+
forwardRequest: async () => {
|
|
1532
|
+
forwarderTracker.markCalled();
|
|
1533
|
+
forwardCalls += 1;
|
|
1534
|
+
if (forwardCalls === 1) {
|
|
1535
|
+
throw new BridgeProcessCapacityError();
|
|
1536
|
+
}
|
|
1537
|
+
return { status: 200 };
|
|
1538
|
+
},
|
|
1539
|
+
});
|
|
1540
|
+
const rawClient = connectRawClient(clientSide);
|
|
1541
|
+
try {
|
|
1542
|
+
const response = await new Promise((resolve, reject) => {
|
|
1543
|
+
const req = rawClient.request({
|
|
1544
|
+
":method": "GET",
|
|
1545
|
+
":path": "/api/agents/me",
|
|
1546
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1547
|
+
});
|
|
1548
|
+
let status = 0;
|
|
1549
|
+
let body = "";
|
|
1550
|
+
req.setEncoding("utf8");
|
|
1551
|
+
req.on("response", (headers) => {
|
|
1552
|
+
status = Number(headers[":status"]) || 0;
|
|
1553
|
+
});
|
|
1554
|
+
req.on("data", (chunk) => (body += chunk));
|
|
1555
|
+
req.on("end", () => resolve({ status, body }));
|
|
1556
|
+
req.on("error", reject);
|
|
1557
|
+
req.end();
|
|
1558
|
+
});
|
|
1559
|
+
expect(response.status).toBe(503);
|
|
1560
|
+
expect(forwarderTracker.called).toBe(true);
|
|
1561
|
+
// A raw reset delivers no body at all: parsing it here proves the
|
|
1562
|
+
// full JSON response actually reached the client, not merely the
|
|
1563
|
+
// status line.
|
|
1564
|
+
expect(JSON.parse(response.body)).toEqual({
|
|
1565
|
+
error: "The bridge host reached its reserved process body byte ceiling. Retry later.",
|
|
1566
|
+
});
|
|
1567
|
+
// The stream ended with a real response, not a raw reset: the session
|
|
1568
|
+
// stays healthy, so a second, complete request still succeeds.
|
|
1569
|
+
const survivingResponse = await expectSessionStillServesARequest(rawClient, {
|
|
1570
|
+
method: "GET",
|
|
1571
|
+
path: "/api/agents/me",
|
|
1572
|
+
token: bridgeToken,
|
|
1573
|
+
});
|
|
1574
|
+
expect(survivingResponse.status).toBe(200);
|
|
1575
|
+
}
|
|
1576
|
+
finally {
|
|
1577
|
+
rawClient.close();
|
|
1578
|
+
await handle.close();
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1581
|
+
it("test_a_denied_concatenated_request_body_never_allocates_the_copy", async () => {
|
|
1582
|
+
// The chunk-array copy and the concatenated copy are two separate live
|
|
1583
|
+
// buffers, so each must reserve on its own. Leave room for the first but
|
|
1584
|
+
// not the second: a correct reader checks the reservation before
|
|
1585
|
+
// `Buffer.concat` allocates the copy, so the denied concatenated copy
|
|
1586
|
+
// must never call `Buffer.concat` at all.
|
|
1587
|
+
const chunkBytes = 200_000;
|
|
1588
|
+
const filler = createBridgeBodyReservation();
|
|
1589
|
+
expect(filler.reserve(HTTP2_BRIDGE_MAX_PROCESS_BODY_BYTES - Math.floor(chunkBytes * 1.5))).toBe(true);
|
|
1590
|
+
const concatSpy = vi.spyOn(Buffer, "concat");
|
|
1591
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
1592
|
+
const { handle, bridgeToken, clientSide } = bindTestServer({
|
|
1593
|
+
forwardRequest: async () => {
|
|
1594
|
+
forwarderTracker.markCalled();
|
|
1595
|
+
return { status: 200 };
|
|
1596
|
+
},
|
|
1597
|
+
});
|
|
1598
|
+
const rawClient = connectRawClient(clientSide);
|
|
1599
|
+
try {
|
|
1600
|
+
const response = await new Promise((resolve, reject) => {
|
|
1601
|
+
const req = rawClient.request({
|
|
1602
|
+
":method": "POST",
|
|
1603
|
+
":path": "/api/issues/abc/comments",
|
|
1604
|
+
authorization: `Bearer ${bridgeToken}`,
|
|
1605
|
+
});
|
|
1606
|
+
let status = 0;
|
|
1607
|
+
let body = "";
|
|
1608
|
+
req.setEncoding("utf8");
|
|
1609
|
+
req.on("response", (headers) => {
|
|
1610
|
+
status = Number(headers[":status"]) || 0;
|
|
1611
|
+
});
|
|
1612
|
+
req.on("data", (chunk) => (body += chunk));
|
|
1613
|
+
req.on("end", () => resolve({ status, body }));
|
|
1614
|
+
req.on("error", reject);
|
|
1615
|
+
req.write(Buffer.alloc(chunkBytes, "a"), () => {
|
|
1616
|
+
// Give the host a turn to receive the one chunk and reserve its
|
|
1617
|
+
// bytes before this test ends the stream and triggers the
|
|
1618
|
+
// concatenated-copy reservation attempt.
|
|
1619
|
+
setTimeout(() => {
|
|
1620
|
+
// The chunk-array reservation alone must already hold, on top of
|
|
1621
|
+
// the filler above, exactly the one chunk's bytes: the
|
|
1622
|
+
// concatenated-copy reservation has not run yet, because the
|
|
1623
|
+
// stream has not ended.
|
|
1624
|
+
expect(getBridgeBodyReservedBytesForTest()).toBe(filler.heldBytes + chunkBytes);
|
|
1625
|
+
req.end();
|
|
1626
|
+
}, 50);
|
|
1627
|
+
});
|
|
1628
|
+
});
|
|
1629
|
+
expect(response.status).toBe(503);
|
|
1630
|
+
expect(forwarderTracker.called).toBe(false);
|
|
1631
|
+
expect(concatSpy).not.toHaveBeenCalled();
|
|
1632
|
+
}
|
|
1633
|
+
finally {
|
|
1634
|
+
concatSpy.mockRestore();
|
|
1635
|
+
rawClient.close();
|
|
1636
|
+
await handle.close();
|
|
1637
|
+
filler.release();
|
|
1638
|
+
}
|
|
1639
|
+
});
|
|
1640
|
+
describe("parseCanonicalBridgeRequestPath", () => {
|
|
1641
|
+
it("parses an origin-form path with a query exactly one time", () => {
|
|
1642
|
+
const result = parseCanonicalBridgeRequestPath({ ":path": "/api/issues/abc?foo=bar" });
|
|
1643
|
+
expect(result).toEqual({ ok: true, value: { pathname: "/api/issues/abc", query: "?foo=bar" } });
|
|
1644
|
+
});
|
|
1645
|
+
it("test_a_non_origin_form_path_is_rejected", () => {
|
|
1646
|
+
expect(parseCanonicalBridgeRequestPath({ ":path": "http://evil.example/api" })).toEqual({
|
|
1647
|
+
ok: false,
|
|
1648
|
+
reason: "non_origin_form",
|
|
1649
|
+
});
|
|
1650
|
+
expect(parseCanonicalBridgeRequestPath({ ":path": "//evil.example/api" })).toEqual({
|
|
1651
|
+
ok: false,
|
|
1652
|
+
reason: "non_origin_form",
|
|
1653
|
+
});
|
|
1654
|
+
expect(parseCanonicalBridgeRequestPath({ ":path": "*" })).toEqual({
|
|
1655
|
+
ok: false,
|
|
1656
|
+
reason: "non_origin_form",
|
|
1657
|
+
});
|
|
1658
|
+
});
|
|
1659
|
+
it("test_an_encoded_separator_or_a_dot_segment_is_rejected", () => {
|
|
1660
|
+
expect(parseCanonicalBridgeRequestPath({ ":path": "/api%2fissues/abc" })).toEqual({
|
|
1661
|
+
ok: false,
|
|
1662
|
+
reason: "encoded_slash",
|
|
1663
|
+
});
|
|
1664
|
+
expect(parseCanonicalBridgeRequestPath({ ":path": "/api\\issues/abc" })).toEqual({
|
|
1665
|
+
ok: false,
|
|
1666
|
+
reason: "backslash",
|
|
1667
|
+
});
|
|
1668
|
+
expect(parseCanonicalBridgeRequestPath({ ":path": "/api/issues\0/abc" })).toEqual({
|
|
1669
|
+
ok: false,
|
|
1670
|
+
reason: "nul_byte",
|
|
1671
|
+
});
|
|
1672
|
+
expect(parseCanonicalBridgeRequestPath({ ":path": "/api/%00/abc" })).toEqual({
|
|
1673
|
+
ok: false,
|
|
1674
|
+
reason: "nul_byte",
|
|
1675
|
+
});
|
|
1676
|
+
expect(parseCanonicalBridgeRequestPath({ ":path": "/api/../secrets" })).toEqual({
|
|
1677
|
+
ok: false,
|
|
1678
|
+
reason: "dot_segment",
|
|
1679
|
+
});
|
|
1680
|
+
expect(parseCanonicalBridgeRequestPath({ ":path": "/api/%2e%2e/secrets" })).toEqual({
|
|
1681
|
+
ok: false,
|
|
1682
|
+
reason: "dot_segment",
|
|
1683
|
+
});
|
|
1684
|
+
expect(parseCanonicalBridgeRequestPath({ ":path": "/api/./issues" })).toEqual({
|
|
1685
|
+
ok: false,
|
|
1686
|
+
reason: "dot_segment",
|
|
1687
|
+
});
|
|
1688
|
+
});
|
|
1689
|
+
it("rejects a duplicate pseudo-header", () => {
|
|
1690
|
+
expect(parseCanonicalBridgeRequestPath({ ":path": ["/api/agents/me", "/api/agents/other"] })).toEqual({ ok: false, reason: "duplicate_pseudo_header" });
|
|
1691
|
+
});
|
|
1692
|
+
it("rejects a missing path", () => {
|
|
1693
|
+
expect(parseCanonicalBridgeRequestPath({})).toEqual({ ok: false, reason: "missing_path" });
|
|
1694
|
+
});
|
|
1695
|
+
});
|
|
1696
|
+
it("buildHttp2BridgeForwardUrl resolves the parsed pathname and query against the base URL", () => {
|
|
1697
|
+
const url = buildHttp2BridgeForwardUrl("http://127.0.0.1:4000", {
|
|
1698
|
+
pathname: "/api/issues/abc",
|
|
1699
|
+
query: "?foo=bar",
|
|
1700
|
+
});
|
|
1701
|
+
expect(url.toString()).toBe("http://127.0.0.1:4000/api/issues/abc?foo=bar");
|
|
1702
|
+
});
|
|
1703
|
+
it("classifyStreamAgainstGoaway classifies by the last processed stream id", () => {
|
|
1704
|
+
expect(classifyStreamAgainstGoaway(1, 3)).toBe("accepted");
|
|
1705
|
+
expect(classifyStreamAgainstGoaway(3, 3)).toBe("accepted");
|
|
1706
|
+
expect(classifyStreamAgainstGoaway(5, 3)).toBe("not_accepted");
|
|
1707
|
+
});
|
|
1708
|
+
it("test_a_stream_without_the_bridge_token_never_reaches_the_forwarder", async () => {
|
|
1709
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
1710
|
+
const { handle, clientSide } = bindTestServer({
|
|
1711
|
+
forwardRequest: async (request) => {
|
|
1712
|
+
forwarderTracker.markCalled();
|
|
1713
|
+
return { status: 200, body: Buffer.from(JSON.stringify({ path: request.pathname }), "utf8") };
|
|
1714
|
+
},
|
|
1715
|
+
});
|
|
1716
|
+
const rawClient = connectRawClient(clientSide);
|
|
1717
|
+
try {
|
|
1718
|
+
const response = await new Promise((resolve, reject) => {
|
|
1719
|
+
// No `authorization` header at all: the stream never carries a token.
|
|
1720
|
+
const req = rawClient.request({ ":method": "GET", ":path": "/api/agents/me" });
|
|
1721
|
+
let status = 0;
|
|
1722
|
+
let body = "";
|
|
1723
|
+
req.on("response", (headers) => {
|
|
1724
|
+
status = Number(headers[":status"]) || 0;
|
|
1725
|
+
});
|
|
1726
|
+
req.setEncoding("utf8");
|
|
1727
|
+
req.on("data", (chunk) => (body += chunk));
|
|
1728
|
+
req.on("end", () => resolve({ status, body }));
|
|
1729
|
+
req.on("error", reject);
|
|
1730
|
+
req.end();
|
|
1731
|
+
});
|
|
1732
|
+
expect(response.status).toBe(401);
|
|
1733
|
+
expect(forwarderTracker.called).toBe(false);
|
|
1734
|
+
}
|
|
1735
|
+
finally {
|
|
1736
|
+
rawClient.close();
|
|
1737
|
+
await handle.close();
|
|
1738
|
+
}
|
|
1739
|
+
});
|
|
1740
|
+
it("test_a_denied_stream_with_an_unfinished_body_still_frees_its_stream_slot", async () => {
|
|
1741
|
+
// The server answers a denied stream (an invalid token, here) at once,
|
|
1742
|
+
// before it ever reads the request body. A peer that leaves that body
|
|
1743
|
+
// unfinished must not hold the stream open past the same idle bound an
|
|
1744
|
+
// authenticated request gets — otherwise, up to
|
|
1745
|
+
// `HTTP2_BRIDGE_MAX_CONCURRENT_STREAMS` denied streams could each retain
|
|
1746
|
+
// a slot forever and block every legitimate callback.
|
|
1747
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
1748
|
+
const { handle, clientSide } = bindTestServer({
|
|
1749
|
+
requestBodyTimeoutMs: 30,
|
|
1750
|
+
forwardRequest: async () => {
|
|
1751
|
+
forwarderTracker.markCalled();
|
|
1752
|
+
return { status: 200 };
|
|
1753
|
+
},
|
|
1754
|
+
});
|
|
1755
|
+
const rawClient = connectRawClient(clientSide);
|
|
1756
|
+
try {
|
|
1757
|
+
const startMs = Date.now();
|
|
1758
|
+
const closed = new Promise((resolve) => {
|
|
1759
|
+
const req = rawClient.request({
|
|
1760
|
+
":method": "POST",
|
|
1761
|
+
":path": "/api/issues/abc/comments",
|
|
1762
|
+
// No `authorization` header: the token check denies this stream
|
|
1763
|
+
// before the body is ever read.
|
|
1764
|
+
});
|
|
1765
|
+
req.on("error", () => undefined);
|
|
1766
|
+
req.on("close", () => resolve());
|
|
1767
|
+
// Drain the response so its readable side reaches `end`: nothing in
|
|
1768
|
+
// this test reads the response body otherwise, and an unread
|
|
1769
|
+
// response can itself hold the client stream open past `close`.
|
|
1770
|
+
req.resume();
|
|
1771
|
+
// A partial body the request never ends: without its own bound, the
|
|
1772
|
+
// inbound half of this stream would stay open indefinitely.
|
|
1773
|
+
req.write("partial-body");
|
|
1774
|
+
});
|
|
1775
|
+
await closed;
|
|
1776
|
+
expect(Date.now() - startMs).toBeLessThan(5_000);
|
|
1777
|
+
expect(forwarderTracker.called).toBe(false);
|
|
1778
|
+
// The session survives the freed stream: a second, complete request
|
|
1779
|
+
// still succeeds, proving the session itself stayed open and healthy.
|
|
1780
|
+
const survivingResponse = await expectSessionStillServesARequest(rawClient, {
|
|
1781
|
+
method: "GET",
|
|
1782
|
+
path: "/api/agents/me",
|
|
1783
|
+
token: BRIDGE_TOKEN,
|
|
1784
|
+
});
|
|
1785
|
+
expect(survivingResponse.status).toBe(200);
|
|
1786
|
+
}
|
|
1787
|
+
finally {
|
|
1788
|
+
rawClient.close();
|
|
1789
|
+
await handle.close();
|
|
1790
|
+
}
|
|
1791
|
+
});
|
|
1792
|
+
it("serves only exact GET schema discovery through HTTP/2", async () => {
|
|
1793
|
+
const schema = { openapi: "3.1.0", paths: {} };
|
|
1794
|
+
const forwarded = [];
|
|
1795
|
+
const { gateway, handle } = createTestPair({
|
|
1796
|
+
forwardRequest: async (request) => {
|
|
1797
|
+
forwarded.push(`${request.method} ${request.pathname}`);
|
|
1798
|
+
return { status: 200, body: Buffer.from(JSON.stringify(schema)) };
|
|
1799
|
+
},
|
|
1800
|
+
});
|
|
1801
|
+
try {
|
|
1802
|
+
for (const [method, path, status] of [
|
|
1803
|
+
["GET", "/api/openapi.json", 200],
|
|
1804
|
+
["POST", "/api/openapi.json", 403],
|
|
1805
|
+
["PATCH", "/api/openapi.json", 403],
|
|
1806
|
+
["DELETE", "/api/openapi.json", 403],
|
|
1807
|
+
["GET", "/api/openapi.json/extra", 403],
|
|
1808
|
+
["GET", "/api/openapiXjson", 403],
|
|
1809
|
+
["GET", "/api/secrets", 403],
|
|
1810
|
+
]) {
|
|
1811
|
+
const response = await gateway.forwardRequest({
|
|
1812
|
+
method, path, query: "", headers: {}, body: Buffer.alloc(0), receivedToken: BRIDGE_TOKEN,
|
|
1813
|
+
});
|
|
1814
|
+
expect(response.status).toBe(status);
|
|
1815
|
+
if (status === 200)
|
|
1816
|
+
expect(JSON.parse(response.body.toString())).toEqual(schema);
|
|
1817
|
+
}
|
|
1818
|
+
expect(forwarded).toEqual(["GET /api/openapi.json"]);
|
|
1819
|
+
}
|
|
1820
|
+
finally {
|
|
1821
|
+
await gateway.close();
|
|
1822
|
+
await handle.close();
|
|
1823
|
+
}
|
|
1824
|
+
});
|
|
1825
|
+
it("rejects a route the allowlist does not carry, before the forwarder runs", async () => {
|
|
1826
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
1827
|
+
const { gateway, handle } = createTestPair({
|
|
1828
|
+
forwardRequest: async () => {
|
|
1829
|
+
forwarderTracker.markCalled();
|
|
1830
|
+
return { status: 200 };
|
|
1831
|
+
},
|
|
1832
|
+
});
|
|
1833
|
+
try {
|
|
1834
|
+
const response = await gateway.forwardRequest({
|
|
1835
|
+
method: "DELETE",
|
|
1836
|
+
path: "/api/agents/me",
|
|
1837
|
+
query: "",
|
|
1838
|
+
headers: {},
|
|
1839
|
+
body: Buffer.alloc(0),
|
|
1840
|
+
receivedToken: BRIDGE_TOKEN,
|
|
1841
|
+
});
|
|
1842
|
+
expect(response.status).toBe(403);
|
|
1843
|
+
expect(forwarderTracker.called).toBe(false);
|
|
1844
|
+
}
|
|
1845
|
+
finally {
|
|
1846
|
+
await gateway.close();
|
|
1847
|
+
await handle.close();
|
|
1848
|
+
}
|
|
1849
|
+
});
|
|
1850
|
+
it("test_a_maximum_size_multipart_attachment_upload_fits_through_the_bridge", async () => {
|
|
1851
|
+
// A file at the exact attachment content ceiling (10 MiB — the same
|
|
1852
|
+
// figure `MAX_ATTACHMENT_BYTES` in `server/src/attachment-types.ts`
|
|
1853
|
+
// enforces) still crosses the bridge wrapped in a multipart body: the
|
|
1854
|
+
// boundary line and the part's own headers add bytes on top of that file
|
|
1855
|
+
// content. The bridge's own body limit needs headroom for that framing,
|
|
1856
|
+
// or a valid maximum-size attachment fails here before the server ever
|
|
1857
|
+
// sees it.
|
|
1858
|
+
const maxAttachmentBytes = 10 * 1024 * 1024;
|
|
1859
|
+
const boundary = "----PaperclipTestBoundary1234567890abcdef";
|
|
1860
|
+
const preamble = Buffer.from(`--${boundary}\r\n` +
|
|
1861
|
+
`Content-Disposition: form-data; name="file"; filename="attachment.bin"\r\n` +
|
|
1862
|
+
`Content-Type: application/octet-stream\r\n\r\n`, "utf8");
|
|
1863
|
+
const fileContent = Buffer.alloc(maxAttachmentBytes, 0x61);
|
|
1864
|
+
const epilogue = Buffer.from(`\r\n--${boundary}--\r\n`, "utf8");
|
|
1865
|
+
const multipartBody = Buffer.concat([preamble, fileContent, epilogue]);
|
|
1866
|
+
// The wrapper really does add bytes on top of the file content alone —
|
|
1867
|
+
// otherwise this test would prove nothing about framing headroom.
|
|
1868
|
+
expect(multipartBody.byteLength).toBeGreaterThan(maxAttachmentBytes);
|
|
1869
|
+
expect(multipartBody.byteLength).toBeLessThanOrEqual(DEFAULT_SANDBOX_CALLBACK_BRIDGE_MAX_BODY_BYTES);
|
|
1870
|
+
let receivedBodyBytes = 0;
|
|
1871
|
+
const { gateway, handle } = createTestPair({
|
|
1872
|
+
routes: HTTP2_SANDBOX_CALLBACK_BRIDGE_ROUTE_ALLOWLIST,
|
|
1873
|
+
forwardRequest: async (request) => {
|
|
1874
|
+
receivedBodyBytes = request.body.byteLength;
|
|
1875
|
+
return { status: 201, body: Buffer.from("{}", "utf8") };
|
|
1876
|
+
},
|
|
1877
|
+
});
|
|
1878
|
+
try {
|
|
1879
|
+
const response = await gateway.forwardRequest({
|
|
1880
|
+
method: "POST",
|
|
1881
|
+
path: "/api/companies/co-1/issues/issue-1/attachments",
|
|
1882
|
+
query: "",
|
|
1883
|
+
headers: { "content-type": `multipart/form-data; boundary=${boundary}` },
|
|
1884
|
+
body: multipartBody,
|
|
1885
|
+
receivedToken: BRIDGE_TOKEN,
|
|
1886
|
+
});
|
|
1887
|
+
expect(response.status).toBe(201);
|
|
1888
|
+
expect(receivedBodyBytes).toBe(multipartBody.byteLength);
|
|
1889
|
+
}
|
|
1890
|
+
finally {
|
|
1891
|
+
await gateway.close();
|
|
1892
|
+
await handle.close();
|
|
1893
|
+
}
|
|
1894
|
+
}, 20_000);
|
|
1895
|
+
it("the sandbox gateway keeps its own token check before it opens a stream", async () => {
|
|
1896
|
+
const forwarderTracker = createForwarderCallTracker();
|
|
1897
|
+
const { gateway, handle } = createTestPair({
|
|
1898
|
+
forwardRequest: async () => {
|
|
1899
|
+
forwarderTracker.markCalled();
|
|
1900
|
+
return { status: 200 };
|
|
1901
|
+
},
|
|
1902
|
+
});
|
|
1903
|
+
try {
|
|
1904
|
+
await expect(gateway.forwardRequest({
|
|
1905
|
+
method: "GET",
|
|
1906
|
+
path: "/api/agents/me",
|
|
1907
|
+
query: "",
|
|
1908
|
+
headers: {},
|
|
1909
|
+
body: Buffer.alloc(0),
|
|
1910
|
+
receivedToken: "wrong-token",
|
|
1911
|
+
})).rejects.toThrow(/invalid bridge token/i);
|
|
1912
|
+
expect(forwarderTracker.called).toBe(false);
|
|
1913
|
+
}
|
|
1914
|
+
finally {
|
|
1915
|
+
await gateway.close();
|
|
1916
|
+
await handle.close();
|
|
1917
|
+
}
|
|
1918
|
+
});
|
|
1919
|
+
it("wrapDuplexChannelAsNodeDuplex relays writes and pushes through onData", async () => {
|
|
1920
|
+
const written = [];
|
|
1921
|
+
let dataListener;
|
|
1922
|
+
let exitListener;
|
|
1923
|
+
const channel = {
|
|
1924
|
+
write: (data) => {
|
|
1925
|
+
written.push(Buffer.from(data));
|
|
1926
|
+
},
|
|
1927
|
+
onData: (listener) => {
|
|
1928
|
+
dataListener = listener;
|
|
1929
|
+
},
|
|
1930
|
+
onExit: (listener) => {
|
|
1931
|
+
exitListener = listener;
|
|
1932
|
+
},
|
|
1933
|
+
stop: () => undefined,
|
|
1934
|
+
close: async () => undefined,
|
|
1935
|
+
};
|
|
1936
|
+
const duplex = wrapDuplexChannelAsNodeDuplex(channel);
|
|
1937
|
+
const received = [];
|
|
1938
|
+
duplex.on("data", (chunk) => received.push(chunk));
|
|
1939
|
+
duplex.write(Buffer.from("outbound"));
|
|
1940
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
1941
|
+
expect(Buffer.concat(written).toString("utf8")).toBe("outbound");
|
|
1942
|
+
dataListener?.(Buffer.from("inbound"));
|
|
1943
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
1944
|
+
expect(Buffer.concat(received).toString("utf8")).toBe("inbound");
|
|
1945
|
+
const ended = new Promise((resolve) => duplex.on("end", resolve));
|
|
1946
|
+
exitListener?.({ exitCode: 0 });
|
|
1947
|
+
await ended;
|
|
1948
|
+
});
|
|
1949
|
+
it("wrapDuplexChannelAsNodeDuplex queues chunks past a full readable side and drains them once the consumer reads again", async () => {
|
|
1950
|
+
let dataListener;
|
|
1951
|
+
const channel = {
|
|
1952
|
+
write: () => undefined,
|
|
1953
|
+
onData: (listener) => {
|
|
1954
|
+
dataListener = listener;
|
|
1955
|
+
},
|
|
1956
|
+
onExit: () => undefined,
|
|
1957
|
+
stop: () => undefined,
|
|
1958
|
+
close: async () => undefined,
|
|
1959
|
+
};
|
|
1960
|
+
const duplex = wrapDuplexChannelAsNodeDuplex(channel, { maxBufferedReadBytes: 1_000_000 });
|
|
1961
|
+
const received = [];
|
|
1962
|
+
// The first chunk alone passes the readable side's default 64 KiB
|
|
1963
|
+
// high-water mark, so `push()` reports the readable side full. The two
|
|
1964
|
+
// chunks that follow queue in the wrapper instead of pushing past that
|
|
1965
|
+
// signal — this is the bounded flow-control path this fix adds.
|
|
1966
|
+
dataListener?.(Buffer.alloc(70_000, "a"));
|
|
1967
|
+
dataListener?.(Buffer.from("-second-"));
|
|
1968
|
+
dataListener?.(Buffer.from("-third-"));
|
|
1969
|
+
// Attaching a "data" listener now starts flowing mode, which drives the
|
|
1970
|
+
// wrapper's own `read()` until every queued chunk drains, proving no
|
|
1971
|
+
// queued chunk was dropped and the arrival order held.
|
|
1972
|
+
const drainedAll = new Promise((resolve) => {
|
|
1973
|
+
duplex.on("data", (chunk) => {
|
|
1974
|
+
received.push(chunk);
|
|
1975
|
+
if (Buffer.concat(received).includes("-third-"))
|
|
1976
|
+
resolve();
|
|
1977
|
+
});
|
|
1978
|
+
});
|
|
1979
|
+
await drainedAll;
|
|
1980
|
+
expect(Buffer.concat(received).toString("utf8").endsWith("-second--third-")).toBe(true);
|
|
1981
|
+
});
|
|
1982
|
+
it("wrapDuplexChannelAsNodeDuplex stops the channel and destroys the duplex once the bounded read backpressure buffer overflows", async () => {
|
|
1983
|
+
let dataListener;
|
|
1984
|
+
let stopped = false;
|
|
1985
|
+
const channel = {
|
|
1986
|
+
write: () => undefined,
|
|
1987
|
+
onData: (listener) => {
|
|
1988
|
+
dataListener = listener;
|
|
1989
|
+
},
|
|
1990
|
+
onExit: () => undefined,
|
|
1991
|
+
stop: () => {
|
|
1992
|
+
stopped = true;
|
|
1993
|
+
},
|
|
1994
|
+
close: async () => undefined,
|
|
1995
|
+
};
|
|
1996
|
+
// The cap must clear the default 65,536-byte readable high-water mark,
|
|
1997
|
+
// so the first chunk below still passes the direct-push check and
|
|
1998
|
+
// forces `push()` to report the readable side full; the overflow this
|
|
1999
|
+
// test proves comes from the queue that follows, not from that first
|
|
2000
|
+
// chunk on its own.
|
|
2001
|
+
const duplex = wrapDuplexChannelAsNodeDuplex(channel, { maxBufferedReadBytes: 80_000 });
|
|
2002
|
+
// No consumer ever attaches, so the readable side never drains: every
|
|
2003
|
+
// chunk past the first, which alone passes the default high-water mark,
|
|
2004
|
+
// fills the bounded queue instead of the unbounded internal buffer a
|
|
2005
|
+
// caller ignoring `push()`'s return would grow.
|
|
2006
|
+
const errored = new Promise((resolve) => duplex.on("error", resolve));
|
|
2007
|
+
dataListener?.(Buffer.alloc(70_000, "a")); // passes the high-water mark and the cap; still pushed directly.
|
|
2008
|
+
dataListener?.(Buffer.alloc(30_000, "b")); // queues: 30,000 of the 80,000-byte cap.
|
|
2009
|
+
dataListener?.(Buffer.alloc(30_000, "b")); // queues: 60,000 of the 80,000-byte cap.
|
|
2010
|
+
dataListener?.(Buffer.alloc(30_000, "b")); // 90,000 queued bytes passes the cap.
|
|
2011
|
+
const error = await errored;
|
|
2012
|
+
expect(error.message).toMatch(/backpressure/i);
|
|
2013
|
+
expect(stopped).toBe(true);
|
|
2014
|
+
});
|
|
2015
|
+
it("stops the channel when the read queue passes the lowered byte bound", async () => {
|
|
2016
|
+
let dataListener;
|
|
2017
|
+
let stopped = false;
|
|
2018
|
+
const channel = {
|
|
2019
|
+
write: () => undefined,
|
|
2020
|
+
onData: (listener) => {
|
|
2021
|
+
dataListener = listener;
|
|
2022
|
+
},
|
|
2023
|
+
onExit: () => undefined,
|
|
2024
|
+
stop: () => {
|
|
2025
|
+
stopped = true;
|
|
2026
|
+
},
|
|
2027
|
+
close: async () => undefined,
|
|
2028
|
+
};
|
|
2029
|
+
// No override: this test proves the default bound itself is the direct
|
|
2030
|
+
// 524,288-byte value, no longer derived from `HTTP2_BRIDGE_MAX_SESSION_MEMORY`.
|
|
2031
|
+
// No consumer ever attaches, so the readable side never drains.
|
|
2032
|
+
expect(DEFAULT_HTTP2_BRIDGE_MAX_BUFFERED_READ_BYTES).toBe(524_288);
|
|
2033
|
+
const duplex = wrapDuplexChannelAsNodeDuplex(channel);
|
|
2034
|
+
const errored = new Promise((resolve) => duplex.on("error", resolve));
|
|
2035
|
+
dataListener?.(Buffer.alloc(70_000, "a")); // passes the default high-water mark; still pushed directly.
|
|
2036
|
+
dataListener?.(Buffer.alloc(500_000, "b")); // queues most of the 524,288-byte default cap.
|
|
2037
|
+
dataListener?.(Buffer.alloc(60_000, "b")); // passes the default cap.
|
|
2038
|
+
const error = await errored;
|
|
2039
|
+
expect(error.message).toMatch(/backpressure/i);
|
|
2040
|
+
expect(stopped).toBe(true);
|
|
2041
|
+
});
|
|
2042
|
+
it("wrapDuplexChannelAsNodeDuplex fails closed on one inbound chunk larger than the bounded read backpressure buffer, before the queue holds anything to compare it against", async () => {
|
|
2043
|
+
let dataListener;
|
|
2044
|
+
let stopped = false;
|
|
2045
|
+
const channel = {
|
|
2046
|
+
write: () => undefined,
|
|
2047
|
+
onData: (listener) => {
|
|
2048
|
+
dataListener = listener;
|
|
2049
|
+
},
|
|
2050
|
+
onExit: () => undefined,
|
|
2051
|
+
stop: () => {
|
|
2052
|
+
stopped = true;
|
|
2053
|
+
},
|
|
2054
|
+
close: async () => undefined,
|
|
2055
|
+
};
|
|
2056
|
+
// No consumer ever attaches, and the queue is empty when this chunk
|
|
2057
|
+
// arrives, so a check that only bounds the queue's cumulative size (and
|
|
2058
|
+
// not one chunk's own size) would let this chunk reach `push()` unbound.
|
|
2059
|
+
const duplex = wrapDuplexChannelAsNodeDuplex(channel, { maxBufferedReadBytes: 5_000 });
|
|
2060
|
+
const errored = new Promise((resolve) => duplex.on("error", resolve));
|
|
2061
|
+
dataListener?.(Buffer.alloc(10_000, "a")); // exceeds the 5,000-byte cap on its own, on the very first chunk.
|
|
2062
|
+
const error = await errored;
|
|
2063
|
+
expect(error.message).toMatch(/backpressure/i);
|
|
2064
|
+
expect(stopped).toBe(true);
|
|
2065
|
+
});
|
|
2066
|
+
it("wrapDuplexChannelAsNodeDuplex fails closed once the read backpressure queue stalls with no drain, even though it stays under the byte cap", async () => {
|
|
2067
|
+
let dataListener;
|
|
2068
|
+
let stopped = false;
|
|
2069
|
+
const channel = {
|
|
2070
|
+
write: () => undefined,
|
|
2071
|
+
onData: (listener) => {
|
|
2072
|
+
dataListener = listener;
|
|
2073
|
+
},
|
|
2074
|
+
onExit: () => undefined,
|
|
2075
|
+
stop: () => {
|
|
2076
|
+
stopped = true;
|
|
2077
|
+
},
|
|
2078
|
+
close: async () => undefined,
|
|
2079
|
+
};
|
|
2080
|
+
// A generous byte cap, so the byte-cap check above never fires: this
|
|
2081
|
+
// test proves the independent time bound catches a stuck consumer the
|
|
2082
|
+
// byte cap alone would miss.
|
|
2083
|
+
const duplex = wrapDuplexChannelAsNodeDuplex(channel, {
|
|
2084
|
+
maxBufferedReadBytes: 10_000_000,
|
|
2085
|
+
readBackpressureStallMs: 40,
|
|
2086
|
+
});
|
|
2087
|
+
const errored = new Promise((resolve) => duplex.on("error", resolve));
|
|
2088
|
+
dataListener?.(Buffer.alloc(70_000, "a")); // passes the high-water mark; pushed directly, no consumer drains it.
|
|
2089
|
+
dataListener?.(Buffer.from("queued-and-never-drained")); // queues; no "data" listener ever attaches to drain it.
|
|
2090
|
+
const error = await errored;
|
|
2091
|
+
expect(error.message).toMatch(/stall/i);
|
|
2092
|
+
expect(stopped).toBe(true);
|
|
2093
|
+
});
|
|
2094
|
+
it("wrapDuplexChannelAsNodeDuplex clears the stall bound once the queue fully drains, instead of firing later on an idle channel", async () => {
|
|
2095
|
+
let dataListener;
|
|
2096
|
+
const channel = {
|
|
2097
|
+
write: () => undefined,
|
|
2098
|
+
onData: (listener) => {
|
|
2099
|
+
dataListener = listener;
|
|
2100
|
+
},
|
|
2101
|
+
onExit: () => undefined,
|
|
2102
|
+
stop: () => undefined,
|
|
2103
|
+
close: async () => undefined,
|
|
2104
|
+
};
|
|
2105
|
+
// A short stall bound, so this test proves the drain below clears the
|
|
2106
|
+
// timer instead of merely finishing before a long one would have fired.
|
|
2107
|
+
const duplex = wrapDuplexChannelAsNodeDuplex(channel, {
|
|
2108
|
+
maxBufferedReadBytes: 10_000_000,
|
|
2109
|
+
readBackpressureStallMs: 20,
|
|
2110
|
+
});
|
|
2111
|
+
const received = [];
|
|
2112
|
+
const drainedAll = new Promise((resolve) => {
|
|
2113
|
+
duplex.on("data", (chunk) => {
|
|
2114
|
+
received.push(chunk);
|
|
2115
|
+
if (Buffer.concat(received).includes("-third-"))
|
|
2116
|
+
resolve();
|
|
2117
|
+
});
|
|
2118
|
+
});
|
|
2119
|
+
dataListener?.(Buffer.alloc(70_000, "a")); // passes the high-water mark; queues the chunks that follow.
|
|
2120
|
+
dataListener?.(Buffer.from("-second-"));
|
|
2121
|
+
dataListener?.(Buffer.from("-third-"));
|
|
2122
|
+
await drainedAll;
|
|
2123
|
+
// Wait past the stall bound with the channel now idle and the queue
|
|
2124
|
+
// empty. A timer the full drain above did not clear would fire here.
|
|
2125
|
+
await new Promise((resolve) => setTimeout(resolve, 60));
|
|
2126
|
+
expect(duplex.destroyed).toBe(false);
|
|
2127
|
+
});
|
|
2128
|
+
});
|
|
2129
|
+
//# sourceMappingURL=http2-bridge-server.test.js.map
|