@nifrajs/core 4.0.2 → 5.0.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.
- package/README.md +6 -2
- package/dist/assurance.d.ts +71 -5
- package/dist/assurance.d.ts.map +1 -1
- package/dist/assurance.js +90 -25
- package/dist/assurance.js.map +1 -1
- package/dist/capabilities.d.ts +6 -0
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +52 -0
- package/dist/capabilities.js.map +1 -1
- package/dist/durable-effect.js +1 -1
- package/dist/durable-effect.js.map +1 -1
- package/dist/durable-saga.d.ts +6 -0
- package/dist/durable-saga.d.ts.map +1 -1
- package/dist/durable-saga.js +83 -10
- package/dist/durable-saga.js.map +1 -1
- package/dist/edge-kit.d.ts +7 -2
- package/dist/edge-kit.d.ts.map +1 -1
- package/dist/edge-kit.js +7 -2
- package/dist/edge-kit.js.map +1 -1
- package/dist/effect-scope.d.ts.map +1 -1
- package/dist/effect-scope.js +16 -5
- package/dist/effect-scope.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/evidence.d.ts +2 -0
- package/dist/evidence.d.ts.map +1 -1
- package/dist/evidence.js +9 -1
- package/dist/evidence.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/durable-shared.d.ts +1 -2
- package/dist/internal/durable-shared.d.ts.map +1 -1
- package/dist/internal/durable-shared.js +25 -44
- package/dist/internal/durable-shared.js.map +1 -1
- package/dist/internal/durable-types.d.ts +3 -0
- package/dist/internal/durable-types.d.ts.map +1 -1
- package/dist/internal/hook-origins.d.ts +67 -0
- package/dist/internal/hook-origins.d.ts.map +1 -0
- package/dist/internal/hook-origins.js +86 -0
- package/dist/internal/hook-origins.js.map +1 -0
- package/dist/internal/route-assurance.d.ts +6 -3
- package/dist/internal/route-assurance.d.ts.map +1 -1
- package/dist/internal/route-assurance.js +29 -14
- package/dist/internal/route-assurance.js.map +1 -1
- package/dist/internal/route-program.d.ts +1 -1
- package/dist/internal/route-program.d.ts.map +1 -1
- package/dist/internal/route-program.js +59 -16
- package/dist/internal/route-program.js.map +1 -1
- package/dist/reconciliation-worker.d.ts.map +1 -1
- package/dist/reconciliation-worker.js +12 -2
- package/dist/reconciliation-worker.js.map +1 -1
- package/dist/reflection.d.ts +53 -2
- package/dist/reflection.d.ts.map +1 -1
- package/dist/reflection.js +352 -5
- package/dist/reflection.js.map +1 -1
- package/dist/router/pattern.d.ts +1 -1
- package/dist/router/pattern.d.ts.map +1 -1
- package/dist/router/pattern.js +6 -3
- package/dist/router/pattern.js.map +1 -1
- package/dist/security/baseline.d.ts +3 -0
- package/dist/security/baseline.d.ts.map +1 -1
- package/dist/security/baseline.js +9 -5
- package/dist/security/baseline.js.map +1 -1
- package/dist/server/auth.d.ts +32 -3
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +23 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/body.d.ts +7 -0
- package/dist/server/body.d.ts.map +1 -1
- package/dist/server/body.js +34 -1
- package/dist/server/body.js.map +1 -1
- package/dist/server/client-ip.d.ts +3 -0
- package/dist/server/client-ip.d.ts.map +1 -1
- package/dist/server/client-ip.js.map +1 -1
- package/dist/server/context.d.ts +2 -1
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/idempotency-lane.d.ts +6 -3
- package/dist/server/idempotency-lane.d.ts.map +1 -1
- package/dist/server/idempotency-lane.js +38 -17
- package/dist/server/idempotency-lane.js.map +1 -1
- package/dist/server/install.d.ts +2 -0
- package/dist/server/install.d.ts.map +1 -1
- package/dist/server/install.js +2 -0
- package/dist/server/install.js.map +1 -1
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/logger.js +3 -1
- package/dist/server/logger.js.map +1 -1
- package/dist/server/methods.d.ts +1 -0
- package/dist/server/methods.d.ts.map +1 -1
- package/dist/server/methods.js +1 -0
- package/dist/server/methods.js.map +1 -1
- package/dist/server/node-outcome.d.ts.map +1 -1
- package/dist/server/node-outcome.js +29 -11
- package/dist/server/node-outcome.js.map +1 -1
- package/dist/server/platform-adapters.d.ts.map +1 -1
- package/dist/server/platform-adapters.js +3 -2
- package/dist/server/platform-adapters.js.map +1 -1
- package/dist/server/request-context.d.ts +2 -1
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js +4 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/respond.d.ts +44 -11
- package/dist/server/respond.d.ts.map +1 -1
- package/dist/server/respond.js +192 -79
- package/dist/server/respond.js.map +1 -1
- package/dist/server/response-observer-runtime.d.ts.map +1 -1
- package/dist/server/response-observer-runtime.js +10 -41
- package/dist/server/response-observer-runtime.js.map +1 -1
- package/dist/server/runtime-core.d.ts +2 -0
- package/dist/server/runtime-core.d.ts.map +1 -1
- package/dist/server/runtime-core.js +2 -0
- package/dist/server/runtime-core.js.map +1 -1
- package/dist/server/server-types.d.ts +29 -5
- package/dist/server/server-types.d.ts.map +1 -1
- package/dist/server/server.d.ts +44 -31
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +444 -321
- package/dist/server/server.js.map +1 -1
- package/dist/server/sse.d.ts +11 -1
- package/dist/server/sse.d.ts.map +1 -1
- package/dist/server/sse.js +18 -3
- package/dist/server/sse.js.map +1 -1
- package/dist/server/transport-lane.d.ts +7 -1
- package/dist/server/transport-lane.d.ts.map +1 -1
- package/dist/server/transport-lane.js.map +1 -1
- package/dist/server/webhook.d.ts +5 -2
- package/dist/server/webhook.d.ts.map +1 -1
- package/dist/server/webhook.js +17 -1
- package/dist/server/webhook.js.map +1 -1
- package/dist/server/websocket.d.ts +22 -14
- package/dist/server/websocket.d.ts.map +1 -1
- package/dist/server/websocket.js +21 -0
- package/dist/server/websocket.js.map +1 -1
- package/dist/server/ws-hook.d.ts +7 -1
- package/dist/server/ws-hook.d.ts.map +1 -1
- package/dist/server.d.ts +3 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/single-copy.d.ts +6 -0
- package/dist/single-copy.d.ts.map +1 -1
- package/dist/single-copy.js +20 -1
- package/dist/single-copy.js.map +1 -1
- package/dist/tool-contract.d.ts.map +1 -1
- package/dist/tool-contract.js +71 -40
- package/dist/tool-contract.js.map +1 -1
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +3 -1
- package/dist/ws.js.map +1 -1
- package/package.json +1 -1
- package/src/assurance.ts +180 -31
- package/src/capabilities.ts +59 -0
- package/src/durable-effect.ts +1 -1
- package/src/durable-saga.ts +94 -10
- package/src/edge-kit.ts +7 -2
- package/src/effect-scope.ts +15 -5
- package/src/errors.ts +1 -0
- package/src/evidence.ts +12 -3
- package/src/index.ts +6 -1
- package/src/internal/durable-shared.ts +25 -37
- package/src/internal/durable-types.ts +3 -0
- package/src/internal/hook-origins.ts +153 -0
- package/src/internal/route-assurance.ts +32 -18
- package/src/internal/route-program.ts +98 -44
- package/src/reconciliation-worker.ts +10 -2
- package/src/reflection.ts +455 -4
- package/src/router/pattern.ts +6 -3
- package/src/security/baseline.ts +10 -5
- package/src/server/auth.ts +45 -3
- package/src/server/body.ts +34 -1
- package/src/server/client-ip.ts +3 -0
- package/src/server/context.ts +2 -1
- package/src/server/idempotency-lane.ts +64 -22
- package/src/server/install.ts +3 -0
- package/src/server/logger.ts +3 -1
- package/src/server/methods.ts +1 -0
- package/src/server/node-outcome.ts +34 -12
- package/src/server/platform-adapters.ts +3 -2
- package/src/server/request-context.ts +5 -0
- package/src/server/respond.ts +222 -69
- package/src/server/response-observer-runtime.ts +16 -38
- package/src/server/runtime-core.ts +2 -0
- package/src/server/server-types.ts +29 -5
- package/src/server/server.ts +659 -530
- package/src/server/sse.ts +84 -55
- package/src/server/transport-lane.ts +7 -1
- package/src/server/webhook.ts +22 -3
- package/src/server/websocket.ts +44 -5
- package/src/server/ws-hook.ts +5 -0
- package/src/server.ts +5 -0
- package/src/single-copy.ts +20 -1
- package/src/tool-contract.ts +67 -39
- package/src/ws.ts +4 -0
|
@@ -8,13 +8,17 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { StandardSchemaV1 } from "../schema/standard.ts"
|
|
11
|
+
import { markAuthenticatedRequest } from "../server/auth.ts"
|
|
11
12
|
import { pathnameOf } from "../server/http.ts"
|
|
13
|
+
import { queryObjectOf } from "../server/query.ts"
|
|
12
14
|
import type { Registry } from "../server/registry.ts"
|
|
13
15
|
import {
|
|
16
|
+
CONTEXT_SEARCH,
|
|
14
17
|
CONTEXT_SET,
|
|
15
18
|
EMPTY_RESPONSE_CONTROLS,
|
|
16
19
|
isResponseResult,
|
|
17
20
|
type ResponseResult,
|
|
21
|
+
requestOf,
|
|
18
22
|
status,
|
|
19
23
|
} from "../server/runtime-core.ts"
|
|
20
24
|
import type { CtxSet, MaybePromise, RawContext, RequestSource, Server } from "../server/server.ts"
|
|
@@ -136,14 +140,14 @@ interface RouteProgramRuntime {
|
|
|
136
140
|
entry: RouteEntry,
|
|
137
141
|
error: unknown,
|
|
138
142
|
ctx: RawContext,
|
|
139
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
143
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
140
144
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
141
145
|
): MaybePromise<T>
|
|
142
146
|
finishProgramResult<T>(
|
|
143
147
|
entry: RouteEntry,
|
|
144
148
|
ctx: RawContext,
|
|
145
149
|
result: unknown,
|
|
146
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
150
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
147
151
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
148
152
|
): MaybePromise<T>
|
|
149
153
|
}
|
|
@@ -156,6 +160,22 @@ function isEarly(value: unknown): value is Response | ResponseResult {
|
|
|
156
160
|
return value instanceof Response || isResponseResult(value)
|
|
157
161
|
}
|
|
158
162
|
|
|
163
|
+
/** A hook's early reply. A `status(...)` or `c.json(...)` one is held to the route's response
|
|
164
|
+
* contract like the handler's. */
|
|
165
|
+
function finishEarly<T>(
|
|
166
|
+
host: RouteProgramRuntime,
|
|
167
|
+
entry: RouteEntry,
|
|
168
|
+
ctx: RawContext,
|
|
169
|
+
value: unknown,
|
|
170
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
171
|
+
wrapResponse: (response: Response | ResponseResult) => T,
|
|
172
|
+
): ProgramResult<T> {
|
|
173
|
+
return entry.responseContract !== undefined &&
|
|
174
|
+
(value === ctx.jsonReply || isResponseResult(value))
|
|
175
|
+
? host.finishProgramResult(entry, ctx, value, finalize, wrapResponse)
|
|
176
|
+
: finalize(value, responseSet(ctx), ctx)
|
|
177
|
+
}
|
|
178
|
+
|
|
159
179
|
function authHeadersOf(source: RequestSource): import("../server/auth.ts").AuthHeaders {
|
|
160
180
|
return Object.freeze({
|
|
161
181
|
get: (name: string) => source.headers.get(name),
|
|
@@ -166,15 +186,38 @@ function authHeadersOf(source: RequestSource): import("../server/auth.ts").AuthH
|
|
|
166
186
|
})
|
|
167
187
|
}
|
|
168
188
|
|
|
189
|
+
/** A fresh parse of `search`, frozen with its arrays, so a stage never holds what validation reads. */
|
|
190
|
+
function frozenQueryOf(search: string): Readonly<Record<string, string | readonly string[]>> {
|
|
191
|
+
const query = queryObjectOf(search)
|
|
192
|
+
for (const key in query) {
|
|
193
|
+
const value = query[key]
|
|
194
|
+
if (Array.isArray(value)) Object.freeze(value)
|
|
195
|
+
}
|
|
196
|
+
return Object.freeze(query)
|
|
197
|
+
}
|
|
198
|
+
|
|
169
199
|
function authenticationInputOf(
|
|
170
200
|
source: RequestSource,
|
|
171
201
|
ctx: RawContext,
|
|
172
202
|
): import("../server/auth.ts").AuthenticationInput<unknown> {
|
|
203
|
+
let query: Readonly<Record<string, string | readonly string[]>> | undefined
|
|
204
|
+
// Accessors, so a stage that reads neither the query nor the request costs nothing for them; the
|
|
205
|
+
// search is the context's own, the one `c.query` and its validation parse.
|
|
173
206
|
return Object.freeze({
|
|
174
207
|
method: source.method,
|
|
175
208
|
pathname: pathnameOf(source.url),
|
|
176
209
|
headers: authHeadersOf(source),
|
|
177
210
|
params: Object.freeze({ ...ctx.params }),
|
|
211
|
+
get search() {
|
|
212
|
+
return ctx[CONTEXT_SEARCH]
|
|
213
|
+
},
|
|
214
|
+
get query() {
|
|
215
|
+
query ??= frozenQueryOf(ctx[CONTEXT_SEARCH])
|
|
216
|
+
return query
|
|
217
|
+
},
|
|
218
|
+
get request() {
|
|
219
|
+
return ctx.req
|
|
220
|
+
},
|
|
178
221
|
env: ctx.env,
|
|
179
222
|
clientIp: ctx.clientIp,
|
|
180
223
|
signal: ctx.signal,
|
|
@@ -206,7 +249,7 @@ function runAuthenticationStages<T>(
|
|
|
206
249
|
program: RouteProgram,
|
|
207
250
|
source: RequestSource,
|
|
208
251
|
ctx: RawContext,
|
|
209
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
252
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
210
253
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
211
254
|
start = 0,
|
|
212
255
|
validationComplete = false,
|
|
@@ -245,24 +288,31 @@ function runAuthenticationStages<T>(
|
|
|
245
288
|
try {
|
|
246
289
|
outcome = stage.run(input)
|
|
247
290
|
} catch {
|
|
248
|
-
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx))
|
|
291
|
+
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx), ctx)
|
|
249
292
|
}
|
|
250
293
|
if (outcome instanceof Promise) {
|
|
251
294
|
return outcome.then(
|
|
252
|
-
(settled) =>
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
295
|
+
(settled) => {
|
|
296
|
+
// The rest of the program runs inside this callback: a derive or handler that throws
|
|
297
|
+
// synchronously would otherwise reject the request instead of reaching onError.
|
|
298
|
+
try {
|
|
299
|
+
return finishAuthentication(
|
|
300
|
+
host,
|
|
301
|
+
entry,
|
|
302
|
+
program,
|
|
303
|
+
source,
|
|
304
|
+
ctx,
|
|
305
|
+
finalize,
|
|
306
|
+
wrapResponse,
|
|
307
|
+
settled,
|
|
308
|
+
start + 1,
|
|
309
|
+
validationComplete,
|
|
310
|
+
)
|
|
311
|
+
} catch (error) {
|
|
312
|
+
return host.handleProgramError(entry, error, ctx, finalize, wrapResponse)
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
() => finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx), ctx),
|
|
266
316
|
)
|
|
267
317
|
}
|
|
268
318
|
return finishAuthentication(
|
|
@@ -285,7 +335,7 @@ function finishAuthentication<T>(
|
|
|
285
335
|
program: RouteProgram,
|
|
286
336
|
source: RequestSource,
|
|
287
337
|
ctx: RawContext,
|
|
288
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
338
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
289
339
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
290
340
|
result: import("../server/auth.ts").AuthenticationResult<unknown>,
|
|
291
341
|
next: number,
|
|
@@ -296,24 +346,24 @@ function finishAuthentication<T>(
|
|
|
296
346
|
typeof result !== "object" ||
|
|
297
347
|
(result.kind !== "authenticated" && result.kind !== "rejected")
|
|
298
348
|
) {
|
|
299
|
-
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx))
|
|
349
|
+
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx), ctx)
|
|
300
350
|
}
|
|
301
351
|
if (result.kind === "rejected") {
|
|
302
352
|
if (!validAuthenticationFailureReason(result.reason)) {
|
|
303
|
-
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx))
|
|
353
|
+
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx), ctx)
|
|
304
354
|
}
|
|
305
355
|
if (
|
|
306
356
|
result.response !== undefined &&
|
|
307
357
|
!(result.response instanceof Response) &&
|
|
308
358
|
!isResponseResult(result.response)
|
|
309
359
|
) {
|
|
310
|
-
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx))
|
|
360
|
+
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx), ctx)
|
|
311
361
|
}
|
|
312
362
|
const response = result.response ?? defaultAuthenticationResponse(result.reason)
|
|
313
|
-
return finalize(response, responseSet(ctx))
|
|
363
|
+
return finalize(response, responseSet(ctx), ctx)
|
|
314
364
|
}
|
|
315
365
|
if (result.principal === null || result.principal === undefined) {
|
|
316
|
-
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx))
|
|
366
|
+
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx), ctx)
|
|
317
367
|
}
|
|
318
368
|
Object.defineProperty(ctx, "principal", {
|
|
319
369
|
value: result.principal,
|
|
@@ -321,6 +371,7 @@ function finishAuthentication<T>(
|
|
|
321
371
|
configurable: true,
|
|
322
372
|
writable: false,
|
|
323
373
|
})
|
|
374
|
+
markAuthenticatedRequest(requestOf(source))
|
|
324
375
|
return runAuthenticationStages(
|
|
325
376
|
host,
|
|
326
377
|
entry,
|
|
@@ -341,7 +392,7 @@ function runFastAuthenticatedProgram<T>(
|
|
|
341
392
|
program: RouteProgram,
|
|
342
393
|
source: RequestSource,
|
|
343
394
|
ctx: RawContext,
|
|
344
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
395
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
345
396
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
346
397
|
validationIndex: number,
|
|
347
398
|
): ProgramResult<T> {
|
|
@@ -375,7 +426,7 @@ async function continueFastAuthenticatedProgram<T>(
|
|
|
375
426
|
program: RouteProgram,
|
|
376
427
|
source: RequestSource,
|
|
377
428
|
ctx: RawContext,
|
|
378
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
429
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
379
430
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
380
431
|
validationIndex: number,
|
|
381
432
|
pending: Promise<unknown>,
|
|
@@ -403,7 +454,7 @@ function runFastAuthenticatedHandler<T>(
|
|
|
403
454
|
entry: RouteEntry,
|
|
404
455
|
program: RouteProgram,
|
|
405
456
|
ctx: RawContext,
|
|
406
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
457
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
407
458
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
408
459
|
): ProgramResult<T> {
|
|
409
460
|
let result: unknown
|
|
@@ -427,7 +478,7 @@ function runValidationBeforeAuthentication<T>(
|
|
|
427
478
|
program: RouteProgram,
|
|
428
479
|
source: RequestSource,
|
|
429
480
|
ctx: RawContext,
|
|
430
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
481
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
431
482
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
432
483
|
validationIndex = 0,
|
|
433
484
|
): ProgramResult<T> {
|
|
@@ -461,7 +512,7 @@ async function continueValidationBeforeAuthentication<T>(
|
|
|
461
512
|
program: RouteProgram,
|
|
462
513
|
source: RequestSource,
|
|
463
514
|
ctx: RawContext,
|
|
464
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
515
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
465
516
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
466
517
|
validationIndex: number,
|
|
467
518
|
pending: Promise<unknown>,
|
|
@@ -490,7 +541,7 @@ function runValidatedProgram<T>(
|
|
|
490
541
|
program: RouteProgram,
|
|
491
542
|
source: RequestSource,
|
|
492
543
|
ctx: RawContext,
|
|
493
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
544
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
494
545
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
495
546
|
): ProgramResult<T> {
|
|
496
547
|
if (program.hasDecorations) Object.assign(ctx, program.decorations)
|
|
@@ -506,7 +557,7 @@ async function runAuthBeforeValidation<T>(
|
|
|
506
557
|
program: RouteProgram,
|
|
507
558
|
source: RequestSource,
|
|
508
559
|
ctx: RawContext,
|
|
509
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
560
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
510
561
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
511
562
|
): Promise<T> {
|
|
512
563
|
try {
|
|
@@ -515,14 +566,15 @@ async function runAuthBeforeValidation<T>(
|
|
|
515
566
|
for (const derive of program.derives) {
|
|
516
567
|
const result = derive(ctx)
|
|
517
568
|
const settled = result instanceof Promise ? await result : result
|
|
518
|
-
if (isEarly(settled)) return
|
|
569
|
+
if (isEarly(settled)) return finishEarly(host, entry, ctx, settled, finalize, wrapResponse)
|
|
519
570
|
Object.assign(ctx, settled)
|
|
520
571
|
}
|
|
521
572
|
|
|
522
573
|
for (const before of program.beforeHandle) {
|
|
523
574
|
const result = before(ctx)
|
|
524
575
|
const settled = result instanceof Promise ? await result : result
|
|
525
|
-
if (settled !== undefined)
|
|
576
|
+
if (settled !== undefined)
|
|
577
|
+
return finishEarly(host, entry, ctx, settled, finalize, wrapResponse)
|
|
526
578
|
}
|
|
527
579
|
|
|
528
580
|
for (const stage of program.validationStages) {
|
|
@@ -559,7 +611,7 @@ export function executeRouteProgram<T, R extends Registry, Ctx>(
|
|
|
559
611
|
program: RouteProgram,
|
|
560
612
|
source: RequestSource,
|
|
561
613
|
ctx: RawContext,
|
|
562
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
614
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
563
615
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
564
616
|
): ProgramResult<T> {
|
|
565
617
|
const host = runtime as unknown as RouteProgramRuntime
|
|
@@ -594,7 +646,7 @@ function runSync<T>(
|
|
|
594
646
|
program: RouteProgram,
|
|
595
647
|
source: RequestSource,
|
|
596
648
|
ctx: RawContext,
|
|
597
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
649
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
598
650
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
599
651
|
validationIndex: number,
|
|
600
652
|
initial: unknown,
|
|
@@ -631,7 +683,7 @@ function runHooksSync<T>(
|
|
|
631
683
|
program: RouteProgram,
|
|
632
684
|
source: RequestSource,
|
|
633
685
|
ctx: RawContext,
|
|
634
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
686
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
635
687
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
636
688
|
deriveIndex: number,
|
|
637
689
|
beforeIndex: number,
|
|
@@ -654,7 +706,7 @@ function runHooksSync<T>(
|
|
|
654
706
|
initial,
|
|
655
707
|
)
|
|
656
708
|
}
|
|
657
|
-
if (isEarly(outcome)) return
|
|
709
|
+
if (isEarly(outcome)) return finishEarly(host, entry, ctx, outcome, finalize, wrapResponse)
|
|
658
710
|
Object.assign(ctx, outcome)
|
|
659
711
|
}
|
|
660
712
|
for (let i = beforeIndex; i < program.beforeHandle.length; i++) {
|
|
@@ -673,7 +725,7 @@ function runHooksSync<T>(
|
|
|
673
725
|
initial,
|
|
674
726
|
)
|
|
675
727
|
}
|
|
676
|
-
if (outcome !== undefined) return
|
|
728
|
+
if (outcome !== undefined) return finishEarly(host, entry, ctx, outcome, finalize, wrapResponse)
|
|
677
729
|
}
|
|
678
730
|
|
|
679
731
|
const output = program.handler(ctx)
|
|
@@ -719,7 +771,7 @@ async function continueProgram<T>(
|
|
|
719
771
|
program: RouteProgram,
|
|
720
772
|
source: RequestSource,
|
|
721
773
|
ctx: RawContext,
|
|
722
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
774
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
723
775
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
724
776
|
pendingPhase: PendingPhase,
|
|
725
777
|
pending: Promise<unknown>,
|
|
@@ -731,10 +783,11 @@ async function continueProgram<T>(
|
|
|
731
783
|
if (pendingPhase.kind === "validation") {
|
|
732
784
|
if (settled !== undefined) return wrapResponse(settled as Response | ResponseResult)
|
|
733
785
|
} else if (pendingPhase.kind === "derive") {
|
|
734
|
-
if (isEarly(settled)) return
|
|
786
|
+
if (isEarly(settled)) return finishEarly(host, entry, ctx, settled, finalize, wrapResponse)
|
|
735
787
|
Object.assign(ctx, settled)
|
|
736
788
|
} else if (pendingPhase.kind === "before") {
|
|
737
|
-
if (settled !== undefined)
|
|
789
|
+
if (settled !== undefined)
|
|
790
|
+
return finishEarly(host, entry, ctx, settled, finalize, wrapResponse)
|
|
738
791
|
} else if (pendingPhase.kind === "handler") {
|
|
739
792
|
result = settled
|
|
740
793
|
} else {
|
|
@@ -762,7 +815,7 @@ async function runAsyncStages<T>(
|
|
|
762
815
|
program: RouteProgram,
|
|
763
816
|
source: RequestSource,
|
|
764
817
|
ctx: RawContext,
|
|
765
|
-
finalize: (result: unknown, set: CtxSet) => T,
|
|
818
|
+
finalize: (result: unknown, set: CtxSet, ctx?: RawContext) => T,
|
|
766
819
|
wrapResponse: (response: Response | ResponseResult) => T,
|
|
767
820
|
pendingPhase: PendingPhase,
|
|
768
821
|
result: unknown,
|
|
@@ -786,7 +839,7 @@ async function runAsyncStages<T>(
|
|
|
786
839
|
for (let i = deriveStart; i < program.derives.length; i++) {
|
|
787
840
|
const outcome = program.derives[i]!(ctx)
|
|
788
841
|
const settled = outcome instanceof Promise ? await outcome : outcome
|
|
789
|
-
if (isEarly(settled)) return
|
|
842
|
+
if (isEarly(settled)) return finishEarly(host, entry, ctx, settled, finalize, wrapResponse)
|
|
790
843
|
Object.assign(ctx, settled)
|
|
791
844
|
}
|
|
792
845
|
}
|
|
@@ -800,7 +853,8 @@ async function runAsyncStages<T>(
|
|
|
800
853
|
for (let i = beforeStart; i < program.beforeHandle.length; i++) {
|
|
801
854
|
const outcome = program.beforeHandle[i]!(ctx)
|
|
802
855
|
const settled = outcome instanceof Promise ? await outcome : outcome
|
|
803
|
-
if (settled !== undefined)
|
|
856
|
+
if (settled !== undefined)
|
|
857
|
+
return finishEarly(host, entry, ctx, settled, finalize, wrapResponse)
|
|
804
858
|
}
|
|
805
859
|
const output = program.handler(ctx)
|
|
806
860
|
result = output instanceof Promise ? await output : output
|
|
@@ -116,14 +116,22 @@ async function boundedMap<T>(
|
|
|
116
116
|
run: (value: T) => Promise<void>,
|
|
117
117
|
): Promise<void> {
|
|
118
118
|
let next = 0
|
|
119
|
+
let failure: { readonly error: unknown } | undefined
|
|
119
120
|
const worker = async (): Promise<void> => {
|
|
120
|
-
|
|
121
|
+
while (failure === undefined) {
|
|
121
122
|
const index = next++
|
|
122
123
|
if (index >= values.length) return
|
|
123
|
-
|
|
124
|
+
try {
|
|
125
|
+
await run(values[index] as T)
|
|
126
|
+
} catch (error) {
|
|
127
|
+
failure ??= { error }
|
|
128
|
+
}
|
|
124
129
|
}
|
|
125
130
|
}
|
|
131
|
+
// A failure starts no new work but lets every started handler settle: the caller releases the
|
|
132
|
+
// lease next, and a handler still running then overlaps the worker that takes the lease over.
|
|
126
133
|
await Promise.all(Array.from({ length: Math.min(concurrency, values.length) }, () => worker()))
|
|
134
|
+
if (failure !== undefined) throw failure.error
|
|
127
135
|
}
|
|
128
136
|
|
|
129
137
|
export async function runReconciliationWorker<Finding>(
|