mppx 0.4.12 → 0.5.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/Expires.d.ts +7 -0
  3. package/dist/Expires.d.ts.map +1 -1
  4. package/dist/Expires.js +21 -0
  5. package/dist/Expires.js.map +1 -1
  6. package/dist/server/Mppx.js +6 -5
  7. package/dist/server/Mppx.js.map +1 -1
  8. package/dist/stripe/server/Charge.d.ts.map +1 -1
  9. package/dist/stripe/server/Charge.js +3 -3
  10. package/dist/stripe/server/Charge.js.map +1 -1
  11. package/dist/tempo/Methods.d.ts +3 -0
  12. package/dist/tempo/Methods.d.ts.map +1 -1
  13. package/dist/tempo/Methods.js +1 -0
  14. package/dist/tempo/Methods.js.map +1 -1
  15. package/dist/tempo/client/Charge.d.ts +3 -0
  16. package/dist/tempo/client/Charge.d.ts.map +1 -1
  17. package/dist/tempo/client/Charge.js +18 -2
  18. package/dist/tempo/client/Charge.js.map +1 -1
  19. package/dist/tempo/client/Methods.d.ts +3 -0
  20. package/dist/tempo/client/Methods.d.ts.map +1 -1
  21. package/dist/tempo/internal/proof.d.ts +23 -0
  22. package/dist/tempo/internal/proof.d.ts.map +1 -0
  23. package/dist/tempo/internal/proof.js +17 -0
  24. package/dist/tempo/internal/proof.js.map +1 -0
  25. package/dist/tempo/server/Charge.d.ts +3 -0
  26. package/dist/tempo/server/Charge.d.ts.map +1 -1
  27. package/dist/tempo/server/Charge.js +32 -4
  28. package/dist/tempo/server/Charge.js.map +1 -1
  29. package/dist/tempo/server/Methods.d.ts +3 -0
  30. package/dist/tempo/server/Methods.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/Expires.ts +25 -0
  33. package/src/cli/cli.test.ts +230 -1
  34. package/src/middlewares/elysia.test.ts +127 -4
  35. package/src/middlewares/express.test.ts +120 -54
  36. package/src/middlewares/hono.test.ts +73 -34
  37. package/src/middlewares/nextjs.test.ts +159 -36
  38. package/src/server/Mppx.test.ts +86 -0
  39. package/src/server/Mppx.ts +5 -5
  40. package/src/stripe/server/Charge.ts +3 -7
  41. package/src/tempo/Methods.test.ts +26 -0
  42. package/src/tempo/Methods.ts +1 -0
  43. package/src/tempo/client/Charge.ts +26 -3
  44. package/src/tempo/internal/charge.test.ts +66 -0
  45. package/src/tempo/internal/proof.test.ts +36 -0
  46. package/src/tempo/internal/proof.ts +19 -0
  47. package/src/tempo/server/Charge.test.ts +362 -1
  48. package/src/tempo/server/Charge.ts +40 -2
  49. package/src/tempo/server/Session.test.ts +1123 -53
  50. package/src/tempo/server/internal/transport.test.ts +32 -0
  51. package/src/tempo/session/Chain.test.ts +35 -0
  52. package/src/tempo/session/Sse.test.ts +31 -0
@@ -82,6 +82,19 @@ function makeReceipt() {
82
82
  }
83
83
  }
84
84
 
