mppx 0.1.1 → 0.2.1

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 (221) hide show
  1. package/dist/Challenge.d.ts +18 -18
  2. package/dist/Challenge.d.ts.map +1 -1
  3. package/dist/Challenge.js +8 -8
  4. package/dist/Challenge.js.map +1 -1
  5. package/dist/Errors.d.ts +58 -8
  6. package/dist/Errors.d.ts.map +1 -1
  7. package/dist/Errors.js +51 -9
  8. package/dist/Errors.js.map +1 -1
  9. package/dist/Method.d.ts +154 -0
  10. package/dist/Method.d.ts.map +1 -0
  11. package/dist/Method.js +81 -0
  12. package/dist/Method.js.map +1 -0
  13. package/dist/PaymentRequest.d.ts +5 -5
  14. package/dist/PaymentRequest.d.ts.map +1 -1
  15. package/dist/PaymentRequest.js +11 -6
  16. package/dist/PaymentRequest.js.map +1 -1
  17. package/dist/cli.js +67 -18
  18. package/dist/cli.js.map +1 -1
  19. package/dist/client/Methods.d.ts +2 -2
  20. package/dist/client/Methods.d.ts.map +1 -1
  21. package/dist/client/Methods.js +2 -2
  22. package/dist/client/Methods.js.map +1 -1
  23. package/dist/client/Mppx.d.ts +7 -7
  24. package/dist/client/Mppx.d.ts.map +1 -1
  25. package/dist/client/Mppx.js +3 -3
  26. package/dist/client/Mppx.js.map +1 -1
  27. package/dist/client/internal/Fetch.d.ts +10 -10
  28. package/dist/client/internal/Fetch.d.ts.map +1 -1
  29. package/dist/client/internal/Fetch.js +2 -2
  30. package/dist/client/internal/Fetch.js.map +1 -1
  31. package/dist/index.d.ts +1 -2
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +1 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/mcp-sdk/client/McpClient.d.ts +6 -6
  36. package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -1
  37. package/dist/mcp-sdk/client/McpClient.js +4 -4
  38. package/dist/mcp-sdk/client/McpClient.js.map +1 -1
  39. package/dist/middlewares/elysia.d.ts +1 -1
  40. package/dist/middlewares/express.d.ts +1 -1
  41. package/dist/middlewares/hono.d.ts +1 -1
  42. package/dist/middlewares/internal/mppx.d.ts +7 -7
  43. package/dist/middlewares/internal/mppx.d.ts.map +1 -1
  44. package/dist/middlewares/internal/mppx.js +5 -5
  45. package/dist/middlewares/internal/mppx.js.map +1 -1
  46. package/dist/middlewares/nextjs.d.ts +1 -1
  47. package/dist/proxy/Service.js +2 -2
  48. package/dist/proxy/Service.js.map +1 -1
  49. package/dist/server/Methods.d.ts +2 -2
  50. package/dist/server/Methods.d.ts.map +1 -1
  51. package/dist/server/Methods.js +2 -2
  52. package/dist/server/Methods.js.map +1 -1
  53. package/dist/server/Mppx.d.ts +17 -17
  54. package/dist/server/Mppx.d.ts.map +1 -1
  55. package/dist/server/Mppx.js +9 -9
  56. package/dist/server/Mppx.js.map +1 -1
  57. package/dist/stripe/{Intents.d.ts → Methods.d.ts} +22 -22
  58. package/dist/stripe/Methods.d.ts.map +1 -0
  59. package/dist/stripe/Methods.js +42 -0
  60. package/dist/stripe/Methods.js.map +1 -0
  61. package/dist/stripe/client/Charge.d.ts +40 -27
  62. package/dist/stripe/client/Charge.d.ts.map +1 -1
  63. package/dist/stripe/client/Charge.js +15 -7
  64. package/dist/stripe/client/Charge.js.map +1 -1
  65. package/dist/stripe/client/{MethodIntents.d.ts → Methods.d.ts} +24 -23
  66. package/dist/stripe/client/Methods.d.ts.map +1 -0
  67. package/dist/stripe/client/{MethodIntents.js → Methods.js} +3 -3
  68. package/dist/stripe/client/Methods.js.map +1 -0
  69. package/dist/stripe/client/index.d.ts +1 -1
  70. package/dist/stripe/client/index.d.ts.map +1 -1
  71. package/dist/stripe/client/index.js +1 -1
  72. package/dist/stripe/client/index.js.map +1 -1
  73. package/dist/stripe/index.d.ts +1 -1
  74. package/dist/stripe/index.d.ts.map +1 -1
  75. package/dist/stripe/index.js +1 -1
  76. package/dist/stripe/index.js.map +1 -1
  77. package/dist/stripe/internal/types.d.ts +25 -0
  78. package/dist/stripe/internal/types.d.ts.map +1 -0
  79. package/dist/stripe/internal/types.js +2 -0
  80. package/dist/stripe/internal/types.js.map +1 -0
  81. package/dist/stripe/server/Charge.d.ts +47 -28
  82. package/dist/stripe/server/Charge.d.ts.map +1 -1
  83. package/dist/stripe/server/Charge.js +90 -32
  84. package/dist/stripe/server/Charge.js.map +1 -1
  85. package/dist/stripe/server/{MethodIntents.d.ts → Methods.d.ts} +24 -23
  86. package/dist/stripe/server/Methods.d.ts.map +1 -0
  87. package/dist/stripe/server/{MethodIntents.js → Methods.js} +3 -3
  88. package/dist/stripe/server/Methods.js.map +1 -0
  89. package/dist/stripe/server/index.d.ts +1 -1
  90. package/dist/stripe/server/index.d.ts.map +1 -1
  91. package/dist/stripe/server/index.js +1 -1
  92. package/dist/stripe/server/index.js.map +1 -1
  93. package/dist/tempo/{Intents.d.ts → Methods.d.ts} +72 -69
  94. package/dist/tempo/Methods.d.ts.map +1 -0
  95. package/dist/tempo/Methods.js +118 -0
  96. package/dist/tempo/Methods.js.map +1 -0
  97. package/dist/tempo/client/ChannelOps.d.ts +1 -1
  98. package/dist/tempo/client/ChannelOps.js +1 -1
  99. package/dist/tempo/client/Charge.d.ts +25 -25
  100. package/dist/tempo/client/Charge.d.ts.map +1 -1
  101. package/dist/tempo/client/Charge.js +3 -3
  102. package/dist/tempo/client/Charge.js.map +1 -1
  103. package/dist/tempo/client/{MethodIntents.d.ts → Methods.d.ts} +74 -70
  104. package/dist/tempo/client/Methods.d.ts.map +1 -0
  105. package/dist/tempo/client/{MethodIntents.js → Methods.js} +3 -3
  106. package/dist/tempo/client/Methods.js.map +1 -0
  107. package/dist/tempo/client/Session.d.ts +49 -45
  108. package/dist/tempo/client/Session.d.ts.map +1 -1
  109. package/dist/tempo/client/Session.js +4 -4
  110. package/dist/tempo/client/Session.js.map +1 -1
  111. package/dist/tempo/client/SessionManager.d.ts +1 -1
  112. package/dist/tempo/client/SessionManager.js +1 -1
  113. package/dist/tempo/client/index.d.ts +1 -1
  114. package/dist/tempo/client/index.d.ts.map +1 -1
  115. package/dist/tempo/client/index.js +1 -1
  116. package/dist/tempo/client/index.js.map +1 -1
  117. package/dist/tempo/index.d.ts +1 -1
  118. package/dist/tempo/index.d.ts.map +1 -1
  119. package/dist/tempo/index.js +1 -1
  120. package/dist/tempo/index.js.map +1 -1
  121. package/dist/tempo/server/Charge.d.ts +27 -27
  122. package/dist/tempo/server/Charge.d.ts.map +1 -1
  123. package/dist/tempo/server/Charge.js +3 -3
  124. package/dist/tempo/server/Charge.js.map +1 -1
  125. package/dist/tempo/server/{MethodIntents.d.ts → Methods.d.ts} +73 -69
  126. package/dist/tempo/server/Methods.d.ts.map +1 -0
  127. package/dist/tempo/server/{MethodIntents.js → Methods.js} +4 -4
  128. package/dist/tempo/server/Methods.js.map +1 -0
  129. package/dist/tempo/server/Session.d.ts +51 -47
  130. package/dist/tempo/server/Session.d.ts.map +1 -1
  131. package/dist/tempo/server/Session.js +4 -4
  132. package/dist/tempo/server/Session.js.map +1 -1
  133. package/dist/tempo/server/index.d.ts +6 -0
  134. package/dist/tempo/server/index.d.ts.map +1 -0
  135. package/dist/tempo/server/index.js +6 -0
  136. package/dist/tempo/server/index.js.map +1 -0
  137. package/package.json +2 -1
  138. package/src/Challenge.test-d.ts +3 -3
  139. package/src/Challenge.test.ts +7 -7
  140. package/src/Challenge.ts +34 -34
  141. package/src/Errors.test.ts +75 -21
  142. package/src/Errors.ts +74 -9
  143. package/src/Method.test.ts +76 -0
  144. package/src/Method.ts +228 -0
  145. package/src/PaymentRequest.test.ts +5 -5
  146. package/src/PaymentRequest.ts +15 -10
  147. package/src/cli.test.ts +12 -22
  148. package/src/cli.ts +74 -21
  149. package/src/client/Methods.ts +2 -2
  150. package/src/client/Mppx.test-d.ts +6 -6
  151. package/src/client/Mppx.test.ts +26 -22
  152. package/src/client/Mppx.ts +10 -10
  153. package/src/client/Transport.test.ts +6 -6
  154. package/src/client/internal/Fetch.ts +21 -24
  155. package/src/index.ts +1 -2
  156. package/src/mcp-sdk/client/McpClient.test.ts +1 -1
  157. package/src/mcp-sdk/client/McpClient.ts +11 -13
  158. package/src/middlewares/elysia.ts +1 -1
  159. package/src/middlewares/express.ts +1 -1
  160. package/src/middlewares/hono.ts +1 -1
  161. package/src/middlewares/internal/mppx.ts +10 -10
  162. package/src/middlewares/nextjs.ts +1 -1
  163. package/src/proxy/Service.ts +2 -2
  164. package/src/server/Methods.ts +2 -2
  165. package/src/server/Mppx.test-d.ts +27 -29
  166. package/src/server/Mppx.test.ts +23 -19
  167. package/src/server/Mppx.ts +43 -43
  168. package/src/server/Transport.test.ts +8 -8
  169. package/src/stripe/{Intents.test.ts → Methods.test.ts} +12 -12
  170. package/src/stripe/Methods.ts +45 -0
  171. package/src/stripe/client/Charge.test.ts +189 -0
  172. package/src/stripe/client/Charge.ts +29 -16
  173. package/src/stripe/client/{MethodIntents.ts → Methods.ts} +2 -2
  174. package/src/stripe/client/index.ts +1 -1
  175. package/src/stripe/index.ts +1 -1
  176. package/src/stripe/internal/types.ts +22 -0
  177. package/src/stripe/server/Charge.test.ts +241 -0
  178. package/src/stripe/server/Charge.ts +124 -38
  179. package/src/stripe/server/{MethodIntents.ts → Methods.ts} +2 -2
  180. package/src/stripe/server/index.ts +1 -1
  181. package/src/tempo/{Intents.test.ts → Methods.test.ts} +15 -15
  182. package/src/tempo/{Intents.ts → Methods.ts} +77 -22
  183. package/src/tempo/client/ChannelOps.ts +1 -1
  184. package/src/tempo/client/Charge.ts +3 -3
  185. package/src/tempo/client/{MethodIntents.ts → Methods.ts} +2 -2
  186. package/src/tempo/client/Session.ts +4 -4
  187. package/src/tempo/client/SessionManager.ts +1 -1
  188. package/src/tempo/client/index.ts +1 -1
  189. package/src/tempo/index.ts +1 -1
  190. package/src/tempo/server/Charge.ts +4 -7
  191. package/src/tempo/server/{MethodIntents.ts → Methods.ts} +3 -3
  192. package/src/tempo/server/Session.test.ts +4 -7
  193. package/src/tempo/server/Session.ts +6 -6
  194. package/src/tempo/server/index.ts +1 -1
  195. package/dist/Intent.d.ts +0 -101
  196. package/dist/Intent.d.ts.map +0 -1
  197. package/dist/Intent.js +0 -83
  198. package/dist/Intent.js.map +0 -1
  199. package/dist/MethodIntent.d.ts +0 -225
  200. package/dist/MethodIntent.d.ts.map +0 -1
  201. package/dist/MethodIntent.js +0 -156
  202. package/dist/MethodIntent.js.map +0 -1
  203. package/dist/stripe/Intents.d.ts.map +0 -1
  204. package/dist/stripe/Intents.js +0 -27
  205. package/dist/stripe/Intents.js.map +0 -1
  206. package/dist/stripe/client/MethodIntents.d.ts.map +0 -1
  207. package/dist/stripe/client/MethodIntents.js.map +0 -1
  208. package/dist/stripe/server/MethodIntents.d.ts.map +0 -1
  209. package/dist/stripe/server/MethodIntents.js.map +0 -1
  210. package/dist/tempo/Intents.d.ts.map +0 -1
  211. package/dist/tempo/Intents.js +0 -81
  212. package/dist/tempo/Intents.js.map +0 -1
  213. package/dist/tempo/client/MethodIntents.d.ts.map +0 -1
  214. package/dist/tempo/client/MethodIntents.js.map +0 -1
  215. package/dist/tempo/server/MethodIntents.d.ts.map +0 -1
  216. package/dist/tempo/server/MethodIntents.js.map +0 -1
  217. package/src/Intent.test.ts +0 -180
  218. package/src/Intent.ts +0 -109
  219. package/src/MethodIntent.test.ts +0 -303
  220. package/src/MethodIntent.ts +0 -388
  221. package/src/stripe/Intents.ts +0 -27
