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
|
@@ -3,6 +3,7 @@ import { tempo as tempo_chain } from 'viem/chains'
|
|
|
3
3
|
|
|
4
4
|
import * as MethodChallenge from '../../../client/internal/MethodChallenge.js'
|
|
5
5
|
import * as MethodResponse from '../../../client/internal/MethodResponse.js'
|
|
6
|
+
import * as Constants from '../../../Constants.js'
|
|
6
7
|
import * as Credential from '../../../Credential.js'
|
|
7
8
|
import * as Method from '../../../Method.js'
|
|
8
9
|
import * as Account from '../../../viem/Account.js'
|
|
@@ -16,6 +17,7 @@ import * as defaults from '../../internal/defaults.js'
|
|
|
16
17
|
import * as Methods from '../../Methods.js'
|
|
17
18
|
import * as Channel from '../precompile/Channel.js'
|
|
18
19
|
import {
|
|
20
|
+
deserializeSessionReceipt,
|
|
19
21
|
isEventStream,
|
|
20
22
|
readSessionChallengeAmount,
|
|
21
23
|
requireSessionCredentialContext,
|
|
@@ -35,6 +37,7 @@ import {
|
|
|
35
37
|
} from './CredentialState.js'
|
|
36
38
|
import { assertWithinMaxDeposit, resolveAutomaticTopUp } from './Runtime.js'
|
|
37
39
|
import {
|
|
40
|
+
getSessionSnapshot,
|
|
38
41
|
handleSseNeedVoucher,
|
|
39
42
|
isTip1034SessionChallenge,
|
|
40
43
|
postTopUp,
|
|
@@ -45,6 +48,67 @@ import {
|
|
|
45
48
|
|
|
46
49
|
export { sessionContextSchema, type SessionContext } from './CredentialState.js'
|
|
47
50
|
|
|
51
|
+
/** Tail promise for each payment scope, used to serialize automatic opens per store. */
|
|
52
|
+
const channelTails = new WeakMap<ChannelStore, Map<string, Promise<void>>>()
|
|
53
|
+
|
|
54
|
+
/** Serializes automatic opens for one payment scope across methods sharing a store. */
|
|
55
|
+
async function lockChannel(store: ChannelStore, key: string) {
|
|
56
|
+
const tails = channelTails.get(store) ?? new Map<string, Promise<void>>()
|
|
57
|
+
channelTails.set(store, tails)
|
|
58
|
+
const previous = tails.get(key)
|
|
59
|
+
let unlock!: () => void
|
|
60
|
+
const current = new Promise<void>((resolve) => {
|
|
61
|
+
unlock = resolve
|
|
62
|
+
})
|
|
63
|
+
tails.set(key, current)
|
|
64
|
+
await previous
|
|
65
|
+
return () => {
|
|
66
|
+
unlock()
|
|
67
|
+
if (tails.get(key) === current) tails.delete(key)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function matchesOpen(
|
|
72
|
+
opened: ChannelEntry,
|
|
73
|
+
acknowledgement: { acceptedCumulative: string; channelId: string } | null | undefined,
|
|
74
|
+
): boolean {
|
|
75
|
+
try {
|
|
76
|
+
return (
|
|
77
|
+
acknowledgement?.channelId.toLowerCase() === opened.channelId.toLowerCase() &&
|
|
78
|
+
BigInt(acknowledgement.acceptedCumulative) === opened.cumulativeAmount
|
|
79
|
+
)
|
|
80
|
+
} catch {
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Returns whether the server acknowledged an open.
|
|
87
|
+
*
|
|
88
|
+
* Receipts must match the originating challenge, channel, and cumulative amount.
|
|
89
|
+
* Replacement challenges may acknowledge the same channel and amount through a
|
|
90
|
+
* session snapshot. Malformed acknowledgements are ignored.
|
|
91
|
+
*/
|
|
92
|
+
function acknowledgesOpen(
|
|
93
|
+
outcome: MethodResponse.AttemptOutcome,
|
|
94
|
+
challenge: TempoSessionChallenge,
|
|
95
|
+
opened: ChannelEntry,
|
|
96
|
+
): boolean {
|
|
97
|
+
try {
|
|
98
|
+
const receiptHeader = outcome.response?.headers.get(Constants.Headers.paymentReceipt)
|
|
99
|
+
if (receiptHeader) {
|
|
100
|
+
const receipt = deserializeSessionReceipt(receiptHeader)
|
|
101
|
+
if (receipt.challengeId === challenge.id && matchesOpen(opened, receipt)) return true
|
|
102
|
+
}
|
|
103
|
+
} catch {}
|
|
104
|
+
return (
|
|
105
|
+
outcome.challenges?.some((candidate) => {
|
|
106
|
+
if (!isTip1034SessionChallenge(candidate)) return false
|
|
107
|
+
return matchesOpen(opened, getSessionSnapshot(candidate))
|
|
108
|
+
}) ?? false
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
48
112
|
/**
|
|
49
113
|
* Creates the low-level TIP-1034 session payment method for use with `Mppx.create()`.
|
|
50
114
|
*
|
|
@@ -100,30 +164,102 @@ export function session(parameters: session.Parameters = {}) {
|
|
|
100
164
|
)
|
|
101
165
|
}
|
|
102
166
|
|
|
167
|
+
const resolveCredentialPlan = async (
|
|
168
|
+
resolved: ChallengeContext,
|
|
169
|
+
context: CredentialContext | undefined,
|
|
170
|
+
entry: ChannelEntry | undefined,
|
|
171
|
+
) =>
|
|
172
|
+
planCredential({
|
|
173
|
+
account: await resolveCredentialAccount(resolved, context, entry),
|
|
174
|
+
entry,
|
|
175
|
+
context,
|
|
176
|
+
decimals,
|
|
177
|
+
maxDeposit,
|
|
178
|
+
resolved,
|
|
179
|
+
})
|
|
180
|
+
|
|
103
181
|
const method = Method.toClient(Methods.session, {
|
|
104
182
|
canHandleChallenge: ({ challenge }) => isTip1034SessionChallenge(challenge),
|
|
105
183
|
context: sessionContextSchema,
|
|
106
|
-
async createCredential(
|
|
184
|
+
async createCredential(parameters) {
|
|
185
|
+
const { challenge, context } = parameters
|
|
107
186
|
const resolved = await resolveChallengeContext({
|
|
108
187
|
challenge,
|
|
109
188
|
escrowOverride,
|
|
110
189
|
getClient,
|
|
111
190
|
})
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
191
|
+
const attempt = MethodResponse.getAttempt(parameters)
|
|
192
|
+
let release = () => {}
|
|
193
|
+
try {
|
|
194
|
+
let entry = await store.get(resolved.key)
|
|
195
|
+
let plan = await resolveCredentialPlan(resolved, context, entry)
|
|
196
|
+
if (attempt && plan.type === 'open') {
|
|
197
|
+
release = await lockChannel(store, resolved.key)
|
|
198
|
+
const nextEntry = await store.get(resolved.key)
|
|
199
|
+
if (nextEntry?.channelId !== entry?.channelId) {
|
|
200
|
+
entry = nextEntry
|
|
201
|
+
if (entry?.opened) {
|
|
202
|
+
await attempt.prepare()
|
|
203
|
+
entry = await store.get(resolved.key)
|
|
204
|
+
}
|
|
205
|
+
plan = await resolveCredentialPlan(resolved, context, entry)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
let pendingOpen: ChannelEntry | undefined
|
|
209
|
+
// Defer opens only when low-level Fetch owns the response lifecycle.
|
|
210
|
+
// SessionManager unregisters this hook and keeps its existing transaction boundary.
|
|
211
|
+
const credentialSink =
|
|
212
|
+
attempt && plan.type === 'open'
|
|
213
|
+
? {
|
|
214
|
+
store: {
|
|
215
|
+
get: (key: string) => store.get(key),
|
|
216
|
+
async set(next: ChannelEntry) {
|
|
217
|
+
pendingOpen = next
|
|
218
|
+
},
|
|
219
|
+
delete: (key: string) => store.delete(key),
|
|
220
|
+
},
|
|
221
|
+
notifyUpdate() {},
|
|
222
|
+
}
|
|
223
|
+
: sink
|
|
224
|
+
const payload = await executeCredentialPlan(
|
|
225
|
+
plan,
|
|
226
|
+
credentialSink,
|
|
227
|
+
AutoSwap.resolve(context?.autoSwap ?? autoSwapParameter, AutoSwap.defaultCurrencies),
|
|
228
|
+
)
|
|
229
|
+
const credential = await serializeCredential(
|
|
230
|
+
challenge,
|
|
231
|
+
payload,
|
|
232
|
+
resolved.chainId,
|
|
233
|
+
plan.account,
|
|
234
|
+
)
|
|
235
|
+
if (attempt && pendingOpen) {
|
|
236
|
+
const opened = pendingOpen
|
|
237
|
+
attempt.settle = async (outcome) => {
|
|
238
|
+
const accepted =
|
|
239
|
+
outcome.status === 'accepted' || acknowledgesOpen(outcome, challenge, opened)
|
|
240
|
+
if (!accepted && outcome.status === 'pending') return false
|
|
241
|
+
try {
|
|
242
|
+
if (accepted) {
|
|
243
|
+
const current = await store.get(resolved.key)
|
|
244
|
+
if (
|
|
245
|
+
!current?.opened ||
|
|
246
|
+
current.channelId.toLowerCase() !== opened.channelId.toLowerCase()
|
|
247
|
+
) {
|
|
248
|
+
await store.set(opened)
|
|
249
|
+
sink.notifyUpdate(opened)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return true
|
|
253
|
+
} finally {
|
|
254
|
+
release()
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
} else release()
|
|
258
|
+
return credential
|
|
259
|
+
} catch (error) {
|
|
260
|
+
release()
|
|
261
|
+
throw error
|
|
262
|
+
}
|
|
127
263
|
},
|
|
128
264
|
})
|
|
129
265
|
|
|
@@ -113,6 +113,20 @@ function makeChannelStore(seed: readonly ChannelEntry[] = []) {
|
|
|
113
113
|
return { store, set, delete: remove, map }
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
function makeDurableChannelStore() {
|
|
117
|
+
const backend = new Map<string, string>()
|
|
118
|
+
const store = createJsonChannelStore({
|
|
119
|
+
get: (key) => backend.get(key),
|
|
120
|
+
set: (key, value) => {
|
|
121
|
+
backend.set(key, value)
|
|
122
|
+
},
|
|
123
|
+
delete: (key) => {
|
|
124
|
+
backend.delete(key)
|
|
125
|
+
},
|
|
126
|
+
})
|
|
127
|
+
return { backend, store }
|
|
128
|
+
}
|
|
129
|
+
|
|
116
130
|
function makeChallenge(overrides: Record<string, unknown> = {}): Challenge.Challenge {
|
|
117
131
|
return Challenge.from({
|
|
118
132
|
id: challengeId,
|
|
@@ -657,7 +671,7 @@ describe('Session', () => {
|
|
|
657
671
|
})
|
|
658
672
|
})
|
|
659
673
|
|
|
660
|
-
test('rolls back
|
|
674
|
+
test('rolls back optimistic opens across replacement challenges', async () => {
|
|
661
675
|
const { store, delete: remove } = makeChannelStore()
|
|
662
676
|
const postedPayloads: SessionCredentialPayload[] = []
|
|
663
677
|
const challenge = (feePayer: boolean) =>
|
|
@@ -669,6 +683,8 @@ describe('Session', () => {
|
|
|
669
683
|
feePayer,
|
|
670
684
|
},
|
|
671
685
|
})
|
|
686
|
+
const original = challenge(true)
|
|
687
|
+
const replacement = challenge(false)
|
|
672
688
|
let openCount = 0
|
|
673
689
|
const mockFetch = vi.fn().mockImplementation((_input, init?: RequestInit) => {
|
|
674
690
|
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
@@ -677,10 +693,11 @@ describe('Session', () => {
|
|
|
677
693
|
: undefined
|
|
678
694
|
if (payload) postedPayloads.push(payload)
|
|
679
695
|
|
|
680
|
-
if (!payload) return Promise.resolve(make402Response(
|
|
696
|
+
if (!payload) return Promise.resolve(make402Response(original))
|
|
681
697
|
if (payload.action === 'open') {
|
|
682
698
|
openCount++
|
|
683
|
-
if (openCount === 1) return Promise.resolve(make402Response(
|
|
699
|
+
if (openCount === 1) return Promise.resolve(make402Response(replacement))
|
|
700
|
+
if (openCount === 2) return Promise.resolve(make402Response(original))
|
|
684
701
|
return Promise.resolve(makeOkResponse())
|
|
685
702
|
}
|
|
686
703
|
return Promise.resolve(new Response('unexpected voucher', { status: 500 }))
|
|
@@ -696,12 +713,33 @@ describe('Session', () => {
|
|
|
696
713
|
const response = await s.fetch('https://api.example.com/data')
|
|
697
714
|
|
|
698
715
|
expect(response.status).toBe(200)
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
expect(
|
|
702
|
-
expect(
|
|
703
|
-
|
|
704
|
-
|
|
716
|
+
expect(postedPayloads.map((payload) => payload.action)).toEqual(['open', 'open', 'open'])
|
|
717
|
+
expect(new Set(postedPayloads.map((payload) => payload.channelId)).size).toBe(3)
|
|
718
|
+
expect(remove).toHaveBeenCalledTimes(2)
|
|
719
|
+
expect(s.channelId).toBe(postedPayloads[2]?.channelId)
|
|
720
|
+
})
|
|
721
|
+
|
|
722
|
+
test('reuses prepared voucher state when an identical challenge succeeds', async () => {
|
|
723
|
+
const vouchers: Extract<SessionCredentialPayload, { action: 'voucher' }>[] = []
|
|
724
|
+
const mockFetch = vi.fn().mockImplementation((_input, init?: RequestInit) => {
|
|
725
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
726
|
+
if (!authorization) return Promise.resolve(make402Response())
|
|
727
|
+
const payload = Credential.deserialize<SessionCredentialPayload>(authorization).payload
|
|
728
|
+
if (payload.action !== 'voucher') throw new Error('expected voucher')
|
|
729
|
+
vouchers.push(payload)
|
|
730
|
+
return Promise.resolve(vouchers.length === 1 ? make402Response() : makeOkResponse())
|
|
731
|
+
})
|
|
732
|
+
const s = sessionManager({
|
|
733
|
+
account,
|
|
734
|
+
client,
|
|
735
|
+
fetch: mockFetch as typeof globalThis.fetch,
|
|
736
|
+
channelStore: makeChannelStore([channelEntry()]).store,
|
|
737
|
+
})
|
|
738
|
+
|
|
739
|
+
expect((await s.fetch('https://api.example.com/data')).status).toBe(200)
|
|
740
|
+
expect(vouchers.map((voucher) => voucher.cumulativeAmount)).toEqual(['2000000', '2000000'])
|
|
741
|
+
expect(s.cumulative).toBe(2_000_000n)
|
|
742
|
+
expect(s.state.status).toBe('active')
|
|
705
743
|
})
|
|
706
744
|
|
|
707
745
|
test('does not bootstrap when disabled', async () => {
|
|
@@ -989,6 +1027,60 @@ describe('Session', () => {
|
|
|
989
1027
|
})
|
|
990
1028
|
})
|
|
991
1029
|
|
|
1030
|
+
test('preserves a snapshot-acknowledged open when its voucher fails', async () => {
|
|
1031
|
+
const { backend, store } = makeDurableChannelStore()
|
|
1032
|
+
const payloads: SessionCredentialPayload[] = []
|
|
1033
|
+
const mockFetch = vi.fn().mockImplementation((_input, init?: RequestInit) => {
|
|
1034
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
1035
|
+
if (!authorization)
|
|
1036
|
+
return Promise.resolve(make402Response(makeChallenge({ suggestedDeposit: '10000000' })))
|
|
1037
|
+
const payload = Credential.deserialize<SessionCredentialPayload>(authorization).payload
|
|
1038
|
+
payloads.push(payload)
|
|
1039
|
+
if (payload.action !== 'open')
|
|
1040
|
+
return Promise.resolve(new Response('failed', { status: 500 }))
|
|
1041
|
+
return Promise.resolve(
|
|
1042
|
+
make402Response(
|
|
1043
|
+
makeChallenge({
|
|
1044
|
+
methodDetails: {
|
|
1045
|
+
escrowContract: tip20ChannelEscrow,
|
|
1046
|
+
chainId: 4217,
|
|
1047
|
+
sessionProtocol: Constants.SessionProtocols.v2,
|
|
1048
|
+
sessionSnapshot: {
|
|
1049
|
+
acceptedCumulative: '1000000',
|
|
1050
|
+
chainId: 4217,
|
|
1051
|
+
channelId: payload.channelId,
|
|
1052
|
+
deposit: '10000000',
|
|
1053
|
+
descriptor: payload.descriptor,
|
|
1054
|
+
escrow: tip20ChannelEscrow,
|
|
1055
|
+
requiredCumulative: '2000000',
|
|
1056
|
+
settled: '0',
|
|
1057
|
+
spent: '1000000',
|
|
1058
|
+
units: 1,
|
|
1059
|
+
},
|
|
1060
|
+
},
|
|
1061
|
+
}),
|
|
1062
|
+
),
|
|
1063
|
+
)
|
|
1064
|
+
})
|
|
1065
|
+
const s = sessionManager({
|
|
1066
|
+
account,
|
|
1067
|
+
client,
|
|
1068
|
+
fetch: mockFetch as typeof globalThis.fetch,
|
|
1069
|
+
maxDeposit: '10',
|
|
1070
|
+
channelStore: store,
|
|
1071
|
+
})
|
|
1072
|
+
|
|
1073
|
+
expect((await s.fetch('https://api.example.com/data')).status).toBe(500)
|
|
1074
|
+
expect(payloads.map((payload) => payload.action)).toEqual(['open', 'voucher'])
|
|
1075
|
+
expect(s.channelId).toBe(payloads[0]?.channelId)
|
|
1076
|
+
expect(s.cumulative).toBe(1_000_000n)
|
|
1077
|
+
expect(JSON.parse([...backend.values()][0]!)).toMatchObject({
|
|
1078
|
+
channelId: payloads[0]?.channelId,
|
|
1079
|
+
cumulativeAmount: '1000000',
|
|
1080
|
+
opened: true,
|
|
1081
|
+
})
|
|
1082
|
+
})
|
|
1083
|
+
|
|
992
1084
|
test('preemptively top-ups before signing an HTTP voucher that exceeds deposit', async () => {
|
|
993
1085
|
const postedPayloads: SessionCredentialPayload[] = []
|
|
994
1086
|
let challengeCount = 0
|
|
@@ -1055,6 +1147,121 @@ describe('Session', () => {
|
|
|
1055
1147
|
units: 2,
|
|
1056
1148
|
})
|
|
1057
1149
|
})
|
|
1150
|
+
|
|
1151
|
+
test('preserves a successful automatic top-up when the paid retry fails', async () => {
|
|
1152
|
+
const { map, store } = makeChannelStore()
|
|
1153
|
+
const payloads: SessionCredentialPayload[] = []
|
|
1154
|
+
const mockFetch = vi.fn().mockImplementation((_input, init?: RequestInit) => {
|
|
1155
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
1156
|
+
if (!authorization)
|
|
1157
|
+
return Promise.resolve(make402Response(makeChallenge({ suggestedDeposit: '1000000' })))
|
|
1158
|
+
const payload = Credential.deserialize<SessionCredentialPayload>(authorization).payload
|
|
1159
|
+
payloads.push(payload)
|
|
1160
|
+
if (payload.action === 'open')
|
|
1161
|
+
return Promise.resolve(
|
|
1162
|
+
new Response('ok', {
|
|
1163
|
+
headers: {
|
|
1164
|
+
[Constants.Headers.paymentReceipt]: serializeSessionReceipt(
|
|
1165
|
+
createSessionReceipt({
|
|
1166
|
+
acceptedCumulative: 1_000_000n,
|
|
1167
|
+
challengeId,
|
|
1168
|
+
channelId: payload.channelId,
|
|
1169
|
+
spent: 1_000_000n,
|
|
1170
|
+
units: 3,
|
|
1171
|
+
}),
|
|
1172
|
+
),
|
|
1173
|
+
},
|
|
1174
|
+
}),
|
|
1175
|
+
)
|
|
1176
|
+
if (payload.action === 'topUp') return Promise.resolve(new Response(null, { status: 204 }))
|
|
1177
|
+
return Promise.resolve(make402Response(makeChallenge({ suggestedDeposit: '1000000' })))
|
|
1178
|
+
})
|
|
1179
|
+
const s = sessionManager({
|
|
1180
|
+
account,
|
|
1181
|
+
client,
|
|
1182
|
+
fetch: mockFetch as typeof globalThis.fetch,
|
|
1183
|
+
maxDeposit: '3',
|
|
1184
|
+
channelStore: store,
|
|
1185
|
+
})
|
|
1186
|
+
|
|
1187
|
+
await s.fetch('https://api.example.com/data')
|
|
1188
|
+
expect((await s.fetch('https://api.example.com/data')).status).toBe(402)
|
|
1189
|
+
|
|
1190
|
+
expect(payloads.map((payload) => payload.action)).toEqual([
|
|
1191
|
+
'open',
|
|
1192
|
+
'topUp',
|
|
1193
|
+
'voucher',
|
|
1194
|
+
'voucher',
|
|
1195
|
+
'voucher',
|
|
1196
|
+
])
|
|
1197
|
+
expect(s.cumulative).toBe(1_000_000n)
|
|
1198
|
+
expect(s.state).toMatchObject({ status: 'active', deposit: '2000000', units: 3 })
|
|
1199
|
+
expect([...map.values()][0]).toMatchObject({
|
|
1200
|
+
cumulativeAmount: 1_000_000n,
|
|
1201
|
+
deposit: 2_000_000n,
|
|
1202
|
+
})
|
|
1203
|
+
})
|
|
1204
|
+
|
|
1205
|
+
test('preserves a topped-up resumed channel in durable storage after payment fails', async () => {
|
|
1206
|
+
const { store } = makeDurableChannelStore()
|
|
1207
|
+
const seeded = channelEntry({ cumulativeAmount: 1_000_000n, deposit: 1_000_000n })
|
|
1208
|
+
await store.set(seeded)
|
|
1209
|
+
const payloads: SessionCredentialPayload[] = []
|
|
1210
|
+
const mockFetch = vi.fn().mockImplementation((_input, init?: RequestInit) => {
|
|
1211
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
1212
|
+
if (!authorization)
|
|
1213
|
+
return Promise.resolve(make402Response(makeChallenge({ suggestedDeposit: '1000000' })))
|
|
1214
|
+
const payload = Credential.deserialize<SessionCredentialPayload>(authorization).payload
|
|
1215
|
+
payloads.push(payload)
|
|
1216
|
+
return Promise.resolve(
|
|
1217
|
+
payload.action === 'topUp'
|
|
1218
|
+
? new Response(null, { status: 204 })
|
|
1219
|
+
: new Response('failed', { status: 500 }),
|
|
1220
|
+
)
|
|
1221
|
+
})
|
|
1222
|
+
const s = sessionManager({
|
|
1223
|
+
account,
|
|
1224
|
+
client,
|
|
1225
|
+
fetch: mockFetch as typeof globalThis.fetch,
|
|
1226
|
+
maxDeposit: '3',
|
|
1227
|
+
channelStore: store,
|
|
1228
|
+
})
|
|
1229
|
+
|
|
1230
|
+
expect((await s.fetch('https://api.example.com/data')).status).toBe(500)
|
|
1231
|
+
|
|
1232
|
+
expect(payloads.map((payload) => payload.action)).toEqual(['topUp', 'voucher'])
|
|
1233
|
+
expect(s.cumulative).toBe(1_000_000n)
|
|
1234
|
+
expect(s.state).toMatchObject({ status: 'active', deposit: '2000000' })
|
|
1235
|
+
await expect(store.get(entryKey(seeded))).resolves.toMatchObject({
|
|
1236
|
+
cumulativeAmount: 1_000_000n,
|
|
1237
|
+
deposit: 2_000_000n,
|
|
1238
|
+
opened: true,
|
|
1239
|
+
})
|
|
1240
|
+
})
|
|
1241
|
+
|
|
1242
|
+
test.each([
|
|
1243
|
+
['new', false],
|
|
1244
|
+
['replacement', true],
|
|
1245
|
+
] as const)('removes a failed %s durable channel', async (_kind, seeded) => {
|
|
1246
|
+
const { backend, store } = makeDurableChannelStore()
|
|
1247
|
+
if (seeded) await store.set(channelEntry())
|
|
1248
|
+
const mockFetch = vi.fn().mockImplementation((_input, init?: RequestInit) => {
|
|
1249
|
+
const authorization = new Headers(init?.headers).get(Constants.Headers.authorization)
|
|
1250
|
+
return Promise.resolve(
|
|
1251
|
+
authorization ? new Response('failed', { status: 500 }) : make402Response(),
|
|
1252
|
+
)
|
|
1253
|
+
})
|
|
1254
|
+
const s = sessionManager({
|
|
1255
|
+
account,
|
|
1256
|
+
client,
|
|
1257
|
+
fetch: mockFetch as typeof globalThis.fetch,
|
|
1258
|
+
channelStore: store,
|
|
1259
|
+
})
|
|
1260
|
+
|
|
1261
|
+
expect((await s.fetch('https://api.example.com/data')).status).toBe(500)
|
|
1262
|
+
expect(s.channelId).toBeUndefined()
|
|
1263
|
+
expect(backend.size).toBe(0)
|
|
1264
|
+
})
|
|
1058
1265
|
})
|
|
1059
1266
|
|
|
1060
1267
|
describe('.topUp()', () => {
|
|
@@ -58,6 +58,7 @@ import { applyTopUpResult, resolveManualTopUp, type TopUpRequirement } from './T
|
|
|
58
58
|
import {
|
|
59
59
|
openWebSocketSession,
|
|
60
60
|
prepareWebSocketSession,
|
|
61
|
+
probeWebSocketSession,
|
|
61
62
|
type WebSocketDriverOptions,
|
|
62
63
|
} from './Transports.js'
|
|
63
64
|
|
|
@@ -243,11 +244,14 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
243
244
|
|
|
244
245
|
/** Removes a failed channel from candidacy for the rest of this manager's life. */
|
|
245
246
|
async function ignoreChannel(entry: ChannelEntry) {
|
|
247
|
+
const key = entryKey(entry)
|
|
246
248
|
ignoredChannelIds.add(entry.channelId)
|
|
247
|
-
|
|
249
|
+
channelUse?.seenExisting.delete(key)
|
|
250
|
+
if (channelUse?.resumed === entry) channelUse.resumed = undefined
|
|
251
|
+
await Promise.resolve(backing.delete(key)).catch(() => undefined)
|
|
248
252
|
}
|
|
249
253
|
|
|
250
|
-
async function
|
|
254
|
+
async function rollbackCreatedChannels() {
|
|
251
255
|
const use = channelUse
|
|
252
256
|
if (!use?.created.size) return
|
|
253
257
|
for (const [key, entry] of use.created) {
|
|
@@ -255,19 +259,31 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
255
259
|
await Promise.resolve(backing.delete(key)).catch(() => undefined)
|
|
256
260
|
}
|
|
257
261
|
use.created.clear()
|
|
258
|
-
restoreRuntime(use.previous)
|
|
262
|
+
await restoreRuntime(use.committed ?? use.previous)
|
|
259
263
|
}
|
|
260
264
|
|
|
261
|
-
|
|
265
|
+
/** Commits a newly created channel once an authoritative server snapshot references it. */
|
|
266
|
+
function commitReferencedChannel(challenge: Challenge.Challenge): boolean {
|
|
262
267
|
const snapshot = Constants.getMethodDetail<{ channelId?: unknown }>(
|
|
263
268
|
challenge.request.methodDetails,
|
|
264
269
|
Constants.MethodDetailKeys.sessionSnapshot,
|
|
265
270
|
)
|
|
266
271
|
if (typeof snapshot?.channelId !== 'string') return false
|
|
267
272
|
const use = channelUse
|
|
268
|
-
if (!use?.created.size) return false
|
|
269
|
-
for (const entry of use.created
|
|
270
|
-
if (
|
|
273
|
+
if (!use?.created.size || !runtime.channel) return false
|
|
274
|
+
for (const [key, entry] of use.created) {
|
|
275
|
+
if (
|
|
276
|
+
entry.channelId.toLowerCase() !== snapshot.channelId.toLowerCase() ||
|
|
277
|
+
runtime.channel.channelId.toLowerCase() !== snapshot.channelId.toLowerCase()
|
|
278
|
+
)
|
|
279
|
+
continue
|
|
280
|
+
use.created.delete(key)
|
|
281
|
+
use.committed = captureRuntimeStateSnapshot({
|
|
282
|
+
channel: runtime.channel,
|
|
283
|
+
spent: runtime.spent,
|
|
284
|
+
state: runtime.state,
|
|
285
|
+
})
|
|
286
|
+
return true
|
|
271
287
|
}
|
|
272
288
|
return false
|
|
273
289
|
}
|
|
@@ -276,6 +292,14 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
276
292
|
return dispatchSessionEvent(runtime, event)
|
|
277
293
|
}
|
|
278
294
|
|
|
295
|
+
function activeUnits() {
|
|
296
|
+
const state =
|
|
297
|
+
runtime.state.status === 'active'
|
|
298
|
+
? runtime.state
|
|
299
|
+
: (channelUse?.committed?.state ?? channelUse?.previous.state)
|
|
300
|
+
return state?.status === 'active' ? state.units : 0
|
|
301
|
+
}
|
|
302
|
+
|
|
279
303
|
function commitDurableTopUp(entry: ChannelEntry) {
|
|
280
304
|
const use = channelUse
|
|
281
305
|
const baseline = use?.committed?.channel?.entry ?? use?.resumed
|
|
@@ -312,7 +336,7 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
312
336
|
challengeId: runtime.lastChallenge.id,
|
|
313
337
|
entry,
|
|
314
338
|
spent: runtime.spent.toString(),
|
|
315
|
-
units:
|
|
339
|
+
units: activeUnits(),
|
|
316
340
|
})
|
|
317
341
|
}
|
|
318
342
|
commitDurableTopUp(entry)
|
|
@@ -332,11 +356,15 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
332
356
|
const use = channelUse
|
|
333
357
|
const isRepeatedRetryChallenge =
|
|
334
358
|
use && use.challengesReceived > 0 && challenge.id === runtime.lastChallenge?.id
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
359
|
+
const isSameRetryChallenge =
|
|
360
|
+
isRepeatedRetryChallenge &&
|
|
361
|
+
Challenge.serialize(challenge) === Challenge.serialize(runtime.lastChallenge!)
|
|
339
362
|
if (use) use.challengesReceived++
|
|
363
|
+
if (isSameRetryChallenge) return undefined
|
|
364
|
+
if (!commitReferencedChannel(challenge)) {
|
|
365
|
+
if (use?.created.size) await rollbackCreatedChannels()
|
|
366
|
+
else if (isRepeatedRetryChallenge) await restoreRuntime(use.committed ?? use.previous)
|
|
367
|
+
}
|
|
340
368
|
runtime.lastChallenge = challenge
|
|
341
369
|
dispatch({ type: 'challengeReceived', challengeId: challenge.id })
|
|
342
370
|
return undefined
|
|
@@ -558,7 +586,7 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
558
586
|
challengeId: runtime.lastChallenge.id,
|
|
559
587
|
entry: applied.channel,
|
|
560
588
|
spent: runtime.spent.toString(),
|
|
561
|
-
units:
|
|
589
|
+
units: activeUnits(),
|
|
562
590
|
})
|
|
563
591
|
commitDurableTopUp(applied.channel)
|
|
564
592
|
}
|
|
@@ -589,7 +617,7 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
589
617
|
})
|
|
590
618
|
}
|
|
591
619
|
|
|
592
|
-
function restoreCumulative(channelId: Hex.Hex, cumulativeAmount: bigint) {
|
|
620
|
+
async function restoreCumulative(channelId: Hex.Hex, cumulativeAmount: bigint) {
|
|
593
621
|
const restored = restoreCumulativeAuthorization({
|
|
594
622
|
channel: runtime.channel,
|
|
595
623
|
channelId,
|
|
@@ -606,14 +634,16 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
606
634
|
spent: restored.spent,
|
|
607
635
|
units: restored.units,
|
|
608
636
|
})
|
|
637
|
+
await backing.set(runtime.channel)
|
|
609
638
|
}
|
|
610
639
|
}
|
|
611
640
|
|
|
612
|
-
function restoreRuntime(snapshot: RuntimeSnapshot) {
|
|
641
|
+
async function restoreRuntime(snapshot: RuntimeSnapshot) {
|
|
613
642
|
const restored = restoreRuntimeStateSnapshot(snapshot, runtime.channel)
|
|
614
643
|
runtime.channel = restored.channel
|
|
615
644
|
runtime.spent = restored.spent
|
|
616
645
|
runtime.state = restored.state
|
|
646
|
+
if (runtime.channel?.opened) await backing.set(runtime.channel)
|
|
617
647
|
}
|
|
618
648
|
|
|
619
649
|
function toPaymentResponse(response: Response): PaymentResponse {
|
|
@@ -679,7 +709,8 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
679
709
|
try {
|
|
680
710
|
response = await wrappedFetch(input, effectiveInit)
|
|
681
711
|
} catch (error) {
|
|
682
|
-
|
|
712
|
+
if (use.created.size) await rollbackCreatedChannels()
|
|
713
|
+
else await restoreRuntime(use.committed ?? previous)
|
|
683
714
|
if (await retryWithoutResumed()) continue
|
|
684
715
|
throw error
|
|
685
716
|
}
|
|
@@ -706,7 +737,8 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
706
737
|
}
|
|
707
738
|
}
|
|
708
739
|
if (!attemptedHttpManagement && !paymentResponse.ok && !paymentResponse.receipt) {
|
|
709
|
-
|
|
740
|
+
if (use.created.size) await rollbackCreatedChannels()
|
|
741
|
+
else await restoreRuntime(use.committed ?? previous)
|
|
710
742
|
if (await retryWithoutResumed()) continue
|
|
711
743
|
return paymentResponse
|
|
712
744
|
}
|
|
@@ -838,6 +870,20 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
838
870
|
setSocketSession(session) {
|
|
839
871
|
runtime.socketSession = session
|
|
840
872
|
},
|
|
873
|
+
async refreshChallenge() {
|
|
874
|
+
const refreshed = await probeWebSocketSession({
|
|
875
|
+
fetch: config.fetch,
|
|
876
|
+
input: httpUrl,
|
|
877
|
+
probeInit: requestInitWithSessionHint(
|
|
878
|
+
httpUrl,
|
|
879
|
+
signalInit,
|
|
880
|
+
runtime.channel?.opened ? runtime.channel.channelId : undefined,
|
|
881
|
+
),
|
|
882
|
+
signal: init?.signal,
|
|
883
|
+
})
|
|
884
|
+
runtime.lastChallenge = refreshed.challenge
|
|
885
|
+
return refreshed.challenge
|
|
886
|
+
},
|
|
841
887
|
assertVoucherWithinLocalLimit,
|
|
842
888
|
acceptReceipt: updateSpentFromReceipt,
|
|
843
889
|
rejectCloseReady: receipts.rejectCloseReady,
|