mppx 0.5.1 → 0.5.3
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 +13 -0
- package/dist/Credential.d.ts +12 -0
- package/dist/Credential.d.ts.map +1 -1
- package/dist/Credential.js +22 -4
- package/dist/Credential.js.map +1 -1
- package/dist/Method.d.ts +4 -0
- package/dist/Method.d.ts.map +1 -1
- package/dist/Method.js +2 -1
- package/dist/Method.js.map +1 -1
- package/dist/proxy/Proxy.d.ts.map +1 -1
- package/dist/proxy/Proxy.js +52 -8
- package/dist/proxy/Proxy.js.map +1 -1
- package/dist/proxy/internal/Route.d.ts.map +1 -1
- package/dist/proxy/internal/Route.js +7 -3
- package/dist/proxy/internal/Route.js.map +1 -1
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +90 -71
- package/dist/server/Mppx.js.map +1 -1
- package/dist/server/Transport.d.ts +5 -1
- package/dist/server/Transport.d.ts.map +1 -1
- package/dist/server/Transport.js +52 -7
- package/dist/server/Transport.js.map +1 -1
- package/dist/server/internal/html/config.d.ts +7 -0
- package/dist/server/internal/html/config.d.ts.map +1 -0
- package/dist/server/internal/html/config.js +3 -0
- package/dist/server/internal/html/config.js.map +1 -0
- package/dist/server/internal/html/serviceWorker.gen.d.ts +2 -0
- package/dist/server/internal/html/serviceWorker.gen.d.ts.map +1 -0
- package/dist/server/internal/html/serviceWorker.gen.js +3 -0
- package/dist/server/internal/html/serviceWorker.gen.js.map +1 -0
- package/dist/stripe/server/Charge.d.ts +5 -0
- package/dist/stripe/server/Charge.d.ts.map +1 -1
- package/dist/stripe/server/Charge.js +14 -6
- package/dist/stripe/server/Charge.js.map +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts +2 -0
- package/dist/stripe/server/internal/html.gen.d.ts.map +1 -0
- package/dist/stripe/server/internal/html.gen.js +3 -0
- package/dist/stripe/server/internal/html.gen.js.map +1 -0
- package/dist/tempo/server/Charge.d.ts +2 -0
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +15 -9
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Session.d.ts.map +1 -1
- package/dist/tempo/server/Session.js +3 -2
- package/dist/tempo/server/Session.js.map +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts +2 -0
- package/dist/tempo/server/internal/html.gen.d.ts.map +1 -0
- package/dist/tempo/server/internal/html.gen.js +3 -0
- package/dist/tempo/server/internal/html.gen.js.map +1 -0
- package/dist/tempo/server/internal/transport.d.ts +1 -1
- package/dist/tempo/server/internal/transport.d.ts.map +1 -1
- package/dist/tempo/server/internal/transport.js +45 -58
- package/dist/tempo/server/internal/transport.js.map +1 -1
- package/package.json +2 -2
- package/src/Credential.ts +28 -4
- package/src/Method.ts +6 -1
- package/src/env.d.ts +1 -0
- package/src/mcp-sdk/server/Transport.test.ts +6 -0
- package/src/proxy/Proxy.test.ts +188 -1
- package/src/proxy/Proxy.ts +58 -9
- package/src/proxy/internal/Route.test.ts +9 -0
- package/src/proxy/internal/Route.ts +5 -2
- package/src/server/Mppx.test.ts +171 -18
- package/src/server/Mppx.ts +120 -79
- package/src/server/Transport.test.ts +16 -2
- package/src/server/Transport.ts +61 -7
- package/src/server/internal/html/config.ts +8 -0
- package/src/server/internal/html/serviceWorker.client.ts +28 -0
- package/src/server/internal/html/serviceWorker.gen.ts +2 -0
- package/src/server/internal/html/serviceWorker.ts +27 -0
- package/src/server/internal/html/tsconfig.worker.client.json +8 -0
- package/src/server/internal/html/tsconfig.worker.json +8 -0
- package/src/stripe/server/Charge.ts +19 -5
- package/src/stripe/server/internal/html/main.ts +106 -0
- package/src/stripe/server/internal/html/node_modules/.bin/mppx.src +21 -0
- package/src/stripe/server/internal/html/package.json +9 -0
- package/src/stripe/server/internal/html/stripe-js-pure.d.ts +7 -0
- package/src/stripe/server/internal/html/tsconfig.json +8 -0
- package/src/stripe/server/internal/html.gen.ts +2 -0
- package/src/tempo/server/Charge.ts +20 -8
- package/src/tempo/server/Session.ts +3 -2
- package/src/tempo/server/internal/html/main.ts +71 -0
- package/src/tempo/server/internal/html/node_modules/.bin/mppx.src +21 -0
- package/src/tempo/server/internal/html/package.json +10 -0
- package/src/tempo/server/internal/html/tsconfig.json +8 -0
- package/src/tempo/server/internal/html.gen.ts +2 -0
- package/src/tempo/server/internal/transport.test.ts +37 -31
- package/src/tempo/server/internal/transport.ts +44 -58
- package/src/tsconfig.json +1 -1
|
@@ -113,19 +113,20 @@ describe('sse transport', () => {
|
|
|
113
113
|
expect((credential!.payload as any).channelId).toBe(channelId)
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
test('
|
|
116
|
+
test('respondReceipt derives SSE context from the verified credential', async () => {
|
|
117
117
|
const store = memoryStore()
|
|
118
118
|
await seedChannel(store, 10000000n)
|
|
119
119
|
const transport = sse({ store })
|
|
120
120
|
|
|
121
|
-
const
|
|
122
|
-
transport.getCredential(request)
|
|
121
|
+
const credential = makeCredential()
|
|
123
122
|
|
|
124
123
|
async function* gen() {
|
|
125
124
|
yield 'test'
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
const response = transport.respondReceipt({
|
|
128
|
+
credential,
|
|
129
|
+
input: new Request('https://test.example.com/session'),
|
|
129
130
|
receipt: makeReceipt(),
|
|
130
131
|
response: gen(),
|
|
131
132
|
challengeId,
|
|
@@ -151,8 +152,7 @@ describe('sse transport', () => {
|
|
|
151
152
|
const store = memoryStore()
|
|
152
153
|
await seedChannel(store, 10000000n)
|
|
153
154
|
const transport = sse({ store })
|
|
154
|
-
|
|
155
|
-
transport.getCredential(makeAuthorizedRequest())
|
|
155
|
+
const request = makeAuthorizedRequest()
|
|
156
156
|
|
|
157
157
|
async function* gen() {
|
|
158
158
|
yield 'hello'
|
|
@@ -160,6 +160,8 @@ describe('sse transport', () => {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
const response = transport.respondReceipt({
|
|
163
|
+
credential: makeCredential(),
|
|
164
|
+
input: request,
|
|
163
165
|
receipt: makeReceipt(),
|
|
164
166
|
response: gen(),
|
|
165
167
|
challengeId,
|
|
@@ -184,10 +186,11 @@ describe('sse transport', () => {
|
|
|
184
186
|
const store = memoryStore()
|
|
185
187
|
await seedChannel(store, 10000000n)
|
|
186
188
|
const transport = sse({ store })
|
|
187
|
-
|
|
188
|
-
transport.getCredential(makeAuthorizedRequest())
|
|
189
|
+
const request = makeAuthorizedRequest()
|
|
189
190
|
|
|
190
191
|
const response = transport.respondReceipt({
|
|
192
|
+
credential: makeCredential(),
|
|
193
|
+
input: request,
|
|
191
194
|
receipt: makeReceipt(),
|
|
192
195
|
response: async function* (stream) {
|
|
193
196
|
await stream.charge()
|
|
@@ -202,8 +205,7 @@ describe('sse transport', () => {
|
|
|
202
205
|
const store = memoryStore()
|
|
203
206
|
await seedChannel(store, 10000000n)
|
|
204
207
|
const transport = sse({ store })
|
|
205
|
-
|
|
206
|
-
transport.getCredential(makeAuthorizedRequest())
|
|
208
|
+
const request = makeAuthorizedRequest()
|
|
207
209
|
|
|
208
210
|
const encoder = new TextEncoder()
|
|
209
211
|
const upstream = new Response(
|
|
@@ -218,6 +220,8 @@ describe('sse transport', () => {
|
|
|
218
220
|
)
|
|
219
221
|
|
|
220
222
|
const response = transport.respondReceipt({
|
|
223
|
+
credential: makeCredential(),
|
|
224
|
+
input: request,
|
|
221
225
|
receipt: makeReceipt(),
|
|
222
226
|
response: upstream,
|
|
223
227
|
challengeId,
|
|
@@ -241,6 +245,8 @@ describe('sse transport', () => {
|
|
|
241
245
|
})
|
|
242
246
|
|
|
243
247
|
const response = transport.respondReceipt({
|
|
248
|
+
credential: makeCredential(),
|
|
249
|
+
input: new Request('https://test.example.com/session'),
|
|
244
250
|
receipt,
|
|
245
251
|
response: plainResponse,
|
|
246
252
|
challengeId,
|
|
@@ -249,34 +255,24 @@ describe('sse transport', () => {
|
|
|
249
255
|
expect(response.headers.get('Payment-Receipt')).toBeTruthy()
|
|
250
256
|
})
|
|
251
257
|
|
|
252
|
-
test('respondReceipt
|
|
258
|
+
test('respondReceipt no longer depends on prior getCredential side effects', async () => {
|
|
253
259
|
const store = memoryStore()
|
|
254
260
|
await seedChannel(store, 10000000n)
|
|
255
261
|
const transport = sse({ store })
|
|
256
|
-
|
|
257
|
-
transport.getCredential(makeAuthorizedRequest())
|
|
262
|
+
const request = makeAuthorizedRequest()
|
|
258
263
|
|
|
259
264
|
async function* gen() {
|
|
260
265
|
yield 'first'
|
|
261
266
|
}
|
|
262
267
|
|
|
263
|
-
transport.respondReceipt({
|
|
268
|
+
const response = transport.respondReceipt({
|
|
269
|
+
credential: makeCredential(),
|
|
270
|
+
input: request,
|
|
264
271
|
receipt: makeReceipt(),
|
|
265
272
|
response: gen(),
|
|
266
273
|
challengeId,
|
|
267
274
|
})
|
|
268
|
-
|
|
269
|
-
async function* gen2() {
|
|
270
|
-
yield 'second'
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
expect(() =>
|
|
274
|
-
transport.respondReceipt({
|
|
275
|
-
receipt: makeReceipt(),
|
|
276
|
-
response: gen2(),
|
|
277
|
-
challengeId,
|
|
278
|
-
}),
|
|
279
|
-
).toThrow('No SSE context available')
|
|
275
|
+
expect(response.headers.get('Content-Type')).toContain('text/event-stream')
|
|
280
276
|
})
|
|
281
277
|
|
|
282
278
|
test('respondReceipt throws when no SSE context available', () => {
|
|
@@ -287,8 +283,15 @@ describe('sse transport', () => {
|
|
|
287
283
|
yield 'hello'
|
|
288
284
|
}
|
|
289
285
|
|
|
286
|
+
const credential = Credential.from({
|
|
287
|
+
challenge: makeChallenge(),
|
|
288
|
+
payload: { signature: '0xabc123', type: 'transaction' },
|
|
289
|
+
})
|
|
290
|
+
|
|
290
291
|
expect(() =>
|
|
291
292
|
transport.respondReceipt({
|
|
293
|
+
credential,
|
|
294
|
+
input: new Request('https://test.example.com/session'),
|
|
292
295
|
receipt: makeReceipt(),
|
|
293
296
|
response: gen(),
|
|
294
297
|
challengeId,
|
|
@@ -300,14 +303,15 @@ describe('sse transport', () => {
|
|
|
300
303
|
const store = memoryStore()
|
|
301
304
|
await seedChannel(store, 10000000n)
|
|
302
305
|
const transport = sse({ store })
|
|
303
|
-
|
|
304
|
-
transport.getCredential(makeAuthorizedRequest())
|
|
306
|
+
const request = makeAuthorizedRequest()
|
|
305
307
|
|
|
306
308
|
const plainResponse = new Response(JSON.stringify({ content: 'hello' }), {
|
|
307
309
|
headers: { 'Content-Type': 'application/json' },
|
|
308
310
|
})
|
|
309
311
|
|
|
310
312
|
const response = transport.respondReceipt({
|
|
313
|
+
credential: makeCredential(),
|
|
314
|
+
input: request,
|
|
311
315
|
receipt: makeReceipt(),
|
|
312
316
|
response: plainResponse,
|
|
313
317
|
challengeId,
|
|
@@ -328,11 +332,12 @@ describe('sse transport', () => {
|
|
|
328
332
|
const store = memoryStore()
|
|
329
333
|
await seedChannel(store, 10000000n)
|
|
330
334
|
const transport = sse({ store })
|
|
331
|
-
|
|
332
|
-
transport.getCredential(makeAuthorizedRequest())
|
|
335
|
+
const request = makeAuthorizedRequest()
|
|
333
336
|
|
|
334
337
|
const managementResponse = new Response(null, { status: 204 })
|
|
335
338
|
const response = transport.respondReceipt({
|
|
339
|
+
credential: makeCredential(),
|
|
340
|
+
input: request,
|
|
336
341
|
receipt: makeReceipt(),
|
|
337
342
|
response: managementResponse,
|
|
338
343
|
challengeId,
|
|
@@ -353,14 +358,15 @@ describe('sse transport', () => {
|
|
|
353
358
|
await seedChannel(store, 10000000n)
|
|
354
359
|
|
|
355
360
|
const transport = sse({ store, poll: true })
|
|
356
|
-
|
|
357
|
-
transport.getCredential(makeAuthorizedRequest())
|
|
361
|
+
const request = makeAuthorizedRequest()
|
|
358
362
|
|
|
359
363
|
async function* gen() {
|
|
360
364
|
yield 'test'
|
|
361
365
|
}
|
|
362
366
|
|
|
363
367
|
const response = transport.respondReceipt({
|
|
368
|
+
credential: makeCredential(),
|
|
369
|
+
input: request,
|
|
364
370
|
receipt: makeReceipt(),
|
|
365
371
|
response: gen(),
|
|
366
372
|
challengeId,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tempo-specific SSE transport that wraps the base HTTP transport
|
|
3
|
-
* with metering logic (context capture from credentials, per-token
|
|
3
|
+
* with metering logic (context capture from verified credentials, per-token
|
|
4
4
|
* charging via Sse.serve).
|
|
5
5
|
*
|
|
6
6
|
* @internal
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import * as Transport from '../../../server/Transport.js'
|
|
9
9
|
import * as ChannelStore from '../../session/ChannelStore.js'
|
|
10
10
|
import * as Sse_core from '../../session/Sse.js'
|
|
11
|
+
import type { SessionCredentialPayload } from '../../session/Types.js'
|
|
11
12
|
|
|
12
13
|
/** SSE transport with Tempo session controller. */
|
|
13
14
|
export type Sse = Transport.Sse<Sse_core.SessionController>
|
|
@@ -33,30 +34,24 @@ export function sse(options: sse.Options & { store: ChannelStore.ChannelStore })
|
|
|
33
34
|
return store
|
|
34
35
|
})()
|
|
35
36
|
|
|
36
|
-
const contextMap = new Map<string, Sse_core.fromRequest.Context & { signal?: AbortSignal }>()
|
|
37
|
-
|
|
38
37
|
const base = Transport.http()
|
|
39
38
|
return Transport.from<Request, Response, Transport.ReceiptResponseOf<Sse>, Response>({
|
|
40
39
|
name: 'sse',
|
|
41
40
|
|
|
42
41
|
getCredential(request) {
|
|
43
|
-
|
|
44
|
-
if (credential) {
|
|
45
|
-
try {
|
|
46
|
-
const ctx = Sse_core.fromRequest(request)
|
|
47
|
-
contextMap.set(ctx.challengeId, { ...ctx, signal: request.signal })
|
|
48
|
-
} catch {
|
|
49
|
-
// ignore — non-SSE credentials won't have session context
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return credential
|
|
42
|
+
return base.getCredential(request)
|
|
53
43
|
},
|
|
54
44
|
|
|
55
45
|
respondChallenge(options) {
|
|
56
46
|
return base.respondChallenge(options) as Response
|
|
57
47
|
},
|
|
58
48
|
|
|
59
|
-
respondReceipt({ receipt, response, challengeId }) {
|
|
49
|
+
respondReceipt({ credential, receipt, response, challengeId, input }) {
|
|
50
|
+
const payload = credential.payload as Partial<SessionCredentialPayload>
|
|
51
|
+
if (!payload.channelId) throw new Error('No SSE context available')
|
|
52
|
+
const channelId = payload.channelId
|
|
53
|
+
const tickCost = BigInt(credential.challenge.request.amount as string)
|
|
54
|
+
|
|
60
55
|
// Auto-detect upstream SSE responses and parse them into an
|
|
61
56
|
// AsyncIterable so they flow through the metered pipeline.
|
|
62
57
|
// This lets proxy consumers simply pass `result.withReceipt(upstreamRes)`
|
|
@@ -67,10 +62,6 @@ export function sse(options: sse.Options & { store: ChannelStore.ChannelStore })
|
|
|
67
62
|
: response
|
|
68
63
|
|
|
69
64
|
if (isAsyncGeneratorFunction(resolved) || isAsyncIterable(resolved)) {
|
|
70
|
-
const ctx = contextMap.get(challengeId)
|
|
71
|
-
if (!ctx) throw new Error('No SSE context available — credential was not parsed')
|
|
72
|
-
contextMap.delete(challengeId)
|
|
73
|
-
|
|
74
65
|
// Pass async generator functions directly so Sse.serve gives them
|
|
75
66
|
// a SessionController for manual charge(). Pass raw AsyncIterables
|
|
76
67
|
// as-is so Sse.serve auto-charges per yielded value.
|
|
@@ -79,17 +70,19 @@ export function sse(options: sse.Options & { store: ChannelStore.ChannelStore })
|
|
|
79
70
|
: (resolved as AsyncIterable<string>)
|
|
80
71
|
const stream = Sse_core.serve({
|
|
81
72
|
store,
|
|
82
|
-
channelId
|
|
73
|
+
channelId,
|
|
83
74
|
challengeId,
|
|
84
|
-
tickCost
|
|
75
|
+
tickCost,
|
|
85
76
|
pollIntervalMs: pollingInterval,
|
|
86
77
|
generate,
|
|
87
|
-
signal:
|
|
78
|
+
signal: input.signal,
|
|
88
79
|
})
|
|
89
80
|
return Sse_core.toResponse(stream)
|
|
90
81
|
}
|
|
91
82
|
|
|
92
83
|
const baseResponse = base.respondReceipt({
|
|
84
|
+
credential,
|
|
85
|
+
input,
|
|
93
86
|
receipt,
|
|
94
87
|
response: response as Response,
|
|
95
88
|
challengeId,
|
|
@@ -97,45 +90,38 @@ export function sse(options: sse.Options & { store: ChannelStore.ChannelStore })
|
|
|
97
90
|
|
|
98
91
|
// Non-SSE response (e.g. upstream returned JSON instead of event-stream).
|
|
99
92
|
// Need to deduct tickCost so request isn't free.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// Null-body statuses (e.g. 204 from management actions) cannot carry a
|
|
105
|
-
// response body per Fetch/HTTP semantics.
|
|
106
|
-
if (isNullBodyStatus(baseResponse.status)) {
|
|
107
|
-
return baseResponse
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const stream = new ReadableStream<Uint8Array>({
|
|
111
|
-
async start(controller) {
|
|
112
|
-
// deduction completes before consumer reads
|
|
113
|
-
await ChannelStore.deductFromChannel(store, ctx.channelId, ctx.tickCost)
|
|
114
|
-
if (!baseResponse.body) {
|
|
115
|
-
controller.close()
|
|
116
|
-
return
|
|
117
|
-
}
|
|
118
|
-
const reader = baseResponse.body.getReader()
|
|
119
|
-
try {
|
|
120
|
-
while (true) {
|
|
121
|
-
const { done, value } = await reader.read()
|
|
122
|
-
if (done) break
|
|
123
|
-
controller.enqueue(value)
|
|
124
|
-
}
|
|
125
|
-
} finally {
|
|
126
|
-
reader.releaseLock()
|
|
127
|
-
controller.close()
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
})
|
|
131
|
-
return new Response(stream, {
|
|
132
|
-
status: baseResponse.status,
|
|
133
|
-
statusText: baseResponse.statusText,
|
|
134
|
-
headers: baseResponse.headers,
|
|
135
|
-
})
|
|
93
|
+
// Null-body statuses (e.g. 204 from management actions) cannot carry a
|
|
94
|
+
// response body per Fetch/HTTP semantics.
|
|
95
|
+
if (isNullBodyStatus(baseResponse.status)) {
|
|
96
|
+
return baseResponse
|
|
136
97
|
}
|
|
137
98
|
|
|
138
|
-
|
|
99
|
+
const stream = new ReadableStream<Uint8Array>({
|
|
100
|
+
async start(controller) {
|
|
101
|
+
// deduction completes before consumer reads
|
|
102
|
+
await ChannelStore.deductFromChannel(store, channelId, tickCost)
|
|
103
|
+
if (!baseResponse.body) {
|
|
104
|
+
controller.close()
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
const reader = baseResponse.body.getReader()
|
|
108
|
+
try {
|
|
109
|
+
while (true) {
|
|
110
|
+
const { done, value } = await reader.read()
|
|
111
|
+
if (done) break
|
|
112
|
+
controller.enqueue(value)
|
|
113
|
+
}
|
|
114
|
+
} finally {
|
|
115
|
+
reader.releaseLock()
|
|
116
|
+
controller.close()
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
return new Response(stream, {
|
|
121
|
+
status: baseResponse.status,
|
|
122
|
+
statusText: baseResponse.statusText,
|
|
123
|
+
headers: baseResponse.headers,
|
|
124
|
+
})
|
|
139
125
|
},
|
|
140
126
|
})
|
|
141
127
|
}
|
package/src/tsconfig.json
CHANGED