mppx 0.3.14 → 0.3.16

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 (60) hide show
  1. package/README.md +1 -0
  2. package/dist/Challenge.d.ts +38 -0
  3. package/dist/Challenge.d.ts.map +1 -1
  4. package/dist/Challenge.js +62 -0
  5. package/dist/Challenge.js.map +1 -1
  6. package/dist/bin.d.ts +3 -0
  7. package/dist/bin.d.ts.map +1 -0
  8. package/dist/bin.js +4 -0
  9. package/dist/bin.js.map +1 -0
  10. package/dist/cli.d.ts +26 -2
  11. package/dist/cli.d.ts.map +1 -1
  12. package/dist/cli.js +1478 -915
  13. package/dist/cli.js.map +1 -1
  14. package/dist/client/Mppx.d.ts +2 -0
  15. package/dist/client/Mppx.d.ts.map +1 -1
  16. package/dist/client/Mppx.js +2 -0
  17. package/dist/client/Mppx.js.map +1 -1
  18. package/dist/client/internal/Fetch.d.ts.map +1 -1
  19. package/dist/client/internal/Fetch.js +16 -4
  20. package/dist/client/internal/Fetch.js.map +1 -1
  21. package/dist/middlewares/internal/mppx.d.ts +6 -1
  22. package/dist/middlewares/internal/mppx.d.ts.map +1 -1
  23. package/dist/middlewares/internal/mppx.js +4 -0
  24. package/dist/middlewares/internal/mppx.js.map +1 -1
  25. package/dist/server/Mppx.d.ts +79 -1
  26. package/dist/server/Mppx.d.ts.map +1 -1
  27. package/dist/server/Mppx.js +135 -7
  28. package/dist/server/Mppx.js.map +1 -1
  29. package/dist/tempo/client/ChannelOps.d.ts.map +1 -1
  30. package/dist/tempo/client/ChannelOps.js +1 -0
  31. package/dist/tempo/client/ChannelOps.js.map +1 -1
  32. package/dist/tempo/server/Charge.d.ts.map +1 -1
  33. package/dist/tempo/server/Charge.js +4 -4
  34. package/dist/tempo/server/Charge.js.map +1 -1
  35. package/dist/tempo/session/Chain.d.ts.map +1 -1
  36. package/dist/tempo/session/Chain.js +9 -6
  37. package/dist/tempo/session/Chain.js.map +1 -1
  38. package/package.json +4 -4
  39. package/src/Challenge.ts +72 -0
  40. package/src/bin.ts +4 -0
  41. package/src/cli.test.ts +180 -252
  42. package/src/cli.ts +1085 -485
  43. package/src/client/Mppx.test-d.ts +9 -0
  44. package/src/client/Mppx.test.ts +78 -0
  45. package/src/client/Mppx.ts +5 -0
  46. package/src/client/internal/Fetch.test.ts +1 -1
  47. package/src/client/internal/Fetch.ts +18 -6
  48. package/src/middlewares/internal/mppx.test.ts +152 -0
  49. package/src/middlewares/internal/mppx.ts +22 -3
  50. package/src/server/Mppx.test-d.ts +94 -299
  51. package/src/server/Mppx.test.ts +650 -0
  52. package/src/server/Mppx.ts +213 -9
  53. package/src/tempo/client/ChannelOps.ts +1 -0
  54. package/src/tempo/server/Charge.ts +4 -3
  55. package/src/tempo/session/Chain.ts +8 -5
  56. package/dist/tempo/internal/simulate.d.ts +0 -21
  57. package/dist/tempo/internal/simulate.d.ts.map +0 -1
  58. package/dist/tempo/internal/simulate.js +0 -31
  59. package/dist/tempo/internal/simulate.js.map +0 -1
  60. package/src/tempo/internal/simulate.ts +0 -49
@@ -1,20 +1,13 @@
1
- import { tempo } from 'mppx/server'
2
- import { createClient, http } from 'viem'
3
- import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
4
- import { describe, expectTypeOf, test } from 'vitest'
5
- import * as Method from '../Method.js'
6
- import * as z from '../zod.js'
7
- import * as Mppx from './Mppx.js'
1
+ import { Method, z } from 'mppx'
2
+ import { Mppx } from 'mppx/server'
3
+ import { assertType, describe, expectTypeOf, test } from 'vitest'
8
4
 
