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
package/src/Method.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type * as Challenge from './Challenge.js'
|
|
2
|
-
import * as Constants from './Constants.js'
|
|
3
2
|
import * as Credential from './Credential.js'
|
|
4
3
|
import * as Errors from './Errors.js'
|
|
5
4
|
import * as Expires from './Expires.js'
|
|
6
|
-
import type { ExactPartial, LooseOmit, MaybePromise } from './internal/types.js'
|
|
5
|
+
import type { DeepReadonly, ExactPartial, LooseOmit, MaybePromise } from './internal/types.js'
|
|
7
6
|
import type * as Receipt from './Receipt.js'
|
|
8
7
|
import type * as Html from './server/internal/html/config.js'
|
|
9
8
|
import type * as Transport from './server/Transport.js'
|
|
@@ -195,28 +194,64 @@ export type Server<
|
|
|
195
194
|
transportOverride = undefined,
|
|
196
195
|
extensions extends object = {},
|
|
197
196
|
alias extends string | undefined = string | undefined,
|
|
198
|
-
> = method &
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
197
|
+
> = method &
|
|
198
|
+
ComposableHooks<method> & {
|
|
199
|
+
alias?: alias | undefined
|
|
200
|
+
authorize?: AuthorizeFn<method> | undefined
|
|
201
|
+
defaults?: defaults | undefined
|
|
202
|
+
extensions?: extensions | undefined
|
|
203
|
+
html?: Html.Options | undefined
|
|
204
|
+
preflight?: PreflightFn<method> | undefined
|
|
205
|
+
request?: RequestFn<method> | undefined
|
|
206
|
+
respond?: RespondFn<method> | undefined
|
|
207
|
+
broadcast?: BroadcastFn<method> | undefined
|
|
208
|
+
stableBinding?: StableBindingFn<method> | undefined
|
|
209
|
+
transport?: transportOverride | undefined
|
|
210
|
+
validate?: ValidateFn<method> | undefined
|
|
211
|
+
/**
|
|
212
|
+
* @deprecated Use `validate` for the non-mutating pre-check and `broadcast`
|
|
213
|
+
* for settlement. `verify` combines both operations and may consume payment
|
|
214
|
+
* state, so it cannot support a safe pre-check endpoint.
|
|
215
|
+
*/
|
|
216
|
+
verify: VerifyFn<method>
|
|
217
|
+
}
|
|
218
218
|
export type AnyServer = Server<any, any, any, any, any>
|
|
219
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Decides whether a configured method offer is available for an HTTP request.
|
|
222
|
+
*
|
|
223
|
+
* Called once per configured offer during composition. The payment request is
|
|
224
|
+
* schema-normalized and deeply immutable. `input` is cloned so its body can be
|
|
225
|
+
* safely inspected unless the upstream body was already consumed or locked, in
|
|
226
|
+
* which case the clone preserves request metadata without a body. Returning
|
|
227
|
+
* `false` removes only this method's offer. Successfully matched credential
|
|
228
|
+
* dispatch does not invoke this hook.
|
|
229
|
+
*/
|
|
230
|
+
export type CanOfferFn<method extends Method> = (parameters: {
|
|
231
|
+
input: globalThis.Request
|
|
232
|
+
request: DeepReadonly<z.output<method['schema']['request']>>
|
|
233
|
+
}) => MaybePromise<boolean>
|
|
234
|
+
|
|
235
|
+
/** Hooks supported by every composable server method constructor. */
|
|
236
|
+
/**
|
|
237
|
+
* Optional per-method hook invoked after a payment succeeds.
|
|
238
|
+
*
|
|
239
|
+
* Called after verification completes successfully. Use this for side-effects
|
|
240
|
+
* like recording the payment in an external system.
|
|
241
|
+
*/
|
|
242
|
+
export type OnPaymentSuccessFn<method extends Method> = (parameters: {
|
|
243
|
+
input?: globalThis.Request
|
|
244
|
+
receipt: DeepReadonly<Receipt.Receipt>
|
|
245
|
+
request: DeepReadonly<z.output<method['schema']['request']>>
|
|
246
|
+
}) => MaybePromise<void>
|
|
247
|
+
|
|
248
|
+
export type ComposableHooks<method extends Method> = {
|
|
249
|
+
/** Decides whether this method's configured offer is available for an HTTP request. */
|
|
250
|
+
canOffer?: CanOfferFn<method> | undefined
|
|
251
|
+
/** Invoked after a successful payment on this method. */
|
|
252
|
+
onPaymentSuccess?: OnPaymentSuccessFn<method> | undefined
|
|
253
|
+
}
|
|
254
|
+
|
|
220
255
|
/** Credential creation function for a single method. */
|
|
221
256
|
export type CreateCredentialFn<method extends Method, context = unknown> = (
|
|
222
257
|
parameters: {
|
|
@@ -363,11 +398,9 @@ export async function broadcastCredential<const methods extends readonly AnyServ
|
|
|
363
398
|
/**
|
|
364
399
|
* Parses a submitted credential into the inputs required for method execution.
|
|
365
400
|
*
|
|
366
|
-
* Dispatch is based on the challenge method and intent.
|
|
367
|
-
*
|
|
368
|
-
* the
|
|
369
|
-
* parses the method-specific credential payload before returning the selected
|
|
370
|
-
* method and the unmodified challenge request.
|
|
401
|
+
* Dispatch is based on the challenge method and intent. The helper then asserts
|
|
402
|
+
* challenge expiry and parses the method-specific credential payload before
|
|
403
|
+
* returning the selected method and the unmodified challenge request.
|
|
371
404
|
*
|
|
372
405
|
* This intentionally does not verify that the challenge was issued by a
|
|
373
406
|
* particular host, authorize the caller or requested resource, validate the
|
|
@@ -387,7 +420,7 @@ function prepareCredential(
|
|
|
387
420
|
(method) =>
|
|
388
421
|
method.name === credential.challenge.method && method.intent === credential.challenge.intent,
|
|
389
422
|
)
|
|
390
|
-
const method =
|
|
423
|
+
const method = candidates[0]
|
|
391
424
|
if (!method)
|
|
392
425
|
throw new Errors.InvalidChallengeError({
|
|
393
426
|
id: credential.challenge.id,
|
|
@@ -410,29 +443,6 @@ function prepareCredential(
|
|
|
410
443
|
}
|
|
411
444
|
}
|
|
412
445
|
|
|
413
|
-
/** @internal */
|
|
414
|
-
export function selectServerMethod(
|
|
415
|
-
methods: readonly AnyServer[],
|
|
416
|
-
challenge: Challenge.Challenge,
|
|
417
|
-
): AnyServer | undefined {
|
|
418
|
-
if (methods.length <= 1) return methods[0]
|
|
419
|
-
if (
|
|
420
|
-
challenge.method !== Constants.Methods.tempo ||
|
|
421
|
-
challenge.intent !== Constants.Intents.session
|
|
422
|
-
)
|
|
423
|
-
return methods[0]
|
|
424
|
-
|
|
425
|
-
const sessionProtocol = Constants.getMethodDetail(
|
|
426
|
-
challenge.request.methodDetails,
|
|
427
|
-
Constants.MethodDetailKeys.sessionProtocol,
|
|
428
|
-
)
|
|
429
|
-
if (sessionProtocol === undefined || sessionProtocol === Constants.SessionProtocols.v1)
|
|
430
|
-
return methods.find((method) => method.alias === 'sessionLegacy') ?? methods[0]
|
|
431
|
-
if (sessionProtocol === Constants.SessionProtocols.v2)
|
|
432
|
-
return methods.find((method) => method.alias === undefined) ?? methods[0]
|
|
433
|
-
return undefined
|
|
434
|
-
}
|
|
435
|
-
|
|
436
446
|
/**
|
|
437
447
|
* Optional respond function for a server-side method.
|
|
438
448
|
*
|
|
@@ -537,9 +547,11 @@ export function toServer<
|
|
|
537
547
|
const {
|
|
538
548
|
alias,
|
|
539
549
|
authorize,
|
|
550
|
+
canOffer,
|
|
540
551
|
defaults,
|
|
541
552
|
extensions,
|
|
542
553
|
html,
|
|
554
|
+
onPaymentSuccess,
|
|
543
555
|
preflight,
|
|
544
556
|
request,
|
|
545
557
|
respond,
|
|
@@ -563,9 +575,11 @@ export function toServer<
|
|
|
563
575
|
...method,
|
|
564
576
|
alias,
|
|
565
577
|
authorize,
|
|
578
|
+
canOffer,
|
|
566
579
|
defaults,
|
|
567
580
|
extensions,
|
|
568
581
|
html,
|
|
582
|
+
onPaymentSuccess,
|
|
569
583
|
preflight,
|
|
570
584
|
request,
|
|
571
585
|
respond,
|
|
@@ -586,7 +600,7 @@ export declare namespace toServer {
|
|
|
586
600
|
transportOverride extends Transport.AnyTransport | undefined = undefined,
|
|
587
601
|
extensions extends object = {},
|
|
588
602
|
alias extends string | undefined = undefined,
|
|
589
|
-
> = {
|
|
603
|
+
> = ComposableHooks<method> & {
|
|
590
604
|
alias?: alias | undefined
|
|
591
605
|
authorize?: AuthorizeFn<method> | undefined
|
|
592
606
|
defaults?: defaults | undefined
|
|
@@ -598,20 +612,20 @@ export declare namespace toServer {
|
|
|
598
612
|
stableBinding?: StableBindingFn<method> | undefined
|
|
599
613
|
transport?: transportOverride | Transport.AnyTransport | undefined
|
|
600
614
|
} & (
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
615
|
+
| {
|
|
616
|
+
broadcast: BroadcastFn<method>
|
|
617
|
+
validate?: ValidateFn<method> | undefined
|
|
618
|
+
verify?: undefined
|
|
619
|
+
}
|
|
620
|
+
| {
|
|
621
|
+
broadcast?: undefined
|
|
622
|
+
validate?: undefined
|
|
623
|
+
/**
|
|
624
|
+
* @deprecated Use `validate` and `broadcast` for new methods. This
|
|
625
|
+
* combined hook may mutate payment state and cannot power a safe
|
|
626
|
+
* validation-only endpoint.
|
|
627
|
+
*/
|
|
628
|
+
verify: VerifyFn<method>
|
|
629
|
+
}
|
|
630
|
+
)
|
|
617
631
|
}
|
package/src/Store.test-d.ts
CHANGED
|
@@ -20,6 +20,11 @@ test('default AtomicStore accepts any string key on update', () => {
|
|
|
20
20
|
expectTypeOf(store.update).parameter(0).toBeString()
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
+
test('default AtomicStore accepts any string key on tryClaim', () => {
|
|
24
|
+
const store = {} as Store.AtomicStore
|
|
25
|
+
expectTypeOf<NonNullable<typeof store.tryClaim>>().parameter(0).toBeString()
|
|
26
|
+
})
|
|
27
|
+
|
|
23
28
|
test('memory returns AtomicStore', () => {
|
|
24
29
|
const store = Store.memory()
|
|
25
30
|
expectTypeOf(store).toEqualTypeOf<Store.AtomicStore>()
|
|
@@ -110,6 +115,17 @@ test('typed AtomicStore constrains keys on update', () => {
|
|
|
110
115
|
expectTypeOf(store.update).parameter(0).toEqualTypeOf<`mppx:charge:${string}`>()
|
|
111
116
|
})
|
|
112
117
|
|
|
118
|
+
test('typed tryClaim constrains keys', () => {
|
|
119
|
+
type ItemMap = { [key: `mppx:charge:${string}`]: number }
|
|
120
|
+
const store = {} as Store.AtomicStore<ItemMap>
|
|
121
|
+
|
|
122
|
+
expectTypeOf(Store.tryClaim(store, 'mppx:charge:0x123', 1)).toEqualTypeOf<
|
|
123
|
+
boolean | Promise<boolean>
|
|
124
|
+
>()
|
|
125
|
+
// @ts-expect-error — key must match the item map
|
|
126
|
+
Store.tryClaim(store, 'other:0x123', 1)
|
|
127
|
+
})
|
|
128
|
+
|
|
113
129
|
test('typed Store infers value from key', async () => {
|
|
114
130
|
type ItemMap = { [key: `mppx:charge:${string}`]: number }
|
|
115
131
|
const store = {} as Store.Store<ItemMap>
|
package/src/Store.test.ts
CHANGED
|
@@ -140,6 +140,17 @@ describe('keyPrefix', () => {
|
|
|
140
140
|
expect(await backing.get('k')).toBeNull()
|
|
141
141
|
})
|
|
142
142
|
|
|
143
|
+
test('scopes optimized tryClaim to prefixed backing keys', async () => {
|
|
144
|
+
const backing = Store.memory()
|
|
145
|
+
const store = Store.from(backing, { keyPrefix: 'tenant:' })
|
|
146
|
+
const expires = Date.now() + 60_000
|
|
147
|
+
|
|
148
|
+
expect(await Store.tryClaim(store, 'k', expires)).toBe(true)
|
|
149
|
+
expect(await Store.tryClaim(store, 'k', expires)).toBe(false)
|
|
150
|
+
expect(await backing.get('tenant:k')).toEqual({ expires, type: 'mppx:replay' })
|
|
151
|
+
expect(await backing.get('k')).toBeNull()
|
|
152
|
+
})
|
|
153
|
+
|
|
143
154
|
test('returns the original store when keyPrefix is empty', () => {
|
|
144
155
|
const backing = Store.memory()
|
|
145
156
|
expect(Store.from(backing, { keyPrefix: '' })).toBe(backing)
|
|
@@ -189,6 +200,45 @@ describe('keyPrefix', () => {
|
|
|
189
200
|
})
|
|
190
201
|
})
|
|
191
202
|
|
|
203
|
+
describe('tryClaim', () => {
|
|
204
|
+
test('uses an adapter fast path when available', async () => {
|
|
205
|
+
const backing = Store.memory()
|
|
206
|
+
const optimized = backing.tryClaim!
|
|
207
|
+
let calls = 0
|
|
208
|
+
const store = {
|
|
209
|
+
...backing,
|
|
210
|
+
tryClaim(key: string, expires: number) {
|
|
211
|
+
calls++
|
|
212
|
+
return optimized(key, expires)
|
|
213
|
+
},
|
|
214
|
+
}
|
|
215
|
+
const expires = Date.now() + 60_000
|
|
216
|
+
|
|
217
|
+
expect(await Store.tryClaim(store, 'k', expires)).toBe(true)
|
|
218
|
+
expect(await Store.tryClaim(store, 'k', expires)).toBe(false)
|
|
219
|
+
expect(await store.get('k')).toEqual({ expires, type: 'mppx:replay' })
|
|
220
|
+
expect(calls).toBe(2)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
test('falls back to atomic update and replaces expired claims', async () => {
|
|
224
|
+
const store = Store.cloudflare(fakeStringKv())
|
|
225
|
+
expect(store.tryClaim).toBeUndefined()
|
|
226
|
+
const expires = Date.now() + 60_000
|
|
227
|
+
|
|
228
|
+
expect(await Store.tryClaim(store, 'k', Date.now() - 1)).toBe(true)
|
|
229
|
+
expect(await Store.tryClaim(store, 'k', expires)).toBe(true)
|
|
230
|
+
expect(await Store.tryClaim(store, 'k', expires)).toBe(false)
|
|
231
|
+
expect(await store.get('k')).toEqual({ expires, type: 'mppx:replay' })
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
test('preserves legacy replay markers as consumed', async () => {
|
|
235
|
+
const store = Store.memory()
|
|
236
|
+
await store.put('k', Date.now() - 60_000)
|
|
237
|
+
|
|
238
|
+
expect(await Store.tryClaim(store, 'k', Date.now() + 60_000)).toBe(false)
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
192
242
|
describe('json roundtrip behavior', () => {
|
|
193
243
|
test('cloudflare json-roundtrips nested objects', async () => {
|
|
194
244
|
const store = Store.cloudflare(fakeStringKv())
|
package/src/Store.ts
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { Json } from 'ox'
|
|
18
18
|
|
|
19
|
+
import type { MaybePromise } from './internal/types.js'
|
|
20
|
+
|
|
19
21
|
export type StoreItemMap = Record<string, unknown>
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -47,6 +49,20 @@ export type Update<itemMap extends StoreItemMap = StoreItemMap> = <
|
|
|
47
49
|
fn: (current: itemMap[key] | null) => Change<itemMap[key], result>,
|
|
48
50
|
) => Promise<result>
|
|
49
51
|
|
|
52
|
+
/** Attempts to atomically record first use of a replay key through its expiry. */
|
|
53
|
+
export type TryClaim<itemMap extends StoreItemMap = StoreItemMap> = <
|
|
54
|
+
key extends keyof itemMap & string,
|
|
55
|
+
>(
|
|
56
|
+
key: key,
|
|
57
|
+
expires: number,
|
|
58
|
+
) => MaybePromise<boolean>
|
|
59
|
+
|
|
60
|
+
/** Persisted marker used by the {@link tryClaim} fallback. */
|
|
61
|
+
export type ReplayMarker = {
|
|
62
|
+
expires: number
|
|
63
|
+
type: 'mppx:replay'
|
|
64
|
+
}
|
|
65
|
+
|
|
50
66
|
/** Base key-value actions available on every {@link Store}. */
|
|
51
67
|
export type StoreActions<itemMap extends StoreItemMap = StoreItemMap> = {
|
|
52
68
|
get: <key extends keyof itemMap & string>(key: key) => Promise<itemMap[key] | null>
|
|
@@ -57,6 +73,8 @@ export type StoreActions<itemMap extends StoreItemMap = StoreItemMap> = {
|
|
|
57
73
|
/** Atomic actions that can be provided via the `extended` slot. */
|
|
58
74
|
export type AtomicActions<itemMap extends StoreItemMap = StoreItemMap> = {
|
|
59
75
|
update: Update<itemMap>
|
|
76
|
+
/** Optional single-operation fast path for recording expiring replay keys. */
|
|
77
|
+
tryClaim?: TryClaim<itemMap> | undefined
|
|
60
78
|
}
|
|
61
79
|
|
|
62
80
|
/**
|
|
@@ -84,6 +102,42 @@ export type AtomicStore<itemMap extends StoreItemMap = StoreItemMap> = Store<
|
|
|
84
102
|
AtomicActions<itemMap>
|
|
85
103
|
>
|
|
86
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Attempts to atomically record first use of a replay key through its expiry.
|
|
107
|
+
*
|
|
108
|
+
* Returns `true` when this call recorded the key and `false` when it was already
|
|
109
|
+
* recorded and unexpired. `expires` is a Unix timestamp in milliseconds.
|
|
110
|
+
* Uses the adapter's optimized primitive when available and otherwise falls
|
|
111
|
+
* back to {@link AtomicStore.update}.
|
|
112
|
+
*/
|
|
113
|
+
export function tryClaim<itemMap extends StoreItemMap, key extends keyof itemMap & string>(
|
|
114
|
+
store: AtomicStore<itemMap>,
|
|
115
|
+
key: key,
|
|
116
|
+
expires: number,
|
|
117
|
+
): MaybePromise<boolean> {
|
|
118
|
+
if (store.tryClaim) return store.tryClaim(key, expires)
|
|
119
|
+
return store.update(key, (current) => {
|
|
120
|
+
if (current !== null && (!isReplayMarker(current) || current.expires > Date.now()))
|
|
121
|
+
return { op: 'noop', result: false }
|
|
122
|
+
return {
|
|
123
|
+
op: 'set',
|
|
124
|
+
value: { expires, type: 'mppx:replay' } as itemMap[key],
|
|
125
|
+
result: true,
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function isReplayMarker(value: unknown): value is ReplayMarker {
|
|
131
|
+
return (
|
|
132
|
+
typeof value === 'object' &&
|
|
133
|
+
value !== null &&
|
|
134
|
+
'expires' in value &&
|
|
135
|
+
typeof value.expires === 'number' &&
|
|
136
|
+
'type' in value &&
|
|
137
|
+
value.type === 'mppx:replay'
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
87
141
|
/** Options shared by store constructors. */
|
|
88
142
|
export type Options = {
|
|
89
143
|
/** Prefix prepended to every backing store key. */
|
|
@@ -127,6 +181,13 @@ function withKeyPrefix(store: Store | AtomicStore, keyPrefix = ''): Store | Atom
|
|
|
127
181
|
},
|
|
128
182
|
}
|
|
129
183
|
: {}),
|
|
184
|
+
...('tryClaim' in store && typeof store.tryClaim === 'function'
|
|
185
|
+
? {
|
|
186
|
+
tryClaim(key: string, expires: number) {
|
|
187
|
+
return store.tryClaim!(prefixedKey(key), expires)
|
|
188
|
+
},
|
|
189
|
+
}
|
|
190
|
+
: {}),
|
|
130
191
|
} satisfies Store | AtomicStore)
|
|
131
192
|
|
|
132
193
|
const cachedByPrefix = keyPrefixCache.get(store) ?? new Map<string, Store | AtomicStore>()
|
|
@@ -226,6 +287,13 @@ export function memory(options?: memory.Options): AtomicStore {
|
|
|
226
287
|
if (change.op === 'delete') store.delete(key)
|
|
227
288
|
return change.result
|
|
228
289
|
},
|
|
290
|
+
tryClaim(key: string, expires: number) {
|
|
291
|
+
const current = store.has(key) ? Json.parse(store.get(key)!) : null
|
|
292
|
+
if (current !== null && (!isReplayMarker(current) || current.expires > Date.now()))
|
|
293
|
+
return false
|
|
294
|
+
store.set(key, Json.stringify({ expires, type: 'mppx:replay' }))
|
|
295
|
+
return true
|
|
296
|
+
},
|
|
229
297
|
},
|
|
230
298
|
options,
|
|
231
299
|
)
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { RequestListener } from 'node:http'
|
|
2
|
+
|
|
3
|
+
import { Constants as MppConstants, Credential, Method, Receipt, z } from 'mppx'
|
|
4
|
+
import * as Attestation from 'mppx/attestation'
|
|
5
|
+
import * as Tap from 'mppx/attestation/tap'
|
|
6
|
+
import * as WebBotAuth from 'mppx/attestation/web-bot-auth'
|
|
7
|
+
import { Mppx as ClientMppx } from 'mppx/client'
|
|
8
|
+
import { Mppx, Request as ServerRequest } from 'mppx/server'
|
|
9
|
+
import { expect, test } from 'vp/test'
|
|
10
|
+
import { jwkThumbprint, keyPair } from '~test/Attestation.js'
|
|
11
|
+
import * as Http from '~test/Http.js'
|
|
12
|
+
|
|
13
|
+
const method = Method.from({
|
|
14
|
+
name: 'test',
|
|
15
|
+
intent: 'charge',
|
|
16
|
+
schema: {
|
|
17
|
+
credential: { payload: z.object({ token: z.string() }) },
|
|
18
|
+
request: z.object({ amount: z.string(), currency: z.string(), recipient: z.string() }),
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const clientMethod = Method.toClient(method, {
|
|
23
|
+
async createCredential({ challenge }) {
|
|
24
|
+
return Credential.serialize({ challenge, payload: { token: 'paid' } })
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const serverMethod = Method.toServer(method, {
|
|
29
|
+
async verify({ credential }) {
|
|
30
|
+
if (credential.payload.token !== 'paid') throw new Error('Payment credential is invalid.')
|
|
31
|
+
return Receipt.from({
|
|
32
|
+
method: 'test',
|
|
33
|
+
reference: 'test-payment',
|
|
34
|
+
status: 'success',
|
|
35
|
+
timestamp: new Date().toISOString(),
|
|
36
|
+
})
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const charge = {
|
|
41
|
+
amount: '1',
|
|
42
|
+
currency: 'USD',
|
|
43
|
+
expires: new Date(Date.now() + 60_000).toISOString(),
|
|
44
|
+
recipient: 'merchant',
|
|
45
|
+
} as const
|
|
46
|
+
|
|
47
|
+
test('composes TAP and the Web Bot Auth directory profile across an MPP payment retry', async () => {
|
|
48
|
+
const tapKeys = await keyPair()
|
|
49
|
+
const webBotAuthKeys = await keyPair()
|
|
50
|
+
const webBotAuthKeyId = await jwkThumbprint(webBotAuthKeys.publicKey)
|
|
51
|
+
const signatureAgent = 'https://agent.example'
|
|
52
|
+
const nonceStore = Attestation.Store.memory()
|
|
53
|
+
const verifiers = {
|
|
54
|
+
tap: Tap.Server.verifier({
|
|
55
|
+
keyResolver: ({ keyId }) => (keyId === 'tap-agent' ? tapKeys.publicKey : undefined),
|
|
56
|
+
nonceStore,
|
|
57
|
+
}),
|
|
58
|
+
webBotAuth: WebBotAuth.Server.verifier({
|
|
59
|
+
keyResolver: ({ keyId, signatureAgent: advertisedAgent }) =>
|
|
60
|
+
keyId === webBotAuthKeyId && advertisedAgent === signatureAgent
|
|
61
|
+
? webBotAuthKeys.publicKey
|
|
62
|
+
: undefined,
|
|
63
|
+
maxAge: 60,
|
|
64
|
+
nonceStore,
|
|
65
|
+
}),
|
|
66
|
+
}
|
|
67
|
+
const seen = await createServer(verifiers, async (request, payments) => {
|
|
68
|
+
const result = await payments.charge(charge)(request)
|
|
69
|
+
return result.status === 402 ? result.challenge : result.withReceipt(new Response('paid'))
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const client = ClientMppx.create({
|
|
74
|
+
attestation: {
|
|
75
|
+
tap: Tap.Client.signer({
|
|
76
|
+
intent: Tap.Constants.intents.payment,
|
|
77
|
+
key: tapKeys.privateKey,
|
|
78
|
+
keyId: 'tap-agent',
|
|
79
|
+
}),
|
|
80
|
+
webBotAuth: WebBotAuth.Client.signer({
|
|
81
|
+
key: webBotAuthKeys.privateKey,
|
|
82
|
+
keyId: webBotAuthKeyId,
|
|
83
|
+
signatureAgent,
|
|
84
|
+
}),
|
|
85
|
+
},
|
|
86
|
+
methods: [clientMethod],
|
|
87
|
+
polyfill: false,
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const response = await client.fetch(seen.server.url)
|
|
91
|
+
expect(response.status).toBe(200)
|
|
92
|
+
expect(await response.text()).toBe('paid')
|
|
93
|
+
expect(seen.requests).toEqual([false, true])
|
|
94
|
+
|
|
95
|
+
const attemptNonces = seen.signatureInputs.map((input) =>
|
|
96
|
+
[...input.matchAll(/nonce="([^"]+)"/g)].map((match) => match[1]),
|
|
97
|
+
)
|
|
98
|
+
expect(attemptNonces).toHaveLength(2)
|
|
99
|
+
for (const nonces of attemptNonces) {
|
|
100
|
+
expect(nonces).toHaveLength(2)
|
|
101
|
+
expect(new Set(nonces).size).toBe(1)
|
|
102
|
+
expect(nonces[0]).toMatch(/^[A-Za-z0-9_-]{86}$/)
|
|
103
|
+
}
|
|
104
|
+
expect(attemptNonces[0]![0]).not.toBe(attemptNonces[1]![0])
|
|
105
|
+
} finally {
|
|
106
|
+
seen.server.close()
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('requires every configured server attestation', async () => {
|
|
111
|
+
const keys = await keyPair()
|
|
112
|
+
const seen = await createServer(
|
|
113
|
+
{
|
|
114
|
+
tap: Tap.Server.verifier({
|
|
115
|
+
keyResolver: () => keys.publicKey,
|
|
116
|
+
nonceStore: Attestation.Store.memory(),
|
|
117
|
+
}),
|
|
118
|
+
},
|
|
119
|
+
async (request, payments) => {
|
|
120
|
+
const result = await payments.charge(charge)(request)
|
|
121
|
+
return result.status === 402 ? result.challenge : result.withReceipt(new Response('paid'))
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const response = await fetch(seen.server.url)
|
|
127
|
+
expect(response.status).toBe(403)
|
|
128
|
+
expect(await response.text()).toMatch(/tap.*required/)
|
|
129
|
+
} finally {
|
|
130
|
+
seen.server.close()
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('rejects an invalid server attestation before creating a payment challenge', async () => {
|
|
135
|
+
const trustedKeys = await keyPair()
|
|
136
|
+
const untrustedKeys = await keyPair()
|
|
137
|
+
const seen = await createServer(
|
|
138
|
+
{
|
|
139
|
+
tap: Tap.Server.verifier({
|
|
140
|
+
keyResolver: () => trustedKeys.publicKey,
|
|
141
|
+
nonceStore: Attestation.Store.memory(),
|
|
142
|
+
}),
|
|
143
|
+
},
|
|
144
|
+
async (request, payments) => {
|
|
145
|
+
const result = await payments.charge(charge)(request)
|
|
146
|
+
return result.status === 402 ? result.challenge : result.withReceipt(new Response('paid'))
|
|
147
|
+
},
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const signer = Tap.Client.signer({
|
|
152
|
+
intent: Tap.Constants.intents.payment,
|
|
153
|
+
key: untrustedKeys.privateKey,
|
|
154
|
+
keyId: 'tap-agent',
|
|
155
|
+
})
|
|
156
|
+
const request = await signer.sign(new Request(seen.server.url))
|
|
157
|
+
const response = await fetch(request)
|
|
158
|
+
expect(response.status).toBe(401)
|
|
159
|
+
expect(await response.text()).toMatch(/tap.*invalid/)
|
|
160
|
+
expect(seen.requests).toEqual([false])
|
|
161
|
+
} finally {
|
|
162
|
+
seen.server.close()
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
test('rejects empty client and server attestation maps', () => {
|
|
167
|
+
expect(() =>
|
|
168
|
+
ClientMppx.create({
|
|
169
|
+
attestation: {},
|
|
170
|
+
methods: [clientMethod],
|
|
171
|
+
polyfill: false,
|
|
172
|
+
}),
|
|
173
|
+
).toThrow('Mppx client attestation must configure at least one signer.')
|
|
174
|
+
expect(() =>
|
|
175
|
+
Mppx.create({
|
|
176
|
+
attestation: {},
|
|
177
|
+
methods: [serverMethod],
|
|
178
|
+
realm: 'localhost',
|
|
179
|
+
secretKey: 'test-secret-key-test-secret-key-32',
|
|
180
|
+
}),
|
|
181
|
+
).toThrow('Mppx server attestation must configure at least one verifier.')
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
async function createServer(
|
|
185
|
+
verifiers: Attestation.VerifierMap,
|
|
186
|
+
handler: (request: Request, payments: ReturnType<typeof createPayments>) => Promise<Response>,
|
|
187
|
+
) {
|
|
188
|
+
const payments = createPayments(verifiers)
|
|
189
|
+
const requests: boolean[] = []
|
|
190
|
+
const signatureInputs: string[] = []
|
|
191
|
+
const listener = ServerRequest.toNodeListener(async (request) => {
|
|
192
|
+
requests.push(request.headers.has(MppConstants.Headers.authorization))
|
|
193
|
+
signatureInputs.push(request.headers.get(Attestation.Headers.signatureInput) ?? '')
|
|
194
|
+
return handler(request, payments)
|
|
195
|
+
})
|
|
196
|
+
const server = await Http.createServer(((request, response) =>
|
|
197
|
+
listener(request, response)) as RequestListener)
|
|
198
|
+
return { requests, server, signatureInputs }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function createPayments(attestation: Attestation.VerifierMap) {
|
|
202
|
+
return Mppx.create({
|
|
203
|
+
attestation,
|
|
204
|
+
methods: [serverMethod],
|
|
205
|
+
realm: 'localhost',
|
|
206
|
+
secretKey: 'test-secret-key-test-secret-key-32',
|
|
207
|
+
})
|
|
208
|
+
}
|