@nifrajs/core 3.5.0 → 4.0.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 +26 -0
- package/dist/assurance.d.ts +7 -0
- package/dist/assurance.d.ts.map +1 -1
- package/dist/assurance.js +39 -3
- package/dist/assurance.js.map +1 -1
- package/dist/capabilities.d.ts +20 -2
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +47 -15
- package/dist/capabilities.js.map +1 -1
- package/dist/diff.d.ts +2 -1
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +26 -2
- package/dist/diff.js.map +1 -1
- package/dist/durable-adapters.d.ts.map +1 -1
- package/dist/durable-adapters.js +16 -3
- package/dist/durable-adapters.js.map +1 -1
- package/dist/durable-effect.d.ts.map +1 -1
- package/dist/durable-effect.js +6 -3
- package/dist/durable-effect.js.map +1 -1
- package/dist/edge-kit.d.ts +2 -0
- package/dist/edge-kit.d.ts.map +1 -1
- package/dist/edge-kit.js +2 -0
- package/dist/edge-kit.js.map +1 -1
- package/dist/effect-lifecycle.d.ts.map +1 -1
- package/dist/effect-lifecycle.js +2 -1
- package/dist/effect-lifecycle.js.map +1 -1
- package/dist/effect-scope.d.ts.map +1 -1
- package/dist/effect-scope.js +1 -1
- 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 +24 -1
- package/dist/evidence.d.ts.map +1 -1
- package/dist/evidence.js +168 -13
- package/dist/evidence.js.map +1 -1
- package/dist/idempotency.d.ts +31 -0
- package/dist/idempotency.d.ts.map +1 -1
- package/dist/idempotency.js +40 -8
- package/dist/idempotency.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/code-unit-order.d.ts +7 -0
- package/dist/internal/code-unit-order.d.ts.map +1 -0
- package/dist/internal/code-unit-order.js +9 -0
- package/dist/internal/code-unit-order.js.map +1 -0
- package/dist/internal/durable-saga-protocol.d.ts.map +1 -1
- package/dist/internal/durable-saga-protocol.js +2 -0
- package/dist/internal/durable-saga-protocol.js.map +1 -1
- package/dist/internal/route-catalog.d.ts +6 -6
- package/dist/internal/route-catalog.d.ts.map +1 -1
- package/dist/internal/route-catalog.js +20 -18
- package/dist/internal/route-catalog.js.map +1 -1
- package/dist/internal/route-compiler.d.ts +7 -6
- package/dist/internal/route-compiler.d.ts.map +1 -1
- package/dist/internal/route-compiler.js +36 -4
- package/dist/internal/route-compiler.js.map +1 -1
- package/dist/internal/route-execution.d.ts +7 -3
- package/dist/internal/route-execution.d.ts.map +1 -1
- package/dist/internal/route-execution.js.map +1 -1
- package/dist/internal/route-lanes.d.ts +1 -0
- package/dist/internal/route-lanes.d.ts.map +1 -1
- package/dist/internal/route-lanes.js +34 -29
- package/dist/internal/route-lanes.js.map +1 -1
- package/dist/internal/route-program.d.ts +8 -2
- package/dist/internal/route-program.d.ts.map +1 -1
- package/dist/internal/route-program.js +222 -20
- package/dist/internal/route-program.js.map +1 -1
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +8 -5
- package/dist/ledger.js.map +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +41 -37
- package/dist/manifest.js.map +1 -1
- package/dist/mount.d.ts +36 -0
- package/dist/mount.d.ts.map +1 -1
- package/dist/mount.js +32 -0
- package/dist/mount.js.map +1 -1
- package/dist/reflection.d.ts +26 -0
- package/dist/reflection.d.ts.map +1 -1
- package/dist/reflection.js +65 -1
- package/dist/reflection.js.map +1 -1
- package/dist/response-observer.d.ts +1 -1
- package/dist/response-observer.d.ts.map +1 -1
- package/dist/response-observer.js.map +1 -1
- package/dist/router/overlap.d.ts +23 -0
- package/dist/router/overlap.d.ts.map +1 -0
- package/dist/router/overlap.js +224 -0
- package/dist/router/overlap.js.map +1 -0
- package/dist/router/pattern.d.ts +117 -2
- package/dist/router/pattern.d.ts.map +1 -1
- package/dist/router/pattern.js +283 -23
- package/dist/router/pattern.js.map +1 -1
- package/dist/router/router.d.ts +18 -4
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +51 -44
- package/dist/router/router.js.map +1 -1
- package/dist/server/auth.d.ts +53 -0
- package/dist/server/auth.d.ts.map +1 -0
- package/dist/server/auth.js +9 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/bare-error-lane.d.ts +12 -7
- package/dist/server/bare-error-lane.d.ts.map +1 -1
- package/dist/server/bare-error-lane.js +13 -11
- package/dist/server/bare-error-lane.js.map +1 -1
- package/dist/server/body-lane.d.ts +20 -2
- package/dist/server/body-lane.d.ts.map +1 -1
- package/dist/server/body-lane.js +26 -7
- package/dist/server/body-lane.js.map +1 -1
- package/dist/server/body-parser.d.ts +68 -0
- package/dist/server/body-parser.d.ts.map +1 -0
- package/dist/server/body-parser.js +172 -0
- package/dist/server/body-parser.js.map +1 -0
- package/dist/server/body.d.ts.map +1 -1
- package/dist/server/body.js +62 -7
- package/dist/server/body.js.map +1 -1
- package/dist/server/client-ip.d.ts +8 -0
- package/dist/server/client-ip.d.ts.map +1 -1
- package/dist/server/client-ip.js +8 -0
- package/dist/server/client-ip.js.map +1 -1
- package/dist/server/context.d.ts +71 -6
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/contract.d.ts +15 -5
- package/dist/server/contract.d.ts.map +1 -1
- package/dist/server/contract.js +25 -4
- package/dist/server/contract.js.map +1 -1
- package/dist/server/core-copies.d.ts +2 -0
- package/dist/server/core-copies.d.ts.map +1 -0
- package/dist/server/core-copies.js +20 -0
- package/dist/server/core-copies.js.map +1 -0
- package/dist/server/edge.d.ts.map +1 -1
- package/dist/server/edge.js +2 -0
- package/dist/server/edge.js.map +1 -1
- package/dist/server/http.d.ts +11 -0
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +36 -0
- package/dist/server/http.js.map +1 -1
- package/dist/server/idempotency-lane.d.ts +6 -0
- package/dist/server/idempotency-lane.d.ts.map +1 -1
- package/dist/server/idempotency-lane.js +130 -73
- package/dist/server/idempotency-lane.js.map +1 -1
- package/dist/server/install.d.ts +4 -0
- package/dist/server/install.d.ts.map +1 -1
- package/dist/server/install.js +4 -0
- package/dist/server/install.js.map +1 -1
- package/dist/server/methods.d.ts +45 -0
- package/dist/server/methods.d.ts.map +1 -0
- package/dist/server/methods.js +37 -0
- package/dist/server/methods.js.map +1 -0
- package/dist/server/multipart.d.ts +63 -0
- package/dist/server/multipart.d.ts.map +1 -0
- package/dist/server/multipart.js +374 -0
- package/dist/server/multipart.js.map +1 -0
- package/dist/server/node-outcome-hook.d.ts +20 -0
- package/dist/server/node-outcome-hook.d.ts.map +1 -1
- package/dist/server/node-outcome-hook.js +15 -6
- package/dist/server/node-outcome-hook.js.map +1 -1
- package/dist/server/not-found-answer.d.ts +40 -0
- package/dist/server/not-found-answer.d.ts.map +1 -0
- package/dist/server/not-found-answer.js +56 -0
- package/dist/server/not-found-answer.js.map +1 -0
- package/dist/server/not-found.d.ts +23 -0
- package/dist/server/not-found.d.ts.map +1 -0
- package/dist/server/not-found.js +81 -0
- package/dist/server/not-found.js.map +1 -0
- package/dist/server/plugin.d.ts +26 -1
- package/dist/server/plugin.d.ts.map +1 -1
- package/dist/server/plugin.js.map +1 -1
- package/dist/server/proto-guard.d.ts +18 -5
- package/dist/server/proto-guard.d.ts.map +1 -1
- package/dist/server/proto-guard.js +114 -9
- package/dist/server/proto-guard.js.map +1 -1
- package/dist/server/query.d.ts +7 -0
- package/dist/server/query.d.ts.map +1 -1
- package/dist/server/query.js +47 -0
- package/dist/server/query.js.map +1 -1
- package/dist/server/registry.d.ts +13 -2
- package/dist/server/registry.d.ts.map +1 -1
- package/dist/server/request-context.d.ts +11 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js +33 -19
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/request-lineage.d.ts +8 -0
- package/dist/server/request-lineage.d.ts.map +1 -0
- package/dist/server/request-lineage.js +14 -0
- package/dist/server/request-lineage.js.map +1 -0
- package/dist/server/request-target.d.ts +31 -0
- package/dist/server/request-target.d.ts.map +1 -0
- package/dist/server/request-target.js +44 -0
- package/dist/server/request-target.js.map +1 -0
- package/dist/server/respond.d.ts +1 -1
- package/dist/server/respond.d.ts.map +1 -1
- package/dist/server/respond.js +16 -4
- package/dist/server/respond.js.map +1 -1
- package/dist/server/response-contract-lane.d.ts +19 -1
- package/dist/server/response-contract-lane.d.ts.map +1 -1
- package/dist/server/response-contract-lane.js +59 -14
- package/dist/server/response-contract-lane.js.map +1 -1
- package/dist/server/response-observer-runtime.d.ts +9 -5
- package/dist/server/response-observer-runtime.d.ts.map +1 -1
- package/dist/server/response-observer-runtime.js +10 -6
- 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.map +1 -1
- package/dist/server/server-types.d.ts +53 -3
- package/dist/server/server-types.d.ts.map +1 -1
- package/dist/server/server.d.ts +191 -19
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +1206 -218
- package/dist/server/server.js.map +1 -1
- package/dist/server/sse.d.ts.map +1 -1
- package/dist/server/sse.js +10 -7
- package/dist/server/sse.js.map +1 -1
- package/dist/server/static-headers.d.ts +3 -0
- package/dist/server/static-headers.d.ts.map +1 -1
- package/dist/server/static-headers.js +19 -1
- package/dist/server/static-headers.js.map +1 -1
- package/dist/server/transport-lane.d.ts +8 -0
- package/dist/server/transport-lane.d.ts.map +1 -1
- package/dist/server/transport-lane.js +9 -8
- package/dist/server/transport-lane.js.map +1 -1
- package/dist/server/validation.d.ts.map +1 -1
- package/dist/server/validation.js +5 -1
- package/dist/server/validation.js.map +1 -1
- package/dist/server/websocket.d.ts +10 -3
- package/dist/server/websocket.d.ts.map +1 -1
- package/dist/server/websocket.js +7 -4
- package/dist/server/websocket.js.map +1 -1
- package/dist/server/ws-bun.d.ts +2 -0
- package/dist/server/ws-bun.d.ts.map +1 -1
- package/dist/server/ws-bun.js +6 -3
- package/dist/server/ws-bun.js.map +1 -1
- package/dist/server/ws-hook.d.ts +2 -1
- package/dist/server/ws-hook.d.ts.map +1 -1
- package/dist/server.d.ts +7 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +4 -1
- package/dist/server.js.map +1 -1
- package/dist/single-copy-register.js +5 -0
- package/dist/single-copy-register.js.map +1 -1
- package/dist/single-copy.d.ts +46 -3
- package/dist/single-copy.d.ts.map +1 -1
- package/dist/single-copy.js +125 -12
- package/dist/single-copy.js.map +1 -1
- package/dist/tool-contract.d.ts +23 -0
- package/dist/tool-contract.d.ts.map +1 -1
- package/dist/tool-contract.js +37 -3
- package/dist/tool-contract.js.map +1 -1
- package/dist/transport-codec.d.ts.map +1 -1
- package/dist/transport-codec.js +2 -1
- package/dist/transport-codec.js.map +1 -1
- package/package.json +27 -2
- package/src/assurance.ts +52 -2
- package/src/capabilities.ts +81 -22
- package/src/diff.ts +31 -6
- package/src/durable-adapters.ts +20 -3
- package/src/durable-effect.ts +10 -3
- package/src/edge-kit.ts +7 -0
- package/src/effect-lifecycle.ts +5 -1
- package/src/effect-scope.ts +2 -1
- package/src/errors.ts +3 -0
- package/src/evidence.ts +237 -12
- package/src/idempotency.ts +72 -8
- package/src/index.ts +40 -3
- package/src/internal/code-unit-order.ts +8 -0
- package/src/internal/durable-saga-protocol.ts +2 -0
- package/src/internal/route-catalog.ts +19 -18
- package/src/internal/route-compiler.ts +62 -13
- package/src/internal/route-execution.ts +13 -3
- package/src/internal/route-lanes.ts +37 -28
- package/src/internal/route-program.ts +434 -20
- package/src/ledger.ts +11 -5
- package/src/manifest.ts +41 -36
- package/src/mount.ts +62 -0
- package/src/reflection.ts +84 -1
- package/src/response-observer.ts +1 -0
- package/src/router/overlap.ts +258 -0
- package/src/router/pattern.ts +349 -25
- package/src/router/router.ts +76 -51
- package/src/server/auth.ts +75 -0
- package/src/server/bare-error-lane.ts +12 -11
- package/src/server/body-lane.ts +46 -6
- package/src/server/body-parser.ts +241 -0
- package/src/server/body.ts +82 -9
- package/src/server/client-ip.ts +9 -0
- package/src/server/context.ts +219 -12
- package/src/server/contract.ts +54 -9
- package/src/server/core-copies.ts +21 -0
- package/src/server/edge.ts +2 -0
- package/src/server/http.ts +34 -0
- package/src/server/idempotency-lane.ts +147 -72
- package/src/server/install.ts +6 -0
- package/src/server/methods.ts +143 -0
- package/src/server/multipart.ts +424 -0
- package/src/server/node-outcome-hook.ts +40 -7
- package/src/server/not-found-answer.ts +109 -0
- package/src/server/not-found.ts +121 -0
- package/src/server/plugin.ts +45 -1
- package/src/server/proto-guard.ts +128 -9
- package/src/server/query.ts +49 -0
- package/src/server/registry.ts +17 -2
- package/src/server/request-context.ts +44 -16
- package/src/server/request-lineage.ts +15 -0
- package/src/server/request-target.ts +46 -0
- package/src/server/respond.ts +14 -3
- package/src/server/response-contract-lane.ts +97 -16
- package/src/server/response-observer-runtime.ts +37 -8
- package/src/server/runtime-core.ts +2 -0
- package/src/server/server-types.ts +59 -2
- package/src/server/server.ts +1613 -269
- package/src/server/sse.ts +10 -8
- package/src/server/static-headers.ts +22 -1
- package/src/server/transport-lane.ts +17 -9
- package/src/server/validation.ts +7 -1
- package/src/server/websocket.ts +23 -9
- package/src/server/ws-bun.ts +8 -3
- package/src/server/ws-hook.ts +2 -1
- package/src/server.ts +38 -2
- package/src/single-copy-register.ts +5 -0
- package/src/single-copy.ts +160 -12
- package/src/tool-contract.ts +66 -3
- package/src/transport-codec.ts +2 -1
|
@@ -31,6 +31,7 @@ export interface RouteLaneSelection {
|
|
|
31
31
|
|
|
32
32
|
export function selectRouteLanes(options: {
|
|
33
33
|
readonly schema: RouteSchema | undefined
|
|
34
|
+
readonly hasAuth?: boolean
|
|
34
35
|
readonly hasIdempotency: boolean
|
|
35
36
|
readonly hasLedger: boolean
|
|
36
37
|
readonly hasResponseContract: boolean
|
|
@@ -56,9 +57,15 @@ export function selectRouteLanes(options: {
|
|
|
56
57
|
defaultOnValidationError,
|
|
57
58
|
} = options
|
|
58
59
|
|
|
60
|
+
const authBeforeValidation =
|
|
61
|
+
options.hasAuth === true || schema?.validationOrder === "auth-before-validation"
|
|
62
|
+
// No fused lane validates params, headers, or cookies: any of those schemas keeps the stage runner.
|
|
63
|
+
const plain =
|
|
64
|
+
schema?.params === undefined && schema?.headers === undefined && schema?.cookies === undefined
|
|
65
|
+
|
|
59
66
|
const bare =
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
!authBeforeValidation &&
|
|
68
|
+
plain &&
|
|
62
69
|
schema?.body === undefined &&
|
|
63
70
|
schema?.query === undefined &&
|
|
64
71
|
!hasIdempotency &&
|
|
@@ -76,11 +83,11 @@ export function selectRouteLanes(options: {
|
|
|
76
83
|
// exactly `validationError(issues)`, so any recovery semantics keep the generic lane.
|
|
77
84
|
const fusedQuery =
|
|
78
85
|
!bare &&
|
|
86
|
+
!authBeforeValidation &&
|
|
79
87
|
!hasResponseContract &&
|
|
80
88
|
schema?.query !== undefined &&
|
|
81
89
|
schema.body === undefined &&
|
|
82
|
-
|
|
83
|
-
schema.headers === undefined &&
|
|
90
|
+
plain &&
|
|
84
91
|
schema.onValidationError === undefined &&
|
|
85
92
|
!defaultOnValidationError &&
|
|
86
93
|
!hasIdempotency &&
|
|
@@ -92,11 +99,11 @@ export function selectRouteLanes(options: {
|
|
|
92
99
|
around === 0
|
|
93
100
|
|
|
94
101
|
const bodyOnly =
|
|
102
|
+
!authBeforeValidation &&
|
|
95
103
|
!hasResponseContract &&
|
|
96
104
|
schema?.body !== undefined &&
|
|
97
105
|
schema.query === undefined &&
|
|
98
|
-
|
|
99
|
-
schema.headers === undefined &&
|
|
106
|
+
plain &&
|
|
100
107
|
derives === 0 &&
|
|
101
108
|
beforeHandle === 0 &&
|
|
102
109
|
afterHandle === 0 &&
|
|
@@ -104,6 +111,7 @@ export function selectRouteLanes(options: {
|
|
|
104
111
|
|
|
105
112
|
const fusedBody =
|
|
106
113
|
!bare &&
|
|
114
|
+
!authBeforeValidation &&
|
|
107
115
|
bodyOnly &&
|
|
108
116
|
schema.onValidationError === undefined &&
|
|
109
117
|
!defaultOnValidationError &&
|
|
@@ -111,28 +119,29 @@ export function selectRouteLanes(options: {
|
|
|
111
119
|
!hasLedger &&
|
|
112
120
|
around === 0
|
|
113
121
|
|
|
114
|
-
const lane: RouteExecutionLane =
|
|
115
|
-
? "
|
|
116
|
-
:
|
|
117
|
-
? "
|
|
118
|
-
:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
122
|
+
const lane: RouteExecutionLane = authBeforeValidation
|
|
123
|
+
? "lifecycle"
|
|
124
|
+
: bare
|
|
125
|
+
? "bare"
|
|
126
|
+
: bodyOnly
|
|
127
|
+
? "body"
|
|
128
|
+
: !hasResponseContract &&
|
|
129
|
+
schema?.body === undefined &&
|
|
130
|
+
schema?.query !== undefined &&
|
|
131
|
+
plain &&
|
|
132
|
+
derives === 0 &&
|
|
133
|
+
beforeHandle === 0 &&
|
|
134
|
+
afterHandle === 0 &&
|
|
135
|
+
onError === 0
|
|
136
|
+
? "query"
|
|
137
|
+
: "lifecycle"
|
|
129
138
|
|
|
130
139
|
// Lifecycle routes with no params schema are the common middleware shape: derive/before plus an
|
|
131
140
|
// optional query or body schema. Select their complete validation stage at registration so the
|
|
132
141
|
// request path never re-checks params/body presence. Parameter-schema routes retain the generic
|
|
133
142
|
// lifecycle runner until their more involved recovery matrix is selected explicitly.
|
|
134
143
|
const lifecycleLane: LifecycleExecutionLane =
|
|
135
|
-
lane !== "lifecycle" ||
|
|
144
|
+
lane !== "lifecycle" || !plain
|
|
136
145
|
? undefined
|
|
137
146
|
: schema?.body !== undefined
|
|
138
147
|
? schema.query !== undefined
|
|
@@ -145,21 +154,21 @@ export function selectRouteLanes(options: {
|
|
|
145
154
|
// The realistic middleware shape is commonly exactly one synchronous-or-async derive followed by
|
|
146
155
|
// one before hook. Keep the generic runner for every route that can observe decorations, after hooks,
|
|
147
156
|
// error hooks, or response contracts; this lane only removes the two per-request hook-loop dispatches
|
|
148
|
-
// and preserves the same async continuations and error handling. A params or
|
|
149
|
-
// back to the generic lane because the fused builder has no
|
|
157
|
+
// and preserves the same async continuations and error handling. A params, headers, or cookies schema
|
|
158
|
+
// routes back to the generic lane because the fused builder has no validation step for them
|
|
150
159
|
// baked in - adding them would be more code than it saves. A `body` schema is the same story
|
|
151
160
|
// because the fused derive-before / derive-before-after builders don't parse the body: routes with a
|
|
152
161
|
// body schema are routed to the `body-derive-before` / `body-derive-before-after` lane below.
|
|
153
162
|
const lifecycleHookLane =
|
|
154
163
|
lane === "lifecycle" &&
|
|
164
|
+
!authBeforeValidation &&
|
|
155
165
|
!hasResponseContract &&
|
|
156
166
|
!hasDecorations &&
|
|
157
167
|
!hasIdempotency &&
|
|
158
168
|
!hasLedger &&
|
|
159
169
|
schema?.onValidationError === undefined &&
|
|
160
170
|
!defaultOnValidationError &&
|
|
161
|
-
|
|
162
|
-
schema?.headers === undefined &&
|
|
171
|
+
plain &&
|
|
163
172
|
schema?.body === undefined &&
|
|
164
173
|
derives === 1 &&
|
|
165
174
|
beforeHandle === 1 &&
|
|
@@ -177,10 +186,10 @@ export function selectRouteLanes(options: {
|
|
|
177
186
|
// win `fusedBody` already gives for the no-hooks body shape.
|
|
178
187
|
const fusedBodyLifecycle =
|
|
179
188
|
lane === "lifecycle" &&
|
|
189
|
+
!authBeforeValidation &&
|
|
180
190
|
schema?.body !== undefined &&
|
|
181
191
|
schema?.query === undefined &&
|
|
182
|
-
|
|
183
|
-
schema?.headers === undefined &&
|
|
192
|
+
plain &&
|
|
184
193
|
schema?.onValidationError === undefined &&
|
|
185
194
|
!defaultOnValidationError &&
|
|
186
195
|
!hasResponseContract &&
|
|
@@ -8,24 +8,27 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { StandardSchemaV1 } from "../schema/standard.ts"
|
|
11
|
+
import { pathnameOf } from "../server/http.ts"
|
|
11
12
|
import type { Registry } from "../server/registry.ts"
|
|
12
13
|
import {
|
|
13
14
|
CONTEXT_SET,
|
|
14
15
|
EMPTY_RESPONSE_CONTROLS,
|
|
15
16
|
isResponseResult,
|
|
16
17
|
type ResponseResult,
|
|
18
|
+
status,
|
|
17
19
|
} from "../server/runtime-core.ts"
|
|
18
20
|
import type { CtxSet, MaybePromise, RawContext, RequestSource, Server } from "../server/server.ts"
|
|
19
21
|
import type {
|
|
20
22
|
InternalHandler,
|
|
21
23
|
RawAfterHandle,
|
|
24
|
+
RawAuthStage,
|
|
22
25
|
RawBeforeHandle,
|
|
23
26
|
RawDerive,
|
|
24
27
|
RawErrorHandler,
|
|
25
28
|
RouteEntry,
|
|
26
29
|
} from "./route-execution.ts"
|
|
27
30
|
|
|
28
|
-
export type ProgramValidationKind = "headers" | "params" | "body" | "query"
|
|
31
|
+
export type ProgramValidationKind = "headers" | "cookies" | "params" | "body" | "query"
|
|
29
32
|
|
|
30
33
|
export type RouteProgramStage =
|
|
31
34
|
| { readonly kind: ProgramValidationKind; readonly schema: StandardSchemaV1 }
|
|
@@ -37,6 +40,11 @@ export type RouteProgramStage =
|
|
|
37
40
|
/** The complete registration-time lifecycle, in semantic execution order. */
|
|
38
41
|
export interface RouteProgram {
|
|
39
42
|
readonly stages: readonly RouteProgramStage[]
|
|
43
|
+
readonly validationOrder: "validate-before-auth" | "auth-before-validation"
|
|
44
|
+
readonly authBeforeValidation: boolean
|
|
45
|
+
readonly authStages: readonly RawAuthStage[]
|
|
46
|
+
/** Protected route fast lane: auth plus validation/handler, with no general lifecycle hooks. */
|
|
47
|
+
readonly authFastLane: boolean
|
|
40
48
|
readonly validationStages: readonly Extract<RouteProgramStage, { kind: ProgramValidationKind }>[]
|
|
41
49
|
readonly derives: readonly RawDerive[]
|
|
42
50
|
readonly beforeHandle: readonly RawBeforeHandle[]
|
|
@@ -54,6 +62,7 @@ export interface RouteProgramInput {
|
|
|
54
62
|
readonly schema: RouteEntry["schema"]
|
|
55
63
|
readonly handler: InternalHandler
|
|
56
64
|
readonly derives: readonly RawDerive[]
|
|
65
|
+
readonly authStages?: readonly RawAuthStage[]
|
|
57
66
|
readonly beforeHandle: readonly RawBeforeHandle[]
|
|
58
67
|
readonly afterHandle: readonly RawAfterHandle[]
|
|
59
68
|
readonly onError: readonly RawErrorHandler[]
|
|
@@ -69,25 +78,18 @@ export function compileRouteProgram(input: RouteProgramInput): RouteProgram {
|
|
|
69
78
|
const stages: RouteProgramStage[] = []
|
|
70
79
|
const validationStages: Extract<RouteProgramStage, { kind: ProgramValidationKind }>[] = []
|
|
71
80
|
const schema = input.schema
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
stages.push(stage)
|
|
85
|
-
validationStages.push(stage)
|
|
86
|
-
}
|
|
87
|
-
if (schema?.query !== undefined) {
|
|
88
|
-
const stage = { kind: "query", schema: schema.query } as const
|
|
89
|
-
stages.push(stage)
|
|
90
|
-
validationStages.push(stage)
|
|
81
|
+
const authStages = input.authStages ?? []
|
|
82
|
+
const authBeforeValidation =
|
|
83
|
+
authStages.length > 0
|
|
84
|
+
? schema?.validationOrder !== "validate-before-auth"
|
|
85
|
+
: schema?.validationOrder === "auth-before-validation"
|
|
86
|
+
for (const kind of ["headers", "cookies", "params", "body", "query"] as const) {
|
|
87
|
+
const validator = schema?.[kind]
|
|
88
|
+
if (validator !== undefined) {
|
|
89
|
+
const stage = { kind, schema: validator }
|
|
90
|
+
stages.push(stage)
|
|
91
|
+
validationStages.push(stage)
|
|
92
|
+
}
|
|
91
93
|
}
|
|
92
94
|
for (const derive of input.derives) stages.push({ kind: "derive", run: derive })
|
|
93
95
|
for (const before of input.beforeHandle) stages.push({ kind: "before", run: before })
|
|
@@ -96,6 +98,14 @@ export function compileRouteProgram(input: RouteProgramInput): RouteProgram {
|
|
|
96
98
|
|
|
97
99
|
return Object.freeze({
|
|
98
100
|
stages: Object.freeze(stages),
|
|
101
|
+
validationOrder: authBeforeValidation ? "auth-before-validation" : "validate-before-auth",
|
|
102
|
+
authBeforeValidation,
|
|
103
|
+
authStages: Object.freeze([...authStages]),
|
|
104
|
+
authFastLane:
|
|
105
|
+
authStages.length > 0 &&
|
|
106
|
+
input.derives.length === 0 &&
|
|
107
|
+
input.beforeHandle.length === 0 &&
|
|
108
|
+
input.afterHandle.length === 0,
|
|
99
109
|
validationStages: Object.freeze(validationStages),
|
|
100
110
|
derives: Object.freeze([...input.derives]),
|
|
101
111
|
beforeHandle: Object.freeze([...input.beforeHandle]),
|
|
@@ -146,8 +156,395 @@ function isEarly(value: unknown): value is Response | ResponseResult {
|
|
|
146
156
|
return value instanceof Response || isResponseResult(value)
|
|
147
157
|
}
|
|
148
158
|
|
|
159
|
+
function authHeadersOf(source: RequestSource): import("../server/auth.ts").AuthHeaders {
|
|
160
|
+
return Object.freeze({
|
|
161
|
+
get: (name: string) => source.headers.get(name),
|
|
162
|
+
has: (name: string) => source.headers.has(name),
|
|
163
|
+
forEach: (callback: (value: string, name: string) => void) => {
|
|
164
|
+
source.headers.forEach(callback)
|
|
165
|
+
},
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function authenticationInputOf(
|
|
170
|
+
source: RequestSource,
|
|
171
|
+
ctx: RawContext,
|
|
172
|
+
): import("../server/auth.ts").AuthenticationInput<unknown> {
|
|
173
|
+
return Object.freeze({
|
|
174
|
+
method: source.method,
|
|
175
|
+
pathname: pathnameOf(source.url),
|
|
176
|
+
headers: authHeadersOf(source),
|
|
177
|
+
params: Object.freeze({ ...ctx.params }),
|
|
178
|
+
env: ctx.env,
|
|
179
|
+
clientIp: ctx.clientIp,
|
|
180
|
+
signal: ctx.signal,
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function defaultAuthenticationResponse(
|
|
185
|
+
reason: import("../server/auth.ts").AuthenticationFailureReason | undefined,
|
|
186
|
+
): ResponseResult {
|
|
187
|
+
if (reason === "forbidden") return status(403, { ok: false, error: "forbidden" })
|
|
188
|
+
if (reason === "unavailable") return status(503, { ok: false, error: "auth_unavailable" })
|
|
189
|
+
return status(401, { ok: false, error: "unauthorized" })
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function validAuthenticationFailureReason(
|
|
193
|
+
value: unknown,
|
|
194
|
+
): value is import("../server/auth.ts").AuthenticationFailureReason | undefined {
|
|
195
|
+
return (
|
|
196
|
+
value === undefined ||
|
|
197
|
+
value === "unauthenticated" ||
|
|
198
|
+
value === "forbidden" ||
|
|
199
|
+
value === "unavailable"
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function runAuthenticationStages<T>(
|
|
204
|
+
host: RouteProgramRuntime,
|
|
205
|
+
entry: RouteEntry,
|
|
206
|
+
program: RouteProgram,
|
|
207
|
+
source: RequestSource,
|
|
208
|
+
ctx: RawContext,
|
|
209
|
+
finalize: (result: unknown, set: CtxSet) => T,
|
|
210
|
+
wrapResponse: (response: Response | ResponseResult) => T,
|
|
211
|
+
start = 0,
|
|
212
|
+
validationComplete = false,
|
|
213
|
+
): ProgramResult<T> {
|
|
214
|
+
if (start >= program.authStages.length) {
|
|
215
|
+
if (program.authFastLane) {
|
|
216
|
+
const validationIndex = validationComplete ? program.validationStages.length : 0
|
|
217
|
+
return runFastAuthenticatedProgram(
|
|
218
|
+
host,
|
|
219
|
+
entry,
|
|
220
|
+
program,
|
|
221
|
+
source,
|
|
222
|
+
ctx,
|
|
223
|
+
finalize,
|
|
224
|
+
wrapResponse,
|
|
225
|
+
validationIndex,
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
return validationComplete
|
|
229
|
+
? runSync(
|
|
230
|
+
host,
|
|
231
|
+
entry,
|
|
232
|
+
program,
|
|
233
|
+
source,
|
|
234
|
+
ctx,
|
|
235
|
+
finalize,
|
|
236
|
+
wrapResponse,
|
|
237
|
+
program.validationStages.length,
|
|
238
|
+
undefined,
|
|
239
|
+
)
|
|
240
|
+
: runValidatedProgram(host, entry, program, source, ctx, finalize, wrapResponse)
|
|
241
|
+
}
|
|
242
|
+
const input = authenticationInputOf(source, ctx)
|
|
243
|
+
const stage = program.authStages[start]!
|
|
244
|
+
let outcome: MaybePromise<import("../server/auth.ts").AuthenticationResult<unknown>>
|
|
245
|
+
try {
|
|
246
|
+
outcome = stage.run(input)
|
|
247
|
+
} catch {
|
|
248
|
+
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx))
|
|
249
|
+
}
|
|
250
|
+
if (outcome instanceof Promise) {
|
|
251
|
+
return outcome.then(
|
|
252
|
+
(settled) =>
|
|
253
|
+
finishAuthentication(
|
|
254
|
+
host,
|
|
255
|
+
entry,
|
|
256
|
+
program,
|
|
257
|
+
source,
|
|
258
|
+
ctx,
|
|
259
|
+
finalize,
|
|
260
|
+
wrapResponse,
|
|
261
|
+
settled,
|
|
262
|
+
start + 1,
|
|
263
|
+
validationComplete,
|
|
264
|
+
),
|
|
265
|
+
() => finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx)),
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
return finishAuthentication(
|
|
269
|
+
host,
|
|
270
|
+
entry,
|
|
271
|
+
program,
|
|
272
|
+
source,
|
|
273
|
+
ctx,
|
|
274
|
+
finalize,
|
|
275
|
+
wrapResponse,
|
|
276
|
+
outcome,
|
|
277
|
+
start + 1,
|
|
278
|
+
validationComplete,
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function finishAuthentication<T>(
|
|
283
|
+
host: RouteProgramRuntime,
|
|
284
|
+
entry: RouteEntry,
|
|
285
|
+
program: RouteProgram,
|
|
286
|
+
source: RequestSource,
|
|
287
|
+
ctx: RawContext,
|
|
288
|
+
finalize: (result: unknown, set: CtxSet) => T,
|
|
289
|
+
wrapResponse: (response: Response | ResponseResult) => T,
|
|
290
|
+
result: import("../server/auth.ts").AuthenticationResult<unknown>,
|
|
291
|
+
next: number,
|
|
292
|
+
validationComplete = false,
|
|
293
|
+
): ProgramResult<T> {
|
|
294
|
+
if (
|
|
295
|
+
result === null ||
|
|
296
|
+
typeof result !== "object" ||
|
|
297
|
+
(result.kind !== "authenticated" && result.kind !== "rejected")
|
|
298
|
+
) {
|
|
299
|
+
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx))
|
|
300
|
+
}
|
|
301
|
+
if (result.kind === "rejected") {
|
|
302
|
+
if (!validAuthenticationFailureReason(result.reason)) {
|
|
303
|
+
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx))
|
|
304
|
+
}
|
|
305
|
+
if (
|
|
306
|
+
result.response !== undefined &&
|
|
307
|
+
!(result.response instanceof Response) &&
|
|
308
|
+
!isResponseResult(result.response)
|
|
309
|
+
) {
|
|
310
|
+
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx))
|
|
311
|
+
}
|
|
312
|
+
const response = result.response ?? defaultAuthenticationResponse(result.reason)
|
|
313
|
+
return finalize(response, responseSet(ctx))
|
|
314
|
+
}
|
|
315
|
+
if (result.principal === null || result.principal === undefined) {
|
|
316
|
+
return finalize(defaultAuthenticationResponse("unavailable"), responseSet(ctx))
|
|
317
|
+
}
|
|
318
|
+
Object.defineProperty(ctx, "principal", {
|
|
319
|
+
value: result.principal,
|
|
320
|
+
enumerable: true,
|
|
321
|
+
configurable: true,
|
|
322
|
+
writable: false,
|
|
323
|
+
})
|
|
324
|
+
return runAuthenticationStages(
|
|
325
|
+
host,
|
|
326
|
+
entry,
|
|
327
|
+
program,
|
|
328
|
+
source,
|
|
329
|
+
ctx,
|
|
330
|
+
finalize,
|
|
331
|
+
wrapResponse,
|
|
332
|
+
next,
|
|
333
|
+
validationComplete,
|
|
334
|
+
)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Validate first only when a route explicitly opts out of the protected auth-first default. */
|
|
338
|
+
function runFastAuthenticatedProgram<T>(
|
|
339
|
+
host: RouteProgramRuntime,
|
|
340
|
+
entry: RouteEntry,
|
|
341
|
+
program: RouteProgram,
|
|
342
|
+
source: RequestSource,
|
|
343
|
+
ctx: RawContext,
|
|
344
|
+
finalize: (result: unknown, set: CtxSet) => T,
|
|
345
|
+
wrapResponse: (response: Response | ResponseResult) => T,
|
|
346
|
+
validationIndex: number,
|
|
347
|
+
): ProgramResult<T> {
|
|
348
|
+
for (let i = validationIndex; i < program.validationStages.length; i++) {
|
|
349
|
+
const stage = program.validationStages[i]!
|
|
350
|
+
const outcome =
|
|
351
|
+
stage.kind === "body"
|
|
352
|
+
? host.readProgramBody(entry, source, ctx)
|
|
353
|
+
: host.validateProgramStage(entry, stage, source, ctx)
|
|
354
|
+
if (outcome instanceof Promise) {
|
|
355
|
+
return continueFastAuthenticatedProgram(
|
|
356
|
+
host,
|
|
357
|
+
entry,
|
|
358
|
+
program,
|
|
359
|
+
source,
|
|
360
|
+
ctx,
|
|
361
|
+
finalize,
|
|
362
|
+
wrapResponse,
|
|
363
|
+
i + 1,
|
|
364
|
+
outcome,
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
if (outcome !== undefined) return wrapResponse(outcome)
|
|
368
|
+
}
|
|
369
|
+
return runFastAuthenticatedHandler(host, entry, program, ctx, finalize, wrapResponse)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async function continueFastAuthenticatedProgram<T>(
|
|
373
|
+
host: RouteProgramRuntime,
|
|
374
|
+
entry: RouteEntry,
|
|
375
|
+
program: RouteProgram,
|
|
376
|
+
source: RequestSource,
|
|
377
|
+
ctx: RawContext,
|
|
378
|
+
finalize: (result: unknown, set: CtxSet) => T,
|
|
379
|
+
wrapResponse: (response: Response | ResponseResult) => T,
|
|
380
|
+
validationIndex: number,
|
|
381
|
+
pending: Promise<unknown>,
|
|
382
|
+
): Promise<T> {
|
|
383
|
+
try {
|
|
384
|
+
const settled = await pending
|
|
385
|
+
if (settled !== undefined) return wrapResponse(settled as Response | ResponseResult)
|
|
386
|
+
return await runFastAuthenticatedProgram(
|
|
387
|
+
host,
|
|
388
|
+
entry,
|
|
389
|
+
program,
|
|
390
|
+
source,
|
|
391
|
+
ctx,
|
|
392
|
+
finalize,
|
|
393
|
+
wrapResponse,
|
|
394
|
+
validationIndex,
|
|
395
|
+
)
|
|
396
|
+
} catch (error) {
|
|
397
|
+
return await host.handleProgramError(entry, error, ctx, finalize, wrapResponse)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function runFastAuthenticatedHandler<T>(
|
|
402
|
+
host: RouteProgramRuntime,
|
|
403
|
+
entry: RouteEntry,
|
|
404
|
+
program: RouteProgram,
|
|
405
|
+
ctx: RawContext,
|
|
406
|
+
finalize: (result: unknown, set: CtxSet) => T,
|
|
407
|
+
wrapResponse: (response: Response | ResponseResult) => T,
|
|
408
|
+
): ProgramResult<T> {
|
|
409
|
+
let result: unknown
|
|
410
|
+
try {
|
|
411
|
+
result = program.handler(ctx)
|
|
412
|
+
} catch (error) {
|
|
413
|
+
return host.handleProgramError(entry, error, ctx, finalize, wrapResponse)
|
|
414
|
+
}
|
|
415
|
+
if (result instanceof Promise) {
|
|
416
|
+
return result.then(
|
|
417
|
+
(value) => host.finishProgramResult(entry, ctx, value, finalize, wrapResponse),
|
|
418
|
+
(error) => host.handleProgramError(entry, error, ctx, finalize, wrapResponse),
|
|
419
|
+
)
|
|
420
|
+
}
|
|
421
|
+
return host.finishProgramResult(entry, ctx, result, finalize, wrapResponse)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function runValidationBeforeAuthentication<T>(
|
|
425
|
+
host: RouteProgramRuntime,
|
|
426
|
+
entry: RouteEntry,
|
|
427
|
+
program: RouteProgram,
|
|
428
|
+
source: RequestSource,
|
|
429
|
+
ctx: RawContext,
|
|
430
|
+
finalize: (result: unknown, set: CtxSet) => T,
|
|
431
|
+
wrapResponse: (response: Response | ResponseResult) => T,
|
|
432
|
+
validationIndex = 0,
|
|
433
|
+
): ProgramResult<T> {
|
|
434
|
+
for (let i = validationIndex; i < program.validationStages.length; i++) {
|
|
435
|
+
const stage = program.validationStages[i]!
|
|
436
|
+
const outcome =
|
|
437
|
+
stage.kind === "body"
|
|
438
|
+
? host.readProgramBody(entry, source, ctx)
|
|
439
|
+
: host.validateProgramStage(entry, stage, source, ctx)
|
|
440
|
+
if (outcome instanceof Promise) {
|
|
441
|
+
return continueValidationBeforeAuthentication(
|
|
442
|
+
host,
|
|
443
|
+
entry,
|
|
444
|
+
program,
|
|
445
|
+
source,
|
|
446
|
+
ctx,
|
|
447
|
+
finalize,
|
|
448
|
+
wrapResponse,
|
|
449
|
+
i + 1,
|
|
450
|
+
outcome,
|
|
451
|
+
)
|
|
452
|
+
}
|
|
453
|
+
if (outcome !== undefined) return wrapResponse(outcome)
|
|
454
|
+
}
|
|
455
|
+
return runAuthenticationStages(host, entry, program, source, ctx, finalize, wrapResponse, 0, true)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async function continueValidationBeforeAuthentication<T>(
|
|
459
|
+
host: RouteProgramRuntime,
|
|
460
|
+
entry: RouteEntry,
|
|
461
|
+
program: RouteProgram,
|
|
462
|
+
source: RequestSource,
|
|
463
|
+
ctx: RawContext,
|
|
464
|
+
finalize: (result: unknown, set: CtxSet) => T,
|
|
465
|
+
wrapResponse: (response: Response | ResponseResult) => T,
|
|
466
|
+
validationIndex: number,
|
|
467
|
+
pending: Promise<unknown>,
|
|
468
|
+
): Promise<T> {
|
|
469
|
+
try {
|
|
470
|
+
const settled = await pending
|
|
471
|
+
if (settled !== undefined) return wrapResponse(settled as Response | ResponseResult)
|
|
472
|
+
return await runValidationBeforeAuthentication(
|
|
473
|
+
host,
|
|
474
|
+
entry,
|
|
475
|
+
program,
|
|
476
|
+
source,
|
|
477
|
+
ctx,
|
|
478
|
+
finalize,
|
|
479
|
+
wrapResponse,
|
|
480
|
+
validationIndex,
|
|
481
|
+
)
|
|
482
|
+
} catch (error) {
|
|
483
|
+
return await host.handleProgramError(entry, error, ctx, finalize, wrapResponse)
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function runValidatedProgram<T>(
|
|
488
|
+
host: RouteProgramRuntime,
|
|
489
|
+
entry: RouteEntry,
|
|
490
|
+
program: RouteProgram,
|
|
491
|
+
source: RequestSource,
|
|
492
|
+
ctx: RawContext,
|
|
493
|
+
finalize: (result: unknown, set: CtxSet) => T,
|
|
494
|
+
wrapResponse: (response: Response | ResponseResult) => T,
|
|
495
|
+
): ProgramResult<T> {
|
|
496
|
+
if (program.hasDecorations) Object.assign(ctx, program.decorations)
|
|
497
|
+
return runSync(host, entry, program, source, ctx, finalize, wrapResponse, 0, undefined)
|
|
498
|
+
}
|
|
499
|
+
|
|
149
500
|
type ProgramResult<T> = MaybePromise<T>
|
|
150
501
|
|
|
502
|
+
/** Opt-in sensitive-route lane: run authentication/policy hooks before parsing untrusted input. */
|
|
503
|
+
async function runAuthBeforeValidation<T>(
|
|
504
|
+
host: RouteProgramRuntime,
|
|
505
|
+
entry: RouteEntry,
|
|
506
|
+
program: RouteProgram,
|
|
507
|
+
source: RequestSource,
|
|
508
|
+
ctx: RawContext,
|
|
509
|
+
finalize: (result: unknown, set: CtxSet) => T,
|
|
510
|
+
wrapResponse: (response: Response | ResponseResult) => T,
|
|
511
|
+
): Promise<T> {
|
|
512
|
+
try {
|
|
513
|
+
if (program.hasDecorations) Object.assign(ctx, program.decorations)
|
|
514
|
+
|
|
515
|
+
for (const derive of program.derives) {
|
|
516
|
+
const result = derive(ctx)
|
|
517
|
+
const settled = result instanceof Promise ? await result : result
|
|
518
|
+
if (isEarly(settled)) return finalize(settled, responseSet(ctx))
|
|
519
|
+
Object.assign(ctx, settled)
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
for (const before of program.beforeHandle) {
|
|
523
|
+
const result = before(ctx)
|
|
524
|
+
const settled = result instanceof Promise ? await result : result
|
|
525
|
+
if (settled !== undefined) return finalize(settled, responseSet(ctx))
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
for (const stage of program.validationStages) {
|
|
529
|
+
const result =
|
|
530
|
+
stage.kind === "body"
|
|
531
|
+
? await host.readProgramBody(entry, source, ctx)
|
|
532
|
+
: host.validateProgramStage(entry, stage, source, ctx)
|
|
533
|
+
const settled = result instanceof Promise ? await result : result
|
|
534
|
+
if (settled !== undefined) return wrapResponse(settled)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
let result = await program.handler(ctx)
|
|
538
|
+
for (const after of program.afterHandle) {
|
|
539
|
+
const transformed = after(result, ctx)
|
|
540
|
+
result = transformed instanceof Promise ? await transformed : transformed
|
|
541
|
+
}
|
|
542
|
+
return await host.finishProgramResult(entry, ctx, result, finalize, wrapResponse)
|
|
543
|
+
} catch (error) {
|
|
544
|
+
return await host.handleProgramError(entry, error, ctx, finalize, wrapResponse)
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
151
548
|
type PendingPhase =
|
|
152
549
|
| { readonly kind: "validation"; readonly next: number }
|
|
153
550
|
| { readonly kind: "derive"; readonly next: number }
|
|
@@ -167,6 +564,23 @@ export function executeRouteProgram<T, R extends Registry, Ctx>(
|
|
|
167
564
|
): ProgramResult<T> {
|
|
168
565
|
const host = runtime as unknown as RouteProgramRuntime
|
|
169
566
|
try {
|
|
567
|
+
if (program.authStages.length > 0) {
|
|
568
|
+
if (program.hasDecorations) Object.assign(ctx, program.decorations)
|
|
569
|
+
return program.authBeforeValidation
|
|
570
|
+
? runAuthenticationStages(host, entry, program, source, ctx, finalize, wrapResponse)
|
|
571
|
+
: runValidationBeforeAuthentication(
|
|
572
|
+
host,
|
|
573
|
+
entry,
|
|
574
|
+
program,
|
|
575
|
+
source,
|
|
576
|
+
ctx,
|
|
577
|
+
finalize,
|
|
578
|
+
wrapResponse,
|
|
579
|
+
)
|
|
580
|
+
}
|
|
581
|
+
if (program.validationOrder === "auth-before-validation") {
|
|
582
|
+
return runAuthBeforeValidation(host, entry, program, source, ctx, finalize, wrapResponse)
|
|
583
|
+
}
|
|
170
584
|
if (program.hasDecorations) Object.assign(ctx, program.decorations)
|
|
171
585
|
return runSync(host, entry, program, source, ctx, finalize, wrapResponse, 0, undefined)
|
|
172
586
|
} catch (error) {
|