mppx 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/BodyDigest.d.ts +42 -0
- package/dist/BodyDigest.d.ts.map +1 -0
- package/dist/BodyDigest.js +40 -0
- package/dist/BodyDigest.js.map +1 -0
- package/dist/Challenge.d.ts +271 -0
- package/dist/Challenge.d.ts.map +1 -0
- package/dist/Challenge.js +291 -0
- package/dist/Challenge.js.map +1 -0
- package/dist/Credential.d.ts +91 -0
- package/dist/Credential.d.ts.map +1 -0
- package/dist/Credential.js +122 -0
- package/dist/Credential.js.map +1 -0
- package/dist/Errors.d.ts +243 -0
- package/dist/Errors.d.ts.map +1 -0
- package/dist/Errors.js +201 -0
- package/dist/Errors.js.map +1 -0
- package/dist/Expires.d.ts +15 -0
- package/dist/Expires.d.ts.map +1 -0
- package/dist/Expires.js +29 -0
- package/dist/Expires.js.map +1 -0
- package/dist/Intent.d.ts +101 -0
- package/dist/Intent.d.ts.map +1 -0
- package/dist/Intent.js +83 -0
- package/dist/Intent.js.map +1 -0
- package/dist/Mcp.d.ts +74 -0
- package/dist/Mcp.d.ts.map +1 -0
- package/dist/Mcp.js +9 -0
- package/dist/Mcp.js.map +1 -0
- package/dist/MethodIntent.d.ts +225 -0
- package/dist/MethodIntent.d.ts.map +1 -0
- package/dist/MethodIntent.js +156 -0
- package/dist/MethodIntent.js.map +1 -0
- package/dist/PaymentRequest.d.ts +88 -0
- package/dist/PaymentRequest.d.ts.map +1 -0
- package/dist/PaymentRequest.js +81 -0
- package/dist/PaymentRequest.js.map +1 -0
- package/dist/Receipt.d.ts +110 -0
- package/dist/Receipt.d.ts.map +1 -0
- package/dist/Receipt.js +105 -0
- package/dist/Receipt.js.map +1 -0
- package/dist/Store.d.ts +28 -0
- package/dist/Store.d.ts.map +1 -0
- package/dist/Store.js +61 -0
- package/dist/Store.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1219 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/Methods.d.ts +4 -0
- package/dist/client/Methods.d.ts.map +1 -0
- package/dist/client/Methods.js +4 -0
- package/dist/client/Methods.js.map +1 -0
- package/dist/client/Mppx.d.ts +84 -0
- package/dist/client/Mppx.d.ts.map +1 -0
- package/dist/client/Mppx.js +64 -0
- package/dist/client/Mppx.js.map +1 -0
- package/dist/client/Transport.d.ts +56 -0
- package/dist/client/Transport.d.ts.map +1 -0
- package/dist/client/Transport.js +81 -0
- package/dist/client/Transport.js.map +1 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +5 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/internal/Fetch.d.ts +85 -0
- package/dist/client/internal/Fetch.d.ts.map +1 -0
- package/dist/client/internal/Fetch.js +95 -0
- package/dist/client/internal/Fetch.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/types.d.ts +302 -0
- package/dist/internal/types.d.ts.map +1 -0
- package/dist/internal/types.js +2 -0
- package/dist/internal/types.js.map +1 -0
- package/dist/mcp-sdk/client/McpClient.d.ts +78 -0
- package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -0
- package/dist/mcp-sdk/client/McpClient.js +98 -0
- package/dist/mcp-sdk/client/McpClient.js.map +1 -0
- package/dist/mcp-sdk/client/index.d.ts +3 -0
- package/dist/mcp-sdk/client/index.d.ts.map +1 -0
- package/dist/mcp-sdk/client/index.js +3 -0
- package/dist/mcp-sdk/client/index.js.map +1 -0
- package/dist/mcp-sdk/server/Transport.d.ts +43 -0
- package/dist/mcp-sdk/server/Transport.d.ts.map +1 -0
- package/dist/mcp-sdk/server/Transport.js +71 -0
- package/dist/mcp-sdk/server/Transport.js.map +1 -0
- package/dist/mcp-sdk/server/index.d.ts +2 -0
- package/dist/mcp-sdk/server/index.d.ts.map +1 -0
- package/dist/mcp-sdk/server/index.js +2 -0
- package/dist/mcp-sdk/server/index.js.map +1 -0
- package/dist/middlewares/elysia.d.ts +51 -0
- package/dist/middlewares/elysia.d.ts.map +1 -0
- package/dist/middlewares/elysia.js +59 -0
- package/dist/middlewares/elysia.js.map +1 -0
- package/dist/middlewares/express.d.ts +46 -0
- package/dist/middlewares/express.d.ts.map +1 -0
- package/dist/middlewares/express.js +69 -0
- package/dist/middlewares/express.js.map +1 -0
- package/dist/middlewares/hono.d.ts +46 -0
- package/dist/middlewares/hono.d.ts.map +1 -0
- package/dist/middlewares/hono.js +57 -0
- package/dist/middlewares/hono.js.map +1 -0
- package/dist/middlewares/internal/mppx.d.ts +16 -0
- package/dist/middlewares/internal/mppx.d.ts.map +1 -0
- package/dist/middlewares/internal/mppx.js +16 -0
- package/dist/middlewares/internal/mppx.js.map +1 -0
- package/dist/middlewares/nextjs.d.ts +45 -0
- package/dist/middlewares/nextjs.d.ts.map +1 -0
- package/dist/middlewares/nextjs.js +57 -0
- package/dist/middlewares/nextjs.js.map +1 -0
- package/dist/proxy/Proxy.d.ts +47 -0
- package/dist/proxy/Proxy.d.ts.map +1 -0
- package/dist/proxy/Proxy.js +126 -0
- package/dist/proxy/Proxy.js.map +1 -0
- package/dist/proxy/Service.d.ts +100 -0
- package/dist/proxy/Service.d.ts.map +1 -0
- package/dist/proxy/Service.js +147 -0
- package/dist/proxy/Service.js.map +1 -0
- package/dist/proxy/index.d.ts +7 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/index.js +7 -0
- package/dist/proxy/index.js.map +1 -0
- package/dist/proxy/internal/Headers.d.ts +3 -0
- package/dist/proxy/internal/Headers.d.ts.map +1 -0
- package/dist/proxy/internal/Headers.js +41 -0
- package/dist/proxy/internal/Headers.js.map +1 -0
- package/dist/proxy/internal/Route.d.ts +14 -0
- package/dist/proxy/internal/Route.d.ts.map +1 -0
- package/dist/proxy/internal/Route.js +47 -0
- package/dist/proxy/internal/Route.js.map +1 -0
- package/dist/proxy/services/anthropic.d.ts +29 -0
- package/dist/proxy/services/anthropic.d.ts.map +1 -0
- package/dist/proxy/services/anthropic.js +30 -0
- package/dist/proxy/services/anthropic.js.map +1 -0
- package/dist/proxy/services/openai.d.ts +29 -0
- package/dist/proxy/services/openai.d.ts.map +1 -0
- package/dist/proxy/services/openai.js +30 -0
- package/dist/proxy/services/openai.js.map +1 -0
- package/dist/proxy/services/stripe.d.ts +29 -0
- package/dist/proxy/services/stripe.d.ts.map +1 -0
- package/dist/proxy/services/stripe.js +30 -0
- package/dist/proxy/services/stripe.js.map +1 -0
- package/dist/server/Methods.d.ts +3 -0
- package/dist/server/Methods.d.ts.map +1 -0
- package/dist/server/Methods.js +3 -0
- package/dist/server/Methods.js.map +1 -0
- package/dist/server/Mppx.d.ts +116 -0
- package/dist/server/Mppx.d.ts.map +1 -0
- package/dist/server/Mppx.js +207 -0
- package/dist/server/Mppx.js.map +1 -0
- package/dist/server/NodeListener.d.ts +3 -0
- package/dist/server/NodeListener.d.ts.map +1 -0
- package/dist/server/NodeListener.js +3 -0
- package/dist/server/NodeListener.js.map +1 -0
- package/dist/server/Request.d.ts +24 -0
- package/dist/server/Request.d.ts.map +1 -0
- package/dist/server/Request.js +26 -0
- package/dist/server/Request.js.map +1 -0
- package/dist/server/Response.d.ts +10 -0
- package/dist/server/Response.d.ts.map +1 -0
- package/dist/server/Response.js +15 -0
- package/dist/server/Response.js.map +1 -0
- package/dist/server/Transport.d.ts +93 -0
- package/dist/server/Transport.d.ts.map +1 -0
- package/dist/server/Transport.js +132 -0
- package/dist/server/Transport.js.map +1 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -0
- package/dist/stripe/Intents.d.ts +54 -0
- package/dist/stripe/Intents.d.ts.map +1 -0
- package/dist/stripe/Intents.js +27 -0
- package/dist/stripe/Intents.js.map +1 -0
- package/dist/stripe/client/Charge.d.ts +114 -0
- package/dist/stripe/client/Charge.d.ts.map +1 -0
- package/dist/stripe/client/Charge.js +77 -0
- package/dist/stripe/client/Charge.js.map +1 -0
- package/dist/stripe/client/MethodIntents.d.ts +80 -0
- package/dist/stripe/client/MethodIntents.d.ts.map +1 -0
- package/dist/stripe/client/MethodIntents.js +34 -0
- package/dist/stripe/client/MethodIntents.js.map +1 -0
- package/dist/stripe/client/index.d.ts +3 -0
- package/dist/stripe/client/index.d.ts.map +1 -0
- package/dist/stripe/client/index.js +3 -0
- package/dist/stripe/client/index.js.map +1 -0
- package/dist/stripe/index.d.ts +2 -0
- package/dist/stripe/index.d.ts.map +1 -0
- package/dist/stripe/index.js +2 -0
- package/dist/stripe/index.js.map +1 -0
- package/dist/stripe/server/Charge.d.ts +74 -0
- package/dist/stripe/server/Charge.d.ts.map +1 -0
- package/dist/stripe/server/Charge.js +79 -0
- package/dist/stripe/server/Charge.js.map +1 -0
- package/dist/stripe/server/MethodIntents.d.ts +65 -0
- package/dist/stripe/server/MethodIntents.d.ts.map +1 -0
- package/dist/stripe/server/MethodIntents.js +21 -0
- package/dist/stripe/server/MethodIntents.js.map +1 -0
- package/dist/stripe/server/index.d.ts +3 -0
- package/dist/stripe/server/index.d.ts.map +1 -0
- package/dist/stripe/server/index.js +3 -0
- package/dist/stripe/server/index.js.map +1 -0
- package/dist/tempo/Attribution.d.ts +101 -0
- package/dist/tempo/Attribution.d.ts.map +1 -0
- package/dist/tempo/Attribution.js +124 -0
- package/dist/tempo/Attribution.js.map +1 -0
- package/dist/tempo/Intents.d.ts +132 -0
- package/dist/tempo/Intents.d.ts.map +1 -0
- package/dist/tempo/Intents.js +81 -0
- package/dist/tempo/Intents.js.map +1 -0
- package/dist/tempo/client/ChannelOps.d.ts +54 -0
- package/dist/tempo/client/ChannelOps.d.ts.map +1 -0
- package/dist/tempo/client/ChannelOps.js +138 -0
- package/dist/tempo/client/ChannelOps.js.map +1 -0
- package/dist/tempo/client/Charge.d.ts +76 -0
- package/dist/tempo/client/Charge.d.ts.map +1 -0
- package/dist/tempo/client/Charge.js +69 -0
- package/dist/tempo/client/Charge.js.map +1 -0
- package/dist/tempo/client/MethodIntents.d.ts +157 -0
- package/dist/tempo/client/MethodIntents.d.ts.map +1 -0
- package/dist/tempo/client/MethodIntents.js +25 -0
- package/dist/tempo/client/MethodIntents.js.map +1 -0
- package/dist/tempo/client/Session.d.ts +159 -0
- package/dist/tempo/client/Session.d.ts.map +1 -0
- package/dist/tempo/client/Session.js +263 -0
- package/dist/tempo/client/Session.js.map +1 -0
- package/dist/tempo/client/SessionManager.d.ts +62 -0
- package/dist/tempo/client/SessionManager.d.ts.map +1 -0
- package/dist/tempo/client/SessionManager.js +196 -0
- package/dist/tempo/client/SessionManager.js.map +1 -0
- package/dist/tempo/client/index.d.ts +6 -0
- package/dist/tempo/client/index.d.ts.map +1 -0
- package/dist/tempo/client/index.js +5 -0
- package/dist/tempo/client/index.js.map +1 -0
- package/dist/tempo/index.d.ts +3 -0
- package/dist/tempo/index.d.ts.map +1 -0
- package/dist/tempo/index.js +3 -0
- package/dist/tempo/index.js.map +1 -0
- package/dist/tempo/internal/account.d.ts +32 -0
- package/dist/tempo/internal/account.d.ts.map +1 -0
- package/dist/tempo/internal/account.js +33 -0
- package/dist/tempo/internal/account.js.map +1 -0
- package/dist/tempo/internal/defaults.d.ts +18 -0
- package/dist/tempo/internal/defaults.d.ts.map +1 -0
- package/dist/tempo/internal/defaults.js +18 -0
- package/dist/tempo/internal/defaults.js.map +1 -0
- package/dist/tempo/internal/types.d.ts +11 -0
- package/dist/tempo/internal/types.d.ts.map +1 -0
- package/dist/tempo/internal/types.js +2 -0
- package/dist/tempo/internal/types.js.map +1 -0
- package/dist/tempo/server/Charge.d.ts +77 -0
- package/dist/tempo/server/Charge.d.ts.map +1 -0
- package/dist/tempo/server/Charge.js +228 -0
- package/dist/tempo/server/Charge.js.map +1 -0
- package/dist/tempo/server/MethodIntents.d.ts +140 -0
- package/dist/tempo/server/MethodIntents.d.ts.map +1 -0
- package/dist/tempo/server/MethodIntents.js +26 -0
- package/dist/tempo/server/MethodIntents.js.map +1 -0
- package/dist/tempo/server/Session.d.ts +148 -0
- package/dist/tempo/server/Session.d.ts.map +1 -0
- package/dist/tempo/server/Session.js +529 -0
- package/dist/tempo/server/Session.js.map +1 -0
- package/dist/tempo/server/internal/transport.d.ts +47 -0
- package/dist/tempo/server/internal/transport.d.ts.map +1 -0
- package/dist/tempo/server/internal/transport.js +118 -0
- package/dist/tempo/server/internal/transport.js.map +1 -0
- package/dist/tempo/stream/Chain.d.ts +52 -0
- package/dist/tempo/stream/Chain.d.ts.map +1 -0
- package/dist/tempo/stream/Chain.js +215 -0
- package/dist/tempo/stream/Chain.js.map +1 -0
- package/dist/tempo/stream/Channel.d.ts +26 -0
- package/dist/tempo/stream/Channel.d.ts.map +1 -0
- package/dist/tempo/stream/Channel.js +27 -0
- package/dist/tempo/stream/Channel.js.map +1 -0
- package/dist/tempo/stream/ChannelStore.d.ts +103 -0
- package/dist/tempo/stream/ChannelStore.d.ts.map +1 -0
- package/dist/tempo/stream/ChannelStore.js +100 -0
- package/dist/tempo/stream/ChannelStore.js.map +1 -0
- package/dist/tempo/stream/Receipt.d.ts +22 -0
- package/dist/tempo/stream/Receipt.d.ts.map +1 -0
- package/dist/tempo/stream/Receipt.js +34 -0
- package/dist/tempo/stream/Receipt.js.map +1 -0
- package/dist/tempo/stream/Sse.d.ts +134 -0
- package/dist/tempo/stream/Sse.d.ts.map +1 -0
- package/dist/tempo/stream/Sse.js +288 -0
- package/dist/tempo/stream/Sse.js.map +1 -0
- package/dist/tempo/stream/Types.d.ts +78 -0
- package/dist/tempo/stream/Types.d.ts.map +1 -0
- package/dist/tempo/stream/Types.js +2 -0
- package/dist/tempo/stream/Types.js.map +1 -0
- package/dist/tempo/stream/Voucher.d.ts +20 -0
- package/dist/tempo/stream/Voucher.d.ts.map +1 -0
- package/dist/tempo/stream/Voucher.js +98 -0
- package/dist/tempo/stream/Voucher.js.map +1 -0
- package/dist/tempo/stream/escrow.abi.d.ts +598 -0
- package/dist/tempo/stream/escrow.abi.d.ts.map +1 -0
- package/dist/tempo/stream/escrow.abi.js +760 -0
- package/dist/tempo/stream/escrow.abi.js.map +1 -0
- package/dist/tempo/stream/index.d.ts +8 -0
- package/dist/tempo/stream/index.d.ts.map +1 -0
- package/dist/tempo/stream/index.js +8 -0
- package/dist/tempo/stream/index.js.map +1 -0
- package/dist/viem/Account.d.ts +12 -0
- package/dist/viem/Account.d.ts.map +1 -0
- package/dist/viem/Account.js +14 -0
- package/dist/viem/Account.js.map +1 -0
- package/dist/viem/Client.d.ts +21 -0
- package/dist/viem/Client.d.ts.map +1 -0
- package/dist/viem/Client.js +19 -0
- package/dist/viem/Client.js.map +1 -0
- package/dist/zod.d.ts +17 -0
- package/dist/zod.d.ts.map +1 -0
- package/dist/zod.js +35 -0
- package/dist/zod.js.map +1 -0
- package/package.json +117 -4
- package/src/BodyDigest.test.ts +43 -0
- package/src/BodyDigest.ts +53 -0
- package/src/Challenge.test-d.ts +81 -0
- package/src/Challenge.test.ts +414 -0
- package/src/Challenge.ts +429 -0
- package/src/Credential.test.ts +227 -0
- package/src/Credential.ts +154 -0
- package/src/Errors.test.ts +402 -0
- package/src/Errors.ts +348 -0
- package/src/Expires.ts +34 -0
- package/src/Intent.test.ts +180 -0
- package/src/Intent.ts +109 -0
- package/src/Mcp.ts +81 -0
- package/src/MethodIntent.test.ts +303 -0
- package/src/MethodIntent.ts +388 -0
- package/src/PaymentRequest.test.ts +152 -0
- package/src/PaymentRequest.ts +107 -0
- package/src/Receipt.test.ts +98 -0
- package/src/Receipt.ts +129 -0
- package/src/Store.ts +84 -0
- package/src/cli.test.ts +542 -0
- package/src/cli.ts +1319 -0
- package/src/client/Methods.ts +3 -0
- package/src/client/Mppx.test-d.ts +90 -0
- package/src/client/Mppx.test.ts +468 -0
- package/src/client/Mppx.ts +149 -0
- package/src/client/Transport.test.ts +283 -0
- package/src/client/Transport.ts +115 -0
- package/src/client/index.ts +4 -0
- package/src/client/internal/Fetch.test-d.ts +57 -0
- package/src/client/internal/Fetch.test.ts +281 -0
- package/src/client/internal/Fetch.ts +157 -0
- package/src/env.d.ts +11 -0
- package/src/index.ts +12 -0
- package/src/internal/types.ts +403 -0
- package/src/mcp-sdk/client/McpClient.test-d.ts +109 -0
- package/src/mcp-sdk/client/McpClient.test.ts +219 -0
- package/src/mcp-sdk/client/McpClient.ts +187 -0
- package/src/mcp-sdk/client/index.ts +2 -0
- package/src/mcp-sdk/server/Transport.ts +94 -0
- package/src/mcp-sdk/server/index.ts +1 -0
- package/src/middlewares/elysia.ts +66 -0
- package/src/middlewares/express.test.ts +155 -0
- package/src/middlewares/express.ts +82 -0
- package/src/middlewares/hono.test.ts +148 -0
- package/src/middlewares/hono.ts +62 -0
- package/src/middlewares/internal/mppx.ts +30 -0
- package/src/middlewares/nextjs.test.ts +164 -0
- package/src/middlewares/nextjs.ts +66 -0
- package/src/proxy/Proxy.test.ts +472 -0
- package/src/proxy/Proxy.ts +175 -0
- package/src/proxy/Service.test.ts +125 -0
- package/src/proxy/Service.ts +227 -0
- package/src/proxy/index.ts +6 -0
- package/src/proxy/internal/Headers.test.ts +100 -0
- package/src/proxy/internal/Headers.ts +40 -0
- package/src/proxy/internal/Route.test.ts +143 -0
- package/src/proxy/internal/Route.ts +54 -0
- package/src/proxy/services/anthropic.ts +45 -0
- package/src/proxy/services/openai.test.ts +97 -0
- package/src/proxy/services/openai.ts +48 -0
- package/src/proxy/services/stripe.ts +49 -0
- package/src/server/Methods.ts +2 -0
- package/src/server/Mppx.test-d.ts +343 -0
- package/src/server/Mppx.test.ts +342 -0
- package/src/server/Mppx.ts +378 -0
- package/src/server/NodeListener.test.ts +188 -0
- package/src/server/NodeListener.ts +3 -0
- package/src/server/Request.test.ts +102 -0
- package/src/server/Request.ts +33 -0
- package/src/server/Response.test.ts +31 -0
- package/src/server/Response.ts +27 -0
- package/src/server/Transport.test.ts +294 -0
- package/src/server/Transport.ts +222 -0
- package/src/server/index.ts +8 -0
- package/src/stripe/Charge.integration.test.ts +326 -0
- package/src/stripe/Intents.test.ts +52 -0
- package/src/stripe/Intents.ts +27 -0
- package/src/stripe/client/Charge.ts +119 -0
- package/src/stripe/client/MethodIntents.ts +37 -0
- package/src/stripe/client/index.ts +2 -0
- package/src/stripe/index.ts +1 -0
- package/src/stripe/server/Charge.ts +121 -0
- package/src/stripe/server/MethodIntents.ts +24 -0
- package/src/stripe/server/index.ts +2 -0
- package/src/tempo/Attribution.test.ts +187 -0
- package/src/tempo/Attribution.ts +156 -0
- package/src/tempo/Intents.test.ts +84 -0
- package/src/tempo/Intents.ts +93 -0
- package/src/tempo/client/ChannelOps.ts +233 -0
- package/src/tempo/client/Charge.ts +84 -0
- package/src/tempo/client/MethodIntents.ts +28 -0
- package/src/tempo/client/Session.ts +369 -0
- package/src/tempo/client/SessionManager.test.ts +223 -0
- package/src/tempo/client/SessionManager.ts +270 -0
- package/src/tempo/client/index.ts +5 -0
- package/src/tempo/index.ts +2 -0
- package/src/tempo/internal/account.ts +47 -0
- package/src/tempo/internal/defaults.ts +20 -0
- package/src/tempo/internal/types.ts +8 -0
- package/src/tempo/server/Charge.test.ts +847 -0
- package/src/tempo/server/Charge.ts +309 -0
- package/src/tempo/server/MethodIntents.ts +29 -0
- package/src/tempo/server/Session.test.ts +1349 -0
- package/src/tempo/server/Session.ts +773 -0
- package/src/tempo/server/Sse.test.ts +289 -0
- package/src/tempo/server/index.ts +5 -0
- package/src/tempo/server/internal/transport.ts +153 -0
- package/src/tempo/stream/Chain.ts +333 -0
- package/src/tempo/stream/Channel.ts +50 -0
- package/src/tempo/stream/ChannelStore.test.ts +473 -0
- package/src/tempo/stream/ChannelStore.ts +202 -0
- package/src/tempo/stream/Receipt.test.ts +84 -0
- package/src/tempo/stream/Receipt.ts +45 -0
- package/src/tempo/stream/Sse.test.ts +401 -0
- package/src/tempo/stream/Sse.ts +375 -0
- package/src/tempo/stream/Types.ts +86 -0
- package/src/tempo/stream/Voucher.test.ts +134 -0
- package/src/tempo/stream/Voucher.ts +123 -0
- package/src/tempo/stream/escrow.abi.ts +759 -0
- package/src/tempo/stream/index.ts +7 -0
- package/src/tsconfig.json +10 -0
- package/src/viem/Account.test.ts +71 -0
- package/src/viem/Account.ts +30 -0
- package/src/viem/Client.test.ts +58 -0
- package/src/viem/Client.ts +33 -0
- package/src/zod.ts +47 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import type { Hex } from 'ox'
|
|
2
|
+
import { type Address, parseUnits, type Account as viem_Account } from 'viem'
|
|
3
|
+
import { tempo as tempo_chain } from 'viem/chains'
|
|
4
|
+
import type * as Challenge from '../../Challenge.js'
|
|
5
|
+
import * as MethodIntent from '../../MethodIntent.js'
|
|
6
|
+
import * as Account from '../../viem/Account.js'
|
|
7
|
+
import * as Client from '../../viem/Client.js'
|
|
8
|
+
import * as z from '../../zod.js'
|
|
9
|
+
import * as Intents from '../Intents.js'
|
|
10
|
+
import * as defaults from '../internal/defaults.js'
|
|
11
|
+
import type { StreamCredentialPayload } from '../stream/Types.js'
|
|
12
|
+
import { signVoucher } from '../stream/Voucher.js'
|
|
13
|
+
import {
|
|
14
|
+
type ChannelEntry,
|
|
15
|
+
createOpenPayload,
|
|
16
|
+
createVoucherPayload,
|
|
17
|
+
resolveEscrow,
|
|
18
|
+
serializeCredential,
|
|
19
|
+
tryRecoverChannel,
|
|
20
|
+
} from './ChannelOps.js'
|
|
21
|
+
|
|
22
|
+
export const streamContextSchema = z.object({
|
|
23
|
+
account: z.optional(z.custom<Account.getResolver.Parameters['account']>()),
|
|
24
|
+
action: z.optional(z.enum(['open', 'topUp', 'voucher', 'close'])),
|
|
25
|
+
channelId: z.optional(z.string()),
|
|
26
|
+
cumulativeAmount: z.optional(z.amount()),
|
|
27
|
+
cumulativeAmountRaw: z.optional(z.string()),
|
|
28
|
+
transaction: z.optional(z.string()),
|
|
29
|
+
authorizedSigner: z.optional(z.string()),
|
|
30
|
+
additionalDeposit: z.optional(z.amount()),
|
|
31
|
+
additionalDepositRaw: z.optional(z.string()),
|
|
32
|
+
depositRaw: z.optional(z.string()),
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export type StreamContext = z.infer<typeof streamContextSchema>
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates a session payment MethodIntent plugin for use with `Mppx.create()`.
|
|
39
|
+
*
|
|
40
|
+
* Supports both auto mode (set `deposit` to manage channels automatically)
|
|
41
|
+
* and manual mode (pass `context.action` to control each step).
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* // Auto mode
|
|
46
|
+
* import { Mppx, tempo } from 'mppx/client'
|
|
47
|
+
*
|
|
48
|
+
* const mppx = Mppx.create({
|
|
49
|
+
* methods: [tempo({
|
|
50
|
+
* account: privateKeyToAccount('0x...'),
|
|
51
|
+
* deposit: '10',
|
|
52
|
+
* })],
|
|
53
|
+
* })
|
|
54
|
+
*
|
|
55
|
+
* const res = await mppx.fetch('/api/chat?prompt=hello')
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* // Manual mode
|
|
61
|
+
* const mppx = Mppx.create({
|
|
62
|
+
* methods: [tempo({ account })],
|
|
63
|
+
* })
|
|
64
|
+
*
|
|
65
|
+
* const credential = await mppx.createCredential(response, {
|
|
66
|
+
* action: 'voucher',
|
|
67
|
+
* channelId: '0x...',
|
|
68
|
+
* cumulativeAmount: '1',
|
|
69
|
+
* })
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export function session(parameters: session.Parameters = {}) {
|
|
73
|
+
const { decimals = defaults.decimals } = parameters
|
|
74
|
+
|
|
75
|
+
const getClient = Client.getResolver({
|
|
76
|
+
chain: tempo_chain,
|
|
77
|
+
getClient: parameters.getClient,
|
|
78
|
+
rpcUrl: defaults.rpcUrl,
|
|
79
|
+
})
|
|
80
|
+
const getAccount = Account.getResolver({ account: parameters.account })
|
|
81
|
+
|
|
82
|
+
const maxDeposit =
|
|
83
|
+
parameters.maxDeposit !== undefined ? parseUnits(parameters.maxDeposit, decimals) : undefined
|
|
84
|
+
|
|
85
|
+
const escrowContractMap = new Map<string, Address>()
|
|
86
|
+
const channels = new Map<string, ChannelEntry>()
|
|
87
|
+
const channelIdToKey = new Map<string, string>()
|
|
88
|
+
|
|
89
|
+
function notifyUpdate(entry: ChannelEntry) {
|
|
90
|
+
parameters.onChannelUpdate?.(entry)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function channelKey(payee: Address, currency: Address, escrow: Address): string {
|
|
94
|
+
return `${payee.toLowerCase()}:${currency.toLowerCase()}:${escrow.toLowerCase()}`
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resolveEscrowCached(
|
|
98
|
+
challenge: { request: { methodDetails?: unknown } },
|
|
99
|
+
chainId: number,
|
|
100
|
+
channelId?: string,
|
|
101
|
+
): Address {
|
|
102
|
+
if (channelId) {
|
|
103
|
+
const cached = escrowContractMap.get(channelId)
|
|
104
|
+
if (cached) return cached
|
|
105
|
+
}
|
|
106
|
+
return resolveEscrow(challenge, chainId, parameters.escrowContract)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function autoManageCredential(
|
|
110
|
+
challenge: Challenge.Challenge,
|
|
111
|
+
account: viem_Account,
|
|
112
|
+
context?: StreamContext,
|
|
113
|
+
): Promise<string> {
|
|
114
|
+
const md = challenge.request.methodDetails as
|
|
115
|
+
| { chainId?: number; escrowContract?: string; channelId?: string; feePayer?: boolean }
|
|
116
|
+
| undefined
|
|
117
|
+
const chainId = md?.chainId ?? 0
|
|
118
|
+
const client = await getClient({ chainId })
|
|
119
|
+
const escrowContract = resolveEscrowCached(challenge, chainId)
|
|
120
|
+
const payee = challenge.request.recipient as Address
|
|
121
|
+
const currency = challenge.request.currency as Address
|
|
122
|
+
const amount = BigInt(challenge.request.amount as string)
|
|
123
|
+
|
|
124
|
+
const suggestedDepositRaw = (challenge.request as { suggestedDeposit?: string })
|
|
125
|
+
.suggestedDeposit
|
|
126
|
+
const suggestedDeposit = suggestedDepositRaw ? BigInt(suggestedDepositRaw) : undefined
|
|
127
|
+
|
|
128
|
+
const deposit = (() => {
|
|
129
|
+
if (context?.depositRaw) return BigInt(context.depositRaw)
|
|
130
|
+
if (suggestedDeposit !== undefined && maxDeposit !== undefined)
|
|
131
|
+
return suggestedDeposit < maxDeposit ? suggestedDeposit : maxDeposit
|
|
132
|
+
if (suggestedDeposit !== undefined) return suggestedDeposit
|
|
133
|
+
if (maxDeposit !== undefined) return maxDeposit
|
|
134
|
+
if (parameters.deposit !== undefined) return parseUnits(parameters.deposit, decimals)
|
|
135
|
+
throw new Error(
|
|
136
|
+
'No deposit amount available. Set `deposit`, `maxDeposit`, or ensure the server challenge includes `suggestedDeposit`.',
|
|
137
|
+
)
|
|
138
|
+
})()
|
|
139
|
+
|
|
140
|
+
const key = channelKey(payee, currency, escrowContract)
|
|
141
|
+
let entry = channels.get(key)
|
|
142
|
+
|
|
143
|
+
if (!entry) {
|
|
144
|
+
const suggestedChannelId = (context?.channelId ?? md?.channelId) as Hex.Hex | undefined
|
|
145
|
+
if (suggestedChannelId) {
|
|
146
|
+
const recovered = await tryRecoverChannel(
|
|
147
|
+
client,
|
|
148
|
+
escrowContract,
|
|
149
|
+
suggestedChannelId,
|
|
150
|
+
chainId,
|
|
151
|
+
)
|
|
152
|
+
if (recovered) {
|
|
153
|
+
const contextCumulative = context?.cumulativeAmountRaw
|
|
154
|
+
? BigInt(context.cumulativeAmountRaw)
|
|
155
|
+
: context?.cumulativeAmount
|
|
156
|
+
? parseUnits(context.cumulativeAmount, decimals)
|
|
157
|
+
: undefined
|
|
158
|
+
if (contextCumulative !== undefined) recovered.cumulativeAmount = contextCumulative
|
|
159
|
+
channels.set(key, recovered)
|
|
160
|
+
channelIdToKey.set(recovered.channelId, key)
|
|
161
|
+
escrowContractMap.set(recovered.channelId, escrowContract)
|
|
162
|
+
entry = recovered
|
|
163
|
+
notifyUpdate(entry)
|
|
164
|
+
} else if (context?.channelId) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Channel ${context.channelId} cannot be reused (closed or not found on-chain).`,
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let payload: StreamCredentialPayload
|
|
173
|
+
|
|
174
|
+
if (entry?.opened) {
|
|
175
|
+
entry.cumulativeAmount += amount
|
|
176
|
+
payload = await createVoucherPayload(
|
|
177
|
+
client,
|
|
178
|
+
account,
|
|
179
|
+
entry.channelId,
|
|
180
|
+
entry.cumulativeAmount,
|
|
181
|
+
escrowContract,
|
|
182
|
+
chainId,
|
|
183
|
+
parameters.authorizedSigner,
|
|
184
|
+
)
|
|
185
|
+
notifyUpdate(entry)
|
|
186
|
+
} else {
|
|
187
|
+
const result = await createOpenPayload(client, account, {
|
|
188
|
+
authorizedSigner: parameters.authorizedSigner,
|
|
189
|
+
escrowContract,
|
|
190
|
+
payee,
|
|
191
|
+
currency,
|
|
192
|
+
deposit,
|
|
193
|
+
initialAmount: amount,
|
|
194
|
+
chainId,
|
|
195
|
+
feePayer: md?.feePayer,
|
|
196
|
+
})
|
|
197
|
+
channels.set(key, result.entry)
|
|
198
|
+
channelIdToKey.set(result.entry.channelId, key)
|
|
199
|
+
escrowContractMap.set(result.entry.channelId, escrowContract)
|
|
200
|
+
payload = result.payload
|
|
201
|
+
notifyUpdate(result.entry)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return serializeCredential(challenge, payload, chainId, account)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function manualCredential(
|
|
208
|
+
challenge: Challenge.Challenge,
|
|
209
|
+
account: viem_Account,
|
|
210
|
+
context: StreamContext,
|
|
211
|
+
): Promise<string> {
|
|
212
|
+
const md = challenge.request.methodDetails as
|
|
213
|
+
| { chainId?: number; escrowContract?: string; channelId?: string }
|
|
214
|
+
| undefined
|
|
215
|
+
const chainId = md?.chainId ?? 0
|
|
216
|
+
const client = await getClient({ chainId })
|
|
217
|
+
|
|
218
|
+
const action = context.action!
|
|
219
|
+
const { channelId: channelIdRaw, transaction, authorizedSigner } = context
|
|
220
|
+
const channelId = channelIdRaw as Hex.Hex
|
|
221
|
+
const cumulativeAmount = context.cumulativeAmountRaw
|
|
222
|
+
? BigInt(context.cumulativeAmountRaw)
|
|
223
|
+
: context.cumulativeAmount
|
|
224
|
+
? parseUnits(context.cumulativeAmount, decimals)
|
|
225
|
+
: undefined
|
|
226
|
+
const resolvedAdditionalDeposit = context.additionalDepositRaw
|
|
227
|
+
? BigInt(context.additionalDepositRaw)
|
|
228
|
+
: context.additionalDeposit
|
|
229
|
+
? parseUnits(context.additionalDeposit, decimals)
|
|
230
|
+
: undefined
|
|
231
|
+
|
|
232
|
+
const escrowContract = resolveEscrowCached(challenge, chainId, channelId)
|
|
233
|
+
escrowContractMap.set(channelId, escrowContract)
|
|
234
|
+
|
|
235
|
+
let payload: StreamCredentialPayload
|
|
236
|
+
|
|
237
|
+
switch (action) {
|
|
238
|
+
case 'open': {
|
|
239
|
+
if (!transaction) throw new Error('transaction required for open action')
|
|
240
|
+
if (cumulativeAmount === undefined)
|
|
241
|
+
throw new Error('cumulativeAmount required for open action')
|
|
242
|
+
const signature = await signVoucher(
|
|
243
|
+
client,
|
|
244
|
+
account,
|
|
245
|
+
{ channelId, cumulativeAmount },
|
|
246
|
+
escrowContract,
|
|
247
|
+
chainId,
|
|
248
|
+
parameters.authorizedSigner,
|
|
249
|
+
)
|
|
250
|
+
payload = {
|
|
251
|
+
action: 'open',
|
|
252
|
+
type: 'transaction',
|
|
253
|
+
channelId,
|
|
254
|
+
transaction: transaction as Hex.Hex,
|
|
255
|
+
authorizedSigner: (authorizedSigner as Address) ?? account.address,
|
|
256
|
+
cumulativeAmount: cumulativeAmount.toString(),
|
|
257
|
+
signature,
|
|
258
|
+
}
|
|
259
|
+
break
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
case 'topUp':
|
|
263
|
+
if (!transaction) throw new Error('transaction required for topUp action')
|
|
264
|
+
if (resolvedAdditionalDeposit === undefined)
|
|
265
|
+
throw new Error('additionalDeposit required for topUp action')
|
|
266
|
+
payload = {
|
|
267
|
+
action: 'topUp',
|
|
268
|
+
type: 'transaction',
|
|
269
|
+
channelId,
|
|
270
|
+
transaction: transaction as Hex.Hex,
|
|
271
|
+
additionalDeposit: resolvedAdditionalDeposit.toString(),
|
|
272
|
+
}
|
|
273
|
+
break
|
|
274
|
+
|
|
275
|
+
case 'voucher': {
|
|
276
|
+
if (cumulativeAmount === undefined)
|
|
277
|
+
throw new Error('cumulativeAmount required for voucher action')
|
|
278
|
+
payload = await createVoucherPayload(
|
|
279
|
+
client,
|
|
280
|
+
account,
|
|
281
|
+
channelId,
|
|
282
|
+
cumulativeAmount,
|
|
283
|
+
escrowContract,
|
|
284
|
+
chainId,
|
|
285
|
+
parameters.authorizedSigner,
|
|
286
|
+
)
|
|
287
|
+
const key = channelIdToKey.get(channelId)
|
|
288
|
+
if (key) {
|
|
289
|
+
const entry = channels.get(key)
|
|
290
|
+
if (entry) {
|
|
291
|
+
entry.cumulativeAmount =
|
|
292
|
+
entry.cumulativeAmount > cumulativeAmount ? entry.cumulativeAmount : cumulativeAmount
|
|
293
|
+
notifyUpdate(entry)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
break
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
case 'close': {
|
|
300
|
+
if (cumulativeAmount === undefined)
|
|
301
|
+
throw new Error('cumulativeAmount required for close action')
|
|
302
|
+
const signature = await signVoucher(
|
|
303
|
+
client,
|
|
304
|
+
account,
|
|
305
|
+
{ channelId, cumulativeAmount },
|
|
306
|
+
escrowContract,
|
|
307
|
+
chainId,
|
|
308
|
+
parameters.authorizedSigner,
|
|
309
|
+
)
|
|
310
|
+
payload = {
|
|
311
|
+
action: 'close',
|
|
312
|
+
channelId,
|
|
313
|
+
cumulativeAmount: cumulativeAmount.toString(),
|
|
314
|
+
signature,
|
|
315
|
+
}
|
|
316
|
+
const closeKey = channelIdToKey.get(channelId)
|
|
317
|
+
if (closeKey) {
|
|
318
|
+
const entry = channels.get(closeKey)
|
|
319
|
+
if (entry) {
|
|
320
|
+
entry.opened = false
|
|
321
|
+
entry.cumulativeAmount =
|
|
322
|
+
entry.cumulativeAmount > cumulativeAmount ? entry.cumulativeAmount : cumulativeAmount
|
|
323
|
+
notifyUpdate(entry)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
break
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return serializeCredential(challenge, payload, chainId, account)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return MethodIntent.toClient(Intents.session, {
|
|
334
|
+
context: streamContextSchema,
|
|
335
|
+
|
|
336
|
+
async createCredential({ challenge, context }) {
|
|
337
|
+
const chainId = challenge.request.methodDetails?.chainId ?? 0
|
|
338
|
+
const client = await getClient({ chainId })
|
|
339
|
+
const account = getAccount(client, context)
|
|
340
|
+
|
|
341
|
+
if (!context?.action && (parameters.deposit !== undefined || maxDeposit !== undefined))
|
|
342
|
+
return autoManageCredential(challenge, account, context)
|
|
343
|
+
|
|
344
|
+
if (context?.action) return manualCredential(challenge, account, context)
|
|
345
|
+
|
|
346
|
+
throw new Error(
|
|
347
|
+
'No `action` in context and no `deposit` or `maxDeposit` configured. Either provide context with action/channelId/cumulativeAmount, or configure `deposit`/`maxDeposit` for auto-management.',
|
|
348
|
+
)
|
|
349
|
+
},
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export declare namespace session {
|
|
354
|
+
type Parameters = Account.getResolver.Parameters &
|
|
355
|
+
Client.getResolver.Parameters & {
|
|
356
|
+
/** Address authorized to sign vouchers. Defaults to the account address. Use when a separate access key (e.g. secp256k1) signs vouchers while the root account funds the channel. */
|
|
357
|
+
authorizedSigner?: Address | undefined
|
|
358
|
+
/** Token decimals for parsing human-readable amounts (default: 6). */
|
|
359
|
+
decimals?: number | undefined
|
|
360
|
+
/** Initial deposit amount in human-readable units (e.g. "10" for 10 tokens). When set, the method handles the full channel lifecycle (open, voucher, cumulative tracking) automatically. */
|
|
361
|
+
deposit?: string | undefined
|
|
362
|
+
/** Escrow contract address override. Derived from challenge or defaults if not provided. */
|
|
363
|
+
escrowContract?: Address | undefined
|
|
364
|
+
/** Maximum deposit in human-readable units (e.g. "10"). Caps the server's `suggestedDeposit`. Enables auto-management like `deposit`. */
|
|
365
|
+
maxDeposit?: string | undefined
|
|
366
|
+
/** Called whenever channel state changes (open, voucher, close, recovery). */
|
|
367
|
+
onChannelUpdate?: ((entry: ChannelEntry) => void) | undefined
|
|
368
|
+
}
|
|
369
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { Hex } from 'viem'
|
|
2
|
+
import { describe, expect, test, vi } from 'vitest'
|
|
3
|
+
import * as Challenge from '../../Challenge.js'
|
|
4
|
+
import { formatNeedVoucherEvent, parseEvent } from '../stream/Sse.js'
|
|
5
|
+
import type { NeedVoucherEvent, StreamReceipt } from '../stream/Types.js'
|
|
6
|
+
import { sessionManager } from './SessionManager.js'
|
|
7
|
+
|
|
8
|
+
const channelId = '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex
|
|
9
|
+
const challengeId = 'test-challenge-1'
|
|
10
|
+
const realm = 'test.example.com'
|
|
11
|
+
|
|
12
|
+
function makeChallenge(overrides: Record<string, unknown> = {}): Challenge.Challenge {
|
|
13
|
+
return Challenge.from({
|
|
14
|
+
id: challengeId,
|
|
15
|
+
realm,
|
|
16
|
+
method: 'tempo',
|
|
17
|
+
intent: 'session',
|
|
18
|
+
request: {
|
|
19
|
+
amount: '1000000',
|
|
20
|
+
currency: '0x20c0000000000000000000000000000000000001',
|
|
21
|
+
recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00',
|
|
22
|
+
decimals: 6,
|
|
23
|
+
methodDetails: {
|
|
24
|
+
escrowContract: '0x9d136eEa063eDE5418A6BC7bEafF009bBb6CFa70',
|
|
25
|
+
chainId: 4217,
|
|
26
|
+
},
|
|
27
|
+
...overrides,
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function make402Response(challenge?: Challenge.Challenge): Response {
|
|
33
|
+
const c = challenge ?? makeChallenge()
|
|
34
|
+
return new Response(null, {
|
|
35
|
+
status: 402,
|
|
36
|
+
headers: { 'WWW-Authenticate': Challenge.serialize(c) },
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function makeOkResponse(body?: string): Response {
|
|
41
|
+
return new Response(body ?? 'ok', { status: 200 })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function makeSseResponse(events: string[]): Response {
|
|
45
|
+
const body = events.join('')
|
|
46
|
+
return new Response(body, {
|
|
47
|
+
status: 200,
|
|
48
|
+
headers: { 'Content-Type': 'text/event-stream' },
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
describe('Session', () => {
|
|
53
|
+
describe('parseEvent round-trip via SSE', () => {
|
|
54
|
+
test('parses message events from SSE stream', () => {
|
|
55
|
+
const raw = 'event: message\ndata: hello world\n\n'
|
|
56
|
+
const event = parseEvent(raw)
|
|
57
|
+
expect(event).toEqual({ type: 'message', data: 'hello world' })
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('parses payment-need-voucher events', () => {
|
|
61
|
+
const params: NeedVoucherEvent = {
|
|
62
|
+
channelId,
|
|
63
|
+
requiredCumulative: '6000000',
|
|
64
|
+
acceptedCumulative: '5000000',
|
|
65
|
+
deposit: '10000000',
|
|
66
|
+
}
|
|
67
|
+
const raw = formatNeedVoucherEvent(params)
|
|
68
|
+
const event = parseEvent(raw)
|
|
69
|
+
expect(event).toEqual({ type: 'payment-need-voucher', data: params })
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('session creation', () => {
|
|
74
|
+
test('creates session with initial state', () => {
|
|
75
|
+
const s = sessionManager({
|
|
76
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
77
|
+
maxDeposit: '10',
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
expect(s.channelId).toBeUndefined()
|
|
81
|
+
expect(s.cumulative).toBe(0n)
|
|
82
|
+
expect(s.opened).toBe(false)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
describe('.fetch()', () => {
|
|
87
|
+
test('passes through non-402 responses', async () => {
|
|
88
|
+
const mockFetch = vi.fn().mockResolvedValue(makeOkResponse('hello'))
|
|
89
|
+
|
|
90
|
+
const s = sessionManager({
|
|
91
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
92
|
+
fetch: mockFetch as typeof globalThis.fetch,
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const res = await s.fetch('https://api.example.com/data')
|
|
96
|
+
expect(res.status).toBe(200)
|
|
97
|
+
expect(await res.text()).toBe('hello')
|
|
98
|
+
expect(mockFetch).toHaveBeenCalledOnce()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('throws on 402 without maxDeposit or open channel', async () => {
|
|
102
|
+
const mockFetch = vi.fn().mockResolvedValue(make402Response())
|
|
103
|
+
|
|
104
|
+
const s = sessionManager({
|
|
105
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
106
|
+
fetch: mockFetch as typeof globalThis.fetch,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
await expect(s.fetch('https://api.example.com/data')).rejects.toThrow(
|
|
110
|
+
'no `deposit` or `maxDeposit` configured',
|
|
111
|
+
)
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
describe('.open()', () => {
|
|
116
|
+
test('throws when no challenge is available', async () => {
|
|
117
|
+
const s = sessionManager({
|
|
118
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
119
|
+
maxDeposit: '10',
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
await expect(s.open()).rejects.toThrow('No challenge available')
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
describe('.sse() event parsing', () => {
|
|
127
|
+
test('yields only message data from SSE stream', async () => {
|
|
128
|
+
const events = [
|
|
129
|
+
'event: message\ndata: chunk1\n\n',
|
|
130
|
+
'event: message\ndata: chunk2\n\n',
|
|
131
|
+
`event: payment-receipt\ndata: ${JSON.stringify({
|
|
132
|
+
method: 'tempo',
|
|
133
|
+
intent: 'session',
|
|
134
|
+
status: 'success',
|
|
135
|
+
timestamp: '2025-01-01T00:00:00.000Z',
|
|
136
|
+
reference: channelId,
|
|
137
|
+
challengeId,
|
|
138
|
+
channelId,
|
|
139
|
+
acceptedCumulative: '2000000',
|
|
140
|
+
spent: '2000000',
|
|
141
|
+
units: 2,
|
|
142
|
+
} satisfies StreamReceipt)}\n\n`,
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
let callCount = 0
|
|
146
|
+
const mockFetch = vi.fn().mockImplementation(() => {
|
|
147
|
+
callCount++
|
|
148
|
+
if (callCount === 1) return Promise.resolve(makeSseResponse(events))
|
|
149
|
+
return Promise.resolve(makeOkResponse())
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const s = sessionManager({
|
|
153
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
154
|
+
fetch: mockFetch as typeof globalThis.fetch,
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// Manually set channel state to skip auto-open flow
|
|
158
|
+
;(s as any).__test_setChannel?.()
|
|
159
|
+
|
|
160
|
+
const receiptCb = vi.fn()
|
|
161
|
+
const iterable = await s.sse('https://api.example.com/stream', {
|
|
162
|
+
onReceipt: receiptCb,
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const messages: string[] = []
|
|
166
|
+
for await (const msg of iterable) {
|
|
167
|
+
messages.push(msg)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
expect(messages).toEqual(['chunk1', 'chunk2'])
|
|
171
|
+
expect(receiptCb).toHaveBeenCalledOnce()
|
|
172
|
+
expect(receiptCb.mock.calls[0]![0].units).toBe(2)
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
describe('error handling', () => {
|
|
177
|
+
test('.sse() silently skips payment-need-voucher when no channel open', async () => {
|
|
178
|
+
const needVoucher: NeedVoucherEvent = {
|
|
179
|
+
channelId,
|
|
180
|
+
requiredCumulative: '2000000',
|
|
181
|
+
acceptedCumulative: '1000000',
|
|
182
|
+
deposit: '10000000',
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const events = [
|
|
186
|
+
'event: message\ndata: chunk1\n\n',
|
|
187
|
+
formatNeedVoucherEvent(needVoucher),
|
|
188
|
+
'event: message\ndata: chunk2\n\n',
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
const mockFetch = vi.fn().mockResolvedValue(makeSseResponse(events))
|
|
192
|
+
|
|
193
|
+
const s = sessionManager({
|
|
194
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
195
|
+
fetch: mockFetch as typeof globalThis.fetch,
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
const iterable = await s.sse('https://api.example.com/stream')
|
|
199
|
+
|
|
200
|
+
const messages: string[] = []
|
|
201
|
+
for await (const msg of iterable) {
|
|
202
|
+
messages.push(msg)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
expect(messages).toEqual(['chunk1', 'chunk2'])
|
|
206
|
+
expect(mockFetch).toHaveBeenCalledOnce()
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
describe('.close()', () => {
|
|
211
|
+
test('is no-op when not opened', async () => {
|
|
212
|
+
const mockFetch = vi.fn()
|
|
213
|
+
|
|
214
|
+
const s = sessionManager({
|
|
215
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
216
|
+
fetch: mockFetch as typeof globalThis.fetch,
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
await s.close()
|
|
220
|
+
expect(mockFetch).not.toHaveBeenCalled()
|
|
221
|
+
})
|
|
222
|
+
})
|
|
223
|
+
})
|