85
+ async function readResponseText(response: Response): Promise<string> {
86
+ if (!response.body) return ''
87
+ const reader = response.body.getReader()
88
+ const decoder = new TextDecoder()
89
+ let result = ''
90
+ while (true) {
91
+ const { done, value } = await reader.read()
92
+ if (done) break
93
+ result += decoder.decode(value, { stream: true })
94
+ }
95
+ return result
96
+ }
97
+
85
98
  describe('sse transport', () => {
86
99
  test('getCredential returns null when no Authorization header', () => {
87
100
  const store = memoryStore()
@@ -152,6 +165,19 @@ describe('sse transport', () => {
152
165
  challengeId,
153
166
  })
154
167
  expect(response.headers.get('Content-Type')).toContain('text/event-stream')
168
+
169
+ const body = await readResponseText(response)
170
+ const receiptRaw = body.split('event: payment-receipt\ndata: ')[1]?.split('\n\n')[0]
171
+ const terminalReceipt = JSON.parse(receiptRaw!)
172
+
173
+ expect(response.headers.get('Payment-Receipt')).toBeNull()
174
+ expect(body).toContain('event: message\ndata: hello\n\n')
175
+ expect(body).toContain('event: message\ndata: world\n\n')
176
+ expect(body).toContain('event: payment-receipt\n')
177
+ expect(terminalReceipt.challengeId).toBe(challengeId)
178
+ expect(terminalReceipt.channelId).toBe(channelId)
179
+ expect(terminalReceipt.units).toBe(2)
180
+ expect(terminalReceipt.spent).toBe('2000000')
155
181
  })
156
182
 
157
183
  test('respondReceipt with AsyncGeneratorFunction passes stream controller', async () => {
@@ -197,6 +223,12 @@ describe('sse transport', () => {
197
223
  challengeId,
198
224
  })
199
225
  expect(response.headers.get('Content-Type')).toContain('text/event-stream')
226
+
227
+ const body = await readResponseText(response)
228
+ expect(response.headers.get('Payment-Receipt')).toBeNull()
229
+ expect(body).toContain('event: message\ndata: chunk1\n\n')
230
+ expect(body).toContain('event: message\ndata: chunk2\n\n')
231
+ expect(body).toContain('event: payment-receipt\n')
200
232
  })
201
233
 
202
234
  test('respondReceipt with plain Response delegates to base http transport', () => {
@@ -539,6 +539,41 @@ describe.runIf(isLocalnet)('on-chain', () => {
539
539
  ).rejects.toThrow('topUp transaction amount')
540
540
  })
541
541
 
542
+ test('rejects when post-broadcast deposit does not exceed declared previousDeposit', async () => {
543
+ const salt = nextSalt()
544
+ const deposit = 5_000_000n
545
+ const topUpAmount = 1_000_000n
546
+
547
+ const { channelId } = await openChannel({
548
+ escrow: escrowContract,
549
+ payer,
550
+ payee: recipient,
551
+ token: currency,
552
+ deposit,
553
+ salt,
554
+ })
555
+
556
+ const { serializedTransaction } = await signTopUpChannel({
557
+ escrow: escrowContract,
558
+ payer,
559
+ channelId,
560
+ token: currency,
561
+ amount: topUpAmount,
562
+ })
563
+
564
+ await expect(
565
+ broadcastTopUpTransaction({
566
+ client,
567
+ serializedTransaction,
568
+ escrowContract,
569
+ channelId,
570
+ currency: asset,
571
+ declaredDeposit: topUpAmount,
572
+ previousDeposit: deposit + topUpAmount,
573
+ }),
574
+ ).rejects.toThrow('channel deposit did not increase after topUp')
575
+ })
576
+
542
577
  test('successful broadcast returns txHash and newDeposit', async () => {
543
578
  const salt = nextSalt()
544
579
  const deposit = 5_000_000n
@@ -366,6 +366,37 @@ describe('serve', () => {
366
366
  expect(receipt.challengeId).toBe(challengeId)
367
367
  })
368
368
 
369
+ test('emits exactly one terminal payment-receipt event at stream end', async () => {
370
+ const storage = memoryStore()
371
+ await seedChannel(storage, 2000000n)
372
+
373
+ const stream = serve({
374
+ store: storage,
375
+ channelId,
376
+ challengeId,
377
+ tickCost: 1000000n,
378
+ generate: generate(['one', 'two']),
379
+ })
380
+
381
+ const output = await readStream(stream)
382
+ const events = output
383
+ .trim()
384
+ .split('\n\n')
385
+ .filter((chunk) => chunk.length > 0)
386
+ .map((chunk) => parseEvent(`${chunk}\n\n`))
387
+ .filter((event): event is NonNullable<typeof event> => event !== null)
388
+
389
+ const terminal = events.at(-1)
390
+ expect(terminal?.type).toBe('payment-receipt')
391
+ if (terminal?.type !== 'payment-receipt') throw new Error('expected terminal payment receipt')
392
+
393
+ expect(events.filter((event) => event.type === 'payment-receipt')).toHaveLength(1)
394
+ expect(terminal.data.challengeId).toBe(challengeId)
395
+ expect(terminal.data.channelId).toBe(channelId)
396
+ expect(terminal.data.units).toBe(2)
397
+ expect(terminal.data.spent).toBe('2000000')
398
+ })
399
+
369
400
  test('handles empty generator', async () => {
370
401
  const storage = memoryStore()
371
402
  await seedChannel(storage, 1000000n)