mppx 0.5.1 → 0.5.4

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.
Files changed (111) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/Credential.d.ts +12 -0
  3. package/dist/Credential.d.ts.map +1 -1
  4. package/dist/Credential.js +22 -4
  5. package/dist/Credential.js.map +1 -1
  6. package/dist/Method.d.ts +4 -0
  7. package/dist/Method.d.ts.map +1 -1
  8. package/dist/Method.js +2 -1
  9. package/dist/Method.js.map +1 -1
  10. package/dist/cli/cli.d.ts.map +1 -1
  11. package/dist/cli/cli.js +11 -9
  12. package/dist/cli/cli.js.map +1 -1
  13. package/dist/cli/plugins/tempo.d.ts.map +1 -1
  14. package/dist/cli/plugins/tempo.js +3 -3
  15. package/dist/cli/plugins/tempo.js.map +1 -1
  16. package/dist/cli/utils.d.ts +2 -0
  17. package/dist/cli/utils.d.ts.map +1 -1
  18. package/dist/cli/utils.js +10 -5
  19. package/dist/cli/utils.js.map +1 -1
  20. package/dist/proxy/Proxy.d.ts.map +1 -1
  21. package/dist/proxy/Proxy.js +52 -8
  22. package/dist/proxy/Proxy.js.map +1 -1
  23. package/dist/proxy/internal/Route.d.ts.map +1 -1
  24. package/dist/proxy/internal/Route.js +7 -3
  25. package/dist/proxy/internal/Route.js.map +1 -1
  26. package/dist/server/Mppx.d.ts.map +1 -1
  27. package/dist/server/Mppx.js +90 -71
  28. package/dist/server/Mppx.js.map +1 -1
  29. package/dist/server/Transport.d.ts +5 -1
  30. package/dist/server/Transport.d.ts.map +1 -1
  31. package/dist/server/Transport.js +71 -7
  32. package/dist/server/Transport.js.map +1 -1
  33. package/dist/server/internal/html/config.d.ts +144 -0
  34. package/dist/server/internal/html/config.d.ts.map +1 -0
  35. package/dist/server/internal/html/config.js +303 -0
  36. package/dist/server/internal/html/config.js.map +1 -0
  37. package/dist/server/internal/html/serviceWorker.gen.d.ts +2 -0
  38. package/dist/server/internal/html/serviceWorker.gen.d.ts.map +1 -0
  39. package/dist/server/internal/html/serviceWorker.gen.js +3 -0
  40. package/dist/server/internal/html/serviceWorker.gen.js.map +1 -0
  41. package/dist/stripe/internal/types.d.ts +6 -0
  42. package/dist/stripe/internal/types.d.ts.map +1 -1
  43. package/dist/stripe/server/Charge.d.ts +30 -16
  44. package/dist/stripe/server/Charge.d.ts.map +1 -1
  45. package/dist/stripe/server/Charge.js +35 -6
  46. package/dist/stripe/server/Charge.js.map +1 -1
  47. package/dist/stripe/server/internal/html/types.d.ts +2 -0
  48. package/dist/stripe/server/internal/html/types.d.ts.map +1 -0
  49. package/dist/stripe/server/internal/html/types.js +2 -0
  50. package/dist/stripe/server/internal/html/types.js.map +1 -0
  51. package/dist/stripe/server/internal/html.gen.d.ts +2 -0
  52. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -0
  53. package/dist/stripe/server/internal/html.gen.js +3 -0
  54. package/dist/stripe/server/internal/html.gen.js.map +1 -0
  55. package/dist/tempo/server/Charge.d.ts +33 -26
  56. package/dist/tempo/server/Charge.d.ts.map +1 -1
  57. package/dist/tempo/server/Charge.js +46 -11
  58. package/dist/tempo/server/Charge.js.map +1 -1
  59. package/dist/tempo/server/Session.d.ts.map +1 -1
  60. package/dist/tempo/server/Session.js +3 -2
  61. package/dist/tempo/server/Session.js.map +1 -1
  62. package/dist/tempo/server/internal/html.gen.d.ts +2 -0
  63. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -0
  64. package/dist/tempo/server/internal/html.gen.js +3 -0
  65. package/dist/tempo/server/internal/html.gen.js.map +1 -0
  66. package/dist/tempo/server/internal/transport.d.ts +1 -1
  67. package/dist/tempo/server/internal/transport.d.ts.map +1 -1
  68. package/dist/tempo/server/internal/transport.js +45 -58
  69. package/dist/tempo/server/internal/transport.js.map +1 -1
  70. package/package.json +2 -2
  71. package/src/Credential.ts +28 -4
  72. package/src/Method.ts +6 -1
  73. package/src/cli/cli.ts +11 -8
  74. package/src/cli/plugins/tempo.ts +3 -2
  75. package/src/cli/utils.test.ts +64 -0
  76. package/src/cli/utils.ts +10 -4
  77. package/src/env.d.ts +1 -0
  78. package/src/mcp-sdk/server/Transport.test.ts +6 -0
  79. package/src/proxy/Proxy.test.ts +188 -1
  80. package/src/proxy/Proxy.ts +58 -9
  81. package/src/proxy/internal/Route.test.ts +9 -0
  82. package/src/proxy/internal/Route.ts +5 -2
  83. package/src/server/Mppx.test.ts +171 -18
  84. package/src/server/Mppx.ts +120 -79
  85. package/src/server/Transport.test.ts +232 -2
  86. package/src/server/Transport.ts +84 -7
  87. package/src/server/internal/html/config.ts +414 -0
  88. package/src/server/internal/html/serviceWorker.client.ts +28 -0
  89. package/src/server/internal/html/serviceWorker.gen.ts +2 -0
  90. package/src/server/internal/html/serviceWorker.ts +27 -0
  91. package/src/server/internal/html/tsconfig.worker.client.json +8 -0
  92. package/src/server/internal/html/tsconfig.worker.json +8 -0
  93. package/src/stripe/internal/types.ts +20 -0
  94. package/src/stripe/server/Charge.ts +62 -6
  95. package/src/stripe/server/internal/html/main.ts +174 -0
  96. package/src/stripe/server/internal/html/node_modules/.bin/mppx.src +21 -0
  97. package/src/stripe/server/internal/html/package.json +9 -0
  98. package/src/stripe/server/internal/html/stripe-js-pure.d.ts +7 -0
  99. package/src/stripe/server/internal/html/tsconfig.json +8 -0
  100. package/src/stripe/server/internal/html/types.ts +5 -0
  101. package/src/stripe/server/internal/html.gen.ts +2 -0
  102. package/src/tempo/server/Charge.ts +64 -10
  103. package/src/tempo/server/Session.ts +3 -2
  104. package/src/tempo/server/internal/html/main.ts +111 -0
  105. package/src/tempo/server/internal/html/node_modules/.bin/mppx.src +21 -0
  106. package/src/tempo/server/internal/html/package.json +10 -0
  107. package/src/tempo/server/internal/html/tsconfig.json +8 -0
  108. package/src/tempo/server/internal/html.gen.ts +2 -0
  109. package/src/tempo/server/internal/transport.test.ts +37 -31
  110. package/src/tempo/server/internal/transport.ts +44 -58
  111. package/src/tsconfig.json +1 -1
