mppx 0.6.31 → 0.7.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/CHANGELOG.md +17 -0
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +9 -7
- package/dist/Challenge.js.map +1 -1
- package/dist/Constants.d.ts +46 -0
- package/dist/Constants.d.ts.map +1 -0
- package/dist/Constants.js +46 -0
- package/dist/Constants.js.map +1 -0
- package/dist/Credential.d.ts.map +1 -1
- package/dist/Credential.js +5 -4
- package/dist/Credential.js.map +1 -1
- package/dist/Method.d.ts +32 -4
- package/dist/Method.d.ts.map +1 -1
- package/dist/Method.js +5 -2
- package/dist/Method.js.map +1 -1
- package/dist/Receipt.d.ts.map +1 -1
- package/dist/Receipt.js +3 -2
- package/dist/Receipt.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +19 -11
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/plugins/tempo.d.ts.map +1 -1
- package/dist/cli/plugins/tempo.js +17 -6
- package/dist/cli/plugins/tempo.js.map +1 -1
- package/dist/cli/utils.d.ts +5 -0
- package/dist/cli/utils.d.ts.map +1 -1
- package/dist/cli/utils.js +10 -0
- package/dist/cli/utils.js.map +1 -1
- package/dist/client/Methods.d.ts +5 -2
- package/dist/client/Methods.d.ts.map +1 -1
- package/dist/client/Methods.js +5 -2
- package/dist/client/Methods.js.map +1 -1
- package/dist/client/Transport.d.ts.map +1 -1
- package/dist/client/Transport.js +4 -5
- package/dist/client/Transport.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +14 -6
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/evm/server/Methods.d.ts +1 -1
- package/dist/evm/server/Methods.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/AcceptPayment.d.ts +3 -0
- package/dist/internal/AcceptPayment.d.ts.map +1 -1
- package/dist/internal/AcceptPayment.js +15 -11
- package/dist/internal/AcceptPayment.js.map +1 -1
- package/dist/mcp-sdk/client/McpClient.d.ts +12 -5
- package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -1
- package/dist/mcp-sdk/client/McpClient.js +55 -42
- package/dist/mcp-sdk/client/McpClient.js.map +1 -1
- package/dist/server/Mppx.d.ts +11 -3
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +76 -27
- package/dist/server/Mppx.js.map +1 -1
- package/dist/server/Response.d.ts.map +1 -1
- package/dist/server/Response.js +2 -1
- package/dist/server/Response.js.map +1 -1
- package/dist/server/Transport.d.ts.map +1 -1
- package/dist/server/Transport.js +4 -3
- package/dist/server/Transport.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/stripe/client/Charge.d.ts +1 -1
- package/dist/stripe/client/Charge.d.ts.map +1 -1
- package/dist/stripe/client/Charge.js +3 -1
- package/dist/stripe/client/Charge.js.map +1 -1
- package/dist/stripe/server/Charge.d.ts +1 -1
- package/dist/stripe/server/Charge.d.ts.map +1 -1
- package/dist/stripe/server/Charge.js +9 -2
- package/dist/stripe/server/Charge.js.map +1 -1
- package/dist/stripe/server/Methods.d.ts +1 -1
- package/dist/stripe/server/Methods.d.ts.map +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
- package/dist/stripe/server/internal/html.gen.js +1 -1
- package/dist/stripe/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/Methods.d.ts +18 -0
- package/dist/tempo/Methods.d.ts.map +1 -1
- package/dist/tempo/Methods.js +16 -1
- package/dist/tempo/Methods.js.map +1 -1
- package/dist/tempo/client/Charge.d.ts +6 -0
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +9 -2
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +36 -7
- package/dist/tempo/client/Methods.d.ts.map +1 -1
- package/dist/tempo/client/Methods.js +12 -5
- package/dist/tempo/client/Methods.js.map +1 -1
- package/dist/tempo/client/index.d.ts +7 -4
- package/dist/tempo/client/index.d.ts.map +1 -1
- package/dist/tempo/client/index.js +5 -3
- package/dist/tempo/client/index.js.map +1 -1
- package/dist/tempo/index.d.ts +1 -0
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +1 -0
- package/dist/tempo/index.js.map +1 -1
- package/dist/tempo/internal/fee-payer.d.ts +21 -1
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js +109 -4
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/{client → legacy/client}/ChannelOps.d.ts +19 -6
- package/dist/tempo/legacy/client/ChannelOps.d.ts.map +1 -0
- package/dist/tempo/{client → legacy/client}/ChannelOps.js +9 -3
- package/dist/tempo/legacy/client/ChannelOps.js.map +1 -0
- package/dist/tempo/{client → legacy/client}/Session.d.ts +23 -4
- package/dist/tempo/legacy/client/Session.d.ts.map +1 -0
- package/dist/tempo/{client → legacy/client}/Session.js +14 -7
- package/dist/tempo/legacy/client/Session.js.map +1 -0
- package/dist/tempo/{client → legacy/client}/SessionManager.d.ts +20 -5
- package/dist/tempo/legacy/client/SessionManager.d.ts.map +1 -0
- package/dist/tempo/{client → legacy/client}/SessionManager.js +20 -16
- package/dist/tempo/legacy/client/SessionManager.js.map +1 -0
- package/dist/tempo/legacy/client/index.d.ts +7 -0
- package/dist/tempo/legacy/client/index.d.ts.map +1 -0
- package/dist/tempo/legacy/client/index.js +5 -0
- package/dist/tempo/legacy/client/index.js.map +1 -0
- package/dist/tempo/legacy/index.d.ts +7 -0
- package/dist/tempo/legacy/index.d.ts.map +1 -0
- package/dist/tempo/legacy/index.js +7 -0
- package/dist/tempo/legacy/index.js.map +1 -0
- package/dist/tempo/{server → legacy/server}/Session.d.ts +28 -11
- package/dist/tempo/legacy/server/Session.d.ts.map +1 -0
- package/dist/tempo/{server → legacy/server}/Session.js +12 -10
- package/dist/tempo/legacy/server/Session.js.map +1 -0
- package/dist/tempo/legacy/server/index.d.ts +5 -0
- package/dist/tempo/legacy/server/index.d.ts.map +1 -0
- package/dist/tempo/legacy/server/index.js +5 -0
- package/dist/tempo/legacy/server/index.js.map +1 -0
- package/dist/tempo/{session → legacy/session}/Chain.d.ts +30 -23
- package/dist/tempo/legacy/session/Chain.d.ts.map +1 -0
- package/dist/tempo/{session → legacy/session}/Chain.js +12 -11
- package/dist/tempo/legacy/session/Chain.js.map +1 -0
- package/dist/tempo/{session → legacy/session}/Channel.d.ts +1 -0
- package/dist/tempo/legacy/session/Channel.d.ts.map +1 -0
- package/dist/tempo/legacy/session/Channel.js.map +1 -0
- package/dist/tempo/legacy/session/ChannelStore.d.ts +22 -0
- package/dist/tempo/legacy/session/ChannelStore.d.ts.map +1 -0
- package/dist/tempo/legacy/session/ChannelStore.js +6 -0
- package/dist/tempo/legacy/session/ChannelStore.js.map +1 -0
- package/dist/tempo/legacy/session/Types.d.ts +73 -0
- package/dist/tempo/legacy/session/Types.d.ts.map +1 -0
- package/dist/tempo/legacy/session/Types.js.map +1 -0
- package/dist/tempo/{session → legacy/session}/Voucher.d.ts +4 -4
- package/dist/tempo/legacy/session/Voucher.d.ts.map +1 -0
- package/dist/tempo/{session → legacy/session}/Voucher.js +1 -1
- package/dist/tempo/legacy/session/Voucher.js.map +1 -0
- package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts +1 -0
- package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts.map +1 -1
- package/dist/tempo/{session → legacy/session}/escrow.abi.js +1 -0
- package/dist/tempo/legacy/session/escrow.abi.js.map +1 -0
- package/dist/tempo/legacy/session/index.d.ts +9 -0
- package/dist/tempo/legacy/session/index.d.ts.map +1 -0
- package/dist/tempo/legacy/session/index.js +9 -0
- package/dist/tempo/legacy/session/index.js.map +1 -0
- package/dist/tempo/server/Charge.d.ts +1 -1
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +13 -16
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +63 -6
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Methods.js +36 -8
- package/dist/tempo/server/Methods.js.map +1 -1
- package/dist/tempo/server/Subscription.d.ts +1 -1
- package/dist/tempo/server/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/index.d.ts +6 -5
- package/dist/tempo/server/index.d.ts.map +1 -1
- package/dist/tempo/server/index.js +5 -5
- package/dist/tempo/server/index.js.map +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/server/internal/request-body.d.ts +7 -2
- package/dist/tempo/server/internal/request-body.d.ts.map +1 -1
- package/dist/tempo/server/internal/request-body.js +20 -3
- package/dist/tempo/server/internal/request-body.js.map +1 -1
- package/dist/tempo/server/internal/transport.d.ts +8 -4
- package/dist/tempo/server/internal/transport.d.ts.map +1 -1
- package/dist/tempo/server/internal/transport.js +8 -7
- package/dist/tempo/server/internal/transport.js.map +1 -1
- package/dist/tempo/session/Snapshot.d.ts +32 -0
- package/dist/tempo/session/Snapshot.d.ts.map +1 -0
- package/dist/tempo/session/Snapshot.js +37 -0
- package/dist/tempo/session/Snapshot.js.map +1 -0
- package/dist/tempo/session/client/ChannelOps.d.ts +82 -0
- package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -0
- package/dist/tempo/session/client/ChannelOps.js +204 -0
- package/dist/tempo/session/client/ChannelOps.js.map +1 -0
- package/dist/tempo/session/client/CredentialState.d.ts +262 -0
- package/dist/tempo/session/client/CredentialState.d.ts.map +1 -0
- package/dist/tempo/session/client/CredentialState.js +417 -0
- package/dist/tempo/session/client/CredentialState.js.map +1 -0
- package/dist/tempo/session/client/ReceiptCoordinator.d.ts +26 -0
- package/dist/tempo/session/client/ReceiptCoordinator.d.ts.map +1 -0
- package/dist/tempo/session/client/ReceiptCoordinator.js +61 -0
- package/dist/tempo/session/client/ReceiptCoordinator.js.map +1 -0
- package/dist/tempo/session/client/Runtime.d.ts +464 -0
- package/dist/tempo/session/client/Runtime.d.ts.map +1 -0
- package/dist/tempo/session/client/Runtime.js +499 -0
- package/dist/tempo/session/client/Runtime.js.map +1 -0
- package/dist/tempo/session/client/Session.d.ts +132 -0
- package/dist/tempo/session/client/Session.d.ts.map +1 -0
- package/dist/tempo/session/client/Session.js +55 -0
- package/dist/tempo/session/client/Session.js.map +1 -0
- package/dist/tempo/session/client/SessionManager.d.ts +120 -0
- package/dist/tempo/session/client/SessionManager.d.ts.map +1 -0
- package/dist/tempo/session/client/SessionManager.js +627 -0
- package/dist/tempo/session/client/SessionManager.js.map +1 -0
- package/dist/tempo/session/client/Transports.d.ts +449 -0
- package/dist/tempo/session/client/Transports.d.ts.map +1 -0
- package/dist/tempo/session/client/Transports.js +721 -0
- package/dist/tempo/session/client/Transports.js.map +1 -0
- package/dist/tempo/session/client/index.d.ts +12 -0
- package/dist/tempo/session/client/index.d.ts.map +1 -0
- package/dist/tempo/session/client/index.js +5 -0
- package/dist/tempo/session/client/index.js.map +1 -0
- package/dist/tempo/session/index.d.ts +7 -8
- package/dist/tempo/session/index.d.ts.map +1 -1
- package/dist/tempo/session/index.js +7 -8
- package/dist/tempo/session/index.js.map +1 -1
- package/dist/tempo/session/precompile/Chain.d.ts +319 -0
- package/dist/tempo/session/precompile/Chain.d.ts.map +1 -0
- package/dist/tempo/session/precompile/Chain.js +492 -0
- package/dist/tempo/session/precompile/Chain.js.map +1 -0
- package/dist/tempo/session/precompile/Channel.d.ts +46 -0
- package/dist/tempo/session/precompile/Channel.d.ts.map +1 -0
- package/dist/tempo/session/precompile/Channel.js +56 -0
- package/dist/tempo/session/precompile/Channel.js.map +1 -0
- package/dist/tempo/session/precompile/Protocol.d.ts +308 -0
- package/dist/tempo/session/precompile/Protocol.d.ts.map +1 -0
- package/dist/tempo/session/precompile/Protocol.js +264 -0
- package/dist/tempo/session/precompile/Protocol.js.map +1 -0
- package/dist/tempo/session/precompile/Voucher.d.ts +40 -0
- package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -0
- package/dist/tempo/session/precompile/Voucher.js +126 -0
- package/dist/tempo/session/precompile/Voucher.js.map +1 -0
- package/dist/tempo/session/precompile/escrow.abi.d.ts +522 -0
- package/dist/tempo/session/precompile/escrow.abi.d.ts.map +1 -0
- package/dist/tempo/session/precompile/escrow.abi.js +224 -0
- package/dist/tempo/session/precompile/escrow.abi.js.map +1 -0
- package/dist/tempo/session/precompile/index.d.ts +24 -0
- package/dist/tempo/session/precompile/index.d.ts.map +1 -0
- package/dist/tempo/session/precompile/index.js +22 -0
- package/dist/tempo/session/precompile/index.js.map +1 -0
- package/dist/tempo/session/server/ChannelOps.d.ts +56 -0
- package/dist/tempo/session/server/ChannelOps.d.ts.map +1 -0
- package/dist/tempo/session/server/ChannelOps.js +91 -0
- package/dist/tempo/session/server/ChannelOps.js.map +1 -0
- package/dist/tempo/session/server/ChannelStore.d.ts +347 -0
- package/dist/tempo/session/server/ChannelStore.d.ts.map +1 -0
- package/dist/tempo/session/server/ChannelStore.js +404 -0
- package/dist/tempo/session/server/ChannelStore.js.map +1 -0
- package/dist/tempo/session/server/CredentialVerification.d.ts +85 -0
- package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -0
- package/dist/tempo/session/server/CredentialVerification.js +494 -0
- package/dist/tempo/session/server/CredentialVerification.js.map +1 -0
- package/dist/tempo/session/server/MeteredStream.d.ts +40 -0
- package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -0
- package/dist/tempo/session/server/MeteredStream.js +42 -0
- package/dist/tempo/session/server/MeteredStream.js.map +1 -0
- package/dist/tempo/session/server/RequestState.d.ts +208 -0
- package/dist/tempo/session/server/RequestState.d.ts.map +1 -0
- package/dist/tempo/session/server/RequestState.js +252 -0
- package/dist/tempo/session/server/RequestState.js.map +1 -0
- package/dist/tempo/session/server/Session.d.ts +169 -0
- package/dist/tempo/session/server/Session.d.ts.map +1 -0
- package/dist/tempo/session/server/Session.js +351 -0
- package/dist/tempo/session/server/Session.js.map +1 -0
- package/dist/tempo/session/server/Settlement.d.ts +185 -0
- package/dist/tempo/session/server/Settlement.d.ts.map +1 -0
- package/dist/tempo/session/server/Settlement.js +250 -0
- package/dist/tempo/session/server/Settlement.js.map +1 -0
- package/dist/tempo/session/{Sse.d.ts → server/Sse.d.ts} +9 -56
- package/dist/tempo/session/server/Sse.d.ts.map +1 -0
- package/dist/tempo/session/server/Sse.js +184 -0
- package/dist/tempo/session/server/Sse.js.map +1 -0
- package/dist/tempo/session/server/Transports.d.ts +89 -0
- package/dist/tempo/session/server/Transports.d.ts.map +1 -0
- package/dist/tempo/session/server/Transports.js +149 -0
- package/dist/tempo/session/server/Transports.js.map +1 -0
- package/dist/tempo/session/server/Ws.d.ts +48 -0
- package/dist/tempo/session/server/Ws.d.ts.map +1 -0
- package/dist/tempo/session/server/Ws.js +244 -0
- package/dist/tempo/session/server/Ws.js.map +1 -0
- package/dist/tempo/session/server/index.d.ts +4 -0
- package/dist/tempo/session/server/index.d.ts.map +1 -0
- package/dist/tempo/session/server/index.js +2 -0
- package/dist/tempo/session/server/index.js.map +1 -0
- package/package.json +6 -1
- package/src/Challenge.ts +9 -7
- package/src/Constants.ts +58 -0
- package/src/Credential.ts +5 -4
- package/src/Method.ts +46 -5
- package/src/Receipt.ts +3 -2
- package/src/cli/cli.test.ts +23 -28
- package/src/cli/cli.ts +23 -10
- package/src/cli/mcp.test.ts +21 -7
- package/src/cli/plugins/tempo.ts +21 -8
- package/src/cli/utils.test.ts +25 -1
- package/src/cli/utils.ts +10 -0
- package/src/client/Methods.ts +5 -2
- package/src/client/Mppx.test-d.ts +10 -0
- package/src/client/Mppx.test.ts +75 -0
- package/src/client/Transport.ts +4 -5
- package/src/client/index.ts +11 -1
- package/src/client/internal/Fetch.test.ts +29 -4
- package/src/client/internal/Fetch.ts +17 -5
- package/src/env.d.ts +1 -1
- package/src/index.ts +1 -0
- package/src/internal/AcceptPayment.test.ts +61 -0
- package/src/internal/AcceptPayment.ts +21 -14
- package/src/mcp-sdk/client/McpClient.integration.test.ts +8 -7
- package/src/mcp-sdk/client/McpClient.test-d.ts +7 -0
- package/src/mcp-sdk/client/McpClient.ts +99 -67
- package/src/mcp-sdk/client/McpClient.unit.test.ts +131 -0
- package/src/middlewares/elysia.test.ts +8 -4
- package/src/middlewares/express.test.ts +8 -4
- package/src/middlewares/hono.test.ts +4 -4
- package/src/middlewares/nextjs.test.ts +8 -4
- package/src/proxy/Proxy.test.ts +8 -8
- package/src/server/Mppx.test-d.ts +54 -0
- package/src/server/Mppx.test.ts +200 -7
- package/src/server/Mppx.ts +487 -406
- package/src/server/Response.ts +2 -1
- package/src/server/Transport.ts +4 -3
- package/src/server/index.ts +1 -0
- package/src/stripe/client/Charge.test.ts +20 -5
- package/src/stripe/client/Charge.ts +6 -2
- package/src/stripe/server/Charge.test.ts +114 -1
- package/src/stripe/server/Charge.ts +13 -2
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/tempo/AccessKeyAuthorization.test.ts +4 -94
- package/src/tempo/Methods.test.ts +45 -17
- package/src/tempo/Methods.ts +22 -0
- package/src/tempo/PublicExports.test-d.ts +105 -0
- package/src/tempo/client/Charge.test.ts +85 -0
- package/src/tempo/client/Charge.ts +19 -2
- package/src/tempo/client/Methods.ts +18 -6
- package/src/tempo/client/index.ts +15 -4
- package/src/tempo/index.ts +1 -0
- package/src/tempo/internal/fee-payer.test.ts +241 -17
- package/src/tempo/internal/fee-payer.ts +150 -4
- package/src/tempo/internal/fee-token.test.ts +14 -9
- package/src/tempo/legacy/AccessKeyAuthorization.test.ts +162 -0
- package/src/tempo/legacy/README.md +9 -0
- package/src/tempo/{client → legacy/client}/ChannelOps.test.ts +6 -7
- package/src/tempo/{client → legacy/client}/ChannelOps.ts +22 -9
- package/src/tempo/{client → legacy/client}/Session.test.ts +51 -9
- package/src/tempo/{client → legacy/client}/Session.ts +25 -11
- package/src/tempo/{client → legacy/client}/SessionManager.test.ts +81 -9
- package/src/tempo/{client → legacy/client}/SessionManager.ts +41 -20
- package/src/tempo/legacy/client/index.ts +6 -0
- package/src/tempo/legacy/index.ts +6 -0
- package/src/tempo/{server → legacy/server}/Session.test.ts +45 -45
- package/src/tempo/{server → legacy/server}/Session.ts +32 -23
- package/src/tempo/legacy/server/index.ts +4 -0
- package/src/tempo/{session → legacy/session}/Chain.test.ts +3 -4
- package/src/tempo/{session → legacy/session}/Chain.ts +94 -63
- package/src/tempo/{session → legacy/session}/Channel.ts +1 -0
- package/src/tempo/legacy/session/ChannelStore.test.ts +58 -0
- package/src/tempo/legacy/session/ChannelStore.ts +39 -0
- package/src/tempo/legacy/session/Types.ts +91 -0
- package/src/tempo/{session → legacy/session}/Voucher.ts +12 -8
- package/src/tempo/{session → legacy/session}/escrow.abi.ts +1 -0
- package/src/tempo/legacy/session/index.ts +8 -0
- package/src/tempo/server/AtomicStore.test-d.ts +16 -11
- package/src/tempo/server/Charge.test.ts +92 -14
- package/src/tempo/server/Charge.ts +18 -16
- package/src/tempo/server/Methods.ts +54 -8
- package/src/tempo/server/Sse.test.ts +2 -2
- package/src/tempo/server/index.ts +6 -5
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/server/internal/request-body.test.ts +37 -4
- package/src/tempo/server/internal/request-body.ts +25 -6
- package/src/tempo/server/internal/transport.test.ts +4 -4
- package/src/tempo/server/internal/transport.ts +19 -10
- package/src/tempo/session/Snapshot.test.ts +41 -0
- package/src/tempo/session/Snapshot.ts +74 -0
- package/src/tempo/session/client/ChannelOps.test.ts +163 -0
- package/src/tempo/session/client/ChannelOps.ts +344 -0
- package/src/tempo/session/client/CredentialState.test.ts +645 -0
- package/src/tempo/session/client/CredentialState.ts +814 -0
- package/src/tempo/session/client/ReceiptCoordinator.ts +95 -0
- package/src/tempo/session/client/Runtime.test.ts +1092 -0
- package/src/tempo/session/client/Runtime.ts +986 -0
- package/src/tempo/session/client/Session.test.ts +734 -0
- package/src/tempo/session/client/Session.ts +97 -0
- package/src/tempo/session/client/SessionManager.test.ts +1308 -0
- package/src/tempo/session/client/SessionManager.ts +845 -0
- package/src/tempo/session/client/Transports.test.ts +837 -0
- package/src/tempo/session/client/Transports.ts +1292 -0
- package/src/tempo/session/client/index.ts +37 -0
- package/src/tempo/session/index.ts +7 -8
- package/src/tempo/session/precompile/Chain.integration.test.ts +321 -0
- package/src/tempo/session/precompile/Chain.test.ts +1258 -0
- package/src/tempo/session/precompile/Chain.ts +979 -0
- package/src/tempo/session/precompile/Channel.test.ts +138 -0
- package/src/tempo/session/precompile/Channel.ts +103 -0
- package/src/tempo/session/precompile/Protocol.test.ts +358 -0
- package/src/tempo/session/precompile/Protocol.ts +520 -0
- package/src/tempo/session/precompile/Voucher.test.ts +316 -0
- package/src/tempo/session/precompile/Voucher.ts +160 -0
- package/src/tempo/session/precompile/escrow.abi.ts +226 -0
- package/src/tempo/session/precompile/index.ts +33 -0
- package/src/tempo/session/server/ChannelOps.test.ts +129 -0
- package/src/tempo/session/server/ChannelOps.ts +157 -0
- package/src/tempo/session/{ChannelStore.test.ts → server/ChannelStore.test.ts} +536 -29
- package/src/tempo/session/server/ChannelStore.ts +835 -0
- package/src/tempo/session/server/CredentialVerification.test.ts +146 -0
- package/src/tempo/session/server/CredentialVerification.ts +710 -0
- package/src/tempo/session/server/MeteredStream.ts +88 -0
- package/src/tempo/session/server/RequestState.test.ts +531 -0
- package/src/tempo/session/server/RequestState.ts +499 -0
- package/src/tempo/session/server/Session.integration.test.ts +444 -0
- package/src/tempo/session/server/Session.test.ts +3253 -0
- package/src/tempo/session/server/Session.ts +543 -0
- package/src/tempo/session/server/Settlement.test.ts +242 -0
- package/src/tempo/session/server/Settlement.ts +470 -0
- package/src/tempo/session/{Sse.test.ts → server/Sse.test.ts} +37 -3
- package/src/tempo/session/server/Sse.ts +256 -0
- package/src/tempo/session/server/Transports.test.ts +346 -0
- package/src/tempo/session/server/Transports.ts +255 -0
- package/src/tempo/session/{Ws.test.ts → server/Ws.test.ts} +4 -4
- package/src/tempo/session/server/Ws.ts +384 -0
- package/src/tempo/session/server/index.ts +8 -0
- package/dist/tempo/client/ChannelOps.d.ts.map +0 -1
- package/dist/tempo/client/ChannelOps.js.map +0 -1
- package/dist/tempo/client/Session.d.ts.map +0 -1
- package/dist/tempo/client/Session.js.map +0 -1
- package/dist/tempo/client/SessionManager.d.ts.map +0 -1
- package/dist/tempo/client/SessionManager.js.map +0 -1
- package/dist/tempo/server/Session.d.ts.map +0 -1
- package/dist/tempo/server/Session.js.map +0 -1
- package/dist/tempo/session/Chain.d.ts.map +0 -1
- package/dist/tempo/session/Chain.js.map +0 -1
- package/dist/tempo/session/Channel.d.ts.map +0 -1
- package/dist/tempo/session/Channel.js.map +0 -1
- package/dist/tempo/session/ChannelStore.d.ts +0 -117
- package/dist/tempo/session/ChannelStore.d.ts.map +0 -1
- package/dist/tempo/session/ChannelStore.js +0 -172
- package/dist/tempo/session/ChannelStore.js.map +0 -1
- package/dist/tempo/session/Receipt.d.ts +0 -22
- package/dist/tempo/session/Receipt.d.ts.map +0 -1
- package/dist/tempo/session/Receipt.js +0 -34
- package/dist/tempo/session/Receipt.js.map +0 -1
- package/dist/tempo/session/Sse.d.ts.map +0 -1
- package/dist/tempo/session/Sse.js +0 -363
- package/dist/tempo/session/Sse.js.map +0 -1
- package/dist/tempo/session/Types.d.ts +0 -78
- package/dist/tempo/session/Types.d.ts.map +0 -1
- package/dist/tempo/session/Types.js.map +0 -1
- package/dist/tempo/session/Voucher.d.ts.map +0 -1
- package/dist/tempo/session/Voucher.js.map +0 -1
- package/dist/tempo/session/Ws.d.ts +0 -87
- package/dist/tempo/session/Ws.d.ts.map +0 -1
- package/dist/tempo/session/Ws.js +0 -443
- package/dist/tempo/session/Ws.js.map +0 -1
- package/dist/tempo/session/escrow.abi.js.map +0 -1
- package/src/tempo/session/ChannelStore.ts +0 -308
- package/src/tempo/session/Receipt.test.ts +0 -89
- package/src/tempo/session/Receipt.ts +0 -46
- package/src/tempo/session/Sse.ts +0 -462
- package/src/tempo/session/Types.ts +0 -86
- package/src/tempo/session/Ws.ts +0 -576
- /package/dist/tempo/{session → legacy/session}/Channel.js +0 -0
- /package/dist/tempo/{session → legacy/session}/Types.js +0 -0
- /package/src/tempo/{session → legacy/session}/Channel.test.ts +0 -0
- /package/src/tempo/{session → legacy/session}/Voucher.test.ts +0 -0
- /package/src/tempo/session/{Sse.fuzz.test.ts → server/Sse.fuzz.test.ts} +0 -0
|
@@ -0,0 +1,986 @@
|
|
|
1
|
+
import { parseUnits, type Hex } from 'viem'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ChannelDescriptor,
|
|
5
|
+
NeedVoucherEvent,
|
|
6
|
+
RawAmountString,
|
|
7
|
+
SessionCredentialPayload,
|
|
8
|
+
SessionReceipt,
|
|
9
|
+
} from '../precompile/Protocol.js'
|
|
10
|
+
import * as Ws from '../precompile/Protocol.js'
|
|
11
|
+
import type { SessionSnapshot } from '../Snapshot.js'
|
|
12
|
+
import type { ChannelEntry } from './ChannelOps.js'
|
|
13
|
+
import type { SessionContext } from './CredentialState.js'
|
|
14
|
+
import type { TempoSessionChallenge } from './Transports.js'
|
|
15
|
+
import type { ActiveSocketSession } from './Transports.js'
|
|
16
|
+
|
|
17
|
+
export { deserializeSnapshot, serializeSnapshot } from '../Snapshot.js'
|
|
18
|
+
export type { SessionSnapshot } from '../Snapshot.js'
|
|
19
|
+
|
|
20
|
+
/** Initial manager state before a session challenge is observed. */
|
|
21
|
+
export type IdleSessionState = { status: 'idle' }
|
|
22
|
+
|
|
23
|
+
/** State after a tempo/session challenge has been selected but before a credential is created. */
|
|
24
|
+
export type ChallengedSessionState = { status: 'challenged'; challengeId: string }
|
|
25
|
+
|
|
26
|
+
/** State while a server snapshot is being used to hydrate a reusable channel. */
|
|
27
|
+
export type HydratingSessionState = {
|
|
28
|
+
status: 'hydrating'
|
|
29
|
+
challengeId: string
|
|
30
|
+
snapshot: SessionSnapshot
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** State while the client is creating or submitting an opening channel credential. */
|
|
34
|
+
export type OpeningSessionState = { status: 'opening'; challengeId: string }
|
|
35
|
+
|
|
36
|
+
/** Active state variant used after a channel is opened, hydrated, or receives a receipt. */
|
|
37
|
+
export type ActiveSessionState = {
|
|
38
|
+
status: 'active'
|
|
39
|
+
challengeId: string
|
|
40
|
+
channelId: Hex
|
|
41
|
+
descriptor: ChannelDescriptor
|
|
42
|
+
/** Highest cumulative voucher amount accepted by the server. */
|
|
43
|
+
acceptedCumulative: RawAmountString
|
|
44
|
+
/** Current channel deposit ceiling, tracked independently from accepted cumulative spend. */
|
|
45
|
+
deposit: RawAmountString
|
|
46
|
+
/** Amount actually consumed by delivered work/content. */
|
|
47
|
+
spent: RawAmountString
|
|
48
|
+
/** Paid units delivered by the server. */
|
|
49
|
+
units: number
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** State when the server needs a larger cumulative voucher but no top-up is needed. */
|
|
53
|
+
export type VoucherNeededSessionState = {
|
|
54
|
+
status: 'voucherNeeded'
|
|
55
|
+
challengeId: string
|
|
56
|
+
channelId: Hex
|
|
57
|
+
descriptor: ChannelDescriptor
|
|
58
|
+
requiredCumulative: RawAmountString
|
|
59
|
+
deposit: RawAmountString
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** State when the server-required cumulative amount exceeds current channel deposit. */
|
|
63
|
+
export type ToppingUpSessionState = {
|
|
64
|
+
status: 'toppingUp'
|
|
65
|
+
challengeId: string
|
|
66
|
+
channelId: Hex
|
|
67
|
+
descriptor: ChannelDescriptor
|
|
68
|
+
deposit: RawAmountString
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** State while the server is settling accepted voucher spend on-chain. */
|
|
72
|
+
export type SettlingSessionState = {
|
|
73
|
+
status: 'settling'
|
|
74
|
+
channelId: Hex
|
|
75
|
+
descriptor: ChannelDescriptor
|
|
76
|
+
deposit: RawAmountString
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** State after unilateral close has been requested and withdrawal is not yet available. */
|
|
80
|
+
export type CloseRequestedSessionState = {
|
|
81
|
+
status: 'closeRequested'
|
|
82
|
+
channelId: Hex
|
|
83
|
+
descriptor: ChannelDescriptor
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** State after the unilateral close delay has elapsed and funds can be withdrawn. */
|
|
87
|
+
export type WithdrawableSessionState = {
|
|
88
|
+
status: 'withdrawable'
|
|
89
|
+
channelId: Hex
|
|
90
|
+
descriptor: ChannelDescriptor
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** State while a cooperative close credential or close transaction is in flight. */
|
|
94
|
+
export type ClosingSessionState = {
|
|
95
|
+
status: 'closing'
|
|
96
|
+
channelId: Hex
|
|
97
|
+
descriptor: ChannelDescriptor
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Terminal state after channel close finalization. */
|
|
101
|
+
export type ClosedSessionState = {
|
|
102
|
+
status: 'closed'
|
|
103
|
+
channelId: Hex
|
|
104
|
+
descriptor: ChannelDescriptor
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Pure state-machine state for a TIP-1034 session. */
|
|
108
|
+
export type SessionState =
|
|
109
|
+
| IdleSessionState
|
|
110
|
+
| ChallengedSessionState
|
|
111
|
+
| HydratingSessionState
|
|
112
|
+
| OpeningSessionState
|
|
113
|
+
| ActiveSessionState
|
|
114
|
+
| VoucherNeededSessionState
|
|
115
|
+
| ToppingUpSessionState
|
|
116
|
+
| SettlingSessionState
|
|
117
|
+
| CloseRequestedSessionState
|
|
118
|
+
| WithdrawableSessionState
|
|
119
|
+
| ClosingSessionState
|
|
120
|
+
| ClosedSessionState
|
|
121
|
+
|
|
122
|
+
/** Data required to construct active session state. */
|
|
123
|
+
export type CreateActiveStateParameters = Omit<ActiveSessionState, 'status'>
|
|
124
|
+
|
|
125
|
+
/** State variants that can follow a need-voucher event. */
|
|
126
|
+
export type NeedVoucherSessionState = VoucherNeededSessionState | ToppingUpSessionState
|
|
127
|
+
|
|
128
|
+
/** Events accepted by the pure session reducer. */
|
|
129
|
+
export type SessionEvent =
|
|
130
|
+
| { type: 'challengeReceived'; challengeId: string }
|
|
131
|
+
| { type: 'challenge'; challengeId: string; snapshot?: SessionSnapshot | undefined }
|
|
132
|
+
| {
|
|
133
|
+
type: 'activated'
|
|
134
|
+
challengeId: string
|
|
135
|
+
entry: ChannelEntry
|
|
136
|
+
spent: RawAmountString
|
|
137
|
+
units?: number | undefined
|
|
138
|
+
}
|
|
139
|
+
| {
|
|
140
|
+
type: 'opened'
|
|
141
|
+
receipt: SessionReceipt
|
|
142
|
+
descriptor: ChannelDescriptor
|
|
143
|
+
deposit: RawAmountString
|
|
144
|
+
}
|
|
145
|
+
| { type: 'hydrated'; snapshot: SessionSnapshot }
|
|
146
|
+
| { type: 'receiptAccepted'; receipt: SessionReceipt; entry: ChannelEntry }
|
|
147
|
+
| { type: 'needVoucher'; event: NeedVoucherEvent; descriptor: ChannelDescriptor }
|
|
148
|
+
| { type: 'topUpStarted' }
|
|
149
|
+
| { type: 'voucherAccepted'; receipt: SessionReceipt; deposit?: string | undefined }
|
|
150
|
+
| { type: 'settleStarted' }
|
|
151
|
+
| { type: 'settled'; receipt: SessionReceipt; deposit?: string | undefined }
|
|
152
|
+
| { type: 'closeRequested' }
|
|
153
|
+
| { type: 'withdrawable' }
|
|
154
|
+
| { type: 'closeStarted' }
|
|
155
|
+
| { type: 'closed'; receipt?: SessionReceipt | undefined }
|
|
156
|
+
|
|
157
|
+
/** IO work requested by the pure reducer. */
|
|
158
|
+
export type SessionEffect =
|
|
159
|
+
| { type: 'hydrate'; snapshot: SessionSnapshot }
|
|
160
|
+
| { type: 'open' }
|
|
161
|
+
| { type: 'topUp'; channelId: Hex; amount: string }
|
|
162
|
+
| { type: 'voucher'; payload?: SessionCredentialPayload | undefined }
|
|
163
|
+
| { type: 'settle'; channelId: Hex }
|
|
164
|
+
| { type: 'requestClose'; channelId: Hex }
|
|
165
|
+
| { type: 'withdraw'; channelId: Hex }
|
|
166
|
+
| { type: 'close'; channelId: Hex }
|
|
167
|
+
|
|
168
|
+
/** Effects emitted by need-voucher transition planning. */
|
|
169
|
+
export type NeedVoucherSessionEffect =
|
|
170
|
+
| Extract<SessionEffect, { type: 'topUp' }>
|
|
171
|
+
| Extract<SessionEffect, { type: 'voucher' }>
|
|
172
|
+
|
|
173
|
+
/** Inputs for deciding whether a need-voucher event needs a voucher or deposit top-up first. */
|
|
174
|
+
export type ResolveNeedVoucherTransitionParameters = {
|
|
175
|
+
/** Current challenge ID retained by the active session state. */
|
|
176
|
+
challengeId: string
|
|
177
|
+
/** Descriptor for the channel requiring more authorization. */
|
|
178
|
+
descriptor: ChannelDescriptor
|
|
179
|
+
/** Server event describing required cumulative authorization and current deposit. */
|
|
180
|
+
event: NeedVoucherEvent
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Result of the need-voucher transition decision. */
|
|
184
|
+
export type NeedVoucherTransition = {
|
|
185
|
+
/** Next machine state. */
|
|
186
|
+
state: NeedVoucherSessionState
|
|
187
|
+
/** Driver effects required to satisfy the server request. */
|
|
188
|
+
effects: NeedVoucherSessionEffect[]
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Return value for every pure state-machine transition. */
|
|
192
|
+
export type SessionTransition = {
|
|
193
|
+
/** State after applying the event. */
|
|
194
|
+
state: SessionState
|
|
195
|
+
/** Declarative IO requested from the transport/precompile driver. */
|
|
196
|
+
effects: SessionEffect[]
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Initial state for a TIP-1034 session state machine. */
|
|
200
|
+
export const initialState = { status: 'idle' } satisfies SessionState
|
|
201
|
+
|
|
202
|
+
/** Constructs the canonical active state shape for the reducer and transport drivers. */
|
|
203
|
+
export function createActiveState(parameters: CreateActiveStateParameters): ActiveSessionState {
|
|
204
|
+
return { status: 'active', ...parameters }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Applies a state-machine event and returns the next state plus requested effects. */
|
|
208
|
+
export function reduce(state: SessionState, event: SessionEvent): SessionTransition {
|
|
209
|
+
switch (event.type) {
|
|
210
|
+
case 'challengeReceived':
|
|
211
|
+
if (state.status === 'closing' || state.status === 'closed') return invalid(state, event)
|
|
212
|
+
return {
|
|
213
|
+
state: { status: 'challenged', challengeId: event.challengeId },
|
|
214
|
+
effects: [],
|
|
215
|
+
}
|
|
216
|
+
case 'challenge': {
|
|
217
|
+
if (state.status !== 'idle' && state.status !== 'active') return invalid(state, event)
|
|
218
|
+
if (event.snapshot) {
|
|
219
|
+
return {
|
|
220
|
+
state: { status: 'hydrating', challengeId: event.challengeId, snapshot: event.snapshot },
|
|
221
|
+
effects: [{ type: 'hydrate', snapshot: event.snapshot }],
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
state: { status: 'opening', challengeId: event.challengeId },
|
|
226
|
+
effects: [{ type: 'open' }],
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
case 'hydrated':
|
|
230
|
+
if (state.status !== 'hydrating') return invalid(state, event)
|
|
231
|
+
return {
|
|
232
|
+
state: activeFromSnapshot(state.challengeId, event.snapshot),
|
|
233
|
+
effects: [],
|
|
234
|
+
}
|
|
235
|
+
case 'activated':
|
|
236
|
+
if (state.status === 'closing' || state.status === 'closed') return invalid(state, event)
|
|
237
|
+
return {
|
|
238
|
+
state: activeStateFromChannel({
|
|
239
|
+
challengeId: event.challengeId,
|
|
240
|
+
entry: event.entry,
|
|
241
|
+
spent: event.spent,
|
|
242
|
+
units: event.units ?? 0,
|
|
243
|
+
}),
|
|
244
|
+
effects: [],
|
|
245
|
+
}
|
|
246
|
+
case 'opened':
|
|
247
|
+
if (state.status !== 'opening') return invalid(state, event)
|
|
248
|
+
return {
|
|
249
|
+
state: activeFromReceipt(state.challengeId, event.receipt, event.descriptor, event.deposit),
|
|
250
|
+
effects: [],
|
|
251
|
+
}
|
|
252
|
+
case 'receiptAccepted':
|
|
253
|
+
if (state.status === 'closing' || state.status === 'closed') return invalid(state, event)
|
|
254
|
+
return {
|
|
255
|
+
state: activeStateFromReceipt(event.receipt, event.entry),
|
|
256
|
+
effects: [],
|
|
257
|
+
}
|
|
258
|
+
case 'needVoucher': {
|
|
259
|
+
if (state.status !== 'active') return invalid(state, event)
|
|
260
|
+
return resolveNeedVoucherTransition({
|
|
261
|
+
challengeId: state.challengeId,
|
|
262
|
+
descriptor: event.descriptor,
|
|
263
|
+
event: event.event,
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
case 'topUpStarted':
|
|
267
|
+
if (state.status !== 'voucherNeeded') return invalid(state, event)
|
|
268
|
+
return {
|
|
269
|
+
state: {
|
|
270
|
+
status: 'toppingUp',
|
|
271
|
+
challengeId: state.challengeId,
|
|
272
|
+
channelId: state.channelId,
|
|
273
|
+
descriptor: state.descriptor,
|
|
274
|
+
deposit: state.deposit,
|
|
275
|
+
},
|
|
276
|
+
effects: [],
|
|
277
|
+
}
|
|
278
|
+
case 'voucherAccepted':
|
|
279
|
+
if (state.status !== 'voucherNeeded' && state.status !== 'toppingUp')
|
|
280
|
+
return invalid(state, event)
|
|
281
|
+
return {
|
|
282
|
+
state: activeFromReceipt(
|
|
283
|
+
state.challengeId,
|
|
284
|
+
event.receipt,
|
|
285
|
+
state.descriptor,
|
|
286
|
+
event.deposit ?? state.deposit,
|
|
287
|
+
),
|
|
288
|
+
effects: [],
|
|
289
|
+
}
|
|
290
|
+
case 'settleStarted':
|
|
291
|
+
if (state.status !== 'active') return invalid(state, event)
|
|
292
|
+
return {
|
|
293
|
+
state: {
|
|
294
|
+
status: 'settling',
|
|
295
|
+
channelId: state.channelId,
|
|
296
|
+
descriptor: state.descriptor,
|
|
297
|
+
deposit: state.deposit,
|
|
298
|
+
},
|
|
299
|
+
effects: [{ type: 'settle', channelId: state.channelId }],
|
|
300
|
+
}
|
|
301
|
+
case 'settled':
|
|
302
|
+
if (state.status !== 'settling') return invalid(state, event)
|
|
303
|
+
return {
|
|
304
|
+
state: activeFromReceipt(
|
|
305
|
+
event.receipt.challengeId,
|
|
306
|
+
event.receipt,
|
|
307
|
+
state.descriptor,
|
|
308
|
+
event.deposit ?? state.deposit,
|
|
309
|
+
),
|
|
310
|
+
effects: [],
|
|
311
|
+
}
|
|
312
|
+
case 'closeRequested':
|
|
313
|
+
if (state.status !== 'active') return invalid(state, event)
|
|
314
|
+
return {
|
|
315
|
+
state: {
|
|
316
|
+
status: 'closeRequested',
|
|
317
|
+
channelId: state.channelId,
|
|
318
|
+
descriptor: state.descriptor,
|
|
319
|
+
},
|
|
320
|
+
effects: [{ type: 'requestClose', channelId: state.channelId }],
|
|
321
|
+
}
|
|
322
|
+
case 'withdrawable':
|
|
323
|
+
if (state.status !== 'closeRequested') return invalid(state, event)
|
|
324
|
+
return {
|
|
325
|
+
state: { status: 'withdrawable', channelId: state.channelId, descriptor: state.descriptor },
|
|
326
|
+
effects: [{ type: 'withdraw', channelId: state.channelId }],
|
|
327
|
+
}
|
|
328
|
+
case 'closeStarted':
|
|
329
|
+
if (
|
|
330
|
+
state.status !== 'active' &&
|
|
331
|
+
state.status !== 'withdrawable' &&
|
|
332
|
+
state.status !== 'closeRequested'
|
|
333
|
+
)
|
|
334
|
+
return invalid(state, event)
|
|
335
|
+
return {
|
|
336
|
+
state: { status: 'closing', channelId: state.channelId, descriptor: state.descriptor },
|
|
337
|
+
effects: [{ type: 'close', channelId: state.channelId }],
|
|
338
|
+
}
|
|
339
|
+
case 'closed':
|
|
340
|
+
if (state.status !== 'closing' && state.status !== 'withdrawable')
|
|
341
|
+
return invalid(state, event)
|
|
342
|
+
return {
|
|
343
|
+
state: { status: 'closed', channelId: state.channelId, descriptor: state.descriptor },
|
|
344
|
+
effects: [],
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Applies a reducer event to mutable manager runtime state. */
|
|
350
|
+
export function dispatchSessionEvent(
|
|
351
|
+
runtime: Pick<SessionManagerRuntime, 'state'>,
|
|
352
|
+
event: SessionEvent,
|
|
353
|
+
): SessionTransition {
|
|
354
|
+
const transition = reduce(runtime.state, event)
|
|
355
|
+
runtime.state = transition.state
|
|
356
|
+
return transition
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** Decides whether a need-voucher event can be answered by voucher or requires top-up first. */
|
|
360
|
+
export function resolveNeedVoucherTransition(
|
|
361
|
+
parameters: ResolveNeedVoucherTransitionParameters,
|
|
362
|
+
): NeedVoucherTransition {
|
|
363
|
+
const { challengeId, descriptor, event } = parameters
|
|
364
|
+
const required = BigInt(event.requiredCumulative)
|
|
365
|
+
const deposit = BigInt(event.deposit)
|
|
366
|
+
|
|
367
|
+
if (required > deposit) {
|
|
368
|
+
return {
|
|
369
|
+
state: {
|
|
370
|
+
status: 'toppingUp',
|
|
371
|
+
challengeId,
|
|
372
|
+
channelId: event.channelId,
|
|
373
|
+
descriptor,
|
|
374
|
+
deposit: event.deposit,
|
|
375
|
+
},
|
|
376
|
+
effects: [
|
|
377
|
+
{ type: 'topUp', channelId: event.channelId, amount: (required - deposit).toString() },
|
|
378
|
+
],
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
state: {
|
|
384
|
+
status: 'voucherNeeded',
|
|
385
|
+
challengeId,
|
|
386
|
+
channelId: event.channelId,
|
|
387
|
+
descriptor,
|
|
388
|
+
requiredCumulative: event.requiredCumulative,
|
|
389
|
+
deposit: event.deposit,
|
|
390
|
+
},
|
|
391
|
+
effects: [{ type: 'voucher' }],
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function activeFromSnapshot(challengeId: string, snapshot: SessionSnapshot): SessionState {
|
|
396
|
+
return createActiveState({
|
|
397
|
+
challengeId,
|
|
398
|
+
channelId: snapshot.channelId,
|
|
399
|
+
descriptor: snapshot.descriptor,
|
|
400
|
+
acceptedCumulative: snapshot.acceptedCumulative,
|
|
401
|
+
deposit: snapshot.deposit,
|
|
402
|
+
spent: snapshot.spent,
|
|
403
|
+
units: snapshot.units ?? 0,
|
|
404
|
+
})
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function activeFromReceipt(
|
|
408
|
+
challengeId: string,
|
|
409
|
+
receipt: SessionReceipt,
|
|
410
|
+
descriptor: ChannelDescriptor,
|
|
411
|
+
deposit: RawAmountString,
|
|
412
|
+
): SessionState {
|
|
413
|
+
return createActiveState({
|
|
414
|
+
challengeId,
|
|
415
|
+
channelId: receipt.channelId,
|
|
416
|
+
descriptor,
|
|
417
|
+
acceptedCumulative: receipt.acceptedCumulative,
|
|
418
|
+
deposit,
|
|
419
|
+
spent: receipt.spent,
|
|
420
|
+
units: receipt.units ?? 0,
|
|
421
|
+
})
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function invalid(state: SessionState, event: SessionEvent): never {
|
|
425
|
+
throw new Error(`Invalid session transition: ${state.status} + ${event.type}`)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** Inputs for validating a cumulative authorization against the local client cap. */
|
|
429
|
+
export type LocalVoucherLimitParameters = {
|
|
430
|
+
/** Cumulative amount being authorized or accepted. */
|
|
431
|
+
cumulativeAmount: bigint
|
|
432
|
+
/** Optional maximum local authorization boundary. Null means uncapped. */
|
|
433
|
+
maxVoucherCumulative: bigint | null
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** Inputs for validating a payment receipt against local client state. */
|
|
437
|
+
export type LocalReceiptValidationParameters = {
|
|
438
|
+
/** Active local channel cache entry. */
|
|
439
|
+
channel: ChannelEntry | null
|
|
440
|
+
/** Optional local authorization cap. Null means uncapped. */
|
|
441
|
+
maxVoucherCumulative: bigint | null
|
|
442
|
+
/** Receipt returned by the server. */
|
|
443
|
+
receipt: SessionReceipt
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** Inputs for deriving the next locally observed spend from a receipt. */
|
|
447
|
+
export type NextReceiptSpendParameters = {
|
|
448
|
+
/** Active local channel cache entry. */
|
|
449
|
+
channel: ChannelEntry | null
|
|
450
|
+
/** Optional local authorization cap. Null means uncapped. */
|
|
451
|
+
maxVoucherCumulative: bigint | null
|
|
452
|
+
/** Receipt returned by the server, when present. */
|
|
453
|
+
receipt: SessionReceipt | null | undefined
|
|
454
|
+
/** Current locally observed spend. */
|
|
455
|
+
spent: bigint
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** Inputs for resolving the initial channel deposit in automatic client mode. */
|
|
459
|
+
export type ResolveOpeningDepositParameters = {
|
|
460
|
+
/** Caller-provided raw deposit override. */
|
|
461
|
+
contextDepositRaw?: string | undefined
|
|
462
|
+
/** Optional local maximum cumulative deposit/authorization boundary. */
|
|
463
|
+
maxDeposit?: bigint | undefined
|
|
464
|
+
/** Current request amount in raw token units. */
|
|
465
|
+
requestAmount: bigint
|
|
466
|
+
/** Server-suggested opening deposit in raw token units. */
|
|
467
|
+
suggestedDepositRaw?: string | undefined
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/** Throws when a cumulative voucher amount exceeds the caller's local cap. */
|
|
471
|
+
export function assertVoucherWithinLocalLimit(parameters: LocalVoucherLimitParameters): void {
|
|
472
|
+
const { cumulativeAmount, maxVoucherCumulative } = parameters
|
|
473
|
+
if (maxVoucherCumulative === null) return
|
|
474
|
+
if (cumulativeAmount <= maxVoucherCumulative) return
|
|
475
|
+
throw new Error(
|
|
476
|
+
`requested voucher amount ${cumulativeAmount} exceeds local maxDeposit ${maxVoucherCumulative}`,
|
|
477
|
+
)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** Validates a server receipt without allowing it to increase the local signing boundary. */
|
|
481
|
+
export function assertReceiptWithinLocalState(parameters: LocalReceiptValidationParameters): void {
|
|
482
|
+
const { channel, maxVoucherCumulative, receipt } = parameters
|
|
483
|
+
if (!channel || receipt.channelId !== channel.channelId) return
|
|
484
|
+
const acceptedCumulative = BigInt(receipt.acceptedCumulative)
|
|
485
|
+
const receiptSpent = BigInt(receipt.spent)
|
|
486
|
+
if (receiptSpent > acceptedCumulative) {
|
|
487
|
+
throw new Error('receipt spent exceeds accepted cumulative voucher amount')
|
|
488
|
+
}
|
|
489
|
+
if (acceptedCumulative > channel.cumulativeAmount) {
|
|
490
|
+
throw new Error('receipt accepted cumulative exceeds local voucher state')
|
|
491
|
+
}
|
|
492
|
+
if (receiptSpent > channel.cumulativeAmount) {
|
|
493
|
+
throw new Error('receipt spent exceeds local voucher state')
|
|
494
|
+
}
|
|
495
|
+
assertVoucherWithinLocalLimit({ cumulativeAmount: acceptedCumulative, maxVoucherCumulative })
|
|
496
|
+
assertVoucherWithinLocalLimit({ cumulativeAmount: receiptSpent, maxVoucherCumulative })
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** Returns the monotonic next local spend after validating an optional receipt. */
|
|
500
|
+
export function nextSpentFromReceipt(parameters: NextReceiptSpendParameters): bigint {
|
|
501
|
+
const { channel, maxVoucherCumulative, receipt, spent } = parameters
|
|
502
|
+
if (!receipt || receipt.channelId !== channel?.channelId) return spent
|
|
503
|
+
assertReceiptWithinLocalState({ channel, maxVoucherCumulative, receipt })
|
|
504
|
+
const next = BigInt(receipt.spent)
|
|
505
|
+
return spent > next ? spent : next
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/** Parses a manager amount. Bigints are raw units; strings are parsed using token decimals. */
|
|
509
|
+
export function parseManagerAmount(amount: string | bigint, decimals: number): bigint {
|
|
510
|
+
if (typeof amount === 'bigint') return amount
|
|
511
|
+
return parseUnits(amount, decimals)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** Resolves the opening deposit from explicit context, server hint, request amount, and local cap. */
|
|
515
|
+
export function resolveOpeningDeposit(parameters: ResolveOpeningDepositParameters): bigint {
|
|
516
|
+
const { contextDepositRaw, maxDeposit, requestAmount, suggestedDepositRaw } = parameters
|
|
517
|
+
assertWithinMaxDeposit(requestAmount, maxDeposit)
|
|
518
|
+
if (contextDepositRaw !== undefined) {
|
|
519
|
+
const deposit = BigInt(contextDepositRaw)
|
|
520
|
+
if (deposit < requestAmount) {
|
|
521
|
+
throw new Error(`opening deposit ${deposit} below request amount ${requestAmount}`)
|
|
522
|
+
}
|
|
523
|
+
return deposit
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const suggestedDeposit =
|
|
527
|
+
suggestedDepositRaw !== undefined ? BigInt(suggestedDepositRaw) : undefined
|
|
528
|
+
const proposed =
|
|
529
|
+
suggestedDeposit !== undefined && suggestedDeposit > requestAmount
|
|
530
|
+
? suggestedDeposit
|
|
531
|
+
: requestAmount
|
|
532
|
+
if (maxDeposit !== undefined) return proposed < maxDeposit ? proposed : maxDeposit
|
|
533
|
+
return proposed
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/** Enforces the optional client-side maximum cumulative voucher authorization. */
|
|
537
|
+
export function assertWithinMaxDeposit(
|
|
538
|
+
cumulativeAmount: bigint,
|
|
539
|
+
maxDeposit: bigint | undefined,
|
|
540
|
+
): void {
|
|
541
|
+
assertVoucherWithinLocalLimit({
|
|
542
|
+
cumulativeAmount,
|
|
543
|
+
maxVoucherCumulative: maxDeposit ?? null,
|
|
544
|
+
})
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/** Predicate used when waiting for a specific session receipt. */
|
|
548
|
+
export type SessionReceiptPredicate = (receipt: SessionReceipt) => boolean
|
|
549
|
+
|
|
550
|
+
/** Resolved data required to close a locally active session channel. */
|
|
551
|
+
export type CloseTarget = {
|
|
552
|
+
/** Challenge used to bind the close credential. */
|
|
553
|
+
challenge: TempoSessionChallenge
|
|
554
|
+
/** Local channel cache entry being closed. */
|
|
555
|
+
channel: ChannelEntry
|
|
556
|
+
/** Channel ID being closed. */
|
|
557
|
+
channelId: Hex
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/** Inputs for choosing the active close target. */
|
|
561
|
+
export type ResolveCloseTargetParameters = {
|
|
562
|
+
/** Current active channel cache entry. */
|
|
563
|
+
channel: ChannelEntry | null
|
|
564
|
+
/** Active WebSocket session, when close is happening in-band. */
|
|
565
|
+
currentSocket: ActiveSocketSession | null
|
|
566
|
+
/** Last HTTP/SSE challenge observed by the manager. */
|
|
567
|
+
lastChallenge: TempoSessionChallenge | null
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/** Inputs for validating socket close-ready spend before signing the final close voucher. */
|
|
571
|
+
export type CloseReadySpendParameters = {
|
|
572
|
+
/** Local cumulative voucher authorization. */
|
|
573
|
+
cumulativeAmount: bigint
|
|
574
|
+
/** Spend reported by the close-ready receipt. */
|
|
575
|
+
readySpent: bigint
|
|
576
|
+
/** Latest receipt-tracked local spend. */
|
|
577
|
+
spent: bigint
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/** Inputs for matching the expected final close receipt. */
|
|
581
|
+
export type ExpectedCloseReceiptParameters = {
|
|
582
|
+
/** Challenge ID used for the close credential. */
|
|
583
|
+
challengeId: string
|
|
584
|
+
/** Channel ID being closed. */
|
|
585
|
+
channelId: Hex
|
|
586
|
+
/** Expected final cumulative/spent amount. */
|
|
587
|
+
expectedCloseAmount: string
|
|
588
|
+
/** Receipt to test. */
|
|
589
|
+
receipt: SessionReceipt
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/** Resolves the currently closeable channel and challenge, or undefined when no channel is open. */
|
|
593
|
+
export function resolveCloseTarget(
|
|
594
|
+
parameters: ResolveCloseTargetParameters,
|
|
595
|
+
): CloseTarget | undefined {
|
|
596
|
+
const { channel, currentSocket, lastChallenge } = parameters
|
|
597
|
+
if (!channel?.opened) return undefined
|
|
598
|
+
|
|
599
|
+
const challenge = currentSocket?.challenge ?? lastChallenge
|
|
600
|
+
const channelId = currentSocket?.channelId ?? channel.channelId
|
|
601
|
+
|
|
602
|
+
if (!challenge) {
|
|
603
|
+
throw new Error(
|
|
604
|
+
'Cannot close session: no challenge available. This usually means close() was called on a SessionManager instance that was recreated after the session was opened. Use the same SessionManager instance that opened the session, or make a request first to receive a fresh 402 challenge.',
|
|
605
|
+
)
|
|
606
|
+
}
|
|
607
|
+
if (!channelId) {
|
|
608
|
+
throw new Error(
|
|
609
|
+
'Cannot close session: no channel ID available. The session may not have been fully opened.',
|
|
610
|
+
)
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return { challenge, channel, channelId }
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/** Highest spend the client may sign for during close based on local receipts and vouchers. */
|
|
617
|
+
export function localCloseSpendLimit(parameters: Omit<CloseReadySpendParameters, 'readySpent'>) {
|
|
618
|
+
const { cumulativeAmount, spent } = parameters
|
|
619
|
+
return cumulativeAmount > spent ? cumulativeAmount : spent
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/** Throws when a close-ready receipt asks the client to sign beyond local state. */
|
|
623
|
+
export function assertCloseReadyWithinLocalState(parameters: CloseReadySpendParameters): void {
|
|
624
|
+
const { cumulativeAmount, readySpent, spent } = parameters
|
|
625
|
+
if (readySpent > localCloseSpendLimit({ cumulativeAmount, spent })) {
|
|
626
|
+
throw new Error('close-ready spent exceeds local voucher state')
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/** Returns whether a receipt is the expected final close settlement receipt. */
|
|
631
|
+
export function isExpectedCloseReceipt(parameters: ExpectedCloseReceiptParameters): boolean {
|
|
632
|
+
const { challengeId, channelId, expectedCloseAmount, receipt } = parameters
|
|
633
|
+
return (
|
|
634
|
+
Boolean(receipt.txHash) &&
|
|
635
|
+
receipt.challengeId === challengeId &&
|
|
636
|
+
receipt.channelId === channelId &&
|
|
637
|
+
receipt.acceptedCumulative === expectedCloseAmount &&
|
|
638
|
+
receipt.spent === expectedCloseAmount
|
|
639
|
+
)
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/** Parameters used to project cached client channel data into an active machine state. */
|
|
643
|
+
export type ActiveStateFromChannelParameters = {
|
|
644
|
+
/** Challenge ID associated with the active payment flow. */
|
|
645
|
+
challengeId: string
|
|
646
|
+
/** Cached channel entry that owns descriptor, deposit, and cumulative authorization. */
|
|
647
|
+
entry: ChannelEntry
|
|
648
|
+
/** Latest locally observed spend in raw units. */
|
|
649
|
+
spent: string
|
|
650
|
+
/** Paid units observed by the active flow. */
|
|
651
|
+
units: number
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/** Parameters used to project a closed channel into machine state. */
|
|
655
|
+
export type ClosedStateFromChannelParameters = {
|
|
656
|
+
/** Channel ID that has been closed. */
|
|
657
|
+
channelId: Hex
|
|
658
|
+
/** Cached channel entry that owns the descriptor. */
|
|
659
|
+
entry: ChannelEntry
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/** Inputs for computing the safest fallback close amount when no fresh close-ready receipt is available. */
|
|
663
|
+
export type FallbackCloseAmountParameters = {
|
|
664
|
+
/** Challenge ID being closed. */
|
|
665
|
+
challengeId: string
|
|
666
|
+
/** Channel ID being closed. */
|
|
667
|
+
channelId: Hex
|
|
668
|
+
/** Last socket close-ready receipt, when one was received. */
|
|
669
|
+
closeReadyReceipt?: SessionReceipt | null | undefined
|
|
670
|
+
/** Current local cumulative voucher authorization. */
|
|
671
|
+
cumulativeAmount: bigint
|
|
672
|
+
/** Number of application chunks delivered over the socket. */
|
|
673
|
+
deliveredChunks?: bigint | undefined
|
|
674
|
+
/** Current socket challenge ID, used to decide whether socket delivery data applies. */
|
|
675
|
+
socketChallengeId?: string | undefined
|
|
676
|
+
/** Current socket channel ID, used to decide whether socket delivery data applies. */
|
|
677
|
+
socketChannelId?: Hex | undefined
|
|
678
|
+
/** Latest locally observed spend from receipts. */
|
|
679
|
+
spent: bigint
|
|
680
|
+
/** Per-message socket charge in raw units. */
|
|
681
|
+
tickCost?: bigint | undefined
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/** Minimal mutable session runtime state that must be restored when an auto-drive attempt fails. */
|
|
685
|
+
export type RuntimeState = {
|
|
686
|
+
/** Current client channel cache entry, when one is active. */
|
|
687
|
+
channel: ChannelEntry | null
|
|
688
|
+
/** Latest locally observed spend from receipts. */
|
|
689
|
+
spent: bigint
|
|
690
|
+
/** Current public state-machine state. */
|
|
691
|
+
state: SessionState
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/** Mutable client runtime state owned by one auto-driving `sessionManager()` instance. */
|
|
695
|
+
export type SessionManagerRuntime = RuntimeState & {
|
|
696
|
+
/** Last Tempo session challenge observed by HTTP/SSE/WebSocket bootstrap. */
|
|
697
|
+
lastChallenge: TempoSessionChallenge | null
|
|
698
|
+
/** Last HTTP resource URL usable for management POSTs. */
|
|
699
|
+
lastUrl: RequestInfo | URL | null
|
|
700
|
+
/** Active WebSocket payment session bookkeeping, when a socket is open. */
|
|
701
|
+
socketSession: ActiveSocketSession | null
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/** Immutable snapshot of mutable runtime fields needed for rollback. */
|
|
705
|
+
export type RuntimeSnapshot = {
|
|
706
|
+
/** Channel fields mutated during optimistic open/top-up/voucher attempts. */
|
|
707
|
+
channel: {
|
|
708
|
+
cumulativeAmount: bigint
|
|
709
|
+
deposit: bigint
|
|
710
|
+
entry: ChannelEntry
|
|
711
|
+
opened: boolean
|
|
712
|
+
} | null
|
|
713
|
+
/** Latest locally observed spend when the snapshot was taken. */
|
|
714
|
+
spent: bigint
|
|
715
|
+
/** State-machine state when the snapshot was taken. */
|
|
716
|
+
state: SessionState
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/** Inputs for applying a server receipt to manager-local runtime state. */
|
|
720
|
+
export type ApplySessionReceiptToRuntimeParameters = {
|
|
721
|
+
/** Optional local cumulative authorization cap. Null means uncapped. */
|
|
722
|
+
maxVoucherCumulative: bigint | null
|
|
723
|
+
/** Receipt returned by a server transport, when present. */
|
|
724
|
+
receipt: SessionReceipt | null | undefined
|
|
725
|
+
/** Mutable manager runtime state to update. */
|
|
726
|
+
runtime: SessionManagerRuntime
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/** Inputs for restoring local cumulative authorization after a failed optimistic voucher retry. */
|
|
730
|
+
export type RestoreCumulativeAuthorizationParameters = {
|
|
731
|
+
/** Active local channel entry, when one is available. */
|
|
732
|
+
channel: ChannelEntry | null
|
|
733
|
+
/** Channel ID whose optimistic cumulative amount should be restored. */
|
|
734
|
+
channelId: Hex
|
|
735
|
+
/** Previous cumulative voucher authorization in raw units. */
|
|
736
|
+
cumulativeAmount: bigint
|
|
737
|
+
/** Last challenge ID observed by the manager, when known. */
|
|
738
|
+
challengeId?: string | undefined
|
|
739
|
+
/** Latest locally observed spend in raw units. */
|
|
740
|
+
spent: bigint
|
|
741
|
+
/** Current public state-machine state, used to preserve active unit count. */
|
|
742
|
+
state: SessionState
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/** Projects cached channel data into an active state-machine state. */
|
|
746
|
+
export function activeStateFromChannel(parameters: ActiveStateFromChannelParameters): SessionState {
|
|
747
|
+
return createActiveState({
|
|
748
|
+
challengeId: parameters.challengeId,
|
|
749
|
+
channelId: parameters.entry.channelId,
|
|
750
|
+
descriptor: parameters.entry.descriptor,
|
|
751
|
+
acceptedCumulative: parameters.entry.cumulativeAmount.toString(),
|
|
752
|
+
deposit: parameters.entry.deposit.toString(),
|
|
753
|
+
spent: parameters.spent,
|
|
754
|
+
units: parameters.units,
|
|
755
|
+
})
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/** Creates the initial mutable runtime state for an auto-driving session manager. */
|
|
759
|
+
export function createSessionManagerRuntime(): SessionManagerRuntime {
|
|
760
|
+
return {
|
|
761
|
+
channel: null,
|
|
762
|
+
lastChallenge: null,
|
|
763
|
+
lastUrl: null,
|
|
764
|
+
spent: 0n,
|
|
765
|
+
socketSession: null,
|
|
766
|
+
state: initialState,
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/** Validates a receipt, advances observed spend, and projects matching receipts into public state. */
|
|
771
|
+
export function applySessionReceiptToRuntime(
|
|
772
|
+
parameters: ApplySessionReceiptToRuntimeParameters,
|
|
773
|
+
): void {
|
|
774
|
+
const { maxVoucherCumulative, receipt, runtime } = parameters
|
|
775
|
+
runtime.spent = nextSpentFromReceipt({
|
|
776
|
+
channel: runtime.channel,
|
|
777
|
+
maxVoucherCumulative,
|
|
778
|
+
receipt,
|
|
779
|
+
spent: runtime.spent,
|
|
780
|
+
})
|
|
781
|
+
if (receipt && runtime.channel?.channelId === receipt.channelId) {
|
|
782
|
+
dispatchSessionEvent(runtime, {
|
|
783
|
+
type: 'receiptAccepted',
|
|
784
|
+
receipt,
|
|
785
|
+
entry: runtime.channel,
|
|
786
|
+
})
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/** Projects a verified receipt plus local descriptor/deposit data into an active state-machine state. */
|
|
791
|
+
export function activeStateFromReceipt(receipt: SessionReceipt, entry: ChannelEntry): SessionState {
|
|
792
|
+
return createActiveState({
|
|
793
|
+
challengeId: receipt.challengeId,
|
|
794
|
+
channelId: receipt.channelId,
|
|
795
|
+
descriptor: entry.descriptor,
|
|
796
|
+
acceptedCumulative: receipt.acceptedCumulative,
|
|
797
|
+
deposit: entry.deposit.toString(),
|
|
798
|
+
spent: receipt.spent,
|
|
799
|
+
units: receipt.units ?? 0,
|
|
800
|
+
})
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/** Projects a closed channel into the public closed state-machine state. */
|
|
804
|
+
export function closedStateFromChannel(parameters: ClosedStateFromChannelParameters): SessionState {
|
|
805
|
+
return {
|
|
806
|
+
status: 'closed',
|
|
807
|
+
channelId: parameters.channelId,
|
|
808
|
+
descriptor: parameters.entry.descriptor,
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/** Projects a final close receipt into the public closed state-machine state. */
|
|
813
|
+
export function closedStateFromReceipt(receipt: SessionReceipt, entry: ChannelEntry): SessionState {
|
|
814
|
+
return closedStateFromChannel({ channelId: receipt.channelId, entry })
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Computes the fallback close amount without authorizing more than the local cumulative voucher.
|
|
819
|
+
*
|
|
820
|
+
* Priority:
|
|
821
|
+
* 1. Matching close-ready receipt spend.
|
|
822
|
+
* 2. Matching socket delivery estimate (`deliveredChunks * tickCost`) clamped by cumulative.
|
|
823
|
+
* 3. Latest receipt-tracked spend for HTTP/SSE.
|
|
824
|
+
*/
|
|
825
|
+
export function computeFallbackCloseAmount(parameters: FallbackCloseAmountParameters): bigint {
|
|
826
|
+
const {
|
|
827
|
+
challengeId,
|
|
828
|
+
channelId,
|
|
829
|
+
closeReadyReceipt,
|
|
830
|
+
cumulativeAmount,
|
|
831
|
+
deliveredChunks = 0n,
|
|
832
|
+
socketChallengeId,
|
|
833
|
+
socketChannelId,
|
|
834
|
+
spent,
|
|
835
|
+
tickCost = 0n,
|
|
836
|
+
} = parameters
|
|
837
|
+
|
|
838
|
+
if (
|
|
839
|
+
closeReadyReceipt &&
|
|
840
|
+
closeReadyReceipt.challengeId === challengeId &&
|
|
841
|
+
closeReadyReceipt.channelId === channelId
|
|
842
|
+
) {
|
|
843
|
+
return BigInt(closeReadyReceipt.spent)
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (socketChallengeId === challengeId && socketChannelId === channelId && tickCost > 0n) {
|
|
847
|
+
const deliveryEstimate = deliveredChunks * tickCost
|
|
848
|
+
const bestSpent = spent > deliveryEstimate ? spent : deliveryEstimate
|
|
849
|
+
return bestSpent > cumulativeAmount ? cumulativeAmount : bestSpent
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
return spent
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Restores a channel's cumulative voucher boundary and returns the refreshed active state.
|
|
857
|
+
*
|
|
858
|
+
* Returns `undefined` when the active channel does not match or no challenge is
|
|
859
|
+
* available to label the active state.
|
|
860
|
+
*/
|
|
861
|
+
export function restoreCumulativeAuthorization(
|
|
862
|
+
parameters: RestoreCumulativeAuthorizationParameters,
|
|
863
|
+
): ActiveSessionState | undefined {
|
|
864
|
+
const { channel, channelId, challengeId, cumulativeAmount, spent, state } = parameters
|
|
865
|
+
if (!channel || channel.channelId !== channelId) return undefined
|
|
866
|
+
channel.cumulativeAmount = cumulativeAmount
|
|
867
|
+
if (!challengeId) return undefined
|
|
868
|
+
return reduce(state, {
|
|
869
|
+
type: 'activated',
|
|
870
|
+
challengeId,
|
|
871
|
+
entry: channel,
|
|
872
|
+
spent: spent.toString(),
|
|
873
|
+
units: state.status === 'active' ? state.units : 0,
|
|
874
|
+
}).state as ActiveSessionState
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/** Captures mutable session runtime fields before an optimistic manager action. */
|
|
878
|
+
export function captureRuntimeSnapshot(runtime: RuntimeState): RuntimeSnapshot {
|
|
879
|
+
return {
|
|
880
|
+
channel:
|
|
881
|
+
runtime.channel === null
|
|
882
|
+
? null
|
|
883
|
+
: {
|
|
884
|
+
entry: runtime.channel,
|
|
885
|
+
cumulativeAmount: runtime.channel.cumulativeAmount,
|
|
886
|
+
deposit: runtime.channel.deposit,
|
|
887
|
+
opened: runtime.channel.opened,
|
|
888
|
+
},
|
|
889
|
+
spent: runtime.spent,
|
|
890
|
+
state: runtime.state,
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/** Restores mutable session runtime fields from a previous snapshot. */
|
|
895
|
+
export function restoreRuntimeSnapshot(
|
|
896
|
+
snapshot: RuntimeSnapshot,
|
|
897
|
+
currentChannel: ChannelEntry | null,
|
|
898
|
+
): RuntimeState {
|
|
899
|
+
if (snapshot.channel) {
|
|
900
|
+
snapshot.channel.entry.cumulativeAmount = snapshot.channel.cumulativeAmount
|
|
901
|
+
snapshot.channel.entry.deposit = snapshot.channel.deposit
|
|
902
|
+
snapshot.channel.entry.opened = snapshot.channel.opened
|
|
903
|
+
return {
|
|
904
|
+
channel: snapshot.channel.entry,
|
|
905
|
+
spent: snapshot.spent,
|
|
906
|
+
state: snapshot.state,
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
if (currentChannel) currentChannel.opened = false
|
|
911
|
+
return {
|
|
912
|
+
channel: null,
|
|
913
|
+
spent: snapshot.spent,
|
|
914
|
+
state: snapshot.state,
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/** Creates a session credential for the selected challenge/context. */
|
|
919
|
+
export type CreateSocketCloseCredential = (
|
|
920
|
+
challenge: TempoSessionChallenge,
|
|
921
|
+
context: SessionContext,
|
|
922
|
+
) => Promise<string>
|
|
923
|
+
|
|
924
|
+
/** Inputs for cooperatively closing an active WebSocket session in-band. */
|
|
925
|
+
export type CloseSocketSessionParameters = {
|
|
926
|
+
/** Raw WebSocket used by the active paid stream. */
|
|
927
|
+
activeSocket: WebSocket
|
|
928
|
+
/** Creates the signed close credential. */
|
|
929
|
+
createSessionCredential: CreateSocketCloseCredential
|
|
930
|
+
/** Active WebSocket session state. */
|
|
931
|
+
currentSocket: ActiveSocketSession
|
|
932
|
+
/** Latest locally tracked spend from receipts. */
|
|
933
|
+
spent: bigint
|
|
934
|
+
/** Channel/challenge pair being closed. */
|
|
935
|
+
target: CloseTarget
|
|
936
|
+
/** Waits for the server's close-ready receipt after requesting stream close. */
|
|
937
|
+
waitForCloseReady(): Promise<SessionReceipt>
|
|
938
|
+
/** Waits for the final settlement receipt matching `predicate`. */
|
|
939
|
+
waitForReceipt(predicate: SessionReceiptPredicate): Promise<SessionReceipt>
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/** Cooperatively closes an active paid WebSocket session and returns the final receipt. */
|
|
943
|
+
export async function closeSocketSession(
|
|
944
|
+
parameters: CloseSocketSessionParameters,
|
|
945
|
+
): Promise<SessionReceipt> {
|
|
946
|
+
const { activeSocket, currentSocket, target } = parameters
|
|
947
|
+
const ready =
|
|
948
|
+
currentSocket.closeReadyReceipt ??
|
|
949
|
+
(await (async () => {
|
|
950
|
+
activeSocket.send(Ws.formatCloseRequestMessage())
|
|
951
|
+
return parameters.waitForCloseReady()
|
|
952
|
+
})())
|
|
953
|
+
const readySpent = BigInt(ready.spent)
|
|
954
|
+
assertCloseReadyWithinLocalState({
|
|
955
|
+
cumulativeAmount: target.channel.cumulativeAmount,
|
|
956
|
+
readySpent,
|
|
957
|
+
spent: parameters.spent,
|
|
958
|
+
})
|
|
959
|
+
|
|
960
|
+
const credential = await parameters.createSessionCredential(target.challenge, {
|
|
961
|
+
action: 'close',
|
|
962
|
+
channelId: target.channelId,
|
|
963
|
+
descriptor: target.channel.descriptor,
|
|
964
|
+
cumulativeAmountRaw: readySpent.toString(),
|
|
965
|
+
})
|
|
966
|
+
|
|
967
|
+
const expectedCloseAmount = readySpent.toString()
|
|
968
|
+
currentSocket.expectedCloseAmount = expectedCloseAmount
|
|
969
|
+
try {
|
|
970
|
+
const pendingReceipt = parameters.waitForReceipt((receipt) =>
|
|
971
|
+
isExpectedCloseReceipt({
|
|
972
|
+
challengeId: target.challenge.id,
|
|
973
|
+
channelId: target.channelId,
|
|
974
|
+
expectedCloseAmount,
|
|
975
|
+
receipt,
|
|
976
|
+
}),
|
|
977
|
+
)
|
|
978
|
+
activeSocket.send(Ws.formatAuthorizationMessage(credential))
|
|
979
|
+
const receipt = await pendingReceipt
|
|
980
|
+
activeSocket.close()
|
|
981
|
+
currentSocket.closeReadyReceipt = null
|
|
982
|
+
return receipt
|
|
983
|
+
} finally {
|
|
984
|
+
currentSocket.expectedCloseAmount = null
|
|
985
|
+
}
|
|
986
|
+
}
|