@nifrajs/core 3.3.0 → 4.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 +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 +6 -1
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +36 -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 +36 -1
- package/dist/evidence.d.ts.map +1 -1
- package/dist/evidence.js +219 -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 +48 -9
- package/dist/idempotency.js.map +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- 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 +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +52 -46
- 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/cookies.d.ts +4 -1
- package/dist/server/cookies.d.ts.map +1 -1
- package/dist/server/cookies.js +40 -2
- package/dist/server/cookies.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 +55 -3
- package/dist/server/server-types.d.ts.map +1 -1
- package/dist/server/server.d.ts +194 -19
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +1342 -274
- 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 +8 -5
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +5 -2
- 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 +54 -7
- 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 +302 -12
- package/src/idempotency.ts +80 -9
- package/src/index.ts +41 -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 +52 -46
- 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/cookies.ts +34 -2
- 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 +61 -2
- package/src/server/server.ts +1798 -355
- 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 +39 -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
package/src/diff.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* yield `info` - never a silent pass presented as proof.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import { type ProjectEvidenceSnapshot, reflectedRoutesFromEvidence } from "./evidence.ts"
|
|
21
22
|
import {
|
|
22
23
|
type JsonSchema,
|
|
23
24
|
type ReflectedRoute,
|
|
@@ -33,8 +34,10 @@ export interface SchemaSnapshot {
|
|
|
33
34
|
|
|
34
35
|
export interface RouteSnapshotSchema {
|
|
35
36
|
readonly headers?: SchemaSnapshot
|
|
37
|
+
readonly cookies?: SchemaSnapshot
|
|
36
38
|
readonly body?: SchemaSnapshot
|
|
37
39
|
readonly query?: SchemaSnapshot
|
|
40
|
+
readonly params?: SchemaSnapshot
|
|
38
41
|
readonly response?: SchemaSnapshot
|
|
39
42
|
readonly sse?: SchemaSnapshot
|
|
40
43
|
readonly errors?: Readonly<Record<string, SchemaSnapshot>>
|
|
@@ -54,7 +57,16 @@ export interface RouteChange {
|
|
|
54
57
|
readonly method: string
|
|
55
58
|
readonly path: string
|
|
56
59
|
/** Which part of the contract changed; "route" for add/remove of the whole route. */
|
|
57
|
-
readonly section:
|
|
60
|
+
readonly section:
|
|
61
|
+
| "route"
|
|
62
|
+
| "headers"
|
|
63
|
+
| "cookies"
|
|
64
|
+
| "body"
|
|
65
|
+
| "query"
|
|
66
|
+
| "params"
|
|
67
|
+
| "response"
|
|
68
|
+
| "sse"
|
|
69
|
+
| "errors"
|
|
58
70
|
/** The top-level field (or error status) the change is about, when field-granular. */
|
|
59
71
|
readonly field?: string
|
|
60
72
|
readonly message: string
|
|
@@ -87,9 +99,19 @@ const schemaSnapshot = (
|
|
|
87
99
|
* `JSON.stringify` unchanged.
|
|
88
100
|
*/
|
|
89
101
|
export function snapshotRoutes(source: unknown): readonly RouteSnapshot[] {
|
|
102
|
+
// Preserve the historical registration order of this low-level helper. The canonical evidence
|
|
103
|
+
// seam intentionally sorts routes for persistence, while callers using snapshotRoutes directly
|
|
104
|
+
// may use the order to compare a live reflection with an older baseline.
|
|
90
105
|
return reflectRoutes(source).map((route) => snapshotRoute(route))
|
|
91
106
|
}
|
|
92
107
|
|
|
108
|
+
/** Snapshot routes from an existing canonical project-evidence pass. */
|
|
109
|
+
export function snapshotRoutesFromEvidence(
|
|
110
|
+
evidence: ProjectEvidenceSnapshot,
|
|
111
|
+
): readonly RouteSnapshot[] {
|
|
112
|
+
return reflectedRoutesFromEvidence(evidence).map((route) => snapshotRoute(route))
|
|
113
|
+
}
|
|
114
|
+
|
|
93
115
|
const snapshotRoute = (route: ReflectedRoute): RouteSnapshot => {
|
|
94
116
|
const schema = route.schema
|
|
95
117
|
if (schema === undefined) return { method: route.method, path: route.path }
|
|
@@ -100,13 +122,17 @@ const snapshotRoute = (route: ReflectedRoute): RouteSnapshot => {
|
|
|
100
122
|
}
|
|
101
123
|
const body = schemaSnapshot(schema.body)
|
|
102
124
|
const headers = schemaSnapshot(schema.headers)
|
|
125
|
+
const cookies = schemaSnapshot(schema.cookies)
|
|
103
126
|
const query = schemaSnapshot(schema.query)
|
|
127
|
+
const params = schemaSnapshot(schema.params)
|
|
104
128
|
const response = schemaSnapshot(schema.response)
|
|
105
129
|
const sse = schemaSnapshot(schema.sse)
|
|
106
130
|
const snapped: RouteSnapshotSchema = {
|
|
107
131
|
...(headers !== undefined ? { headers } : {}),
|
|
132
|
+
...(cookies !== undefined ? { cookies } : {}),
|
|
108
133
|
...(body !== undefined ? { body } : {}),
|
|
109
134
|
...(query !== undefined ? { query } : {}),
|
|
135
|
+
...(params !== undefined ? { params } : {}),
|
|
110
136
|
...(response !== undefined ? { response } : {}),
|
|
111
137
|
...(sse !== undefined ? { sse } : {}),
|
|
112
138
|
...(Object.keys(errors).length > 0 ? { errors } : {}),
|
|
@@ -197,19 +223,40 @@ const fieldMap = (
|
|
|
197
223
|
return new Map(snapshot.fields.map((field) => [field.name, field]))
|
|
198
224
|
}
|
|
199
225
|
|
|
226
|
+
/**
|
|
227
|
+
* An object schema without the parts the field diff already covers: its `properties`, and the
|
|
228
|
+
* `required` names of declared fields. What is left (`additionalProperties`, the `$defs` a field's
|
|
229
|
+
* `$ref` points into, a required name no field declares) changes the contract as surely as a field.
|
|
230
|
+
*/
|
|
231
|
+
const envelopeOf = (schema: JsonSchema): JsonSchema => {
|
|
232
|
+
if (typeof schema !== "object") return schema
|
|
233
|
+
const { properties, required, ...rest } = schema
|
|
234
|
+
const declared = typeof properties === "object" && properties !== null ? properties : {}
|
|
235
|
+
const undeclared = Array.isArray(required)
|
|
236
|
+
? required.filter((name) => typeof name !== "string" || !Object.hasOwn(declared, name))
|
|
237
|
+
: required
|
|
238
|
+
return undeclared === undefined || (Array.isArray(undeclared) && undeclared.length === 0)
|
|
239
|
+
? rest
|
|
240
|
+
: { ...rest, required: undeclared }
|
|
241
|
+
}
|
|
242
|
+
|
|
200
243
|
const diffFields = (ctx: SectionContext, before: SchemaSnapshot, after: SchemaSnapshot): void => {
|
|
244
|
+
const beforeSchema = before.jsonSchema as JsonSchema
|
|
245
|
+
const afterSchema = after.jsonSchema as JsonSchema
|
|
201
246
|
const beforeFields = fieldMap(before)
|
|
202
247
|
const afterFields = fieldMap(after)
|
|
203
248
|
if (beforeFields === undefined || afterFields === undefined) {
|
|
204
249
|
// Non-object schemas: compare the whole JSON Schema in one step.
|
|
205
|
-
const severity = compareJsonSchema(
|
|
206
|
-
ctx.direction,
|
|
207
|
-
before.jsonSchema as JsonSchema,
|
|
208
|
-
after.jsonSchema as JsonSchema,
|
|
209
|
-
)
|
|
250
|
+
const severity = compareJsonSchema(ctx.direction, beforeSchema, afterSchema)
|
|
210
251
|
if (severity !== undefined) push(ctx, severity, `${ctx.section} schema changed`)
|
|
211
252
|
return
|
|
212
253
|
}
|
|
254
|
+
const envelope = compareJsonSchema(
|
|
255
|
+
ctx.direction,
|
|
256
|
+
envelopeOf(beforeSchema),
|
|
257
|
+
envelopeOf(afterSchema),
|
|
258
|
+
)
|
|
259
|
+
if (envelope !== undefined) push(ctx, envelope, `${ctx.section} schema changed`)
|
|
213
260
|
for (const [name, beforeField] of beforeFields) {
|
|
214
261
|
const afterField = afterFields.get(name)
|
|
215
262
|
if (afterField === undefined) {
|
|
@@ -356,7 +403,7 @@ export function diffRouteSnapshots(
|
|
|
356
403
|
const path = beforeRoute.path
|
|
357
404
|
const beforeSchema = beforeRoute.schema ?? {}
|
|
358
405
|
const afterSchema = afterRoute.schema ?? {}
|
|
359
|
-
for (const section of ["headers", "body", "query"] as const) {
|
|
406
|
+
for (const section of ["headers", "cookies", "body", "query", "params"] as const) {
|
|
360
407
|
diffSchemaSection(
|
|
361
408
|
{ method, path, section, direction: "request", changes },
|
|
362
409
|
beforeSchema[section],
|
package/src/durable-adapters.ts
CHANGED
|
@@ -222,7 +222,8 @@ export class MemoryDurableRecordBackend implements DurableRecordBackend {
|
|
|
222
222
|
if (!key.startsWith(prefix) || !input.states.includes(value.state ?? "")) return false
|
|
223
223
|
return input.updatedBefore === undefined || (value.updatedAt ?? 0) <= input.updatedBefore
|
|
224
224
|
})
|
|
225
|
-
|
|
225
|
+
// Code-unit order, the order the cursor comparison below uses (and SQLite's BINARY collation).
|
|
226
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
226
227
|
.filter(([key]) => input.cursor === undefined || key > prefix + input.cursor)
|
|
227
228
|
const page = records.slice(0, input.limit)
|
|
228
229
|
return Object.freeze({
|
|
@@ -382,6 +383,14 @@ export async function runDurableExecutionAdapterConformance(
|
|
|
382
383
|
version: 1,
|
|
383
384
|
}
|
|
384
385
|
requireConformance(await adapter.sagas.create(saga), "saga create rejected")
|
|
386
|
+
requireConformance(
|
|
387
|
+
!(await adapter.sagas.compareAndSet({
|
|
388
|
+
sagaId,
|
|
389
|
+
version: 1,
|
|
390
|
+
record: { ...saga, sagaId: `${sagaId}-other`, state: "completed", updatedAt: 2, version: 2 },
|
|
391
|
+
})),
|
|
392
|
+
"saga CAS accepted a record for another saga",
|
|
393
|
+
)
|
|
385
394
|
requireConformance(
|
|
386
395
|
await adapter.sagas.compareAndSet({
|
|
387
396
|
sagaId,
|
|
@@ -914,11 +923,19 @@ export class DurableObjectRecordBackend implements DurableRecordBackend {
|
|
|
914
923
|
}
|
|
915
924
|
if (selected.length > input.limit) break
|
|
916
925
|
}
|
|
917
|
-
const hasMore = selected.length > input.limit || listed.size > input.limit
|
|
918
926
|
const page = selected.slice(0, input.limit)
|
|
927
|
+
// A full page resumes after its own last record: the matching record that overflowed it was
|
|
928
|
+
// visited but not returned, so the cursor must not pass it. Otherwise every listed key was
|
|
929
|
+
// returned or filtered out, and the next page starts after the last one visited.
|
|
930
|
+
const cursor =
|
|
931
|
+
selected.length > input.limit
|
|
932
|
+
? page.at(-1)?.id
|
|
933
|
+
: listed.size > input.limit
|
|
934
|
+
? lastVisited
|
|
935
|
+
: undefined
|
|
919
936
|
return Object.freeze({
|
|
920
937
|
records: Object.freeze(page.map(({ record }) => clone(record))),
|
|
921
|
-
...(
|
|
938
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
922
939
|
})
|
|
923
940
|
}
|
|
924
941
|
async acquireLease(
|
package/src/durable-effect.ts
CHANGED
|
@@ -57,6 +57,7 @@ export function createDurableEffectJournal(
|
|
|
57
57
|
if (record === undefined) throw new DurableEffectTransitionError(effectId, "missing")
|
|
58
58
|
if (
|
|
59
59
|
record.effectId !== effectId ||
|
|
60
|
+
typeof record.capability !== "string" ||
|
|
60
61
|
!validCapabilityId(record.capability) ||
|
|
61
62
|
!["admission", "executing", "committed", "failed", "unknown"].includes(record.state) ||
|
|
62
63
|
!Number.isSafeInteger(record.version) ||
|
|
@@ -69,7 +70,10 @@ export function createDurableEffectJournal(
|
|
|
69
70
|
if (record.createdAt > record.updatedAt)
|
|
70
71
|
throw new TypeError(`durable effect ${effectId}: updatedAt precedes createdAt`)
|
|
71
72
|
if (record.target !== undefined) assertToken(record.target, "stored durable effect target", 128)
|
|
72
|
-
if (
|
|
73
|
+
if (
|
|
74
|
+
record.digest !== undefined &&
|
|
75
|
+
(typeof record.digest !== "string" || !/^[0-9a-f]{64}$/u.test(record.digest))
|
|
76
|
+
)
|
|
73
77
|
throw new TypeError(`durable effect ${effectId}: store returned an invalid digest`)
|
|
74
78
|
if (record.tenantId !== undefined)
|
|
75
79
|
assertToken(record.tenantId, "stored durable effect tenantId")
|
|
@@ -77,7 +81,10 @@ export function createDurableEffectJournal(
|
|
|
77
81
|
assertToken(record.principalId, "stored durable effect principalId")
|
|
78
82
|
if ((record.tenantId === undefined) !== (record.principalId === undefined))
|
|
79
83
|
throw new TypeError(`durable effect ${effectId}: store returned a partial identity`)
|
|
80
|
-
if (
|
|
84
|
+
if (
|
|
85
|
+
record.errorCode !== undefined &&
|
|
86
|
+
(typeof record.errorCode !== "string" || !ERROR_CODE.test(record.errorCode))
|
|
87
|
+
)
|
|
81
88
|
throw new TypeError(`durable effect ${effectId}: store returned an invalid error code`)
|
|
82
89
|
return record
|
|
83
90
|
}
|
|
@@ -145,7 +152,7 @@ export function createDurableEffectJournal(
|
|
|
145
152
|
await transition(effectId, "executing", "committed")
|
|
146
153
|
},
|
|
147
154
|
async failed(effectId, input) {
|
|
148
|
-
if (!ERROR_CODE.test(input.errorCode))
|
|
155
|
+
if (typeof input.errorCode !== "string" || !ERROR_CODE.test(input.errorCode))
|
|
149
156
|
throw new TypeError("durable effect errorCode is invalid")
|
|
150
157
|
const record = await current(effectId)
|
|
151
158
|
const to = input.began ? "unknown" : "failed"
|
package/src/edge-kit.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* - {@link toResponse} render a {@link ResponseResult} (e.g. a 413 / 415 rejection) to a `Response`
|
|
12
12
|
* - {@link plainError} the structured rejection envelope the lane returns
|
|
13
13
|
* - {@link queryObjectOf} / {@link searchOf} query parse without a full WHATWG URL construction
|
|
14
|
+
* - {@link answerNotFound} run a not-found handler under the same rules (404 only, no 2xx, fail closed)
|
|
14
15
|
*
|
|
15
16
|
* Everything here carries only structure - bytes, guards, and envelopes - never routing, lifecycle,
|
|
16
17
|
* or app state. It is the interface, not the framework.
|
|
@@ -18,6 +19,12 @@
|
|
|
18
19
|
|
|
19
20
|
export { readBodyFramed } from "./server/body-lane.ts"
|
|
20
21
|
export { plainError } from "./server/http.ts"
|
|
22
|
+
export {
|
|
23
|
+
answerNotFound,
|
|
24
|
+
type NotFoundHandler,
|
|
25
|
+
type NotFoundInput,
|
|
26
|
+
notFoundInput,
|
|
27
|
+
} from "./server/not-found-answer.ts"
|
|
21
28
|
export type { ProtoPoisoning } from "./server/proto-guard.ts"
|
|
22
29
|
export { type QueryValue, queryObjectOf, searchOf } from "./server/query.ts"
|
|
23
30
|
export { toResponse } from "./server/respond.ts"
|
package/src/effect-lifecycle.ts
CHANGED
|
@@ -93,7 +93,11 @@ export function emitEffectLifecycle(
|
|
|
93
93
|
if (input.attempt !== undefined && (!Number.isSafeInteger(input.attempt) || input.attempt < 1)) {
|
|
94
94
|
return
|
|
95
95
|
}
|
|
96
|
-
if (
|
|
96
|
+
if (
|
|
97
|
+
input.errorCode !== undefined &&
|
|
98
|
+
(typeof input.errorCode !== "string" || !ERROR_CODE.test(input.errorCode))
|
|
99
|
+
)
|
|
100
|
+
return
|
|
97
101
|
const event: EffectLifecycleEvent = Object.freeze({
|
|
98
102
|
effectId: input.effectId,
|
|
99
103
|
capability: input.capability,
|
package/src/effect-scope.ts
CHANGED
|
@@ -108,7 +108,8 @@ function codeFor(
|
|
|
108
108
|
typeof configured === "function"
|
|
109
109
|
? configured(error, began)
|
|
110
110
|
: (configured ?? (began ? "execution_failed" : "admission_failed"))
|
|
111
|
-
if (!ERROR_CODE.test(code))
|
|
111
|
+
if (typeof code !== "string" || !ERROR_CODE.test(code))
|
|
112
|
+
throw new TypeError("effect errorCode is invalid")
|
|
112
113
|
return code
|
|
113
114
|
}
|
|
114
115
|
|
package/src/errors.ts
CHANGED
|
@@ -27,7 +27,10 @@ export type RouteConfigErrorCode =
|
|
|
27
27
|
| "INVALID_ASSURANCE"
|
|
28
28
|
| "INVALID_IDEMPOTENCY"
|
|
29
29
|
| "INVALID_WS_SEND_VALIDATION"
|
|
30
|
+
| "INVALID_WIRE"
|
|
31
|
+
| "INVALID_VALIDATION_ORDER"
|
|
30
32
|
| "INVALID_BODY_LIMIT"
|
|
33
|
+
| "PLUGIN_RECONFIGURED"
|
|
31
34
|
|
|
32
35
|
/**
|
|
33
36
|
* Thrown at route registration when a route is misconfigured. This is the
|