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.
- package/CHANGELOG.md +12 -0
- package/dist/Method.d.ts +2 -0
- package/dist/Method.d.ts.map +1 -1
- package/dist/Method.js.map +1 -1
- package/dist/middlewares/hono.d.ts.map +1 -1
- package/dist/middlewares/hono.js +5 -1
- package/dist/middlewares/hono.js.map +1 -1
- package/dist/proxy/Proxy.d.ts.map +1 -1
- package/dist/proxy/Proxy.js +19 -1
- package/dist/proxy/Proxy.js.map +1 -1
- package/dist/proxy/internal/Route.d.ts +5 -0
- package/dist/proxy/internal/Route.d.ts.map +1 -1
- package/dist/proxy/internal/Route.js +2 -1
- package/dist/proxy/internal/Route.js.map +1 -1
- package/dist/server/Mppx.d.ts +13 -1
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +107 -36
- package/dist/server/Mppx.js.map +1 -1
- package/dist/server/internal/scope.d.ts +19 -0
- package/dist/server/internal/scope.d.ts.map +1 -0
- package/dist/server/internal/scope.js +33 -0
- package/dist/server/internal/scope.js.map +1 -0
- package/dist/tempo/server/internal/transport.js +2 -3
- package/dist/tempo/server/internal/transport.js.map +1 -1
- package/package.json +1 -1
- package/src/Method.ts +2 -0
- package/src/middlewares/hono.test.ts +95 -1
- package/src/middlewares/hono.ts +6 -1
- package/src/proxy/Proxy.test.ts +116 -0
- package/src/proxy/Proxy.ts +27 -1
- package/src/proxy/internal/Route.ts +2 -1
- package/src/server/Mppx.test-d.ts +18 -0
- package/src/server/Mppx.test.ts +283 -0
- package/src/server/Mppx.ts +153 -45
- package/src/server/internal/scope.ts +43 -0
- package/src/tempo/server/internal/transport.test.ts +84 -0
- 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
|
|
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.
|