@pulse-compute/runtime 0.0.0 → 1.0.0-beta.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/README.md +126 -2
- package/docs/API.md +572 -0
- package/docs/guides/routing.md +202 -0
- package/docs/preview-scope.md +142 -0
- package/package.json +38 -5
- package/src/host.d.ts +420 -0
- package/src/host.js +115 -0
- package/src/index.d.ts +215 -0
- package/src/index.js +11 -0
- package/src/internal/bindings.js +281 -0
- package/src/internal/body.js +254 -0
- package/src/internal/conditional-kv.js +199 -0
- package/src/internal/context.js +275 -0
- package/src/internal/effect-adapter.js +877 -0
- package/src/internal/errors.js +27 -0
- package/src/internal/event-emission.js +182 -0
- package/src/internal/event-execution.js +496 -0
- package/src/internal/fetch.js +221 -0
- package/src/internal/index.js +24 -0
- package/src/internal/logging.js +65 -0
- package/src/internal/package-runtime.js +604 -0
- package/src/internal/path.js +130 -0
- package/src/internal/redaction.js +231 -0
- package/src/internal/response.js +415 -0
- package/src/internal/router.js +487 -0
- package/src/internal/schema.js +340 -0
- package/src/package.d.ts +127 -0
- package/src/package.js +19 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { compileRoutePath, matchRoutePath, normalizeRoutePath } = require('./path.js');
|
|
4
|
+
const { createContext, createRequestView } = require('./context.js');
|
|
5
|
+
const { createJavascriptEffectExecution } = require('./effect-adapter.js');
|
|
6
|
+
const { reportingLevel } = require('./logging.js');
|
|
7
|
+
const {
|
|
8
|
+
PulseRuntimeContractError,
|
|
9
|
+
PulseUnhandledError,
|
|
10
|
+
fetchResponseToResponse,
|
|
11
|
+
isPulseFetchResponse,
|
|
12
|
+
isPulseResult,
|
|
13
|
+
markOpaqueResponse,
|
|
14
|
+
responseBodyClass,
|
|
15
|
+
responseHeaderPairs,
|
|
16
|
+
statusAllowsBody,
|
|
17
|
+
resultToResponse
|
|
18
|
+
} = require('./response.js');
|
|
19
|
+
|
|
20
|
+
const ROUTER_STATE = new WeakMap();
|
|
21
|
+
const TRANSFER_DATA = new WeakMap();
|
|
22
|
+
const NO_ERROR = Symbol('pulse.runtime.no-error');
|
|
23
|
+
|
|
24
|
+
function requireHandler(value, label) {
|
|
25
|
+
if (typeof value !== 'function') throw new TypeError(`Pulse Router ${label} requires a handler function.`);
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function requireRouter(value) {
|
|
30
|
+
if (!ROUTER_STATE.has(value)) throw new TypeError('Pulse Router mount(path, router) requires a Router instance.');
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function mergeParams(left, right) {
|
|
35
|
+
return Object.freeze({ ...(left || {}), ...(right || {}) });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function entry(kind, fields) {
|
|
39
|
+
return Object.freeze({ kind, ...fields });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class Router {
|
|
43
|
+
constructor() {
|
|
44
|
+
ROUTER_STATE.set(this, { entries: [] });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
use(pathOrHandler, maybeHandler) {
|
|
48
|
+
const state = ROUTER_STATE.get(this);
|
|
49
|
+
if (typeof pathOrHandler === 'string') {
|
|
50
|
+
state.entries.push(entry('use', {
|
|
51
|
+
path: compileRoutePath(pathOrHandler, { scoped: true, allowWildcard: true }),
|
|
52
|
+
handler: requireHandler(maybeHandler, 'use(path, handler)')
|
|
53
|
+
}));
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
state.entries.push(entry('use', { path: null, handler: requireHandler(pathOrHandler, 'use(handler)') }));
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get(path, handler) {
|
|
61
|
+
ROUTER_STATE.get(this).entries.push(entry('route', {
|
|
62
|
+
method: 'GET',
|
|
63
|
+
path: compileRoutePath(path, { allowWildcard: true }),
|
|
64
|
+
handler: requireHandler(handler, 'get(path, handler)')
|
|
65
|
+
}));
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
head(path, handler) {
|
|
70
|
+
ROUTER_STATE.get(this).entries.push(entry('route', {
|
|
71
|
+
method: 'HEAD',
|
|
72
|
+
path: compileRoutePath(path, { allowWildcard: true }),
|
|
73
|
+
handler: requireHandler(handler, 'head(path, handler)')
|
|
74
|
+
}));
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
post(path, handler) {
|
|
79
|
+
ROUTER_STATE.get(this).entries.push(entry('route', {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
path: compileRoutePath(path, { allowWildcard: true }),
|
|
82
|
+
handler: requireHandler(handler, 'post(path, handler)')
|
|
83
|
+
}));
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
put(path, handler) {
|
|
88
|
+
ROUTER_STATE.get(this).entries.push(entry('route', {
|
|
89
|
+
method: 'PUT',
|
|
90
|
+
path: compileRoutePath(path, { allowWildcard: true }),
|
|
91
|
+
handler: requireHandler(handler, 'put(path, handler)')
|
|
92
|
+
}));
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
patch(path, handler) {
|
|
97
|
+
ROUTER_STATE.get(this).entries.push(entry('route', {
|
|
98
|
+
method: 'PATCH',
|
|
99
|
+
path: compileRoutePath(path, { allowWildcard: true }),
|
|
100
|
+
handler: requireHandler(handler, 'patch(path, handler)')
|
|
101
|
+
}));
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
delete(path, handler) {
|
|
106
|
+
ROUTER_STATE.get(this).entries.push(entry('route', {
|
|
107
|
+
method: 'DELETE',
|
|
108
|
+
path: compileRoutePath(path, { allowWildcard: true }),
|
|
109
|
+
handler: requireHandler(handler, 'delete(path, handler)')
|
|
110
|
+
}));
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
mount(path, router) {
|
|
115
|
+
ROUTER_STATE.get(this).entries.push(entry('mount', {
|
|
116
|
+
path: compileRoutePath(path, { scoped: true, allowWildcard: true }),
|
|
117
|
+
router: requireRouter(router)
|
|
118
|
+
}));
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
error(handler) {
|
|
123
|
+
ROUTER_STATE.get(this).entries.push(entry('error', { handler: requireHandler(handler, 'error(handler)') }));
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function createTransferFactory() {
|
|
129
|
+
const token = Object.freeze({});
|
|
130
|
+
let transfer = null;
|
|
131
|
+
function next(error) {
|
|
132
|
+
if (transfer) {
|
|
133
|
+
throw new PulseRuntimeContractError('PULSE_RUNTIME_NEXT_MULTIPLE', 'Pulse next() may be invoked at most once by a managed handler.');
|
|
134
|
+
}
|
|
135
|
+
transfer = {};
|
|
136
|
+
Object.defineProperty(transfer, 'kind', { enumerable: false, value: 'pulse.router.transfer' });
|
|
137
|
+
TRANSFER_DATA.set(transfer, Object.freeze({ token, error: error === undefined ? NO_ERROR : error }));
|
|
138
|
+
Object.freeze(transfer);
|
|
139
|
+
return transfer;
|
|
140
|
+
}
|
|
141
|
+
return Object.freeze({
|
|
142
|
+
next,
|
|
143
|
+
token,
|
|
144
|
+
wasCalled: () => transfer !== null,
|
|
145
|
+
transfer: () => transfer
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function transferData(value, token) {
|
|
150
|
+
const data = value && typeof value === 'object' ? TRANSFER_DATA.get(value) : undefined;
|
|
151
|
+
return data && data.token === token ? data : undefined;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function redactedError(frame, error) {
|
|
155
|
+
return frame && frame.effectExecution && typeof frame.effectExecution.redactError === 'function'
|
|
156
|
+
? frame.effectExecution.redactError(error)
|
|
157
|
+
: error;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function containUnexpected(error, frame) {
|
|
161
|
+
const safe = redactedError(frame, error);
|
|
162
|
+
return safe instanceof PulseUnhandledError ? safe : new PulseUnhandledError(safe);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function contractError(code, message) {
|
|
166
|
+
return new PulseRuntimeContractError(code, message);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function normalizeHandlerResponse(value, requestMethod) {
|
|
170
|
+
if (isPulseResult(value)) return resultToResponse(value, requestMethod);
|
|
171
|
+
if (isPulseFetchResponse(value)) return fetchResponseToResponse(value, requestMethod);
|
|
172
|
+
if (value instanceof Response) {
|
|
173
|
+
if (String(requestMethod).toUpperCase() !== 'HEAD' && statusAllowsBody(value.status)) return value;
|
|
174
|
+
if (value.body && !value.bodyUsed) {
|
|
175
|
+
try { await value.body.cancel(); } catch (_) { /* ownership cleanup is best effort */ }
|
|
176
|
+
}
|
|
177
|
+
const response = new Response(null, { status: value.status, statusText: value.statusText, headers: value.headers });
|
|
178
|
+
return responseBodyClass(value) === 'opaque'
|
|
179
|
+
? markOpaqueResponse(response, responseHeaderPairs(value))
|
|
180
|
+
: response;
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function frameWith(frame, fields) {
|
|
186
|
+
return { ...frame, ...fields };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function dispatchRouter(router, frame, startIndex, activeError) {
|
|
190
|
+
const entries = ROUTER_STATE.get(router).entries;
|
|
191
|
+
let index = startIndex;
|
|
192
|
+
let error = activeError;
|
|
193
|
+
|
|
194
|
+
while (index < entries.length) {
|
|
195
|
+
const current = entries[index];
|
|
196
|
+
|
|
197
|
+
if (error !== NO_ERROR) {
|
|
198
|
+
if (current.kind !== 'error') {
|
|
199
|
+
index += 1;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const result = await runErrorHandler(router, frame, index, error, current.handler);
|
|
203
|
+
if (result.kind === 'continue') {
|
|
204
|
+
index = result.index;
|
|
205
|
+
error = result.error;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (current.kind === 'error') {
|
|
212
|
+
index += 1;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (current.kind === 'mount') {
|
|
217
|
+
const match = matchRoutePath(current.path, frame.relativePath);
|
|
218
|
+
if (!match) {
|
|
219
|
+
index += 1;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const childFrame = frameWith(frame, {
|
|
223
|
+
relativePath: match.rest,
|
|
224
|
+
params: mergeParams(frame.params, match.params)
|
|
225
|
+
});
|
|
226
|
+
const childResult = await dispatchRouter(current.router, childFrame, 0, NO_ERROR);
|
|
227
|
+
if (childResult.kind === 'continue') {
|
|
228
|
+
if (childResult.error !== NO_ERROR) {
|
|
229
|
+
error = childResult.error;
|
|
230
|
+
frame = childResult.frame || childFrame;
|
|
231
|
+
}
|
|
232
|
+
index += 1;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
return childResult;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (current.kind === 'use') {
|
|
239
|
+
let handlerFrame = frame;
|
|
240
|
+
if (current.path) {
|
|
241
|
+
const match = matchRoutePath(current.path, frame.relativePath);
|
|
242
|
+
if (!match) {
|
|
243
|
+
index += 1;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
handlerFrame = frameWith(frame, {
|
|
247
|
+
params: mergeParams(frame.params, match.params),
|
|
248
|
+
packageRelativePath: match.rest
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
const result = await runNormalHandler(router, handlerFrame, index, current.handler, false);
|
|
252
|
+
if (result.kind === 'continue') {
|
|
253
|
+
index = result.index;
|
|
254
|
+
error = result.error;
|
|
255
|
+
frame = result.frame || handlerFrame;
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (current.kind === 'route') {
|
|
262
|
+
if (frame.req.method !== current.method) {
|
|
263
|
+
index += 1;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const match = matchRoutePath(current.path, frame.relativePath);
|
|
267
|
+
if (!match) {
|
|
268
|
+
index += 1;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const handlerFrame = frameWith(frame, { params: mergeParams(frame.params, match.params) });
|
|
272
|
+
const result = await runNormalHandler(router, handlerFrame, index, current.handler, true);
|
|
273
|
+
if (result.kind === 'continue') {
|
|
274
|
+
index = result.index;
|
|
275
|
+
error = result.error;
|
|
276
|
+
frame = result.frame || handlerFrame;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
index += 1;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return Object.freeze({ kind: 'continue', index, error, frame });
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async function runNormalHandler(_router, frame, index, handler, routeContext) {
|
|
289
|
+
const transferFactory = createTransferFactory();
|
|
290
|
+
const ctx = createContext({ ...frame, routeContext });
|
|
291
|
+
let output;
|
|
292
|
+
let handlerError;
|
|
293
|
+
try {
|
|
294
|
+
output = await handler(ctx, transferFactory.next);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
handlerError = error;
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
await frame.effectExecution.assertIdle();
|
|
300
|
+
} catch (error) {
|
|
301
|
+
if (handlerError === undefined) handlerError = error;
|
|
302
|
+
}
|
|
303
|
+
if (handlerError !== undefined) {
|
|
304
|
+
return Object.freeze({ kind: 'continue', index: index + 1, error: containUnexpected(handlerError, frame), frame });
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const transfer = transferData(output, transferFactory.token);
|
|
308
|
+
if (transferFactory.wasCalled()) {
|
|
309
|
+
if (!transfer || output !== transferFactory.transfer()) {
|
|
310
|
+
return Object.freeze({
|
|
311
|
+
kind: 'continue',
|
|
312
|
+
index: index + 1,
|
|
313
|
+
error: containUnexpected(contractError(
|
|
314
|
+
'PULSE_RUNTIME_NEXT_NOT_TERMINAL',
|
|
315
|
+
'Pulse next() is a terminal transfer and its exact value must be returned.'
|
|
316
|
+
), frame),
|
|
317
|
+
frame
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
return Object.freeze({
|
|
321
|
+
kind: 'continue',
|
|
322
|
+
index: index + 1,
|
|
323
|
+
error: transfer.error === NO_ERROR ? NO_ERROR : redactedError(frame, transfer.error),
|
|
324
|
+
frame
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const response = await normalizeHandlerResponse(output, frame.req.method);
|
|
329
|
+
if (response) return Object.freeze({ kind: 'response', response });
|
|
330
|
+
return Object.freeze({
|
|
331
|
+
kind: 'continue',
|
|
332
|
+
index: index + 1,
|
|
333
|
+
error: containUnexpected(contractError(
|
|
334
|
+
'PULSE_RUNTIME_HANDLER_RESULT_REQUIRED',
|
|
335
|
+
'Pulse handlers must return a response, next(), or next(error).'
|
|
336
|
+
), frame),
|
|
337
|
+
frame
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async function runErrorHandler(_router, frame, index, activeError, handler) {
|
|
342
|
+
const transferFactory = createTransferFactory();
|
|
343
|
+
const ctx = createContext({ ...frame, routeContext: false });
|
|
344
|
+
let output;
|
|
345
|
+
let handlerError;
|
|
346
|
+
try {
|
|
347
|
+
output = await handler(activeError, ctx, transferFactory.next);
|
|
348
|
+
} catch (error) {
|
|
349
|
+
handlerError = error;
|
|
350
|
+
}
|
|
351
|
+
try {
|
|
352
|
+
await frame.effectExecution.assertIdle();
|
|
353
|
+
} catch (error) {
|
|
354
|
+
if (handlerError === undefined) handlerError = error;
|
|
355
|
+
}
|
|
356
|
+
if (handlerError !== undefined) {
|
|
357
|
+
return Object.freeze({ kind: 'continue', index: index + 1, error: containUnexpected(handlerError, frame), frame });
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const transfer = transferData(output, transferFactory.token);
|
|
361
|
+
if (transferFactory.wasCalled()) {
|
|
362
|
+
if (!transfer || output !== transferFactory.transfer()) {
|
|
363
|
+
return Object.freeze({
|
|
364
|
+
kind: 'continue',
|
|
365
|
+
index: index + 1,
|
|
366
|
+
error: containUnexpected(contractError(
|
|
367
|
+
'PULSE_RUNTIME_NEXT_NOT_TERMINAL',
|
|
368
|
+
'Pulse next() is a terminal transfer and its exact value must be returned.'
|
|
369
|
+
), frame),
|
|
370
|
+
frame
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
return Object.freeze({
|
|
374
|
+
kind: 'continue',
|
|
375
|
+
index: index + 1,
|
|
376
|
+
error: transfer.error === NO_ERROR ? NO_ERROR : redactedError(frame, transfer.error),
|
|
377
|
+
frame
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const response = await normalizeHandlerResponse(output, frame.req.method);
|
|
382
|
+
if (response) return Object.freeze({ kind: 'response', response });
|
|
383
|
+
return Object.freeze({
|
|
384
|
+
kind: 'continue',
|
|
385
|
+
index: index + 1,
|
|
386
|
+
error: containUnexpected(contractError(
|
|
387
|
+
'PULSE_RUNTIME_HANDLER_RESULT_REQUIRED',
|
|
388
|
+
'Pulse error handlers must return a response, next(), or next(error).'
|
|
389
|
+
), frame),
|
|
390
|
+
frame
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async function executeRouter(router, request, options = {}) {
|
|
395
|
+
requireRouter(router);
|
|
396
|
+
if (!(request instanceof Request)) throw new TypeError('Pulse JavaScript execution requires a Web Request.');
|
|
397
|
+
if (typeof options.onLogObservation === 'function') {
|
|
398
|
+
const level = reportingLevel(options.reporting);
|
|
399
|
+
const name = Object.freeze(['off', 'error', 'warn', 'info', 'debug'])[level];
|
|
400
|
+
try {
|
|
401
|
+
options.onLogObservation(Object.freeze({
|
|
402
|
+
version: 'pulse.log-event.v1',
|
|
403
|
+
type: 'logging-config',
|
|
404
|
+
reporting: Object.freeze({ name, level }),
|
|
405
|
+
target: options.target || 'javascript',
|
|
406
|
+
provider: options.provider || null
|
|
407
|
+
}));
|
|
408
|
+
} catch (_) {
|
|
409
|
+
// Logging evidence is best effort.
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const executionSignal = options.signal || request.signal;
|
|
413
|
+
const effectExecution = createJavascriptEffectExecution({
|
|
414
|
+
effectAdapter: options.effectAdapter,
|
|
415
|
+
capabilities: options.capabilities,
|
|
416
|
+
application: options.application,
|
|
417
|
+
request,
|
|
418
|
+
executionKind: 'request',
|
|
419
|
+
signal: executionSignal,
|
|
420
|
+
maxEffects: options.maxEffects,
|
|
421
|
+
kvClock: options.kvClock, deadlineMonotonicMs: options.deadlineMonotonicMs,
|
|
422
|
+
maxBindingNameBytes: options.maxBindingNameBytes,
|
|
423
|
+
maxBindingValueBytes: options.maxBindingValueBytes,
|
|
424
|
+
maxKvNamespaceBytes: options.maxKvNamespaceBytes,
|
|
425
|
+
maxKvKeyBytes: options.maxKvKeyBytes,
|
|
426
|
+
maxKvValueBytes: options.maxKvValueBytes,
|
|
427
|
+
maxKvValueDepth: options.maxKvValueDepth,
|
|
428
|
+
maxKvValueEntries: options.maxKvValueEntries,
|
|
429
|
+
schemaCodecs: options.schemaCodecs,
|
|
430
|
+
strict: options.strict,
|
|
431
|
+
target: options.target,
|
|
432
|
+
provider: options.provider,
|
|
433
|
+
redactionValues: options.redactionValues,
|
|
434
|
+
onEffectObservation: options.onEffectObservation
|
|
435
|
+
});
|
|
436
|
+
const req = createRequestView(request, options.requestHeaders, effectExecution, {
|
|
437
|
+
...options,
|
|
438
|
+
signal: executionSignal,
|
|
439
|
+
redactJsonTraceValue(value) { return effectExecution.redactValue(value); }
|
|
440
|
+
});
|
|
441
|
+
const executionOptions = Object.freeze({
|
|
442
|
+
...options,
|
|
443
|
+
signal: executionSignal,
|
|
444
|
+
redactJsonTraceValue(value) { return effectExecution.redactValue(value); }
|
|
445
|
+
});
|
|
446
|
+
const frame = {
|
|
447
|
+
request,
|
|
448
|
+
req,
|
|
449
|
+
relativePath: normalizeRoutePath(req.path),
|
|
450
|
+
params: Object.freeze({}),
|
|
451
|
+
state: new Map(),
|
|
452
|
+
effectExecution,
|
|
453
|
+
signal: executionSignal,
|
|
454
|
+
executionOptions
|
|
455
|
+
};
|
|
456
|
+
try {
|
|
457
|
+
const result = await dispatchRouter(router, frame, 0, NO_ERROR);
|
|
458
|
+
if (result.kind === 'response') return result.response;
|
|
459
|
+
if (result.error !== NO_ERROR) {
|
|
460
|
+
return new Response('Internal Server Error', {
|
|
461
|
+
status: 500,
|
|
462
|
+
headers: { 'content-type': 'text/plain; charset=utf-8' }
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
return new Response('Not Found', {
|
|
466
|
+
status: 404,
|
|
467
|
+
headers: { 'content-type': 'text/plain; charset=utf-8' }
|
|
468
|
+
});
|
|
469
|
+
} finally {
|
|
470
|
+
try {
|
|
471
|
+
await effectExecution.close();
|
|
472
|
+
} finally {
|
|
473
|
+
if (typeof options.onEffectSummary === 'function') options.onEffectSummary(effectExecution.summary());
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function routerEntries(router) {
|
|
479
|
+
requireRouter(router);
|
|
480
|
+
return Object.freeze([...ROUTER_STATE.get(router).entries]);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
module.exports = Object.freeze({
|
|
484
|
+
Router,
|
|
485
|
+
executeRouter,
|
|
486
|
+
routerEntries
|
|
487
|
+
});
|