@@ -1,180 +0,0 @@
1
- import { Intent } from 'mppx'
2
- import { describe, expect, test } from 'vitest'
3
-
4
- describe('charge', () => {
5
- test('behavior: validates valid request', () => {
6
- const result = Intent.charge.schema.request.parse({
7
- amount: '1000',
8
- currency: 'USD',
9
- decimals: 6,
10
- })
11
- expect(result.amount).toBe('1000000000')
12
- expect(result.currency).toBe('USD')
13
- expect(result.expires).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)
14
- })
15
-
16
- test('behavior: validates with all optional fields', () => {
17
- expect(
18
- Intent.charge.schema.request.parse({
19
- amount: '1000',
20
- currency: 'USD',
21
- decimals: 6,
22
- description: 'Test payment',
23
- expires: '2030-01-01T00:00:00Z',
24
- externalId: 'order-123',
25
- recipient: '0x1234567890abcdef',
26
- }),
27
- ).toMatchInlineSnapshot(`
28
- {
29
- "amount": "1000000000",
30
- "currency": "USD",
31
- "description": "Test payment",
32
- "expires": "2030-01-01T00:00:00Z",
33
- "externalId": "order-123",
34
- "recipient": "0x1234567890abcdef",
35
- }
36
- `)
37
- })
38
-
39
- test('error: invalid amount (non-numeric)', () => {
40
- expect(() =>
41
- Intent.charge.schema.request.parse({
42
- amount: 'abc',
43
- currency: 'USD',
44
- }),
45
- ).toThrowErrorMatchingInlineSnapshot(`
46
- [$ZodError: [
47
- {
48
- "origin": "string",
49
- "code": "invalid_format",
50
- "format": "regex",
51
- "pattern": "/^\\\\d+(\\\\.\\\\d+)?$/",
52
- "path": [
53
- "amount"
54
- ],
55
- "message": "Invalid amount"
56
- },
57
- {
58
- "expected": "number",
59
- "code": "invalid_type",
60
- "path": [
61
- "decimals"
62
- ],
63
- "message": "Invalid input"
64
- }
65
- ]]
66
- `)
67
- })
68
-
69
- test('error: invalid amount (decimal)', () => {
70
- expect(() =>
71
- Intent.charge.schema.request.parse({
72
- amount: '100.50',
73
- currency: 'USD',
74
- }),
75
- ).toThrowErrorMatchingInlineSnapshot(`
76
- [$ZodError: [
77
- {
78
- "expected": "number",
79
- "code": "invalid_type",
80
- "path": [
81
- "decimals"
82
- ],
83
- "message": "Invalid input"
84
- }
85
- ]]
86
- `)
87
- })
88
-
89
- test('error: invalid expires (not ISO 8601)', () => {
90
- expect(() =>
91
- Intent.charge.schema.request.parse({
92
- amount: '1000',
93
- currency: 'USD',
94
- expires: 'not-a-date',
95
- }),
96
- ).toThrowErrorMatchingInlineSnapshot(`
97
- [$ZodError: [
98
- {
99
- "expected": "number",
100
- "code": "invalid_type",
101
- "path": [
102
- "decimals"
103
- ],
104
- "message": "Invalid input"
105
- },
106
- {
107
- "origin": "string",
108
- "code": "invalid_format",
109
- "format": "regex",
110
- "pattern": "/^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d+)?(?:Z|[+-]\\\\d{2}:\\\\d{2})$/",
111
- "path": [
112
- "expires"
113
- ],
114
- "message": "Invalid ISO 8601 datetime"
115
- }
116
- ]]
117
- `)
118
- })
119
- })
120
-
121
- describe('session', () => {
122
- test('behavior: converts amount to base units', () => {
123
- const result = Intent.session.schema.request.parse({
124
- amount: '0.000025',
125
- unitType: 'llm_token',
126
- currency: '0x20c0000000000000000000000000000000000001',
127
- decimals: 6,
128
- recipient: '0x1234567890abcdef1234567890abcdef12345678',
129
- })
130
- expect(result.amount).toBe('25')
131
- expect(result.unitType).toBe('llm_token')
132
- expect(result.currency).toBe('0x20c0000000000000000000000000000000000001')
133
- expect(result).not.toHaveProperty('decimals')
134
- })
135
-
136
- test('behavior: converts suggestedDeposit to base units', () => {
137
- const result = Intent.session.schema.request.parse({
138
- amount: '0.000025',
139
- unitType: 'llm_token',
140
- currency: '0x20c0000000000000000000000000000000000001',
141
- decimals: 6,
142
- recipient: '0x1234567890abcdef1234567890abcdef12345678',
143
- suggestedDeposit: '10',
144
- })
145
- expect(result.amount).toBe('25')
146
- expect(result.suggestedDeposit).toBe('10000000')
147
- })
148
-
149
- test('behavior: works without suggestedDeposit', () => {
150
- const result = Intent.session.schema.request.parse({
151
- amount: '1',
152
- unitType: 'request',
153
- currency: '0x20c0000000000000000000000000000000000001',
154
- decimals: 6,
155
- })
156
- expect(result.amount).toBe('1000000')
157
- expect(result).not.toHaveProperty('suggestedDeposit')
158
- })
159
-
160
- test('error: rejects missing decimals', () => {
161
- expect(() =>
162
- Intent.session.schema.request.parse({
163
- amount: '25',
164
- unitType: 'llm_token',
165
- currency: '0x20c0000000000000000000000000000000000001',
166
- }),
167
- ).toThrow()
168
- })
169
-
170
- test('error: rejects invalid amount', () => {
171
- expect(() =>
172
- Intent.session.schema.request.parse({
173
- amount: 'abc',
174
- unitType: 'llm_token',
175
- currency: '0x20c0000000000000000000000000000000000001',
176
- decimals: 6,
177
- }),
178
- ).toThrow()
179
- })
180
- })
package/src/Intent.ts DELETED
@@ -1,109 +0,0 @@
1
- import { parseUnits } from 'viem'
2
- import * as Expires from './Expires.js'
3
- import * as z from './zod.js'
4
-
5
- /**
6
- * A payment intent.
7
- */
8
- export type Intent = {
9
- name: string
10
- schema: {
11
- request: z.z.ZodMiniObject | z.z.ZodMiniPipe<z.z.ZodMiniObject>
12
- }
13
- }
14
-
15
- /**
16
- * Creates an intent.
17
- *
18
- * @example
19
- * ```ts
20
- * import { z } from 'mppx'
21
- *
22
- * const charge = Intent.from({
23
- * name: 'charge',
24
- * schema: {
25
- * request: z.object({
26
- * amount: z.string(),
27
- * currency: z.string(),
28
- * recipient: z.optional(z.string()),
29
- * }),
30
- * },
31
- * })
32
- * ```
33
- */
34
- export function from<const intent extends Intent>(intent: intent): intent {
35
- return intent
36
- }
37
-
38
- /**
39
- * Charge intent for one-time immediate payments.
40
- *
41
- * @see https://github.com/tempoxyz/payment-auth-spec/blob/main/specs/intents/draft-payment-intent-charge-00.md
42
- */
43
- export const charge = from({
44
- name: 'charge',
45
- schema: {
46
- request: z.pipe(
47
- z.object({
48
- amount: z.amount(),
49
- currency: z.string(),
50
- decimals: z.number(),
51
- description: z.optional(z.string()),
52
- expires: z._default(z.datetime(), () => Expires.minutes(5)),
53
- externalId: z.optional(z.string()),
54
- recipient: z.optional(z.string()),
55
- }),
56
- // Note: Since the spec states we must have `amount` as a base unit, we
57
- // will transform the amount to decimal units based on `decimals`.
58
- z.transform(({ amount, decimals, ...rest }) => ({
59
- ...rest,
60
- amount: parseUnits(amount, decimals).toString(),
61
- })),
62
- ),
63
- },
64
- })
65
-
66
- /**
67
- * Session intent for pay-as-you-go streaming payments.
68
- *
69
- * Uses cumulative vouchers over a payment channel for
70
- * incremental micropayments without per-request transactions.
71
- */
72
- export const session = from({
73
- name: 'session',
74
- schema: {
75
- request: z.pipe(
76
- z.object({
77
- amount: z.amount(),
78
- unitType: z.string(),
79
- currency: z.string(),
80
- decimals: z.number(),
81
- recipient: z.optional(z.string()),
82
- suggestedDeposit: z.optional(z.amount()),
83
- }),
84
- z.transform(({ amount, decimals, suggestedDeposit, ...rest }) => ({
85
- ...rest,
86
- amount: parseUnits(amount, decimals).toString(),
87
- ...(suggestedDeposit
88
- ? { suggestedDeposit: parseUnits(suggestedDeposit, decimals).toString() }
89
- : {}),
90
- })),
91
- ),
92
- },
93
- })
94
-
95
- /** @internal Extracts shape from an intent's request schema, supporting both object and pipe. */
96
- export type ShapeOf<intent extends Intent> = intent['schema']['request'] extends z.z.ZodMiniObject
97
- ? intent['schema']['request']['shape']
98
- : intent['schema']['request'] extends z.z.ZodMiniPipe<infer A>
99
- ? A extends z.z.ZodMiniObject
100
- ? A['shape']
101
- : never
102
- : never
103
-
104
- /** @internal Extracts the inner object from a pipe or returns the schema directly. */
105
- export function shapeOf(intent: Intent): Record<string, z.z.ZodMiniType> {
106
- const { request } = intent.schema
107
- if ('shape' in request) return request.shape as Record<string, z.z.ZodMiniType>
108
- return (request as any)._zod.def.in.shape
109
- }
@@ -1,303 +0,0 @@
1
- import { Intent, MethodIntent, z } from 'mppx'
2
- import { describe, expect, expectTypeOf, test } from 'vitest'
3
-
4
- describe('from', () => {
5
- test('behavior: creates intent', () => {
6
- const intent = MethodIntent.from({
7
- method: 'tempo',
8
- name: 'charge',
9
- schema: {
10
- credential: {
11
- payload: z.object({
12
- signature: z.string(),
13
- }),
14
- },
15
- request: z.object({
16
- amount: z.string(),
17
- currency: z.string(),
18
- }),
19
- },
20
- })
21
-
22
- expect(intent.name).toBe('charge')
23
- expect(intent.method).toBe('tempo')
24
- expect(intent.schema.request).toBeDefined()
25
- expect(intent.schema.credential.payload).toBeDefined()
26
- })
27
-
28
- test('types: name literal is inferred', () => {
29
- const intent = MethodIntent.from({
30
- method: 'tempo',
31
- name: 'charge',
32
- schema: {
33
- credential: { payload: z.object({ sig: z.string() }) },
34
- request: z.object({ amount: z.string() }),
35
- },
36
- })
37
-
38
- expectTypeOf(intent.name).toEqualTypeOf<'charge'>()
39
- })
40
-
41
- test('types: method literal is inferred', () => {
42
- const intent = MethodIntent.from({
43
- method: 'tempo',
44
- name: 'charge',
45
- schema: {
46
- credential: { payload: z.object({ sig: z.string() }) },
47
- request: z.object({ amount: z.string() }),
48
- },
49
- })
50
-
51
- expectTypeOf(intent.method).toEqualTypeOf<'tempo'>()
52
- })
53
-
54
- test('types: schema types are preserved', () => {
55
- const requestSchema = z.object({
56
- amount: z.string(),
57
- currency: z.string(),
58
- })
59
- const payloadSchema = z.object({
60
- signature: z.string(),
61
- type: z.literal('transaction'),
62
- })
63
-
64
- const intent = MethodIntent.from({
65
- method: 'tempo',
66
- name: 'charge',
67
- schema: {
68
- credential: { payload: payloadSchema },
69
- request: requestSchema,
70
- },
71
- })
72
-
73
- expectTypeOf(intent.schema.request).toEqualTypeOf(requestSchema)
74
- expectTypeOf(intent.schema.credential.payload).toEqualTypeOf(payloadSchema)
75
- })
76
- })
77
-
78
- describe('fromIntent', () => {
79
- test('behavior: creates method intent from base intent', () => {
80
- const charge = Intent.from({
81
- name: 'charge',
82
- schema: {
83
- request: z.object({
84
- amount: z.string(),
85
- currency: z.string(),
86
- recipient: z.optional(z.string()),
87
- }),
88
- },
89
- })
90
-
91
- const tempoCharge = MethodIntent.fromIntent(charge, {
92
- method: 'tempo',
93
- schema: {
94
- credential: {
95
- payload: z.object({
96
- signature: z.string(),
97
- type: z.literal('transaction'),
98
- }),
99
- },
100
- request: {
101
- methodDetails: z.object({
102
- chainId: z.number(),
103
- }),
104
- requires: ['recipient'],
105
- },
106
- },
107
- })
108
-
109
- expect(tempoCharge.name).toBe('charge')
110
- expect(tempoCharge.method).toBe('tempo')
111
- expect(tempoCharge.schema.request).toBeDefined()
112
- expect(tempoCharge.schema.credential.payload).toBeDefined()
113
- })
114
-
115
- test('types: name and method literals are inferred', () => {
116
- const charge = Intent.from({
117
- name: 'charge',
118
- schema: {
119
- request: z.object({
120
- amount: z.string(),
121
- currency: z.string(),
122
- }),
123
- },
124
- })
125
-
126
- const tempoCharge = MethodIntent.fromIntent(charge, {
127
- method: 'tempo',
128
- schema: {
129
- credential: {
130
- payload: z.object({ signature: z.string() }),
131
- },
132
- request: {
133
- methodDetails: z.object({}),
134
- requires: [],
135
- },
136
- },
137
- })
138
-
139
- expectTypeOf(tempoCharge.name).toEqualTypeOf<'charge'>()
140
- expectTypeOf(tempoCharge.method).toEqualTypeOf<'tempo'>()
141
- })
142
-
143
- test('types: request output has methodDetails nested', () => {
144
- const charge = Intent.from({
145
- name: 'charge',
146
- schema: {
147
- request: z.object({
148
- amount: z.string(),
149
- currency: z.string(),
150
- recipient: z.optional(z.string()),
151
- }),
152
- },
153
- })
154
-
155
- const tempoCharge = MethodIntent.fromIntent(charge, {
156
- method: 'tempo',
157
- schema: {
158
- credential: { payload: z.object({}) },
159
- request: {
160
- methodDetails: z.object({
161
- chainId: z.number(),
162
- }),
163
- requires: [],
164
- },
165
- },
166
- })
167
-
168
- type Input = z.input<typeof tempoCharge.schema.request>
169
- expectTypeOf<Input>().toExtend<{
170
- amount: string
171
- chainId: number
172
- currency: string
173
- }>()
174
-
175
- type Output = z.output<typeof tempoCharge.schema.request>
176
- expectTypeOf<Output>().toExtend<{
177
- amount: string
178
- currency: string
179
- methodDetails?: { chainId: number }
180
- }>()
181
- })
182
-
183
- test('types: requires makes optional fields required in output type', () => {
184
- const charge = Intent.from({
185
- name: 'charge',
186
- schema: {
187
- request: z.object({
188
- amount: z.string(),
189
- recipient: z.optional(z.string()),
190
- }),
191
- },
192
- })
193
-
194
- const tempoCharge = MethodIntent.fromIntent(charge, {
195
- method: 'tempo',
196
- schema: {
197
- credential: { payload: z.object({}) },
198
- request: {
199
- methodDetails: z.object({}),
200
- requires: ['recipient'],
201
- },
202
- },
203
- })
204
-
205
- type Request = z.infer<typeof tempoCharge.schema.request>
206
- expectTypeOf<Request>().toMatchTypeOf<{ amount: string; recipient: string }>()
207
- })
208
-
209
- test('behavior: request schema includes base intent fields', () => {
210
- const charge = Intent.from({
211
- name: 'charge',
212
- schema: {
213
- request: z.object({
214
- amount: z.string(),
215
- currency: z.string(),
216
- }),
217
- },
218
- })
219
-
220
- const tempoCharge = MethodIntent.fromIntent(charge, {
221
- method: 'tempo',
222
- schema: {
223
- credential: { payload: z.object({}) },
224
- request: {
225
- methodDetails: z.object({}),
226
- requires: [],
227
- },
228
- },
229
- })
230
-
231
- const result = tempoCharge.schema.request.safeParse({
232
- amount: '100',
233
- currency: 'USD',
234
- })
235
- expect(result.success).toBe(true)
236
- })
237
-
238
- test('behavior: request schema includes methodDetails fields', () => {
239
- const charge = Intent.from({
240
- name: 'charge',
241
- schema: {
242
- request: z.object({
243
- amount: z.string(),
244
- }),
245
- },
246
- })
247
-
248
- const tempoCharge = MethodIntent.fromIntent(charge, {
249
- method: 'tempo',
250
- schema: {
251
- credential: { payload: z.object({}) },
252
- request: {
253
- methodDetails: z.object({
254
- chainId: z.number(),
255
- recipient: z.string(),
256
- }),
257
- requires: [],
258
- },
259
- },
260
- })
261
-
262
- const result = tempoCharge.schema.request.safeParse({
263
- amount: '100',
264
- chainId: 42431,
265
- recipient: '0x123',
266
- })
267
- expect(result.success).toBe(true)
268
- })
269
-
270
- test('behavior: requires makes optional fields required', () => {
271
- const charge = Intent.from({
272
- name: 'charge',
273
- schema: {
274
- request: z.object({
275
- amount: z.string(),
276
- recipient: z.optional(z.string()),
277
- }),
278
- },
279
- })
280
-
281
- const tempoCharge = MethodIntent.fromIntent(charge, {
282
- method: 'tempo',
283
- schema: {
284
- credential: { payload: z.object({}) },
285
- request: {
286
- methodDetails: z.object({}),
287
- requires: ['recipient'],
288
- },
289
- },
290
- })
291
-
292
- const withRecipient = tempoCharge.schema.request.safeParse({
293
- amount: '100',
294
- recipient: '0x123',
295
- })
296
- expect(withRecipient.success).toBe(true)
297
-
298
- const withoutRecipient = tempoCharge.schema.request.safeParse({
299
- amount: '100',
300
- })
301
- expect(withoutRecipient.success).toBe(false)
302
- })
303
- })