9
- const account = privateKeyToAccount(generatePrivateKey())
10
- const getClient = () => createClient({ account, transport: http() })
11
-
12
- const fooCharge = Method.from({
13
- name: 'test',
5
+ const mockChargeA = Method.from({
6
+ name: 'alpha',
14
7
  intent: 'charge',
15
8
  schema: {
16
9
  credential: {
17
- payload: z.object({ signature: z.string() }),
10
+ payload: z.object({ token: z.string() }),
18
11
  },
19
12
  request: z.object({
20
13
  amount: z.string(),
@@ -25,317 +18,119 @@ const fooCharge = Method.from({
25
18
  },
26
19
  })
27
20
 
28
- describe('Mppx', () => {
29
- test('has methods and realm properties', () => {
30
- const method = Method.toServer(fooCharge, {
31
- async verify() {
32
- return {
33
- method: 'test',
34
- reference: '0x123',
35
- status: 'success' as const,
36
- timestamp: new Date().toISOString(),
37
- }
38
- },
39
- })
40
-
41
- const handler = Mppx.create({
42
- methods: [method],
43
- realm: 'api.example.com',
44
- secretKey: 'secret',
45
- })
46
-
47
- expectTypeOf(handler.methods).toEqualTypeOf([method] as const)
48
- expectTypeOf(handler.realm).toBeString()
49
- })
50
-
51
- test('has method functions matching methods', () => {
52
- const method = Method.toServer(fooCharge, {
53
- async verify() {
54
- return {
55
- method: 'test',
56
- reference: '0x123',
57
- status: 'success' as const,
58
- timestamp: new Date().toISOString(),
59
- }
60
- },
61
- })
21
+ const mockChargeB = Method.from({
22
+ name: 'beta',
23
+ intent: 'charge',
24
+ schema: {
25
+ credential: {
26
+ payload: z.object({ token: z.string() }),
27
+ },
28
+ request: z.object({
29
+ amount: z.string(),
30
+ currency: z.string(),
31
+ decimals: z.number(),
32
+ recipient: z.string(),
33
+ }),
34
+ },
35
+ })
62
36
 
63
- const handler = Mppx.create({
64
- methods: [method],
65
- realm: 'api.example.com',
66
- secretKey: 'secret',
67
- })
37
+ const alphaMethod = Method.toServer(mockChargeA, {
38
+ async verify() {
39
+ return {
40
+ method: 'alpha',
41
+ reference: 'tx',
42
+ status: 'success' as const,
43
+ timestamp: new Date().toISOString(),
44
+ }
45
+ },
46
+ })
68
47
 
69
- expectTypeOf(handler.charge).toBeFunction()
70
- })
48
+ const betaMethod = Method.toServer(mockChargeB, {
49
+ async verify() {
50
+ return {
51
+ method: 'beta',
52
+ reference: 'tx',
53
+ status: 'success' as const,
54
+ timestamp: new Date().toISOString(),
55
+ }
56
+ },
57
+ })
71
58
 
72
- test('method function options include request', () => {
73
- const method = Method.toServer(fooCharge, {
74
- async verify() {
75
- return {
76
- method: 'test',
77
- reference: '0x123',
78
- status: 'success' as const,
79
- timestamp: new Date().toISOString(),
80
- }
81
- },
82
- })
59
+ const secretKey = 'test-secret'
60
+ const realm = 'api.example.com'
83
61
 
84
- const handler = Mppx.create({
85
- methods: [method],
86
- realm: 'api.example.com',
87
- secretKey: 'secret',
88
- })
62
+ describe('Mppx type tests', () => {
63
+ test('compose exists on the instance and returns a handler', () => {
64
+ const mppx = Mppx.create({ methods: [alphaMethod, betaMethod], realm, secretKey })
89
65
 
90
- handler.charge({
91
- amount: '1000',
92
- currency: '0x1234',
93
- decimals: 6,
94
- expires: '2025-01-01T00:00:00Z',
95
- recipient: '0xabc',
96
- })
66
+ expectTypeOf(mppx.compose).toBeFunction()
97
67
  })
98
68
 
99
- test('method function returns handler that accepts Request', async () => {
100
- const method = Method.toServer(fooCharge, {
101
- async verify() {
102
- return {
103
- method: 'test',
104
- reference: '0x123',
105
- status: 'success' as const,
106
- timestamp: new Date().toISOString(),
107
- }
108
- },
109
- })
110
-
111
- const handler = Mppx.create({
112
- methods: [method],
113
- realm: 'api.example.com',
114
- secretKey: 'secret',
115
- })
69
+ test('compose accepts method reference tuples', () => {
70
+ const mppx = Mppx.create({ methods: [alphaMethod, betaMethod], realm, secretKey })
116
71
 
117
- const chargeHandler = handler.charge({
118
- amount: '1000',
119
- currency: '0x1234',
72
+ const opts = {
73
+ amount: '100',
74
+ currency: '0x01',
120
75
  decimals: 6,
121
- expires: '2025-01-01T00:00:00Z',
122
- recipient: '0xabc',
123
- })
124
-
125
- const result = await chargeHandler(new Request('https://example.com'))
126
-
127
- if (result.status === 402) {
128
- expectTypeOf(result.challenge).toEqualTypeOf<Response>()
129
- } else {
130
- expectTypeOf(result.withReceipt).toBeFunction()
76
+ recipient: '0x02',
131
77
  }
132
- })
133
-
134
- test('multiple methods', () => {
135
- const fooAuthorize = Method.from({
136
- name: 'test',
137
- intent: 'authorize',
138
- schema: {
139
- credential: {
140
- payload: z.object({ token: z.string() }),
141
- },
142
- request: z.object({
143
- scope: z.string(),
144
- duration: z.number(),
145
- }),
146
- },
147
- })
148
78
 
149
- const chargeMethod = Method.toServer(fooCharge, {
150
- defaults: {
151
- currency: '0x1234',
152
- recipient: '0xabc',
153
- },
154
- async verify() {
155
- return {
156
- method: 'test',
157
- reference: '0x123',
158
- status: 'success' as const,
159
- timestamp: new Date().toISOString(),
160
- }
161
- },
162
- })
163
-
164
- const authorizeMethod = Method.toServer(fooAuthorize, {
165
- async verify() {
166
- return {
167
- method: 'test',
168
- reference: '0x456',
169
- status: 'success' as const,
170
- timestamp: new Date().toISOString(),
171
- }
172
- },
173
- })
79
+ // Should compile method reference entries
80
+ const handler = mppx.compose([alphaMethod, opts], [betaMethod, opts])
81
+ expectTypeOf(handler).toBeFunction()
82
+ })
174
83
 
175
- const handler = Mppx.create({
176
- methods: [chargeMethod, authorizeMethod],
177
- realm: 'api.example.com',
178
- secretKey: 'secret',
179
- })
84
+ test('compose accepts string key tuples', () => {
85
+ const mppx = Mppx.create({ methods: [alphaMethod, betaMethod], realm, secretKey })
180
86
 
181
- handler.charge({
182
- amount: '1000',
183
- currency: '0x1234',
87
+ const opts = {
88
+ amount: '100',
89
+ currency: '0x01',
184
90
  decimals: 6,
185
- recipient: '0xabc',
186
- })
91
+ recipient: '0x02',
92
+ }
187
93
 
188
- handler.authorize({
189
- scope: 'read',
190
- duration: 3600,
191
- })
94
+ // Should compile — string key entries
95
+ const handler = mppx.compose(['alpha/charge', opts], ['beta/charge', opts])
96
+ expectTypeOf(handler).toBeFunction()
192
97
  })
193
98
 
194
- describe('defaults', () => {
195
- test('defaulted fields are optional in method options', () => {
196
- const handler = Mppx.create({
197
- methods: [tempo({ currency: '0x1234', recipient: '0xabc', getClient })],
198
- realm: 'api.example.com',
199
- secretKey: 'secret',
200
- })
201
-
202
- // currency and recipient should be optional since they're in defaults
203
- handler.charge({
204
- amount: '1000',
205
- decimals: 6,
206
- })
207
-
208
- // But can still be overridden
209
- handler.charge({
210
- amount: '1000',
211
- currency: '0x5678',
212
- decimals: 6,
213
- recipient: '0xdef',
214
- })
215
- })
216
-
217
- test('non-defaulted fields remain required', () => {
218
- const handler = Mppx.create({
219
- methods: [tempo({ currency: '0x1234', getClient })],
220
- realm: 'api.example.com',
221
- secretKey: 'secret',
222
- })
223
-
224
- // recipient is still required since it's not in defaults
225
- handler.charge({
226
- amount: '1000',
227
- decimals: 6,
228
- recipient: '0xabc',
229
- })
230
- })
231
-
232
- test('no defaults means all fields required', () => {
233
- const handler = Mppx.create({
234
- methods: [tempo({ getClient })],
235
- realm: 'api.example.com',
236
- secretKey: 'secret',
237
- })
238
-
239
- // All required fields must be provided
240
- handler.charge({
241
- amount: '1000',
242
- currency: '0x1234',
243
- decimals: 6,
244
- recipient: '0xabc',
245
- })
246
- })
99
+ test('nested handlers are accessible', () => {
100
+ const mppx = Mppx.create({ methods: [alphaMethod, betaMethod], realm, secretKey })
247
101
 
248
- test('type: defaulted fields are optional in options type', () => {
249
- const handler = Mppx.create({
250
- methods: [tempo({ currency: '0x1234', recipient: '0xabc', getClient })],
251
- realm: 'api.example.com',
252
- secretKey: 'secret',
253
- })
254
-
255
- type ChargeOptions = Parameters<typeof handler.charge>[0]
256
-
257
- // currency and recipient should be optional (include undefined)
258
- expectTypeOf<ChargeOptions['currency']>().toEqualTypeOf<string | undefined>()
259
- expectTypeOf<ChargeOptions['recipient']>().toEqualTypeOf<string | undefined>()
260
-
261
- // amount should still be required (no undefined)
262
- expectTypeOf<ChargeOptions['amount']>().toEqualTypeOf<string>()
263
- })
264
-
265
- test('account as Account defaults recipient', () => {
266
- const handler = Mppx.create({
267
- methods: [tempo.charge({ currency: '0x1234', account })],
268
- realm: 'api.example.com',
269
- secretKey: 'secret',
270
- })
271
-
272
- // recipient is defaulted via account, so it should be optional
273
- handler.charge({
274
- amount: '1000',
275
- decimals: 6,
276
- })
277
-
278
- // can still override recipient
279
- handler.charge({
280
- amount: '1000',
281
- decimals: 6,
282
- recipient: '0xdef',
283
- })
284
- })
102
+ expectTypeOf(mppx.alpha).toBeObject()
103
+ expectTypeOf(mppx.alpha.charge).toBeFunction()
104
+ expectTypeOf(mppx.beta).toBeObject()
105
+ expectTypeOf(mppx.beta.charge).toBeFunction()
106
+ })
285
107
 
286
- test('account as Account with feePayer: true', () => {
287
- const handler = Mppx.create({
288
- methods: [tempo.charge({ currency: '0x1234', account, feePayer: true })],
289
- realm: 'api.example.com',
290
- secretKey: 'secret',
291
- })
108
+ test('slash key handlers are accessible', () => {
109
+ const mppx = Mppx.create({ methods: [alphaMethod, betaMethod], realm, secretKey })
292
110
 
293
- handler.charge({
294
- amount: '1000',
295
- decimals: 6,
296
- })
297
- })
111
+ expectTypeOf(mppx['alpha/charge']).toBeFunction()
112
+ expectTypeOf(mppx['beta/charge']).toBeFunction()
298
113
  })
299
114
 
300
- describe('session getClient', () => {
301
- test('tempo.session requires getClient', () => {
302
- Mppx.create({
303
- methods: [
304
- tempo.session({
305
- currency: '0x1234',
306
- recipient: '0xabc',
307
- getClient,
308
- }),
309
- ],
310
- realm: 'api.example.com',
311
- secretKey: 'secret',
312
- })
313
- })
115
+ test('compose return type is a request handler returning the response union', () => {
116
+ const mppx = Mppx.create({ methods: [alphaMethod], realm, secretKey })
117
+
118
+ const opts = {
119
+ amount: '100',
120
+ currency: '0x01',
121
+ decimals: 6,
122
+ recipient: '0x02',
123
+ }
314
124
 
315
- test('combined tempo() requires getClient', () => {
316
- Mppx.create({
317
- methods: [tempo({ currency: '0x1234', recipient: '0xabc', getClient })],
318
- realm: 'api.example.com',
319
- secretKey: 'secret',
320
- })
321
- })
125
+ const handler = mppx.compose([alphaMethod, opts])
126
+ type HandlerReturn = ReturnType<typeof handler>
322
127
 
323
- test('tempo.charge does not require getClient', () => {
324
- Mppx.create({
325
- methods: [tempo.charge({ currency: '0x1234', recipient: '0xabc' })],
326
- realm: 'api.example.com',
327
- secretKey: 'secret',
328
- })
329
- })
128
+ assertType<Promise<{ status: 402; challenge: Response } | { status: 200; withReceipt: any }>>(
129
+ {} as Awaited<HandlerReturn> as any,
130
+ )
330
131
  })
331
- })
332
-
333
- describe('create.Config', () => {
334
- test('requires methods, realm, and secretKey', () => {
335
- type Config = Mppx.create.Config
336
132
 
337
- expectTypeOf<Config>().toHaveProperty('methods')
338
- expectTypeOf<Config>().toHaveProperty('realm')
339
- expectTypeOf<Config>().toHaveProperty('secretKey')
133
+ test('static Mppx.compose accepts configured handlers', () => {
134
+ expectTypeOf(Mppx.compose).toBeFunction()
340
135
  })
341
136
  })