mppx 0.8.14 → 0.8.17
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 +57 -0
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +40 -35
- package/dist/Challenge.js.map +1 -1
- package/dist/Constants.d.ts +17 -17
- package/dist/Constants.d.ts.map +1 -1
- package/dist/Credential.d.ts.map +1 -1
- package/dist/Errors.d.ts +3 -3
- package/dist/Errors.d.ts.map +1 -1
- package/dist/Errors.js.map +1 -1
- package/dist/Method.d.ts +35 -5
- package/dist/Method.d.ts.map +1 -1
- package/dist/Method.js +7 -22
- package/dist/Method.js.map +1 -1
- package/dist/Store.d.ts +19 -0
- package/dist/Store.d.ts.map +1 -1
- package/dist/Store.js +43 -0
- package/dist/Store.js.map +1 -1
- package/dist/attestation/Client.d.ts +17 -0
- package/dist/attestation/Client.d.ts.map +1 -0
- package/dist/attestation/Client.js +28 -0
- package/dist/attestation/Client.js.map +1 -0
- package/dist/attestation/Constants.d.ts +15 -0
- package/dist/attestation/Constants.d.ts.map +1 -0
- package/dist/attestation/Constants.js +15 -0
- package/dist/attestation/Constants.js.map +1 -0
- package/dist/attestation/Server.d.ts +4 -0
- package/dist/attestation/Server.d.ts.map +1 -0
- package/dist/attestation/Server.js +8 -0
- package/dist/attestation/Server.js.map +1 -0
- package/dist/attestation/Types.d.ts +76 -0
- package/dist/attestation/Types.d.ts.map +1 -0
- package/dist/attestation/Types.js +2 -0
- package/dist/attestation/Types.js.map +1 -0
- package/dist/attestation/index.d.ts +6 -0
- package/dist/attestation/index.d.ts.map +1 -0
- package/dist/attestation/index.js +6 -0
- package/dist/attestation/index.js.map +1 -0
- package/dist/attestation/internal/HttpMessageSignature.d.ts +73 -0
- package/dist/attestation/internal/HttpMessageSignature.d.ts.map +1 -0
- package/dist/attestation/internal/HttpMessageSignature.js +320 -0
- package/dist/attestation/internal/HttpMessageSignature.js.map +1 -0
- package/dist/attestation/internal/SigningContext.d.ts +4 -0
- package/dist/attestation/internal/SigningContext.d.ts.map +1 -0
- package/dist/attestation/internal/SigningContext.js +11 -0
- package/dist/attestation/internal/SigningContext.js.map +1 -0
- package/dist/cli/account.d.ts +14 -14
- package/dist/cli/account.d.ts.map +1 -1
- package/dist/cli/cli.d.ts +6 -6
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +1 -0
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/plugins/tempo.js.map +1 -1
- package/dist/cli/sessions/commands.d.ts +4 -4
- package/dist/cli/sessions/commands.d.ts.map +1 -1
- package/dist/cli/sessions/commands.js.map +1 -1
- package/dist/cli/sessions/store.d.ts.map +1 -1
- package/dist/cli/sessions/store.js.map +1 -1
- package/dist/cli/utils.d.ts +1 -1
- package/dist/cli/utils.d.ts.map +1 -1
- package/dist/cli/utils.js.map +1 -1
- package/dist/cli/validate/discovery.js.map +1 -1
- package/dist/cli/validate/index.d.ts +3 -3
- package/dist/cli/validate/index.d.ts.map +1 -1
- package/dist/cli/validate/payment.d.ts.map +1 -1
- package/dist/cli/validate/payment.js +18 -4
- package/dist/cli/validate/payment.js.map +1 -1
- package/dist/client/Mppx.d.ts +3 -0
- package/dist/client/Mppx.d.ts.map +1 -1
- package/dist/client/Mppx.js +13 -5
- package/dist/client/Mppx.js.map +1 -1
- package/dist/client/Transport.d.ts +3 -5
- package/dist/client/Transport.d.ts.map +1 -1
- package/dist/client/Transport.js +1 -17
- package/dist/client/Transport.js.map +1 -1
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +92 -13
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/client/internal/MethodChallenge.d.ts +2 -0
- package/dist/client/internal/MethodChallenge.d.ts.map +1 -1
- package/dist/client/internal/MethodChallenge.js +4 -0
- package/dist/client/internal/MethodChallenge.js.map +1 -1
- package/dist/client/internal/MethodResponse.d.ts +15 -0
- package/dist/client/internal/MethodResponse.d.ts.map +1 -1
- package/dist/client/internal/MethodResponse.js +10 -0
- package/dist/client/internal/MethodResponse.js.map +1 -1
- package/dist/client/node.js.map +1 -1
- package/dist/discovery/OpenApi.d.ts +3 -5
- package/dist/discovery/OpenApi.d.ts.map +1 -1
- package/dist/discovery/OpenApi.js +13 -34
- package/dist/discovery/OpenApi.js.map +1 -1
- package/dist/discovery/internal/Metadata.d.ts +20 -0
- package/dist/discovery/internal/Metadata.d.ts.map +1 -0
- package/dist/discovery/internal/Metadata.js +36 -0
- package/dist/discovery/internal/Metadata.js.map +1 -0
- package/dist/evm/Methods.d.ts +11 -11
- package/dist/evm/Methods.d.ts.map +1 -1
- package/dist/evm/Types.d.ts +17 -17
- package/dist/evm/Types.d.ts.map +1 -1
- package/dist/evm/client/Charge.d.ts +11 -11
- package/dist/evm/client/Methods.d.ts +11 -11
- package/dist/evm/server/Charge.d.ts +1 -1
- package/dist/evm/server/Charge.d.ts.map +1 -1
- package/dist/evm/server/Charge.js +2 -0
- package/dist/evm/server/Charge.js.map +1 -1
- package/dist/evm/server/Methods.d.ts +11 -11
- package/dist/internal/HeaderCodec.d.ts +2 -2
- package/dist/internal/HeaderCodec.d.ts.map +1 -1
- package/dist/internal/types.d.ts +6 -0
- package/dist/internal/types.d.ts.map +1 -1
- package/dist/middlewares/elysia.js.map +1 -1
- package/dist/middlewares/express.js.map +1 -1
- package/dist/middlewares/hono.js.map +1 -1
- package/dist/middlewares/nextjs.js.map +1 -1
- package/dist/proxy/Proxy.d.ts +2 -0
- package/dist/proxy/Proxy.d.ts.map +1 -1
- package/dist/proxy/Proxy.js +117 -9
- package/dist/proxy/Proxy.js.map +1 -1
- package/dist/proxy/Service.d.ts +36 -1
- package/dist/proxy/Service.d.ts.map +1 -1
- package/dist/proxy/Service.js +9 -8
- package/dist/proxy/Service.js.map +1 -1
- package/dist/proxy/services/anthropic.d.ts.map +1 -1
- package/dist/proxy/services/anthropic.js +1 -0
- package/dist/proxy/services/anthropic.js.map +1 -1
- package/dist/proxy/services/openai.d.ts.map +1 -1
- package/dist/proxy/services/openai.js +1 -0
- package/dist/proxy/services/openai.js.map +1 -1
- package/dist/proxy/services/stripe.d.ts.map +1 -1
- package/dist/proxy/services/stripe.js +1 -0
- package/dist/proxy/services/stripe.js.map +1 -1
- package/dist/server/Mppx.d.ts +75 -13
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +201 -23
- package/dist/server/Mppx.js.map +1 -1
- package/dist/server/internal/html/config.d.ts +9 -9
- package/dist/server/internal/html/config.d.ts.map +1 -1
- package/dist/server/internal/html/constants.d.ts +17 -17
- package/dist/server/internal/html/constants.d.ts.map +1 -1
- package/dist/stripe/Methods.d.ts +10 -10
- package/dist/stripe/Methods.d.ts.map +1 -1
- package/dist/stripe/client/Charge.d.ts +10 -10
- package/dist/stripe/client/Methods.d.ts +10 -10
- package/dist/stripe/internal/constants.d.ts +1 -1
- package/dist/stripe/internal/constants.js +1 -1
- package/dist/stripe/internal/types.d.ts +1 -0
- package/dist/stripe/internal/types.d.ts.map +1 -1
- package/dist/stripe/server/Charge.d.ts +11 -11
- package/dist/stripe/server/Charge.d.ts.map +1 -1
- package/dist/stripe/server/Charge.js +49 -1
- package/dist/stripe/server/Charge.js.map +1 -1
- package/dist/stripe/server/Methods.d.ts +137 -48
- package/dist/stripe/server/Methods.d.ts.map +1 -1
- package/dist/stripe/server/Methods.js +259 -4
- package/dist/stripe/server/Methods.js.map +1 -1
- package/dist/stripe/server/internal/deposit-address.d.ts +13 -0
- package/dist/stripe/server/internal/deposit-address.d.ts.map +1 -0
- package/dist/stripe/server/internal/deposit-address.js +28 -0
- package/dist/stripe/server/internal/deposit-address.js.map +1 -0
- 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/stripe/server/internal/record-payment.d.ts +15 -0
- package/dist/stripe/server/internal/record-payment.d.ts.map +1 -0
- package/dist/stripe/server/internal/record-payment.js +42 -0
- package/dist/stripe/server/internal/record-payment.js.map +1 -0
- package/dist/stripe/server/internal/request.d.ts +25 -0
- package/dist/stripe/server/internal/request.d.ts.map +1 -0
- package/dist/stripe/server/internal/request.js +29 -0
- package/dist/stripe/server/internal/request.js.map +1 -0
- package/dist/tap/Constants.d.ts +28 -0
- package/dist/tap/Constants.d.ts.map +1 -0
- package/dist/tap/Constants.js +32 -0
- package/dist/tap/Constants.js.map +1 -0
- package/dist/tap/Types.d.ts +11 -0
- package/dist/tap/Types.d.ts.map +1 -0
- package/dist/tap/Types.js +2 -0
- package/dist/tap/Types.js.map +1 -0
- package/dist/tap/client/index.d.ts +26 -0
- package/dist/tap/client/index.d.ts.map +1 -0
- package/dist/tap/client/index.js +33 -0
- package/dist/tap/client/index.js.map +1 -0
- package/dist/tap/index.d.ts +5 -0
- package/dist/tap/index.d.ts.map +1 -0
- package/dist/tap/index.js +5 -0
- package/dist/tap/index.js.map +1 -0
- package/dist/tap/server/index.d.ts +20 -0
- package/dist/tap/server/index.d.ts.map +1 -0
- package/dist/tap/server/index.js +36 -0
- package/dist/tap/server/index.js.map +1 -0
- package/dist/tempo/Attribution.d.ts +1 -1
- package/dist/tempo/Methods.d.ts +60 -60
- package/dist/tempo/Methods.d.ts.map +1 -1
- package/dist/tempo/Proof.d.ts +16 -16
- package/dist/tempo/client/Charge.d.ts +19 -19
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +1 -7
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +38 -38
- package/dist/tempo/client/Methods.js.map +1 -1
- package/dist/tempo/client/Subscription.d.ts +23 -23
- package/dist/tempo/client/Subscription.js.map +1 -1
- package/dist/tempo/internal/address.d.ts +2 -1
- package/dist/tempo/internal/address.d.ts.map +1 -1
- package/dist/tempo/internal/address.js +3 -1
- package/dist/tempo/internal/address.js.map +1 -1
- package/dist/tempo/internal/auto-swap.d.ts.map +1 -1
- package/dist/tempo/internal/auto-swap.js +4 -19
- package/dist/tempo/internal/auto-swap.js.map +1 -1
- package/dist/tempo/internal/defaults.d.ts +6 -6
- package/dist/tempo/internal/defaults.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.d.ts +12 -12
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/internal/proof.d.ts +17 -17
- package/dist/tempo/internal/proof.d.ts.map +1 -1
- package/dist/tempo/legacy/client/Session.d.ts +20 -20
- package/dist/tempo/legacy/client/Session.js.map +1 -1
- package/dist/tempo/legacy/client/SessionManager.js.map +1 -1
- package/dist/tempo/legacy/index.d.ts +1 -3
- package/dist/tempo/legacy/index.d.ts.map +1 -1
- package/dist/tempo/legacy/index.js +1 -3
- package/dist/tempo/legacy/index.js.map +1 -1
- package/dist/tempo/legacy/session/escrow.abi.d.ts +374 -374
- package/dist/tempo/legacy/session/escrow.abi.d.ts.map +1 -1
- package/dist/tempo/server/Charge.d.ts +34 -12
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +44 -39
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +37 -52
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Methods.js +0 -14
- package/dist/tempo/server/Methods.js.map +1 -1
- package/dist/tempo/server/Relay.d.ts +14 -5
- package/dist/tempo/server/Relay.d.ts.map +1 -1
- package/dist/tempo/server/Relay.js +6 -4
- package/dist/tempo/server/Relay.js.map +1 -1
- package/dist/tempo/server/SponsorBudget.d.ts.map +1 -1
- package/dist/tempo/server/SponsorBudget.js +7 -1
- package/dist/tempo/server/SponsorBudget.js.map +1 -1
- package/dist/tempo/server/Subscription.d.ts +25 -25
- package/dist/tempo/server/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/Subscription.js +2 -0
- package/dist/tempo/server/Subscription.js.map +1 -1
- package/dist/tempo/server/index.d.ts +1 -1
- package/dist/tempo/server/index.d.ts.map +1 -1
- package/dist/tempo/server/index.js +1 -1
- 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/transport.d.ts +2 -0
- package/dist/tempo/server/internal/transport.d.ts.map +1 -1
- package/dist/tempo/server/internal/transport.js +9 -2
- package/dist/tempo/server/internal/transport.js.map +1 -1
- package/dist/tempo/session/client/ChannelOps.js.map +1 -1
- package/dist/tempo/session/client/CredentialState.d.ts +1 -1
- package/dist/tempo/session/client/CredentialState.js.map +1 -1
- package/dist/tempo/session/client/ReceiptCoordinator.js.map +1 -1
- package/dist/tempo/session/client/Runtime.js.map +1 -1
- package/dist/tempo/session/client/Session.d.ts +19 -19
- package/dist/tempo/session/client/Session.d.ts.map +1 -1
- package/dist/tempo/session/client/Session.js +128 -14
- package/dist/tempo/session/client/Session.js.map +1 -1
- package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/session/client/SessionManager.js +62 -20
- package/dist/tempo/session/client/SessionManager.js.map +1 -1
- package/dist/tempo/session/client/Transports.d.ts +9 -1
- package/dist/tempo/session/client/Transports.d.ts.map +1 -1
- package/dist/tempo/session/client/Transports.js +36 -13
- package/dist/tempo/session/client/Transports.js.map +1 -1
- package/dist/tempo/session/precompile/Chain.d.ts +27 -0
- package/dist/tempo/session/precompile/Chain.d.ts.map +1 -1
- package/dist/tempo/session/precompile/Chain.js +68 -7
- package/dist/tempo/session/precompile/Chain.js.map +1 -1
- package/dist/tempo/session/precompile/Protocol.js.map +1 -1
- package/dist/tempo/session/precompile/Voucher.d.ts +4 -4
- package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -1
- package/dist/tempo/session/precompile/escrow.abi.d.ts +306 -306
- package/dist/tempo/session/precompile/escrow.abi.d.ts.map +1 -1
- package/dist/tempo/session/server/ChannelStore.d.ts +9 -0
- package/dist/tempo/session/server/ChannelStore.d.ts.map +1 -1
- package/dist/tempo/session/server/ChannelStore.js +16 -8
- package/dist/tempo/session/server/ChannelStore.js.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.d.ts +30 -13
- package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.js +207 -13
- package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
- package/dist/tempo/session/server/MeteredStream.d.ts +3 -0
- package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -1
- package/dist/tempo/session/server/MeteredStream.js +3 -1
- package/dist/tempo/session/server/MeteredStream.js.map +1 -1
- package/dist/tempo/session/server/Session.d.ts +32 -21
- package/dist/tempo/session/server/Session.d.ts.map +1 -1
- package/dist/tempo/session/server/Session.js +116 -53
- package/dist/tempo/session/server/Session.js.map +1 -1
- package/dist/tempo/session/server/Sse.d.ts +3 -1
- package/dist/tempo/session/server/Sse.d.ts.map +1 -1
- package/dist/tempo/session/server/Sse.js +2 -1
- package/dist/tempo/session/server/Sse.js.map +1 -1
- package/dist/tempo/session/server/Transports.d.ts +1 -1
- package/dist/tempo/session/server/Transports.d.ts.map +1 -1
- package/dist/tempo/session/server/Transports.js +1 -0
- package/dist/tempo/session/server/Transports.js.map +1 -1
- package/dist/tempo/session/server/Ws.d.ts +6 -1
- package/dist/tempo/session/server/Ws.d.ts.map +1 -1
- package/dist/tempo/session/server/Ws.js +4 -0
- package/dist/tempo/session/server/Ws.js.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts +38 -866
- package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.js +14 -3
- package/dist/tempo/subscription/KeyAuthorization.js.map +1 -1
- package/dist/viem/Account.d.ts.map +1 -1
- package/dist/viem/Account.js.map +1 -1
- package/dist/viem/Client.d.ts.map +1 -1
- package/dist/viem/Client.js +6 -1
- package/dist/viem/Client.js.map +1 -1
- package/dist/web-bot-auth/Constants.d.ts +20 -0
- package/dist/web-bot-auth/Constants.d.ts.map +1 -0
- package/dist/web-bot-auth/Constants.js +24 -0
- package/dist/web-bot-auth/Constants.js.map +1 -0
- package/dist/web-bot-auth/Types.d.ts +10 -0
- package/dist/web-bot-auth/Types.d.ts.map +1 -0
- package/dist/web-bot-auth/Types.js +2 -0
- package/dist/web-bot-auth/Types.js.map +1 -0
- package/dist/web-bot-auth/client/index.d.ts +29 -0
- package/dist/web-bot-auth/client/index.d.ts.map +1 -0
- package/dist/web-bot-auth/client/index.js +64 -0
- package/dist/web-bot-auth/client/index.js.map +1 -0
- package/dist/web-bot-auth/index.d.ts +7 -0
- package/dist/web-bot-auth/index.d.ts.map +1 -0
- package/dist/web-bot-auth/index.js +7 -0
- package/dist/web-bot-auth/index.js.map +1 -0
- package/dist/web-bot-auth/internal/JwkThumbprint.d.ts +5 -0
- package/dist/web-bot-auth/internal/JwkThumbprint.d.ts.map +1 -0
- package/dist/web-bot-auth/internal/JwkThumbprint.js +20 -0
- package/dist/web-bot-auth/internal/JwkThumbprint.js.map +1 -0
- package/dist/web-bot-auth/internal/SignatureAgent.d.ts +3 -0
- package/dist/web-bot-auth/internal/SignatureAgent.d.ts.map +1 -0
- package/dist/web-bot-auth/internal/SignatureAgent.js +18 -0
- package/dist/web-bot-auth/internal/SignatureAgent.js.map +1 -0
- package/dist/web-bot-auth/server/index.d.ts +35 -0
- package/dist/web-bot-auth/server/index.d.ts.map +1 -0
- package/dist/web-bot-auth/server/index.js +88 -0
- package/dist/web-bot-auth/server/index.js.map +1 -0
- package/dist/x402/Assets.d.ts.map +1 -1
- package/dist/x402/Types.d.ts +6 -6
- package/dist/x402/Types.d.ts.map +1 -1
- package/dist/x402/server/EvmCharge.d.ts.map +1 -1
- package/dist/x402/server/EvmCharge.js +32 -20
- package/dist/x402/server/EvmCharge.js.map +1 -1
- package/package.json +20 -4
- package/src/Challenge.fuzz.test.ts +80 -47
- package/src/Challenge.test.ts +65 -0
- package/src/Challenge.ts +39 -35
- package/src/Method.ts +82 -68
- package/src/Store.test-d.ts +16 -0
- package/src/Store.test.ts +50 -0
- package/src/Store.ts +68 -0
- package/src/attestation/Attestation.integration.test.ts +208 -0
- package/src/attestation/Client.test.ts +83 -0
- package/src/attestation/Client.ts +34 -0
- package/src/attestation/Constants.ts +15 -0
- package/src/attestation/Server.test.ts +60 -0
- package/src/attestation/Server.ts +12 -0
- package/src/attestation/Types.test-d.ts +54 -0
- package/src/attestation/Types.ts +77 -0
- package/src/attestation/index.ts +5 -0
- package/src/attestation/internal/HttpMessageSignature.test.ts +101 -0
- package/src/attestation/internal/HttpMessageSignature.ts +429 -0
- package/src/attestation/internal/SigningContext.ts +13 -0
- package/src/bin.js +2 -0
- package/src/cli/cli.ts +1 -0
- package/src/cli/validate/payment.ts +18 -4
- package/src/cli/validate.test.ts +16 -0
- package/src/client/Mppx.test-d.ts +17 -0
- package/src/client/Mppx.test.ts +14 -14
- package/src/client/Mppx.ts +25 -4
- package/src/client/Transport.test.ts +12 -45
- package/src/client/Transport.ts +3 -24
- package/src/client/internal/Fetch.test.ts +77 -0
- package/src/client/internal/Fetch.ts +112 -14
- package/src/client/internal/MethodChallenge.ts +5 -0
- package/src/client/internal/MethodResponse.ts +28 -0
- package/src/discovery/OpenApi.test.ts +170 -0
- package/src/discovery/OpenApi.ts +16 -48
- package/src/discovery/internal/Metadata.ts +51 -0
- package/src/evm/server/Charge.ts +6 -1
- package/src/internal/AcceptPayment.test.ts +12 -8
- package/src/internal/types.ts +9 -0
- package/src/mcp/client/McpClient.integration.test.ts +19 -32
- package/src/middlewares/elysia.test.ts +2 -12
- package/src/middlewares/express.test.ts +2 -12
- package/src/middlewares/hono.test.ts +6 -9
- package/src/middlewares/nextjs.test.ts +2 -12
- package/src/proxy/Proxy.test.ts +26 -19
- package/src/proxy/Proxy.ts +147 -9
- package/src/proxy/Proxy.upstreamError.test.ts +364 -0
- package/src/proxy/Service.test.ts +73 -3
- package/src/proxy/Service.ts +50 -23
- package/src/proxy/services/anthropic.ts +1 -0
- package/src/proxy/services/hooks.test-d.ts +14 -0
- package/src/proxy/services/hooks.test.ts +44 -0
- package/src/proxy/services/openai.ts +1 -0
- package/src/proxy/services/stripe.ts +1 -0
- package/src/server/Methods.test-d.ts +88 -0
- package/src/server/Methods.test.ts +69 -0
- package/src/server/Mppx.test-d.ts +141 -7
- package/src/server/Mppx.test.ts +1017 -119
- package/src/server/Mppx.ts +350 -54
- package/src/stripe/internal/constants.ts +1 -1
- package/src/stripe/internal/types.ts +1 -0
- package/src/stripe/server/Charge.test-d.ts +16 -1
- package/src/stripe/server/Charge.test.ts +79 -0
- package/src/stripe/server/Charge.ts +54 -1
- package/src/stripe/server/Methods.test-d.ts +157 -0
- package/src/stripe/server/Methods.test.ts +403 -0
- package/src/stripe/server/Methods.ts +423 -6
- package/src/stripe/server/internal/deposit-address.ts +57 -0
- package/src/stripe/server/internal/html/node_modules/.bin/mppx +3 -3
- package/src/stripe/server/internal/html/package.json +1 -1
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/stripe/server/internal/record-payment.ts +66 -0
- package/src/stripe/server/internal/request.ts +49 -0
- package/src/tap/Constants.ts +32 -0
- package/src/tap/Tap.test.ts +125 -0
- package/src/tap/Types.ts +11 -0
- package/src/tap/client/index.ts +54 -0
- package/src/tap/index.ts +4 -0
- package/src/tap/server/index.ts +48 -0
- package/src/tempo/PublicExports.test-d.ts +3 -3
- package/src/tempo/client/Charge.ts +1 -19
- package/src/tempo/internal/address.test.ts +42 -0
- package/src/tempo/internal/address.ts +3 -3
- package/src/tempo/internal/auto-swap.test.ts +4 -4
- package/src/tempo/internal/auto-swap.ts +7 -50
- package/src/tempo/legacy/README.md +5 -5
- package/src/tempo/legacy/index.ts +1 -3
- package/src/tempo/server/AtomicStore.test-d.ts +13 -0
- package/src/tempo/server/Charge.test.ts +19 -84
- package/src/tempo/server/Charge.ts +92 -56
- package/src/tempo/server/Methods.ts +0 -20
- package/src/tempo/server/Relay.test.ts +161 -7
- package/src/tempo/server/Relay.ts +15 -6
- package/src/tempo/server/SponsorBudget.test.ts +29 -0
- package/src/tempo/server/SponsorBudget.ts +7 -1
- package/src/tempo/server/Subscription.ts +4 -1
- package/src/tempo/server/index.ts +1 -1
- package/src/tempo/server/internal/html/node_modules/.bin/mppx +3 -3
- package/src/tempo/server/internal/html/package.json +1 -1
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/server/internal/transport.test.ts +22 -3
- package/src/tempo/server/internal/transport.ts +15 -3
- package/src/tempo/session/README.md +119 -0
- package/src/tempo/session/client/Session.test.ts +299 -33
- package/src/tempo/session/client/Session.ts +152 -16
- package/src/tempo/session/client/SessionManager.test.ts +216 -9
- package/src/tempo/session/client/SessionManager.ts +63 -17
- package/src/tempo/session/client/Transports.test.ts +22 -0
- package/src/tempo/session/client/Transports.ts +58 -22
- package/src/tempo/session/precompile/Chain.ts +131 -11
- package/src/tempo/session/server/ChannelStore.ts +33 -12
- package/src/tempo/session/server/CredentialVerification.test.ts +4 -4
- package/src/tempo/session/server/CredentialVerification.ts +340 -28
- package/src/tempo/session/server/MeteredStream.ts +5 -1
- package/src/tempo/session/server/Session.test.ts +483 -36
- package/src/tempo/session/server/Session.ts +159 -60
- package/src/tempo/session/server/Sse.test.ts +77 -0
- package/src/tempo/session/server/Sse.ts +8 -1
- package/src/tempo/session/server/Transports.ts +2 -1
- package/src/tempo/session/server/Ws.test.ts +171 -0
- package/src/tempo/session/server/Ws.ts +10 -1
- package/src/tempo/subscription/KeyAuthorization.test.ts +70 -1
- package/src/tempo/subscription/KeyAuthorization.ts +14 -3
- package/src/viem/Client.test.ts +18 -1
- package/src/viem/Client.ts +7 -1
- package/src/web-bot-auth/Constants.ts +24 -0
- package/src/web-bot-auth/Types.ts +9 -0
- package/src/web-bot-auth/WebBotAuth.test.ts +182 -0
- package/src/web-bot-auth/client/index.ts +84 -0
- package/src/web-bot-auth/index.ts +6 -0
- package/src/web-bot-auth/internal/JwkThumbprint.ts +21 -0
- package/src/web-bot-auth/internal/SignatureAgent.ts +18 -0
- package/src/web-bot-auth/server/index.ts +113 -0
- package/src/x402/Exact.e2e.test.ts +121 -81
- package/src/x402/Exact.localnet.test.ts +1 -1
- package/src/x402/server/EvmCharge.ts +35 -19
- package/dist/tempo/legacy/server/Session.d.ts +0 -208
- package/dist/tempo/legacy/server/Session.d.ts.map +0 -1
- package/dist/tempo/legacy/server/Session.js +0 -698
- package/dist/tempo/legacy/server/Session.js.map +0 -1
- package/dist/tempo/legacy/server/index.d.ts +0 -5
- package/dist/tempo/legacy/server/index.d.ts.map +0 -1
- package/dist/tempo/legacy/server/index.js +0 -5
- package/dist/tempo/legacy/server/index.js.map +0 -1
- package/dist/tempo/legacy/session/ChannelStore.d.ts +0 -22
- package/dist/tempo/legacy/session/ChannelStore.d.ts.map +0 -1
- package/dist/tempo/legacy/session/ChannelStore.js +0 -6
- package/dist/tempo/legacy/session/ChannelStore.js.map +0 -1
- package/src/tempo/legacy/server/Defaults.test-d.ts +0 -9
- package/src/tempo/legacy/server/Session.test.ts +0 -6370
- package/src/tempo/legacy/server/Session.ts +0 -1024
- package/src/tempo/legacy/server/index.ts +0 -4
- package/src/tempo/legacy/session/ChannelStore.test.ts +0 -58
- package/src/tempo/legacy/session/ChannelStore.ts +0 -39
|
@@ -738,8 +738,15 @@ describe('sse transport', () => {
|
|
|
738
738
|
|
|
739
739
|
test('respondReceipt with non-SSE upstream Response still deducts from channel', async () => {
|
|
740
740
|
const store = memoryStore()
|
|
741
|
+
const settled: Array<{ spent: bigint; units: number }> = []
|
|
741
742
|
await seedChannel(store, 10000000n)
|
|
742
|
-
const transport = sse({
|
|
743
|
+
const transport = sse({
|
|
744
|
+
store,
|
|
745
|
+
async settleCharged(channel) {
|
|
746
|
+
settled.push({ spent: channel.spent, units: channel.units })
|
|
747
|
+
return undefined
|
|
748
|
+
},
|
|
749
|
+
})
|
|
743
750
|
const request = new Request('https://test.example.com/session', {
|
|
744
751
|
body: JSON.stringify({ prompt: 'hello' }),
|
|
745
752
|
headers: makeAuthorizedRequest().headers,
|
|
@@ -768,6 +775,7 @@ describe('sse transport', () => {
|
|
|
768
775
|
expect(channel!.units).toBe(1)
|
|
769
776
|
expect(receipt.spent).toBe('1000000')
|
|
770
777
|
expect(receipt.units).toBe(1)
|
|
778
|
+
expect(settled).toEqual([{ spent: 1000000n, units: 1 }])
|
|
771
779
|
|
|
772
780
|
expect(JSON.parse(body)).toEqual({ content: 'hello' })
|
|
773
781
|
expect(response.headers.get('Content-Type')).toBe('application/json')
|
|
@@ -806,8 +814,18 @@ describe('sse transport', () => {
|
|
|
806
814
|
|
|
807
815
|
test('respondReceipt with 204 content response still deducts from channel', async () => {
|
|
808
816
|
const store = memoryStore()
|
|
817
|
+
let resolveSettlement!: (channel: ChannelStore.State) => void
|
|
818
|
+
const settlement = new Promise<ChannelStore.State>((resolve) => {
|
|
819
|
+
resolveSettlement = resolve
|
|
820
|
+
})
|
|
809
821
|
await seedChannel(store, 10000000n)
|
|
810
|
-
const transport = sse({
|
|
822
|
+
const transport = sse({
|
|
823
|
+
store,
|
|
824
|
+
async settleCharged(channel) {
|
|
825
|
+
resolveSettlement(channel)
|
|
826
|
+
return undefined
|
|
827
|
+
},
|
|
828
|
+
})
|
|
811
829
|
const request = new Request('https://test.example.com/session', {
|
|
812
830
|
body: JSON.stringify({ prompt: 'hello' }),
|
|
813
831
|
headers: makeAuthorizedRequest().headers,
|
|
@@ -827,11 +845,12 @@ describe('sse transport', () => {
|
|
|
827
845
|
expect(await response.text()).toBe('')
|
|
828
846
|
const receipt = deserializeSessionReceipt(response.headers.get('Payment-Receipt')!)
|
|
829
847
|
|
|
830
|
-
await
|
|
848
|
+
const settled = await settlement
|
|
831
849
|
|
|
832
850
|
const channel = await store.getChannel(channelId)
|
|
833
851
|
expect(channel!.spent).toBe(1000000n)
|
|
834
852
|
expect(channel!.units).toBe(1)
|
|
853
|
+
expect(settled).toMatchObject({ spent: 1000000n, units: 1 })
|
|
835
854
|
expect(receipt.spent).toBe('1000000')
|
|
836
855
|
expect(receipt.units).toBe(1)
|
|
837
856
|
})
|
|
@@ -12,6 +12,7 @@ import * as Transport from '../../../server/Transport.js'
|
|
|
12
12
|
import type { SessionReceipt } from '../../session/precompile/Protocol.js'
|
|
13
13
|
import { requireSessionCredentialContext } from '../../session/precompile/Protocol.js'
|
|
14
14
|
import * as ChannelStore from '../../session/server/ChannelStore.js'
|
|
15
|
+
import type { SettleChargedSessionChannel } from '../../session/server/Settlement.js'
|
|
15
16
|
import * as Sse_core from '../../session/server/Sse.js'
|
|
16
17
|
import { captureRequestBodyProbe, shouldChargePlainResponse } from './request-body.js'
|
|
17
18
|
|
|
@@ -48,7 +49,12 @@ function hasPrepaidSessionTick(receipt: SessionReceipt): boolean {
|
|
|
48
49
|
* - Auto-detection of upstream SSE responses
|
|
49
50
|
* - Fallback to standard HTTP receipt handling for plain Response
|
|
50
51
|
*/
|
|
51
|
-
export function sse(
|
|
52
|
+
export function sse(
|
|
53
|
+
options: sse.Options & {
|
|
54
|
+
settleCharged?: SettleChargedSessionChannel | undefined
|
|
55
|
+
store: ChannelStore.ChannelStore
|
|
56
|
+
},
|
|
57
|
+
): Sse {
|
|
52
58
|
const { pollingInterval, poll } = options
|
|
53
59
|
|
|
54
60
|
// When `poll` is true, strip `waitForUpdate` so the SSE charge loop
|
|
@@ -117,6 +123,7 @@ export function sse(options: sse.Options & { store: ChannelStore.ChannelStore })
|
|
|
117
123
|
tickCost,
|
|
118
124
|
pollIntervalMs: pollingInterval,
|
|
119
125
|
generate,
|
|
126
|
+
onChargeCommitted: options.settleCharged,
|
|
120
127
|
prepaidUnits: hasPrepaidSessionTick(receipt as SessionReceipt) ? 1 : 0,
|
|
121
128
|
signal: input.signal,
|
|
122
129
|
})
|
|
@@ -174,20 +181,25 @@ export function sse(options: sse.Options & { store: ChannelStore.ChannelStore })
|
|
|
174
181
|
response: response as Response,
|
|
175
182
|
challengeId: verifiedChallengeId,
|
|
176
183
|
})
|
|
184
|
+
const chargePlainResponse = async () => {
|
|
185
|
+
const result = await ChannelStore.deductFromChannel(store, channelId, tickCost)
|
|
186
|
+
if (result.ok) await options.settleCharged?.(result.channel)
|
|
187
|
+
return result
|
|
188
|
+
}
|
|
177
189
|
|
|
178
190
|
// Non-SSE response (e.g. upstream returned JSON instead of event-stream).
|
|
179
191
|
// Need to deduct tickCost so request isn't free.
|
|
180
192
|
// For null-body statuses, the request shape determines whether the
|
|
181
193
|
// response is management (no charge) or plain content (charge one tick).
|
|
182
194
|
if (isNullBodyStatus(chargedResponse.status)) {
|
|
183
|
-
void
|
|
195
|
+
void chargePlainResponse()
|
|
184
196
|
return chargedResponse
|
|
185
197
|
}
|
|
186
198
|
|
|
187
199
|
const stream = new ReadableStream<Uint8Array>({
|
|
188
200
|
async start(controller) {
|
|
189
201
|
// deduction completes before consumer reads
|
|
190
|
-
const result = await
|
|
202
|
+
const result = await chargePlainResponse()
|
|
191
203
|
if (!result.ok) {
|
|
192
204
|
controller.error(
|
|
193
205
|
new Errors.InsufficientBalanceError({
|
|
@@ -83,6 +83,13 @@ sequenceDiagram
|
|
|
83
83
|
route handler. `open` and `voucher` used on non-billable requests are also
|
|
84
84
|
management updates.
|
|
85
85
|
|
|
86
|
+
When `Fetch.from` creates an automatic `open`, it keeps the channel entry
|
|
87
|
+
provisional until the server accepts the response or acknowledges the open with
|
|
88
|
+
a matching receipt or session snapshot. Rejected or unacknowledged opens are
|
|
89
|
+
discarded so the next request retries `open`; concurrent automatic opens for the
|
|
90
|
+
same payment scope are serialized. Direct credential creation and
|
|
91
|
+
`SessionManager` retain their existing lifecycle ownership.
|
|
92
|
+
|
|
86
93
|
The current HTTP contract is deliberately pre-handler: voucher acceptance and
|
|
87
94
|
default request charging occur before the handler runs. A handler failure can
|
|
88
95
|
therefore follow an accepted voucher and recorded `spent`. Sessions does not
|
|
@@ -179,6 +186,118 @@ sequenceDiagram
|
|
|
179
186
|
The initial SSE POST is excluded from default HTTP charging to prevent double
|
|
180
187
|
counting. Stream accounting remains owned by the transport driver.
|
|
181
188
|
|
|
189
|
+
## Server transport configuration
|
|
190
|
+
|
|
191
|
+
A session method owns the account, channel store, verification policy, and
|
|
192
|
+
settlement schedule. Routes add prices and application content. HTTP and SSE
|
|
193
|
+
run through configured route handlers; WebSocket uses a session-bound helper
|
|
194
|
+
because its application messages no longer pass through HTTP.
|
|
195
|
+
|
|
196
|
+
| Server shape | Session option | Content API | Charging |
|
|
197
|
+
| ---------------------- | -------------- | ----------------------------------- | ----------------------------------- |
|
|
198
|
+
| HTTP only | Omit `sse` | `result.withReceipt(Response)` | Once per billable request |
|
|
199
|
+
| SSE only | `sse: true` | `result.withReceipt(AsyncIterable)` | Each `stream.charge()` |
|
|
200
|
+
| WebSocket only | Omit `sse` | `mppx.session.serveWebSocket(...)` | Each `stream.charge()` |
|
|
201
|
+
| HTTP + SSE + WebSocket | `sse: true` | All three APIs | Selected by route and response type |
|
|
202
|
+
|
|
203
|
+
All transports for one session service must share the same configured session
|
|
204
|
+
method and store. `serveWebSocket` is bound to both the store and settlement
|
|
205
|
+
policy, so callers pass neither dependency again. `tempo.Ws.serve` remains the
|
|
206
|
+
low-level adapter for custom integrations.
|
|
207
|
+
|
|
208
|
+
### Shared method configuration
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
const session = tempo.session({
|
|
212
|
+
account,
|
|
213
|
+
currency,
|
|
214
|
+
getClient: () => client,
|
|
215
|
+
settlementSchedule: { units: 100 },
|
|
216
|
+
store,
|
|
217
|
+
// Include when this instance serves any SSE route.
|
|
218
|
+
sse: true,
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
const mppx = Mppx.create({ methods: [session], secretKey })
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`sse: true` enables async-iterable responses without changing ordinary
|
|
225
|
+
`Response` handling, so a mixed server uses one session method.
|
|
226
|
+
|
|
227
|
+
### HTTP
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
const httpRoute = mppx.session({ amount: '0.01', unitType: 'request' })
|
|
231
|
+
const result = await httpRoute(request)
|
|
232
|
+
|
|
233
|
+
if (result.status === 402) return result.challenge
|
|
234
|
+
return result.withReceipt(Response.json({ ok: true }))
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
HTTP accounting charges the configured route amount before application content
|
|
238
|
+
runs. A plain `Response` selects normal HTTP handling even when `sse: true`.
|
|
239
|
+
|
|
240
|
+
### SSE
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
const sseRoute = mppx.session({ amount: '0.001', unitType: 'token' })
|
|
244
|
+
const result = await sseRoute(request)
|
|
245
|
+
|
|
246
|
+
if (result.status === 402) return result.challenge
|
|
247
|
+
return result.withReceipt(async function* (stream) {
|
|
248
|
+
for await (const token of generateTokens()) {
|
|
249
|
+
await stream.charge()
|
|
250
|
+
yield token
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
SSE requires `sse: true` on the shared session method. The async iterable
|
|
256
|
+
selects SSE framing and transport-owned metering.
|
|
257
|
+
|
|
258
|
+
### WebSocket
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
const wsRoute = mppx.session({ amount: '0.001', unitType: 'token' })
|
|
262
|
+
|
|
263
|
+
webSocketServer.on('connection', (socket, request) => {
|
|
264
|
+
void mppx.session.serveWebSocket({
|
|
265
|
+
generate: async function* (stream) {
|
|
266
|
+
for await (const token of generateTokens()) {
|
|
267
|
+
await stream.charge()
|
|
268
|
+
yield token
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
route: wsRoute,
|
|
272
|
+
socket,
|
|
273
|
+
url: new URL(request.url!, `ws://${request.headers.host}`),
|
|
274
|
+
})
|
|
275
|
+
})
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The application still exposes `wsRoute` over HTTP for the initial challenge
|
|
279
|
+
probe. The bound helper reuses the session store and automatic settlement
|
|
280
|
+
schedule for in-band WebSocket vouchers and charges.
|
|
281
|
+
|
|
282
|
+
### Mixed server
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
const httpRoute = mppx.session({ amount: '0.01', unitType: 'request' })
|
|
286
|
+
const sseRoute = mppx.session({ amount: '0.001', unitType: 'token' })
|
|
287
|
+
const wsRoute = mppx.session({ amount: '0.001', unitType: 'token' })
|
|
288
|
+
|
|
289
|
+
async function handler(request: Request) {
|
|
290
|
+
const pathname = new URL(request.url).pathname
|
|
291
|
+
if (pathname === '/api/data') return serveHttp(httpRoute, request)
|
|
292
|
+
if (pathname === '/api/events') return serveSse(sseRoute, request)
|
|
293
|
+
if (pathname === '/ws') return serveWebSocketProbe(wsRoute, request)
|
|
294
|
+
return new Response('Not found', { status: 404 })
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Each route can choose its own price and unit type. Verification, channel state,
|
|
299
|
+
and settlement policy remain shared through the single session method.
|
|
300
|
+
|
|
182
301
|
## Extension boundaries
|
|
183
302
|
|
|
184
303
|
| Change | Preserve |
|
|
@@ -16,7 +16,7 @@ import { escrowAbi } from '../precompile/escrow.abi.js'
|
|
|
16
16
|
import { tip20ChannelEscrow } from '../precompile/Protocol.js'
|
|
17
17
|
import * as Types from '../precompile/Protocol.js'
|
|
18
18
|
import * as Voucher from '../precompile/Voucher.js'
|
|
19
|
-
import { createChannelStore } from './ChannelStore.js'
|
|
19
|
+
import { channelKey, createChannelStore } from './ChannelStore.js'
|
|
20
20
|
import { session } from './Session.js'
|
|
21
21
|
|
|
22
22
|
const account = privateKeyToAccount(
|
|
@@ -60,6 +60,13 @@ type SessionChallenge = Challenge<
|
|
|
60
60
|
typeof Methods.session.name
|
|
61
61
|
>
|
|
62
62
|
|
|
63
|
+
const defaultChannelKey = channelKey({
|
|
64
|
+
chainId,
|
|
65
|
+
escrow: tip20ChannelEscrow,
|
|
66
|
+
payee: descriptor.payee,
|
|
67
|
+
token: descriptor.token,
|
|
68
|
+
})
|
|
69
|
+
|
|
63
70
|
function makeChallenge(overrides: Partial<SessionChallenge['request']> = {}): SessionChallenge {
|
|
64
71
|
const request: SessionChallenge['request'] = {
|
|
65
72
|
amount: '100',
|
|
@@ -78,6 +85,83 @@ function makeChallenge(overrides: Partial<SessionChallenge['request']> = {}): Se
|
|
|
78
85
|
}
|
|
79
86
|
}
|
|
80
87
|
|
|
88
|
+
function makeSessionChallenge(
|
|
89
|
+
overrides: Partial<SessionChallenge['request']> = {},
|
|
90
|
+
): SessionChallenge {
|
|
91
|
+
return makeChallenge({
|
|
92
|
+
...overrides,
|
|
93
|
+
methodDetails: {
|
|
94
|
+
chainId,
|
|
95
|
+
escrowContract: tip20ChannelEscrow,
|
|
96
|
+
sessionProtocol: Constants.SessionProtocols.v2,
|
|
97
|
+
...overrides.methodDetails,
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function paymentRequired(challenge: SessionChallenge, headers?: HeadersInit): Response {
|
|
103
|
+
const responseHeaders = new Headers(headers)
|
|
104
|
+
responseHeaders.set(Constants.Headers.wwwAuthenticate, serializeChallenge(challenge))
|
|
105
|
+
return new Response(null, {
|
|
106
|
+
headers: responseHeaders,
|
|
107
|
+
status: 402,
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function withSnapshot(
|
|
112
|
+
payload: Types.SessionCredentialPayload,
|
|
113
|
+
acceptedCumulative?: string,
|
|
114
|
+
): SessionChallenge {
|
|
115
|
+
if (payload.action !== 'open') throw new Error('expected open credential')
|
|
116
|
+
return {
|
|
117
|
+
...makeSessionChallenge({
|
|
118
|
+
methodDetails: {
|
|
119
|
+
chainId,
|
|
120
|
+
escrowContract: tip20ChannelEscrow,
|
|
121
|
+
sessionProtocol: Constants.SessionProtocols.v2,
|
|
122
|
+
sessionSnapshot: {
|
|
123
|
+
acceptedCumulative: acceptedCumulative ?? payload.cumulativeAmount,
|
|
124
|
+
chainId,
|
|
125
|
+
channelId: payload.channelId,
|
|
126
|
+
deposit: '1000',
|
|
127
|
+
descriptor: payload.descriptor,
|
|
128
|
+
escrow: tip20ChannelEscrow,
|
|
129
|
+
requiredCumulative: '200',
|
|
130
|
+
settled: '0',
|
|
131
|
+
spent: payload.cumulativeAmount,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
}),
|
|
135
|
+
id: 'replacement-id',
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function makeSessionMethod(
|
|
140
|
+
channelStore = createChannelStore(),
|
|
141
|
+
parameters: Partial<session.Parameters> = {},
|
|
142
|
+
) {
|
|
143
|
+
return session({
|
|
144
|
+
account,
|
|
145
|
+
channelStore,
|
|
146
|
+
decimals: 0,
|
|
147
|
+
getClient: () => client,
|
|
148
|
+
maxDeposit: '1000',
|
|
149
|
+
...parameters,
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function makeSessionFetch(
|
|
154
|
+
fetch: typeof globalThis.fetch,
|
|
155
|
+
channelStore = createChannelStore(),
|
|
156
|
+
maxPaymentRetries?: number,
|
|
157
|
+
) {
|
|
158
|
+
return Fetch.from({
|
|
159
|
+
fetch,
|
|
160
|
+
maxPaymentRetries,
|
|
161
|
+
methods: [makeSessionMethod(channelStore)],
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
81
165
|
function deserialize(credential: string) {
|
|
82
166
|
return Credential.deserialize(credential).payload as Types.SessionCredentialPayload
|
|
83
167
|
}
|
|
@@ -233,14 +317,16 @@ describe('precompile client session', () => {
|
|
|
233
317
|
header: string | null
|
|
234
318
|
method: string | undefined
|
|
235
319
|
}[] = []
|
|
320
|
+
const vouchers: string[] = []
|
|
236
321
|
let opened: Extract<Types.SessionCredentialPayload, { action: 'open' }> | undefined
|
|
322
|
+
const paymentRequired = () =>
|
|
323
|
+
new Response(null, {
|
|
324
|
+
status: 402,
|
|
325
|
+
headers: { [Constants.Headers.wwwAuthenticate]: serializeChallenge(challenge) },
|
|
326
|
+
})
|
|
237
327
|
const rawFetch: typeof globalThis.fetch = async (_input, init) => {
|
|
238
328
|
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
239
|
-
if (!authorization)
|
|
240
|
-
return new Response(null, {
|
|
241
|
-
status: 402,
|
|
242
|
-
headers: { [Constants.Headers.wwwAuthenticate]: serializeChallenge(challenge) },
|
|
243
|
-
})
|
|
329
|
+
if (!authorization) return paymentRequired()
|
|
244
330
|
|
|
245
331
|
const payload = deserialize(authorization)
|
|
246
332
|
actions.push(payload.action)
|
|
@@ -250,6 +336,8 @@ describe('precompile client session', () => {
|
|
|
250
336
|
}
|
|
251
337
|
if (payload.action === 'topUp') return new Response(null, { status: 204 })
|
|
252
338
|
if (payload.action !== 'voucher' || !opened) throw new Error('expected open voucher')
|
|
339
|
+
vouchers.push(authorization)
|
|
340
|
+
if (vouchers.length === 1) return paymentRequired()
|
|
253
341
|
|
|
254
342
|
paidRequests.push({
|
|
255
343
|
body: init?.body ?? null,
|
|
@@ -278,11 +366,192 @@ describe('precompile client session', () => {
|
|
|
278
366
|
})
|
|
279
367
|
|
|
280
368
|
expect(await response.text()).toBe('paid')
|
|
281
|
-
expect(actions).toEqual(['open', 'topUp', 'voucher'])
|
|
369
|
+
expect(actions).toEqual(['open', 'topUp', 'voucher', 'voucher'])
|
|
370
|
+
expect(new Set(vouchers).size).toBe(1)
|
|
282
371
|
expect(paidRequests).toEqual([{ body: 'request-body', header: 'preserved', method: 'POST' }])
|
|
283
372
|
})
|
|
284
373
|
|
|
285
|
-
test('
|
|
374
|
+
test('retries an open rejected by a replacement challenge', async () => {
|
|
375
|
+
const challenge = makeSessionChallenge({ suggestedDeposit: '1000' })
|
|
376
|
+
const replacement = { ...challenge, id: 'replacement-id' }
|
|
377
|
+
const channelStore = createChannelStore()
|
|
378
|
+
const actions: Types.SessionCredentialPayload['action'][] = []
|
|
379
|
+
let paidRequests = 0
|
|
380
|
+
const fetch = makeSessionFetch(async (_input, init) => {
|
|
381
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
382
|
+
if (!authorization) return paymentRequired(challenge)
|
|
383
|
+
actions.push(deserialize(authorization).action)
|
|
384
|
+
if (++paidRequests === 1) return paymentRequired(replacement)
|
|
385
|
+
return new Response('rejected', { status: 500 })
|
|
386
|
+
}, channelStore)
|
|
387
|
+
|
|
388
|
+
expect((await fetch('https://example.com/resource')).status).toBe(500)
|
|
389
|
+
expect(actions).toEqual(['open', 'open'])
|
|
390
|
+
expect(await channelStore.get(defaultChannelKey)).toBeUndefined()
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
test('retries an open after the paid request throws', async () => {
|
|
394
|
+
const challenge = makeSessionChallenge({ suggestedDeposit: '1000' })
|
|
395
|
+
const actions: Types.SessionCredentialPayload['action'][] = []
|
|
396
|
+
const fetch = makeSessionFetch(async (_input, init) => {
|
|
397
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
398
|
+
if (!authorization) return paymentRequired(challenge)
|
|
399
|
+
actions.push(deserialize(authorization).action)
|
|
400
|
+
throw new Error('paid request failed')
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
await expect(fetch('https://example.com/resource')).rejects.toThrow('paid request failed')
|
|
404
|
+
await expect(fetch('https://example.com/resource')).rejects.toThrow('paid request failed')
|
|
405
|
+
expect(actions).toEqual(['open', 'open'])
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
test('keeps an open acknowledged by a payment receipt', async () => {
|
|
409
|
+
const challenge = makeSessionChallenge({ suggestedDeposit: '1000' })
|
|
410
|
+
const channelStore = createChannelStore()
|
|
411
|
+
const actions: Types.SessionCredentialPayload['action'][] = []
|
|
412
|
+
const credentials: string[] = []
|
|
413
|
+
let paidRequests = 0
|
|
414
|
+
const fetch = makeSessionFetch(async (_input, init) => {
|
|
415
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
416
|
+
if (!authorization) return paymentRequired(challenge)
|
|
417
|
+
const payload = deserialize(authorization)
|
|
418
|
+
actions.push(payload.action)
|
|
419
|
+
credentials.push(authorization)
|
|
420
|
+
if (++paidRequests === 1) return paymentRequired(challenge)
|
|
421
|
+
if (paidRequests > 2) return new Response('rejected', { status: 500 })
|
|
422
|
+
return paymentRequired(challenge, {
|
|
423
|
+
[Constants.Headers.paymentReceipt]: Types.serializeSessionReceipt(
|
|
424
|
+
Types.createSessionReceipt({
|
|
425
|
+
acceptedCumulative: 100n,
|
|
426
|
+
challengeId: challenge.id,
|
|
427
|
+
channelId: payload.channelId,
|
|
428
|
+
spent: 100n,
|
|
429
|
+
}),
|
|
430
|
+
),
|
|
431
|
+
})
|
|
432
|
+
}, channelStore)
|
|
433
|
+
|
|
434
|
+
expect((await fetch('https://example.com/resource')).status).toBe(500)
|
|
435
|
+
expect(actions).toEqual(['open', 'open', 'voucher'])
|
|
436
|
+
expect(credentials[0]).toBe(credentials[1])
|
|
437
|
+
expect(await channelStore.get(defaultChannelKey)).toMatchObject({ opened: true })
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
test.each([
|
|
441
|
+
[
|
|
442
|
+
'keeps an open acknowledged by the final session snapshot',
|
|
443
|
+
undefined,
|
|
444
|
+
['open', 'voucher'],
|
|
445
|
+
true,
|
|
446
|
+
],
|
|
447
|
+
[
|
|
448
|
+
'fails closed on malformed acknowledgement snapshots without leaking the open lock',
|
|
449
|
+
'not-a-number',
|
|
450
|
+
['open', 'open'],
|
|
451
|
+
undefined,
|
|
452
|
+
],
|
|
453
|
+
])('%s', async (_name, acceptedCumulative, expectedActions, expectedOpened) => {
|
|
454
|
+
const challenge = makeSessionChallenge({ suggestedDeposit: '1000' })
|
|
455
|
+
const channelStore = createChannelStore()
|
|
456
|
+
const actions: Types.SessionCredentialPayload['action'][] = []
|
|
457
|
+
let paidRequests = 0
|
|
458
|
+
const fetch = makeSessionFetch(
|
|
459
|
+
async (_input, init) => {
|
|
460
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
461
|
+
if (!authorization) return paymentRequired(challenge)
|
|
462
|
+
const payload = deserialize(authorization)
|
|
463
|
+
actions.push(payload.action)
|
|
464
|
+
if (++paidRequests > 1) return new Response('rejected', { status: 500 })
|
|
465
|
+
return paymentRequired(withSnapshot(payload, acceptedCumulative))
|
|
466
|
+
},
|
|
467
|
+
channelStore,
|
|
468
|
+
1,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
expect((await fetch('https://example.com/resource')).status).toBe(402)
|
|
472
|
+
expect((await fetch('https://example.com/resource')).status).toBe(500)
|
|
473
|
+
expect(actions).toEqual(expectedActions)
|
|
474
|
+
expect((await channelStore.get(defaultChannelKey))?.opened).toBe(expectedOpened)
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
test('keeps an acknowledged open when challenge ordering throws', async () => {
|
|
478
|
+
const challenge = makeSessionChallenge({ suggestedDeposit: '1000' })
|
|
479
|
+
const channelStore = createChannelStore()
|
|
480
|
+
const actions: Types.SessionCredentialPayload['action'][] = []
|
|
481
|
+
let ordered = 0
|
|
482
|
+
let paidRequests = 0
|
|
483
|
+
const fetch = Fetch.from({
|
|
484
|
+
fetch: async (_input, init) => {
|
|
485
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
486
|
+
if (!authorization) return paymentRequired(challenge)
|
|
487
|
+
const payload = deserialize(authorization)
|
|
488
|
+
actions.push(payload.action)
|
|
489
|
+
if (++paidRequests > 1) return new Response('paid')
|
|
490
|
+
return paymentRequired(withSnapshot(payload))
|
|
491
|
+
},
|
|
492
|
+
methods: [makeSessionMethod(channelStore)],
|
|
493
|
+
orderChallenges(candidates) {
|
|
494
|
+
if (++ordered === 2) throw new Error('ordering failed')
|
|
495
|
+
return candidates
|
|
496
|
+
},
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
await expect(fetch('https://example.com/resource')).rejects.toThrow('ordering failed')
|
|
500
|
+
expect(await (await fetch('https://example.com/resource')).text()).toBe('paid')
|
|
501
|
+
expect(actions).toEqual(['open', 'voucher'])
|
|
502
|
+
expect(await channelStore.get(defaultChannelKey)).toMatchObject({ opened: true })
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
test('serializes concurrent opens against the committed channel', async () => {
|
|
506
|
+
const challenge = makeSessionChallenge()
|
|
507
|
+
const channelStore = createChannelStore()
|
|
508
|
+
const actions: Types.SessionCredentialPayload['action'][] = []
|
|
509
|
+
const vouchers: bigint[] = []
|
|
510
|
+
const firstGate = Promise.withResolvers<void>()
|
|
511
|
+
const firstRequest = Promise.withResolvers<void>()
|
|
512
|
+
const waitersReady = Promise.withResolvers<void>()
|
|
513
|
+
let credentialPlans = 0
|
|
514
|
+
const fetch = Fetch.from({
|
|
515
|
+
fetch: async (input, init) => {
|
|
516
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
517
|
+
if (!authorization) return paymentRequired(challenge)
|
|
518
|
+
const payload = deserialize(authorization)
|
|
519
|
+
actions.push(payload.action)
|
|
520
|
+
if (payload.action === 'topUp') return new Response(null, { status: 204 })
|
|
521
|
+
if (payload.action === 'voucher') vouchers.push(BigInt(payload.cumulativeAmount))
|
|
522
|
+
if (input.toString().endsWith('/first')) {
|
|
523
|
+
firstRequest.resolve()
|
|
524
|
+
await firstGate.promise
|
|
525
|
+
return new Response('paid')
|
|
526
|
+
}
|
|
527
|
+
return new Response('rejected', { status: 500 })
|
|
528
|
+
},
|
|
529
|
+
methods: [
|
|
530
|
+
makeSessionMethod(channelStore, {
|
|
531
|
+
resolveAccount() {
|
|
532
|
+
if (++credentialPlans === 3) waitersReady.resolve()
|
|
533
|
+
return account
|
|
534
|
+
},
|
|
535
|
+
}),
|
|
536
|
+
],
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
const first = fetch('https://example.com/first')
|
|
540
|
+
await firstRequest.promise
|
|
541
|
+
const second = fetch('https://example.com/second')
|
|
542
|
+
const third = fetch('https://example.com/third')
|
|
543
|
+
await waitersReady.promise
|
|
544
|
+
firstGate.resolve()
|
|
545
|
+
|
|
546
|
+
expect((await first).status).toBe(200)
|
|
547
|
+
expect((await second).status).toBe(500)
|
|
548
|
+
expect((await third).status).toBe(500)
|
|
549
|
+
expect(actions).toEqual(['open', 'topUp', 'voucher', 'topUp', 'voucher'])
|
|
550
|
+
expect(vouchers).toEqual([200n, 300n])
|
|
551
|
+
expect(await channelStore.get(defaultChannelKey)).toMatchObject({ opened: true })
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
test('replaces a stored channel owned by another account', async () => {
|
|
286
555
|
const channelStore = createChannelStore()
|
|
287
556
|
const foreign = '0x0000000000000000000000000000000000000004' as Address
|
|
288
557
|
await channelStore.set({
|
|
@@ -295,29 +564,21 @@ describe('precompile client session', () => {
|
|
|
295
564
|
opened: true,
|
|
296
565
|
})
|
|
297
566
|
const actions: Types.SessionCredentialPayload['action'][] = []
|
|
298
|
-
const challenge =
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
})
|
|
305
|
-
const fetch = Fetch.from({
|
|
306
|
-
fetch: async (_input, init) => {
|
|
307
|
-
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
308
|
-
if (!authorization)
|
|
309
|
-
return new Response(null, {
|
|
310
|
-
status: 402,
|
|
311
|
-
headers: { [Constants.Headers.wwwAuthenticate]: serializeChallenge(challenge) },
|
|
312
|
-
})
|
|
313
|
-
actions.push(deserialize(authorization).action)
|
|
314
|
-
return new Response('paid')
|
|
315
|
-
},
|
|
316
|
-
methods: [session({ account, channelStore, decimals: 0, getClient: () => client })],
|
|
317
|
-
})
|
|
567
|
+
const challenge = makeSessionChallenge({ suggestedDeposit: '1000' })
|
|
568
|
+
const fetch = makeSessionFetch(async (_input, init) => {
|
|
569
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
570
|
+
if (!authorization) return paymentRequired(challenge)
|
|
571
|
+
actions.push(deserialize(authorization).action)
|
|
572
|
+
return new Response('paid')
|
|
573
|
+
}, channelStore)
|
|
318
574
|
|
|
319
575
|
expect(await (await fetch('https://example.com/resource')).text()).toBe('paid')
|
|
320
|
-
expect(
|
|
576
|
+
expect(await (await fetch('https://example.com/resource')).text()).toBe('paid')
|
|
577
|
+
expect(actions).toEqual(['open', 'voucher'])
|
|
578
|
+
expect(await channelStore.get(defaultChannelKey)).toMatchObject({
|
|
579
|
+
descriptor: { authorizedSigner: account.address, payer: account.address },
|
|
580
|
+
opened: true,
|
|
581
|
+
})
|
|
321
582
|
})
|
|
322
583
|
|
|
323
584
|
test('uses a matching server snapshot deposit when checking headroom', async () => {
|
|
@@ -519,12 +780,17 @@ describe('precompile client session', () => {
|
|
|
519
780
|
expect(openDeposit(payload)).toBe(100n)
|
|
520
781
|
})
|
|
521
782
|
|
|
522
|
-
test('rejects open
|
|
783
|
+
test('rejects open above maxDeposit without leaking the Fetch lock', async () => {
|
|
523
784
|
const method = session({ account, decimals: 0, maxDeposit: '50', getClient: () => client })
|
|
785
|
+
const fetch = Fetch.from({
|
|
786
|
+
fetch: async () => paymentRequired(makeSessionChallenge()),
|
|
787
|
+
methods: [method],
|
|
788
|
+
})
|
|
524
789
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
790
|
+
for (let attempt = 0; attempt < 2; attempt++)
|
|
791
|
+
await expect(fetch('https://example.com/resource')).rejects.toThrow(
|
|
792
|
+
'requested voucher amount 100 exceeds local maxDeposit 50',
|
|
793
|
+
)
|
|
528
794
|
})
|
|
529
795
|
|
|
530
796
|
test('rejects explicit opening deposits below the request amount', async () => {
|