@@ -113,19 +113,20 @@ describe('sse transport', () => {
113
113
  expect((credential!.payload as any).channelId).toBe(channelId)
114
114
  })
115
115
 
116
- test('getCredential captures SSE context in contextMap', async () => {
116
+ test('respondReceipt derives SSE context from the verified credential', async () => {
117
117
  const store = memoryStore()
118
118
  await seedChannel(store, 10000000n)
119
119
  const transport = sse({ store })
120
120
 
121
- const request = makeAuthorizedRequest()
122
- transport.getCredential(request)
121
+ const credential = makeCredential()
123
122
 
124
123
  async function* gen() {
125
124
  yield 'test'
126
125
  }
127
126
 
128
127
  const response = transport.respondReceipt({
128
+ credential,
129
+ input: new Request('https://test.example.com/session'),
129
130
  receipt: makeReceipt(),
130
131
  response: gen(),
131
132
  challengeId,
@@ -151,8 +152,7 @@ describe('sse transport', () => {
151
152
  const store = memoryStore()
152
153
  await seedChannel(store, 10000000n)
153
154
  const transport = sse({ store })
154
-
155
- transport.getCredential(makeAuthorizedRequest())
155
+ const request = makeAuthorizedRequest()
156
156
 
157
157
  async function* gen() {
158
158
  yield 'hello'
@@ -160,6 +160,8 @@ describe('sse transport', () => {
160
160
  }
161
161
 
162
162
  const response = transport.respondReceipt({
163
+ credential: makeCredential(),
164
+ input: request,
163
165
  receipt: makeReceipt(),
164
166
  response: gen(),
165
167
  challengeId,
@@ -184,10 +186,11 @@ describe('sse transport', () => {
184
186
  const store = memoryStore()
185
187
  await seedChannel(store, 10000000n)
186
188
  const transport = sse({ store })
187
-
188
- transport.getCredential(makeAuthorizedRequest())
189
+ const request = makeAuthorizedRequest()
189
190
 
190
191
  const response = transport.respondReceipt({
192
+ credential: makeCredential(),
193
+ input: request,
191
194
  receipt: makeReceipt(),
192
195
  response: async function* (stream) {
193
196
  await stream.charge()
@@ -202,8 +205,7 @@ describe('sse transport', () => {
202
205
  const store = memoryStore()
203
206
  await seedChannel(store, 10000000n)
204
207
  const transport = sse({ store })
205
-
206
- transport.getCredential(makeAuthorizedRequest())
208
+ const request = makeAuthorizedRequest()
207
209
 
208
210
  const encoder = new TextEncoder()
209
211
  const upstream = new Response(
@@ -218,6 +220,8 @@ describe('sse transport', () => {
218
220
  )
219
221
 
220
222
  const response = transport.respondReceipt({
223
+ credential: makeCredential(),
224
+ input: request,
221
225
  receipt: makeReceipt(),
222
226
  response: upstream,
223
227
  challengeId,
@@ -241,6 +245,8 @@ describe('sse transport', () => {
241
245
  })
242
246
 
243
247
  const response = transport.respondReceipt({
248
+ credential: makeCredential(),
249
+ input: new Request('https://test.example.com/session'),
244
250
  receipt,
245
251
  response: plainResponse,
246
252
  challengeId,
@@ -249,34 +255,24 @@ describe('sse transport', () => {
249
255
  expect(response.headers.get('Payment-Receipt')).toBeTruthy()
250
256
  })
251
257
 
252
- test('respondReceipt cleans up contextMap after use', async () => {
258
+ test('respondReceipt no longer depends on prior getCredential side effects', async () => {
253
259
  const store = memoryStore()
254
260
  await seedChannel(store, 10000000n)
255
261
  const transport = sse({ store })
256
-
257
- transport.getCredential(makeAuthorizedRequest())
262
+ const request = makeAuthorizedRequest()
258
263
 
259
264
  async function* gen() {
260
265
  yield 'first'
261
266
  }
262
267
 
263
- transport.respondReceipt({
268
+ const response = transport.respondReceipt({
269
+ credential: makeCredential(),
270
+ input: request,
264
271
  receipt: makeReceipt(),
265
272
  response: gen(),
266
273
  challengeId,
267
274
  })
268
-
269
- async function* gen2() {
270
- yield 'second'
271
- }
272
-
273
- expect(() =>
274
- transport.respondReceipt({
275
- receipt: makeReceipt(),
276
- response: gen2(),
277
- challengeId,
278
- }),
279
- ).toThrow('No SSE context available')
275
+ expect(response.headers.get('Content-Type')).toContain('text/event-stream')
280
276
  })
281
277
 
282
278
  test('respondReceipt throws when no SSE context available', () => {
@@ -287,8 +283,15 @@ describe('sse transport', () => {
287
283
  yield 'hello'
288
284
  }
289
285
 
286
+ const credential = Credential.from({
287
+ challenge: makeChallenge(),
288
+ payload: { signature: '0xabc123', type: 'transaction' },
289
+ })
290
+
290
291
  expect(() =>
291
292
  transport.respondReceipt({
293
+ credential,
294
+ input: new Request('https://test.example.com/session'),
292
295
  receipt: makeReceipt(),
293
296
  response: gen(),
294
297
  challengeId,
@@ -300,14 +303,15 @@ describe('sse transport', () => {
300
303
  const store = memoryStore()
301
304
  await seedChannel(store, 10000000n)
302
305
  const transport = sse({ store })
303
-
304
- transport.getCredential(makeAuthorizedRequest())
306
+ const request = makeAuthorizedRequest()
305
307
 
306
308
  const plainResponse = new Response(JSON.stringify({ content: 'hello' }), {
307
309
  headers: { 'Content-Type': 'application/json' },
308
310
  })
309
311
 
310
312
  const response = transport.respondReceipt({
313
+ credential: makeCredential(),
314
+ input: request,
311
315
  receipt: makeReceipt(),
312
316
  response: plainResponse,
313
317
  challengeId,
@@ -328,11 +332,12 @@ describe('sse transport', () => {
328
332
  const store = memoryStore()
329
333
  await seedChannel(store, 10000000n)
330
334
  const transport = sse({ store })
331
-
332
- transport.getCredential(makeAuthorizedRequest())
335
+ const request = makeAuthorizedRequest()
333
336
 
334
337
  const managementResponse = new Response(null, { status: 204 })
335
338
  const response = transport.respondReceipt({
339
+ credential: makeCredential(),
340
+ input: request,
336
341
  receipt: makeReceipt(),
337
342
  response: managementResponse,
338
343
  challengeId,
@@ -353,14 +358,15 @@ describe('sse transport', () => {
353
358
  await seedChannel(store, 10000000n)
354
359
 
355
360
  const transport = sse({ store, poll: true })
356
-
357
- transport.getCredential(makeAuthorizedRequest())
361
+ const request = makeAuthorizedRequest()
358
362
 
359
363
  async function* gen() {
360
364
  yield 'test'
361
365
  }
362
366
 
363
367
  const response = transport.respondReceipt({
368
+ credential: makeCredential(),
369
+ input: request,
364
370
  receipt: makeReceipt(),
365
371
  response: gen(),
366
372
  challengeId,
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Tempo-specific SSE transport that wraps the base HTTP transport
3
- * with metering logic (context capture from credentials, per-token
3
+ * with metering logic (context capture from verified credentials, per-token
4
4
  * charging via Sse.serve).
5
5
  *
6
6
  * @internal
@@ -8,6 +8,7 @@
8
8
  import * as Transport from '../../../server/Transport.js'
9
9
  import * as ChannelStore from '../../session/ChannelStore.js'
10
10
  import * as Sse_core from '../../session/Sse.js'
11
+ import type { SessionCredentialPayload } from '../../session/Types.js'
11
12
 
12
13
  /** SSE transport with Tempo session controller. */
13
14
  export type Sse = Transport.Sse<Sse_core.SessionController>
@@ -33,30 +34,24 @@ export function sse(options: sse.Options & { store: ChannelStore.ChannelStore })
33
34
  return store
34
35
  })()
35
36
 
36
- const contextMap = new Map<string, Sse_core.fromRequest.Context & { signal?: AbortSignal }>()
37
-
38
37
  const base = Transport.http()
39
38
  return Transport.from<Request, Response, Transport.ReceiptResponseOf<Sse>, Response>({
40
39
  name: 'sse',
41
40
 
42
41
  getCredential(request) {
43
- const credential = base.getCredential(request)
44
- if (credential) {
45
- try {
46
- const ctx = Sse_core.fromRequest(request)
47
- contextMap.set(ctx.challengeId, { ...ctx, signal: request.signal })
48
- } catch {
49
- // ignore — non-SSE credentials won't have session context
50
- }
51
- }
52
- return credential
42
+ return base.getCredential(request)
53
43
  },
54
44
 
55
45
  respondChallenge(options) {
56
46
  return base.respondChallenge(options) as Response
57
47
  },
58
48
 
59
- respondReceipt({ receipt, response, challengeId }) {
49
+ respondReceipt({ credential, receipt, response, challengeId, input }) {
50
+ const payload = credential.payload as Partial<SessionCredentialPayload>
51
+ if (!payload.channelId) throw new Error('No SSE context available')
52
+ const channelId = payload.channelId
53
+ const tickCost = BigInt(credential.challenge.request.amount as string)
54
+
60
55
  // Auto-detect upstream SSE responses and parse them into an
61
56
  // AsyncIterable so they flow through the metered pipeline.
62
57
  // This lets proxy consumers simply pass `result.withReceipt(upstreamRes)`
@@ -67,10 +62,6 @@ export function sse(options: sse.Options & { store: ChannelStore.ChannelStore })
67
62
  : response
68
63
 
69
64
  if (isAsyncGeneratorFunction(resolved) || isAsyncIterable(resolved)) {
70
- const ctx = contextMap.get(challengeId)
71
- if (!ctx) throw new Error('No SSE context available — credential was not parsed')
72
- contextMap.delete(challengeId)
73
-
74
65
  // Pass async generator functions directly so Sse.serve gives them
75
66
  // a SessionController for manual charge(). Pass raw AsyncIterables
76
67
  // as-is so Sse.serve auto-charges per yielded value.
@@ -79,17 +70,19 @@ export function sse(options: sse.Options & { store: ChannelStore.ChannelStore })
79
70
  : (resolved as AsyncIterable<string>)
80
71
  const stream = Sse_core.serve({
81
72
  store,
82
- channelId: ctx.channelId,
73
+ channelId,
83
74
  challengeId,
84
- tickCost: ctx.tickCost,
75
+ tickCost,
85
76
  pollIntervalMs: pollingInterval,
86
77
  generate,
87
- signal: ctx.signal,
78
+ signal: input.signal,
88
79
  })
89
80
  return Sse_core.toResponse(stream)
90
81
  }
91
82
 
92
83
  const baseResponse = base.respondReceipt({
84
+ credential,
85
+ input,
93
86
  receipt,
94
87
  response: response as Response,
95
88
  challengeId,
@@ -97,45 +90,38 @@ export function sse(options: sse.Options & { store: ChannelStore.ChannelStore })
97
90
 
98
91
  // Non-SSE response (e.g. upstream returned JSON instead of event-stream).
99
92
  // Need to deduct tickCost so request isn't free.
100
- const ctx = contextMap.get(challengeId)
101
- if (ctx) {
102
- contextMap.delete(challengeId)
103
-
104
- // Null-body statuses (e.g. 204 from management actions) cannot carry a
105
- // response body per Fetch/HTTP semantics.
106
- if (isNullBodyStatus(baseResponse.status)) {
107
- return baseResponse
108
- }
109
-
110
- const stream = new ReadableStream<Uint8Array>({
111
- async start(controller) {
112
- // deduction completes before consumer reads
113
- await ChannelStore.deductFromChannel(store, ctx.channelId, ctx.tickCost)
114
- if (!baseResponse.body) {
115
- controller.close()
116
- return
117
- }
118
- const reader = baseResponse.body.getReader()
119
- try {
120
- while (true) {
121
- const { done, value } = await reader.read()
122
- if (done) break
123
- controller.enqueue(value)
124
- }
125
- } finally {
126
- reader.releaseLock()
127
- controller.close()
128
- }
129
- },
130
- })
131
- return new Response(stream, {
132
- status: baseResponse.status,
133
- statusText: baseResponse.statusText,
134
- headers: baseResponse.headers,
135
- })
93
+ // Null-body statuses (e.g. 204 from management actions) cannot carry a
94
+ // response body per Fetch/HTTP semantics.
95
+ if (isNullBodyStatus(baseResponse.status)) {
96
+ return baseResponse
136
97
  }
137
98
 
138
- return baseResponse
99
+ const stream = new ReadableStream<Uint8Array>({
100
+ async start(controller) {
101
+ // deduction completes before consumer reads
102
+ await ChannelStore.deductFromChannel(store, channelId, tickCost)
103
+ if (!baseResponse.body) {
104
+ controller.close()
105
+ return
106
+ }
107
+ const reader = baseResponse.body.getReader()
108
+ try {
109
+ while (true) {
110
+ const { done, value } = await reader.read()
111
+ if (done) break
112
+ controller.enqueue(value)
113
+ }
114
+ } finally {
115
+ reader.releaseLock()
116
+ controller.close()
117
+ }
118
+ },
119
+ })
120
+ return new Response(stream, {
121
+ status: baseResponse.status,
122
+ statusText: baseResponse.statusText,
123
+ headers: baseResponse.headers,
124
+ })
139
125
  },
140
126
  })
141
127
  }
package/src/tsconfig.json CHANGED
@@ -6,5 +6,5 @@
6
6
  "types": ["node"]
7
7
  },
8
8
  "include": ["./**/*.ts"],
9
- "exclude": ["./**/*.test.ts", "./**/*.test-d.ts"]
9
+ "exclude": ["./**/*.test.ts", "./**/*.test-d.ts", "./**/internal/html/**"]
10
10
  }