mppx 0.6.0 → 0.6.2

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 (37) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/Method.d.ts +2 -0
  3. package/dist/Method.d.ts.map +1 -1
  4. package/dist/Method.js.map +1 -1
  5. package/dist/middlewares/hono.d.ts.map +1 -1
  6. package/dist/middlewares/hono.js +5 -1
  7. package/dist/middlewares/hono.js.map +1 -1
  8. package/dist/proxy/Proxy.d.ts.map +1 -1
  9. package/dist/proxy/Proxy.js +19 -1
  10. package/dist/proxy/Proxy.js.map +1 -1
  11. package/dist/proxy/internal/Route.d.ts +5 -0
  12. package/dist/proxy/internal/Route.d.ts.map +1 -1
  13. package/dist/proxy/internal/Route.js +2 -1
  14. package/dist/proxy/internal/Route.js.map +1 -1
  15. package/dist/server/Mppx.d.ts +13 -1
  16. package/dist/server/Mppx.d.ts.map +1 -1
  17. package/dist/server/Mppx.js +107 -36
  18. package/dist/server/Mppx.js.map +1 -1
  19. package/dist/server/internal/scope.d.ts +19 -0
  20. package/dist/server/internal/scope.d.ts.map +1 -0
  21. package/dist/server/internal/scope.js +33 -0
  22. package/dist/server/internal/scope.js.map +1 -0
  23. package/dist/tempo/server/internal/transport.js +2 -3
  24. package/dist/tempo/server/internal/transport.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/Method.ts +2 -0
  27. package/src/middlewares/hono.test.ts +95 -1
  28. package/src/middlewares/hono.ts +6 -1
  29. package/src/proxy/Proxy.test.ts +116 -0
  30. package/src/proxy/Proxy.ts +27 -1
  31. package/src/proxy/internal/Route.ts +2 -1
  32. package/src/server/Mppx.test-d.ts +18 -0
  33. package/src/server/Mppx.test.ts +283 -0
  34. package/src/server/Mppx.ts +153 -45
  35. package/src/server/internal/scope.ts +43 -0
  36. package/src/tempo/server/internal/transport.test.ts +84 -0
  37. package/src/tempo/server/internal/transport.ts +3 -3
@@ -284,6 +284,90 @@ describe('sse transport', () => {
284
284
  expect(terminalReceipt.units).toBe(1)
285
285
  })
286
286
 
287
+ test('respondReceipt uses the verified route unitType instead of the echoed credential unitType', async () => {
288
+ const store = memoryStore()
289
+ await seedChannel(store, 10000000n)
290
+ const transport = sse({ store })
291
+ const request = makeAuthorizedRequest({ unitType: 'token' })
292
+ const credential = makeCredential({ unitType: 'request' })
293
+
294
+ async function* gen() {
295
+ yield 'hello'
296
+ yield 'world'
297
+ yield 'again'
298
+ }
299
+
300
+ const response = transport.respondReceipt({
301
+ credential,
302
+ envelope: {
303
+ capturedRequest: {
304
+ headers: new Headers(request.headers),
305
+ hasBody: request.body !== null,
306
+ method: request.method,
307
+ url: new URL(request.url),
308
+ },
309
+ challenge: makeChallenge({ unitType: 'token' }),
310
+ credential,
311
+ request: makeChallenge({ unitType: 'token' }).request,
312
+ },
313
+ input: request,
314
+ receipt: makeReceipt(),
315
+ response: gen(),
316
+ challengeId,
317
+ })
318
+
319
+ const body = await readResponseText(response)
320
+ const terminalReceipt = readTerminalReceipt(body)
321
+ const channel = await store.getChannel(channelId)
322
+
323
+ expect(channel!.spent).toBe(3000000n)
324
+ expect(channel!.units).toBe(3)
325
+ expect(terminalReceipt.spent).toBe('3000000')
326
+ expect(terminalReceipt.units).toBe(3)
327
+ })
328
+
329
+ test('respondReceipt uses the canonical challenge amount instead of the raw verified route amount', async () => {
330
+ const store = memoryStore()
331
+ await seedChannel(store, 10000000n)
332
+ const transport = sse({ store })
333
+ const request = makeAuthorizedRequest({ unitType: 'token' })
334
+ const credential = makeCredential({ unitType: 'token' })
335
+
336
+ const response = transport.respondReceipt({
337
+ credential,
338
+ envelope: {
339
+ capturedRequest: {
340
+ headers: new Headers(request.headers),
341
+ hasBody: request.body !== null,
342
+ method: request.method,
343
+ url: new URL(request.url),
344
+ },
345
+ challenge: credential.challenge,
346
+ credential,
347
+ request: {
348
+ ...credential.challenge.request,
349
+ amount: '1',
350
+ },
351
+ },
352
+ input: request,
353
+ receipt: makeReceipt(),
354
+ response: new Response('ok', {
355
+ headers: { 'Content-Type': 'text/plain' },
356
+ }),
357
+ challengeId,
358
+ })
359
+
360
+ expect(await response.text()).toBe('ok')
361
+
362
+ const receipt = deserializeSessionReceipt(response.headers.get('Payment-Receipt')!)
363
+ const channel = await store.getChannel(channelId)
364
+
365
+ expect(channel!.spent).toBe(1000000n)
366
+ expect(channel!.units).toBe(1)
367
+ expect(receipt.spent).toBe('1000000')
368
+ expect(receipt.units).toBe(1)
369
+ })
370
+
287
371
  test('respondReceipt with AsyncIterable and non-request unitType still charges per chunk', async () => {
288
372
  const store = memoryStore()
289
373
  await seedChannel(store, 10000000n)
@@ -63,14 +63,14 @@ export function sse(options: sse.Options & { store: ChannelStore.ChannelStore })
63
63
  respondReceipt({ credential, envelope, receipt, response, challengeId, input }) {
64
64
  const verifiedCredential = envelope?.credential ?? credential
65
65
  const verifiedChallengeId = envelope?.challenge.id ?? challengeId
66
+ const verifiedRequest = envelope?.request ?? verifiedCredential.challenge.request
66
67
  const payload = verifiedCredential.payload as Partial<SessionCredentialPayload>
67
68
  if (!payload.channelId) throw new Error('No SSE context available')
69
+
68
70
  const channelId = payload.channelId
69
71
  const tickCost = BigInt(verifiedCredential.challenge.request.amount as string)
70
72
  const unitType =
71
- typeof verifiedCredential.challenge.request.unitType === 'string'
72
- ? verifiedCredential.challenge.request.unitType
73
- : undefined
73
+ typeof verifiedRequest.unitType === 'string' ? verifiedRequest.unitType : undefined
74
74
 
75
75
  // Auto-detect upstream SSE responses and parse them into an
76
76
  // AsyncIterable so they flow through the metered pipeline.