@nifrajs/core 1.12.0 → 2.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 +10 -1
- package/dist/assurance.d.ts +11 -2
- package/dist/assurance.d.ts.map +1 -1
- package/dist/assurance.js +20 -0
- package/dist/assurance.js.map +1 -1
- package/dist/effect-ledger.d.ts +9 -0
- package/dist/effect-ledger.d.ts.map +1 -0
- package/dist/effect-ledger.js +8 -0
- package/dist/effect-ledger.js.map +1 -0
- package/dist/idempotency-plugin.d.ts +8 -0
- package/dist/idempotency-plugin.d.ts.map +1 -0
- package/dist/idempotency-plugin.js +8 -0
- package/dist/idempotency-plugin.js.map +1 -0
- package/dist/index.d.ts +6 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -30
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +7 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +56 -0
- package/dist/mcp.js.map +1 -0
- package/dist/node-direct.d.ts +5 -0
- package/dist/node-direct.d.ts.map +1 -0
- package/dist/node-direct.js +27 -0
- package/dist/node-direct.js.map +1 -0
- package/dist/reflection.d.ts +5 -0
- package/dist/reflection.d.ts.map +1 -1
- package/dist/reflection.js +2 -0
- package/dist/reflection.js.map +1 -1
- package/dist/router/pattern.d.ts +42 -0
- package/dist/router/pattern.d.ts.map +1 -0
- package/dist/router/pattern.js +127 -0
- package/dist/router/pattern.js.map +1 -0
- package/dist/router/router.d.ts +2 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +11 -48
- package/dist/router/router.js.map +1 -1
- package/dist/server/client-ip.d.ts +35 -0
- package/dist/server/client-ip.d.ts.map +1 -0
- package/dist/server/client-ip.js +42 -0
- package/dist/server/client-ip.js.map +1 -0
- package/dist/server/context.d.ts +47 -2
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/contract.d.ts +37 -10
- package/dist/server/contract.d.ts.map +1 -1
- package/dist/server/contract.js +33 -8
- package/dist/server/contract.js.map +1 -1
- package/dist/server/http.d.ts +14 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +45 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/idempotency-lane.d.ts +44 -0
- package/dist/server/idempotency-lane.d.ts.map +1 -0
- package/dist/server/idempotency-lane.js +181 -0
- package/dist/server/idempotency-lane.js.map +1 -0
- package/dist/server/install.d.ts +21 -0
- package/dist/server/install.d.ts.map +1 -0
- package/dist/server/install.js +21 -0
- package/dist/server/install.js.map +1 -0
- package/dist/server/ledger-lane.d.ts +53 -0
- package/dist/server/ledger-lane.d.ts.map +1 -0
- package/dist/server/ledger-lane.js +82 -0
- package/dist/server/ledger-lane.js.map +1 -0
- package/dist/server/mcp-hook.d.ts +28 -0
- package/dist/server/mcp-hook.d.ts.map +1 -0
- package/dist/server/mcp-hook.js +2 -0
- package/dist/server/mcp-hook.js.map +1 -0
- package/dist/server/node-outcome-hook.d.ts +9 -0
- package/dist/server/node-outcome-hook.d.ts.map +1 -0
- package/dist/server/node-outcome-hook.js +2 -0
- package/dist/server/node-outcome-hook.js.map +1 -0
- package/dist/server/node-outcome.d.ts +37 -0
- package/dist/server/node-outcome.d.ts.map +1 -0
- package/dist/server/node-outcome.js +75 -0
- package/dist/server/node-outcome.js.map +1 -0
- package/dist/server/plugin.d.ts +78 -0
- package/dist/server/plugin.d.ts.map +1 -0
- package/dist/server/plugin.js +57 -0
- package/dist/server/plugin.js.map +1 -0
- package/dist/server/query.d.ts +18 -0
- package/dist/server/query.d.ts.map +1 -0
- package/dist/server/query.js +102 -0
- package/dist/server/query.js.map +1 -0
- package/dist/server/registry.d.ts +31 -6
- package/dist/server/registry.d.ts.map +1 -1
- package/dist/server/request-context.d.ts +51 -0
- package/dist/server/request-context.d.ts.map +1 -0
- package/dist/server/request-context.js +184 -0
- package/dist/server/request-context.js.map +1 -0
- package/dist/server/respond.d.ts +17 -0
- package/dist/server/respond.d.ts.map +1 -0
- package/dist/server/respond.js +81 -0
- package/dist/server/respond.js.map +1 -0
- package/dist/server/runtime-core.d.ts +36 -0
- package/dist/server/runtime-core.d.ts.map +1 -0
- package/dist/server/runtime-core.js +51 -0
- package/dist/server/runtime-core.js.map +1 -0
- package/dist/server/server-types.d.ts +203 -0
- package/dist/server/server-types.d.ts.map +1 -0
- package/dist/server/server-types.js +2 -0
- package/dist/server/server-types.js.map +1 -0
- package/dist/server/server.d.ts +135 -358
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +476 -1065
- package/dist/server/server.js.map +1 -1
- package/dist/server/sse-hook.d.ts +10 -0
- package/dist/server/sse-hook.d.ts.map +1 -0
- package/dist/server/sse-hook.js +2 -0
- package/dist/server/sse-hook.js.map +1 -0
- package/dist/server/sse.d.ts +7 -23
- package/dist/server/sse.d.ts.map +1 -1
- package/dist/server/sse.js +18 -0
- package/dist/server/sse.js.map +1 -1
- package/dist/server/websocket.d.ts +17 -1
- package/dist/server/websocket.d.ts.map +1 -1
- package/dist/server/websocket.js.map +1 -1
- package/dist/server/ws-hook.d.ts +8 -11
- package/dist/server/ws-hook.d.ts.map +1 -1
- package/dist/server/ws-hook.js +7 -14
- package/dist/server/ws-hook.js.map +1 -1
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +16 -0
- package/dist/server.js.map +1 -0
- package/dist/ws.d.ts +16 -7
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +25 -10
- package/dist/ws.js.map +1 -1
- package/package.json +82 -6
- package/src/assurance.ts +36 -1
- package/src/effect-ledger.ts +9 -0
- package/src/idempotency-plugin.ts +7 -0
- package/src/index.ts +6 -132
- package/src/mcp.ts +65 -0
- package/src/node-direct.ts +37 -0
- package/src/reflection.ts +7 -0
- package/src/router/pattern.ts +176 -0
- package/src/router/router.ts +11 -69
- package/src/server/client-ip.ts +60 -0
- package/src/server/context.ts +50 -2
- package/src/server/contract.ts +68 -25
- package/src/server/http.ts +58 -0
- package/src/server/idempotency-lane.ts +287 -0
- package/src/server/install.ts +28 -0
- package/src/server/ledger-lane.ts +125 -0
- package/src/server/mcp-hook.ts +41 -0
- package/src/server/node-outcome-hook.ts +9 -0
- package/src/server/node-outcome.ts +112 -0
- package/src/server/plugin.ts +92 -0
- package/src/server/query.ts +119 -0
- package/src/server/registry.ts +36 -6
- package/src/server/request-context.ts +271 -0
- package/src/server/respond.ts +93 -0
- package/src/server/runtime-core.ts +74 -0
- package/src/server/server-types.ts +204 -0
- package/src/server/server.ts +863 -1657
- package/src/server/sse-hook.ts +14 -0
- package/src/server/sse.ts +26 -0
- package/src/server/websocket.ts +20 -0
- package/src/server/ws-hook.ts +8 -19
- package/src/server.ts +92 -0
- package/src/ws.ts +33 -10
- package/dist/invariants.d.ts +0 -70
- package/dist/invariants.d.ts.map +0 -1
- package/dist/invariants.js +0 -487
- package/dist/invariants.js.map +0 -1
- package/src/invariants.ts +0 -604
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/server/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAG9D,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC1E,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/server/contract.ts"],"names":[],"mappings":"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"}
|
package/dist/server/contract.js
CHANGED
|
@@ -36,34 +36,59 @@ export function defineContract(contract) {
|
|
|
36
36
|
* builder, so the result is identical to writing the routes inline — handlers
|
|
37
37
|
* lift over **unchanged** ("graduation"), and body/query schemas validate at the
|
|
38
38
|
* request boundary exactly as in inline mode.
|
|
39
|
+
*
|
|
40
|
+
* Pass a pre-configured `app` to give the contract's routes a middleware chain. A route captures the
|
|
41
|
+
* server's `derive`/`decorate`/assurance chain **at registration**, so anything applied to the
|
|
42
|
+
* returned server afterwards reaches the contract's routes not at all - the app must already carry it:
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* const app = implement(contract, handlers, server().use(auth).derive(sessionOf))
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* That is also the seam that lets `nifra assure` prove rather than merely classify a contract-first
|
|
49
|
+
* app: the plugin that installs the enforcement is what declares the evidence
|
|
50
|
+
* ({@link withRouteAssurance}), and only a plugin installed *before* registration is captured. Handlers
|
|
51
|
+
* then see the app's `Ctx`, and the returned server keeps any routes the app already had.
|
|
39
52
|
*/
|
|
40
|
-
export function implement(contract, handlers) {
|
|
41
|
-
const
|
|
42
|
-
|
|
53
|
+
export function implement(contract, handlers, app) {
|
|
54
|
+
const target = (app ?? new Server());
|
|
55
|
+
const routes = Object.entries(contract).map(([name, op]) => {
|
|
43
56
|
// body/query are validated at the request boundary; `response` is a type + introspection contract
|
|
44
57
|
// ONLY — never validated at runtime and never read on the request hot path (the lifecycle reads
|
|
45
58
|
// schema.body/query by name; the `bare`/bodyOnly/queryOnly fast-path gates ignore `response`). Built
|
|
46
59
|
// once here at bind time, not per request — it just carries a reference to the op's existing schema.
|
|
47
60
|
// A response-less op still yields `undefined`, byte-identical to before.
|
|
48
|
-
const schema = op.
|
|
61
|
+
const schema = op.params !== undefined ||
|
|
62
|
+
op.body !== undefined ||
|
|
49
63
|
op.query !== undefined ||
|
|
50
64
|
op.response !== undefined ||
|
|
51
65
|
op.capabilities !== undefined ||
|
|
52
66
|
op.idempotency !== undefined ||
|
|
53
|
-
op.classification !== undefined
|
|
67
|
+
op.classification !== undefined ||
|
|
68
|
+
op.assurance !== undefined ||
|
|
69
|
+
op.family !== undefined
|
|
54
70
|
? {
|
|
71
|
+
...(op.params !== undefined ? { params: op.params } : {}),
|
|
55
72
|
...(op.body !== undefined ? { body: op.body } : {}),
|
|
56
73
|
...(op.query !== undefined ? { query: op.query } : {}),
|
|
57
74
|
...(op.response !== undefined ? { response: op.response } : {}),
|
|
58
75
|
...(op.capabilities !== undefined ? { capabilities: op.capabilities } : {}),
|
|
59
76
|
...(op.idempotency !== undefined ? { idempotency: op.idempotency } : {}),
|
|
60
77
|
...(op.classification !== undefined ? { classification: op.classification } : {}),
|
|
78
|
+
...(op.assurance !== undefined ? { assurance: op.assurance } : {}),
|
|
79
|
+
...(op.family !== undefined ? { family: op.family } : {}),
|
|
61
80
|
}
|
|
62
81
|
: undefined;
|
|
63
|
-
|
|
64
|
-
|
|
82
|
+
return {
|
|
83
|
+
method: op.method,
|
|
84
|
+
path: op.path,
|
|
85
|
+
schema,
|
|
86
|
+
handler: handlers[name],
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
target.registerBatch(routes);
|
|
65
90
|
// Runtime registered exactly the contract's routes through the inline path; the
|
|
66
91
|
// registry type is computed from the contract inputs + handler return types.
|
|
67
|
-
return
|
|
92
|
+
return target;
|
|
68
93
|
}
|
|
69
94
|
//# sourceMappingURL=contract.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/server/contract.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAA;AAC9E,OAAO,EAAE,OAAO,EAAe,MAAM,qBAAqB,CAAA;AAI1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/server/contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free HTTP helpers shared by the kernel and the opt-in request lanes (idempotency,
|
|
3
|
+
* effect-ledger). Kept in a leaf module so a lane can reuse them without importing the server, and so
|
|
4
|
+
* the server can reuse them without pulling a lane's feature code into the base bundle.
|
|
5
|
+
*/
|
|
6
|
+
/** A uniform JSON error envelope: `{ ok: false, error }` at the given status. */
|
|
7
|
+
export declare function jsonError(status: number, error: string, headers?: Record<string, string>): Response;
|
|
8
|
+
export interface UrlParts {
|
|
9
|
+
readonly pathname: string;
|
|
10
|
+
readonly search: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function urlPartsOf(url: string): UrlParts;
|
|
13
|
+
export declare function pathnameOf(url: string): string;
|
|
14
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/server/http.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,iFAAiF;AACjF,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,QAAQ,CAKV;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAMD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAwBhD;AAID,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free HTTP helpers shared by the kernel and the opt-in request lanes (idempotency,
|
|
3
|
+
* effect-ledger). Kept in a leaf module so a lane can reuse them without importing the server, and so
|
|
4
|
+
* the server can reuse them without pulling a lane's feature code into the base bundle.
|
|
5
|
+
*/
|
|
6
|
+
/** A uniform JSON error envelope: `{ ok: false, error }` at the given status. */
|
|
7
|
+
export function jsonError(status, error, headers) {
|
|
8
|
+
return Response.json({ ok: false, error }, headers !== undefined ? { status, headers } : { status });
|
|
9
|
+
}
|
|
10
|
+
// Extract pathname + query WITHOUT a full WHATWG `new URL(req.url)` parse.
|
|
11
|
+
// `req.url` from every supported runtime is an absolute, already-normalized URL, so the pathname is
|
|
12
|
+
// the substring after `scheme://host[:port]` up to `?`/`#`. Query-schema routes also need the search
|
|
13
|
+
// string; parsing both in one scanner avoids the old `pathnameOf()` + `searchOf()` double scan.
|
|
14
|
+
export function urlPartsOf(url) {
|
|
15
|
+
const schemeEnd = url.indexOf("://");
|
|
16
|
+
const start = schemeEnd === -1 ? url.indexOf("/") : url.indexOf("/", schemeEnd + 3);
|
|
17
|
+
if (start === -1)
|
|
18
|
+
return { pathname: "/", search: "" };
|
|
19
|
+
let pathEnd = url.length;
|
|
20
|
+
let searchStart = -1;
|
|
21
|
+
let searchEnd = url.length;
|
|
22
|
+
for (let i = start; i < url.length; i++) {
|
|
23
|
+
const c = url.charCodeAt(i);
|
|
24
|
+
if (c === 63 /* ? */ && searchStart === -1) {
|
|
25
|
+
pathEnd = i;
|
|
26
|
+
searchStart = i;
|
|
27
|
+
}
|
|
28
|
+
else if (c === 35 /* # */) {
|
|
29
|
+
if (searchStart === -1)
|
|
30
|
+
pathEnd = i;
|
|
31
|
+
searchEnd = i;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
pathname: url.slice(start, pathEnd),
|
|
37
|
+
search: searchStart === -1 ? "" : url.slice(searchStart, searchEnd),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// Extract the pathname WITHOUT a full WHATWG `new URL(req.url)` parse. Kept as a public-ish helper
|
|
41
|
+
// for tests and callers that only need the path; the request hot path uses `urlPartsOf()` once.
|
|
42
|
+
export function pathnameOf(url) {
|
|
43
|
+
return urlPartsOf(url).pathname;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/server/http.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,iFAAiF;AACjF,MAAM,UAAU,SAAS,CACvB,MAAc,EACd,KAAa,EACb,OAAgC;IAEhC,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EACpB,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CACzD,CAAA;AACH,CAAC;AAOD,2EAA2E;AAC3E,oGAAoG;AACpG,qGAAqG;AACrG,gGAAgG;AAChG,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IACpC,MAAM,KAAK,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,CAAC,CAAC,CAAA;IACnF,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;IAEtD,IAAI,OAAO,GAAG,GAAG,CAAC,MAAM,CAAA;IACxB,IAAI,WAAW,GAAG,CAAC,CAAC,CAAA;IACpB,IAAI,SAAS,GAAG,GAAG,CAAC,MAAM,CAAA;IAC1B,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;QAC3B,IAAI,CAAC,KAAK,EAAE,CAAC,OAAO,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;YAC3C,OAAO,GAAG,CAAC,CAAA;YACX,WAAW,GAAG,CAAC,CAAA;QACjB,CAAC;aAAM,IAAI,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,CAAC;YAC5B,IAAI,WAAW,KAAK,CAAC,CAAC;gBAAE,OAAO,GAAG,CAAC,CAAA;YACnC,SAAS,GAAG,CAAC,CAAA;YACb,MAAK;QACP,CAAC;IACH,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC;QACnC,MAAM,EAAE,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC;KACpE,CAAA;AACH,CAAC;AAED,mGAAmG;AACnG,gGAAgG;AAChG,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAA;AACjC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type IdempotencyStore } from "../idempotency.js";
|
|
2
|
+
import type { Platform, RouteSchema } from "./context.js";
|
|
3
|
+
import type { IdentityPlugin } from "./server.js";
|
|
4
|
+
/** Registration-resolved idempotency for a route: the config with its store + defaults pinned. */
|
|
5
|
+
export interface ResolvedIdempotency {
|
|
6
|
+
readonly store: IdempotencyStore;
|
|
7
|
+
readonly ttlMs: number;
|
|
8
|
+
readonly headerName: string;
|
|
9
|
+
readonly namespace: NonNullable<RouteSchema["idempotency"]>["namespace"];
|
|
10
|
+
readonly maxResponseBytes: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* What the dedupe lane needs from the server to run a fresh key: the app-wide body cap and a way to
|
|
14
|
+
* run the buffered request through the route's normal lanes to a concrete `Response`.
|
|
15
|
+
*/
|
|
16
|
+
export interface IdempotencyHost {
|
|
17
|
+
readonly maxBodyBytes: number;
|
|
18
|
+
runLanes(buffered: Request, platform: Platform | undefined, entry: unknown, params: Record<string, string>, search: string | undefined): Promise<Response>;
|
|
19
|
+
}
|
|
20
|
+
/** The injected idempotency implementation the kernel calls through when the plugin is installed. */
|
|
21
|
+
export interface IdempotencyRuntime {
|
|
22
|
+
/** Resolve a route's declared idempotency into a pinned store + defaults, or `undefined` when off. */
|
|
23
|
+
resolve(schema: RouteSchema | undefined, authenticated: boolean, maxBodyBytes: number): ResolvedIdempotency | undefined;
|
|
24
|
+
/** The dedupe lane: read the key, fingerprint the request, consult the store, run/replay/reject. */
|
|
25
|
+
run<T>(config: ResolvedIdempotency, req: Request, platform: Platform | undefined, entry: unknown, params: Record<string, string>, search: string | undefined, wrapResponse: (response: Response) => T, host: IdempotencyHost): Promise<T>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Enable request idempotency. Routes that declare `schema.idempotency` get the dedupe lane: a repeat
|
|
29
|
+
* `Idempotency-Key` replays the stored response instead of re-running the handler. Without this plugin,
|
|
30
|
+
* declaring `schema.idempotency` is a registration error (the safety gate can never be silently
|
|
31
|
+
* dropped). `store` is the app-wide default backing routes that do not pin their own; omit it for a
|
|
32
|
+
* shared in-process store (development/tests only - inject a durable store in production).
|
|
33
|
+
*/
|
|
34
|
+
export interface IdempotencyPluginOptions {
|
|
35
|
+
readonly store?: IdempotencyStore | undefined;
|
|
36
|
+
}
|
|
37
|
+
export declare function idempotency(options?: IdempotencyPluginOptions): IdentityPlugin;
|
|
38
|
+
/**
|
|
39
|
+
* Build an idempotency runtime. `store` is the app-wide default backing routes that declare
|
|
40
|
+
* `schema.idempotency` without their own `store`; when omitted, a shared in-process
|
|
41
|
+
* {@link MemoryIdempotencyStore} is created lazily on first idempotent route.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createIdempotencyRuntime(options?: IdempotencyPluginOptions): IdempotencyRuntime;
|
|
44
|
+
//# sourceMappingURL=idempotency-lane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotency-lane.d.ts","sourceRoot":"","sources":["../../src/server/idempotency-lane.ts"],"names":[],"mappings":"AAUA,OAAO,EAML,KAAK,gBAAgB,EAMtB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAGzD,OAAO,KAAK,EAAa,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5D,kGAAkG;AAClG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAA;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;IACxE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CACN,QAAQ,EAAE,OAAO,EACjB,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,OAAO,CAAC,QAAQ,CAAC,CAAA;CACrB;AAED,qGAAqG;AACrG,MAAM,WAAW,kBAAkB;IACjC,sGAAsG;IACtG,OAAO,CACL,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,aAAa,EAAE,OAAO,EACtB,YAAY,EAAE,MAAM,GACnB,mBAAmB,GAAG,SAAS,CAAA;IAClC,oGAAoG;IACpG,GAAG,CAAC,CAAC,EACH,MAAM,EAAE,mBAAmB,EAC3B,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,CAAC,EACvC,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,CAAC,CAAC,CAAA;CACd;AAOD;;;;;;GAMG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAA;CAC9C;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,cAAc,CAO9E;AAKD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,kBAAkB,CAmL/F"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The opt-in idempotency request lane. Installed by `.use(idempotency())` and reached only by routes
|
|
3
|
+
* that declare `schema.idempotency`, so the kernel never statically imports this code: a bare
|
|
4
|
+
* `server()` tree-shakes the whole dedupe machinery out.
|
|
5
|
+
*
|
|
6
|
+
* The kernel resolves a route's declared idempotency at registration (via {@link IdempotencyRuntime.resolve})
|
|
7
|
+
* and, on the request path, hands a matched idempotent route to {@link IdempotencyRuntime.run}. Everything
|
|
8
|
+
* server-specific is supplied through {@link IdempotencyHost} so this module depends on no server internals.
|
|
9
|
+
*/
|
|
10
|
+
import { RouteConfigError } from "../errors.js";
|
|
11
|
+
import { canonicalizeIdempotencyBody, computeIdempotencyFingerprint, DEFAULT_IDEMPOTENCY_HEADER, DEFAULT_IDEMPOTENCY_TTL_MS, IdempotencyResponseTooLargeError, MemoryIdempotencyStore, responseFromStored, serializeResponse, validIdempotencyKey, validIdempotencyNamespace, } from "../idempotency.js";
|
|
12
|
+
import { readBoundedBytes } from "./body.js";
|
|
13
|
+
import { jsonError, urlPartsOf } from "./http.js";
|
|
14
|
+
import { INSTALL_IDEMPOTENCY } from "./install.js";
|
|
15
|
+
export function idempotency(options) {
|
|
16
|
+
const runtime = createIdempotencyRuntime(options);
|
|
17
|
+
const apply = (app) => {
|
|
18
|
+
;
|
|
19
|
+
app[INSTALL_IDEMPOTENCY](runtime);
|
|
20
|
+
return app;
|
|
21
|
+
};
|
|
22
|
+
return Object.assign(apply, { pluginName: "nifra:idempotency" });
|
|
23
|
+
}
|
|
24
|
+
const RESPONSE_2XX_LOWER = 200;
|
|
25
|
+
const RESPONSE_2XX_UPPER = 300;
|
|
26
|
+
/**
|
|
27
|
+
* Build an idempotency runtime. `store` is the app-wide default backing routes that declare
|
|
28
|
+
* `schema.idempotency` without their own `store`; when omitted, a shared in-process
|
|
29
|
+
* {@link MemoryIdempotencyStore} is created lazily on first idempotent route.
|
|
30
|
+
*/
|
|
31
|
+
export function createIdempotencyRuntime(options) {
|
|
32
|
+
const configuredStore = options?.store;
|
|
33
|
+
let lazyStore;
|
|
34
|
+
const defaultStore = () => {
|
|
35
|
+
if (configuredStore !== undefined)
|
|
36
|
+
return configuredStore;
|
|
37
|
+
lazyStore ??= new MemoryIdempotencyStore();
|
|
38
|
+
return lazyStore;
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
resolve(schema, authenticated, maxBodyBytes) {
|
|
42
|
+
const config = schema?.idempotency;
|
|
43
|
+
if (config === undefined)
|
|
44
|
+
return undefined;
|
|
45
|
+
if (schema?.sse !== undefined) {
|
|
46
|
+
throw new RouteConfigError("INVALID_IDEMPOTENCY", "streaming/SSE responses cannot be used on an idempotency route");
|
|
47
|
+
}
|
|
48
|
+
const store = config.store ?? defaultStore();
|
|
49
|
+
if (config.scope === "durable" && store.durability !== "durable") {
|
|
50
|
+
throw new RouteConfigError("INVALID_IDEMPOTENCY", 'durable idempotency requires a durable store (store.durability must be "durable")');
|
|
51
|
+
}
|
|
52
|
+
const ttlMs = config.ttlMs ?? DEFAULT_IDEMPOTENCY_TTL_MS;
|
|
53
|
+
if (!Number.isSafeInteger(ttlMs) || ttlMs <= 0) {
|
|
54
|
+
throw new RouteConfigError("INVALID_IDEMPOTENCY", "idempotency ttlMs must be a finite positive number");
|
|
55
|
+
}
|
|
56
|
+
const headerName = (config.headerName ?? DEFAULT_IDEMPOTENCY_HEADER).toLowerCase();
|
|
57
|
+
try {
|
|
58
|
+
new Headers().set(headerName, "probe");
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
throw new RouteConfigError("INVALID_IDEMPOTENCY", `invalid idempotency header name ${JSON.stringify(headerName)}`);
|
|
62
|
+
}
|
|
63
|
+
const namespace = config.namespace;
|
|
64
|
+
if (namespace === undefined) {
|
|
65
|
+
throw new RouteConfigError("INVALID_IDEMPOTENCY", "idempotency namespace is required; use a principal resolver or an explicit shared scope");
|
|
66
|
+
}
|
|
67
|
+
if (typeof namespace === "string" && !validIdempotencyNamespace(namespace)) {
|
|
68
|
+
throw new RouteConfigError("INVALID_IDEMPOTENCY", `invalid idempotency namespace ${JSON.stringify(namespace)}`);
|
|
69
|
+
}
|
|
70
|
+
if (authenticated && typeof namespace !== "function") {
|
|
71
|
+
throw new RouteConfigError("INVALID_IDEMPOTENCY", "authenticated idempotency routes require a principal namespace resolver");
|
|
72
|
+
}
|
|
73
|
+
const maxResponseBytes = config.maxResponseBytes ?? maxBodyBytes;
|
|
74
|
+
if (!Number.isSafeInteger(maxResponseBytes) || maxResponseBytes < 1) {
|
|
75
|
+
throw new RouteConfigError("INVALID_IDEMPOTENCY", "idempotency maxResponseBytes must be a positive integer");
|
|
76
|
+
}
|
|
77
|
+
return Object.freeze({
|
|
78
|
+
store,
|
|
79
|
+
ttlMs,
|
|
80
|
+
headerName,
|
|
81
|
+
namespace,
|
|
82
|
+
maxResponseBytes,
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
async run(config, req, platform, entry, params, search, wrapResponse, host) {
|
|
86
|
+
const key = req.headers.get(config.headerName);
|
|
87
|
+
if (key === null || !validIdempotencyKey(key)) {
|
|
88
|
+
return wrapResponse(jsonError(400, "idempotency_key_required"));
|
|
89
|
+
}
|
|
90
|
+
const namespace = typeof config.namespace === "function"
|
|
91
|
+
? await config.namespace(req.clone(), platform)
|
|
92
|
+
: config.namespace;
|
|
93
|
+
if (!validIdempotencyNamespace(namespace)) {
|
|
94
|
+
return wrapResponse(jsonError(500, "idempotency_namespace_invalid"));
|
|
95
|
+
}
|
|
96
|
+
const read = await readBoundedBytes(req, host.maxBodyBytes);
|
|
97
|
+
if (!read.ok) {
|
|
98
|
+
return wrapResponse(jsonError(read.status, read.status === 413 ? "payload_too_large" : "bad_request"));
|
|
99
|
+
}
|
|
100
|
+
const url = urlPartsOf(req.url);
|
|
101
|
+
const canonicalBody = canonicalizeIdempotencyBody(read.bytes, req.headers.get("content-type"));
|
|
102
|
+
const fingerprint = await computeIdempotencyFingerprint(req.method, `${url.pathname}${url.search}`, canonicalBody, req.headers.get("content-type") ?? "");
|
|
103
|
+
const begin = await config.store.begin({ namespace, key, fingerprint, ttlMs: config.ttlMs });
|
|
104
|
+
if (begin.state === "replay") {
|
|
105
|
+
return wrapResponse(responseFromStored(begin.response, { maxBytes: config.maxResponseBytes }));
|
|
106
|
+
}
|
|
107
|
+
if (begin.state === "mismatch")
|
|
108
|
+
return wrapResponse(jsonError(409, "idempotency_key_reused"));
|
|
109
|
+
if (begin.state === "in-flight") {
|
|
110
|
+
return wrapResponse(jsonError(409, "idempotency_in_progress", { "Retry-After": "1" }));
|
|
111
|
+
}
|
|
112
|
+
if (begin.state === "capacity") {
|
|
113
|
+
return wrapResponse(jsonError(503, "idempotency_store_capacity", { "Retry-After": "1" }));
|
|
114
|
+
}
|
|
115
|
+
const reservation = begin.reservation;
|
|
116
|
+
// Fresh key: re-expose the buffered body as a Request so the normal lanes can read + validate it,
|
|
117
|
+
// then run those lanes to a concrete Response.
|
|
118
|
+
const bufferedInit = {
|
|
119
|
+
method: req.method,
|
|
120
|
+
headers: req.headers,
|
|
121
|
+
signal: req.signal,
|
|
122
|
+
};
|
|
123
|
+
if (read.bytes.byteLength > 0)
|
|
124
|
+
bufferedInit.body = read.bytes;
|
|
125
|
+
const buffered = new Request(req.url, bufferedInit);
|
|
126
|
+
let response;
|
|
127
|
+
try {
|
|
128
|
+
response = await host.runLanes(buffered, platform, entry, params, search);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
// The lanes never throw (errors resolve to a 500 Response), but stay fail-safe: release the
|
|
132
|
+
// reservation so a retry isn't wedged, then re-throw.
|
|
133
|
+
await config.store.abandon({ namespace, key, reservation });
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
136
|
+
// Cache only successful responses; release the key on any error so the client can retry.
|
|
137
|
+
if (response.status >= RESPONSE_2XX_LOWER && response.status < RESPONSE_2XX_UPPER) {
|
|
138
|
+
let storedResponse;
|
|
139
|
+
try {
|
|
140
|
+
storedResponse = await serializeResponse(response, { maxBytes: config.maxResponseBytes });
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (!(error instanceof IdempotencyResponseTooLargeError))
|
|
144
|
+
throw error;
|
|
145
|
+
// The effect may already have happened, so never abandon and permit a duplicate execution.
|
|
146
|
+
// Commit a small terminal response under the winning key and return that same response now.
|
|
147
|
+
response = jsonError(507, "idempotency_response_too_large");
|
|
148
|
+
try {
|
|
149
|
+
storedResponse = await serializeResponse(response, {
|
|
150
|
+
maxBytes: config.maxResponseBytes,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch (terminalError) {
|
|
154
|
+
if (!(terminalError instanceof IdempotencyResponseTooLargeError))
|
|
155
|
+
throw terminalError;
|
|
156
|
+
// Even an intentionally tiny bound must remain truthful. An empty 507 preserves terminal
|
|
157
|
+
// status + replay safety without silently storing more bytes than the route permits.
|
|
158
|
+
response = new Response(null, { status: 507 });
|
|
159
|
+
storedResponse = await serializeResponse(response, {
|
|
160
|
+
maxBytes: config.maxResponseBytes,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const completed = await config.store.complete({
|
|
165
|
+
namespace,
|
|
166
|
+
key,
|
|
167
|
+
reservation,
|
|
168
|
+
response: storedResponse,
|
|
169
|
+
});
|
|
170
|
+
if (!completed) {
|
|
171
|
+
return wrapResponse(jsonError(503, "idempotency_reservation_lost", { "Retry-After": "1" }));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
await config.store.abandon({ namespace, key, reservation });
|
|
176
|
+
}
|
|
177
|
+
return wrapResponse(response);
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=idempotency-lane.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotency-lane.js","sourceRoot":"","sources":["../../src/server/idempotency-lane.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symbol-keyed install seams the opt-in feature plugins (`idempotency()`, `effectLedger()`) use to
|
|
3
|
+
* hand their runtime to a server. Kept in a dependency-free leaf, and keyed by symbol, so:
|
|
4
|
+
* - the kernel can implement the seam without importing a feature's code (importing a bare symbol
|
|
5
|
+
* never drags the lane's implementation into the base bundle), and
|
|
6
|
+
* - the seam stays off the server's public typed surface (symbol-keyed methods are not part of it).
|
|
7
|
+
* Both sides reference the SAME registered symbol, so no module has to import the other.
|
|
8
|
+
*/
|
|
9
|
+
/** @internal Install the idempotency runtime on a server (called by the `idempotency()` plugin). */
|
|
10
|
+
export declare const INSTALL_IDEMPOTENCY: unique symbol;
|
|
11
|
+
/** @internal Install the effect-ledger runtime on a server (called by the `effectLedger()` plugin). */
|
|
12
|
+
export declare const INSTALL_EFFECT_LEDGER: unique symbol;
|
|
13
|
+
/** @internal Install the MCP runtime on a server (called by the `mcp()` plugin). */
|
|
14
|
+
export declare const INSTALL_MCP: unique symbol;
|
|
15
|
+
/** @internal Install the SSE streaming runtime on a server (called by the `sse()` plugin). */
|
|
16
|
+
export declare const INSTALL_SSE: unique symbol;
|
|
17
|
+
/** @internal Install the Node-direct renderer on a server (called by the `nodeDirect()` plugin). */
|
|
18
|
+
export declare const INSTALL_NODE_DIRECT: unique symbol;
|
|
19
|
+
/** @internal Install the WebSocket runtime on a server (called by the `websocket()` plugin). */
|
|
20
|
+
export declare const INSTALL_WS: unique symbol;
|
|
21
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/server/install.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oGAAoG;AACpG,eAAO,MAAM,mBAAmB,EAAE,OAAO,MAAwD,CAAA;AAEjG,uGAAuG;AACvG,eAAO,MAAM,qBAAqB,EAAE,OAAO,MAE1C,CAAA;AAED,oFAAoF;AACpF,eAAO,MAAM,WAAW,EAAE,OAAO,MAAgD,CAAA;AAEjF,8FAA8F;AAC9F,eAAO,MAAM,WAAW,EAAE,OAAO,MAAgD,CAAA;AAEjF,oGAAoG;AACpG,eAAO,MAAM,mBAAmB,EAAE,OAAO,MAAwD,CAAA;AAEjG,gGAAgG;AAChG,eAAO,MAAM,UAAU,EAAE,OAAO,MAA+C,CAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symbol-keyed install seams the opt-in feature plugins (`idempotency()`, `effectLedger()`) use to
|
|
3
|
+
* hand their runtime to a server. Kept in a dependency-free leaf, and keyed by symbol, so:
|
|
4
|
+
* - the kernel can implement the seam without importing a feature's code (importing a bare symbol
|
|
5
|
+
* never drags the lane's implementation into the base bundle), and
|
|
6
|
+
* - the seam stays off the server's public typed surface (symbol-keyed methods are not part of it).
|
|
7
|
+
* Both sides reference the SAME registered symbol, so no module has to import the other.
|
|
8
|
+
*/
|
|
9
|
+
/** @internal Install the idempotency runtime on a server (called by the `idempotency()` plugin). */
|
|
10
|
+
export const INSTALL_IDEMPOTENCY = Symbol.for("@nifrajs/core/install-idempotency");
|
|
11
|
+
/** @internal Install the effect-ledger runtime on a server (called by the `effectLedger()` plugin). */
|
|
12
|
+
export const INSTALL_EFFECT_LEDGER = Symbol.for("@nifrajs/core/install-effect-ledger");
|
|
13
|
+
/** @internal Install the MCP runtime on a server (called by the `mcp()` plugin). */
|
|
14
|
+
export const INSTALL_MCP = Symbol.for("@nifrajs/core/install-mcp");
|
|
15
|
+
/** @internal Install the SSE streaming runtime on a server (called by the `sse()` plugin). */
|
|
16
|
+
export const INSTALL_SSE = Symbol.for("@nifrajs/core/install-sse");
|
|
17
|
+
/** @internal Install the Node-direct renderer on a server (called by the `nodeDirect()` plugin). */
|
|
18
|
+
export const INSTALL_NODE_DIRECT = Symbol.for("@nifrajs/core/install-node-direct");
|
|
19
|
+
/** @internal Install the WebSocket runtime on a server (called by the `websocket()` plugin). */
|
|
20
|
+
export const INSTALL_WS = Symbol.for("@nifrajs/core/install-ws");
|
|
21
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/server/install.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oGAAoG;AACpG,MAAM,CAAC,MAAM,mBAAmB,GAAkB,MAAM,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAA;AAEjG,uGAAuG;AACvG,MAAM,CAAC,MAAM,qBAAqB,GAAkB,MAAM,CAAC,GAAG,CAC5D,qCAAqC,CACtC,CAAA;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,WAAW,GAAkB,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;AAEjF,8FAA8F;AAC9F,MAAM,CAAC,MAAM,WAAW,GAAkB,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;AAEjF,oGAAoG;AACpG,MAAM,CAAC,MAAM,mBAAmB,GAAkB,MAAM,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAA;AAEjG,gGAAgG;AAChG,MAAM,CAAC,MAAM,UAAU,GAAkB,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The opt-in per-request effect ledger. Installed by `.use(effectLedger())` and reached only by routes
|
|
3
|
+
* that declare `schema.capabilities`, so the kernel never statically imports this code: a bare
|
|
4
|
+
* `server()` tree-shakes the ledger machinery out.
|
|
5
|
+
*
|
|
6
|
+
* The kernel resolves a capability-declaring route's ledger wiring at registration (via
|
|
7
|
+
* {@link EffectLedgerRuntime.resolve}) and, on the request path, creates + attaches a ledger before the
|
|
8
|
+
* handler and settles it after (via {@link EffectLedgerRuntime.create}/`attach`/`settle`). Nothing here
|
|
9
|
+
* depends on server internals; the sink-error log is supplied per settle.
|
|
10
|
+
*/
|
|
11
|
+
import { type EffectLedgerOptions, type RequestLedger } from "../ledger.js";
|
|
12
|
+
import type { IdentityPlugin } from "./server.js";
|
|
13
|
+
/** Registration-resolved effect-ledger wiring for one capability-declaring route. */
|
|
14
|
+
export interface ResolvedEffectLedger {
|
|
15
|
+
readonly sink: EffectLedgerOptions["sink"];
|
|
16
|
+
readonly maxEntries: number;
|
|
17
|
+
readonly chain: boolean;
|
|
18
|
+
/** The registered route pattern - what the sealed ledger names (never the concrete URL). */
|
|
19
|
+
readonly method: string;
|
|
20
|
+
readonly path: string;
|
|
21
|
+
/** The route's declared capability tokens, surfaced on the sealed ledger. */
|
|
22
|
+
readonly declared: readonly string[];
|
|
23
|
+
}
|
|
24
|
+
/** The injected effect-ledger implementation the kernel calls through when the plugin is installed. */
|
|
25
|
+
export interface EffectLedgerRuntime {
|
|
26
|
+
/** Resolve a capability-declaring route into its ledger wiring, or `undefined` when it declares none. */
|
|
27
|
+
resolve(capabilities: readonly string[], method: string, path: string): ResolvedEffectLedger | undefined;
|
|
28
|
+
/** Create the per-request ledger for a ledgered route. */
|
|
29
|
+
create(resolved: ResolvedEffectLedger): RequestLedger;
|
|
30
|
+
/** Attach a ledger to the request context so `useCapability` can append to it. */
|
|
31
|
+
attach(context: object, ledger: RequestLedger): void;
|
|
32
|
+
/** Seal the ledger and deliver it to the sink (empty ledgers are not delivered). */
|
|
33
|
+
settle<T>(ledger: RequestLedger, resolved: ResolvedEffectLedger, value: T, onSinkError: (fields: {
|
|
34
|
+
method: string;
|
|
35
|
+
path: string;
|
|
36
|
+
name: string;
|
|
37
|
+
}) => void): Promise<T>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build an effect-ledger runtime from the app-wide options. `sink` receives the sealed, token-only
|
|
41
|
+
* ledger when a capability-declaring route settles with recorded entries; `maxEntries` bounds a single
|
|
42
|
+
* request's ledger; `chain` opts into the per-entry hash chain.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createEffectLedgerRuntime(options: EffectLedgerOptions): EffectLedgerRuntime;
|
|
45
|
+
/**
|
|
46
|
+
* Enable the per-request effect ledger. Each route that declares `schema.capabilities` gets a bounded,
|
|
47
|
+
* token-only ledger; `useCapability(c, id, …)` appends one entry per effect, and the sink receives the
|
|
48
|
+
* sealed ledger when the response settles (only when it recorded entries). Token-only by construction -
|
|
49
|
+
* capability ids, phases, counters, digests; never payloads, values, or the concrete URL (the route
|
|
50
|
+
* pattern is recorded). Without this plugin, capability-declaring routes simply carry no ledger.
|
|
51
|
+
*/
|
|
52
|
+
export declare function effectLedger(options: EffectLedgerOptions): IdentityPlugin;
|
|
53
|
+
//# sourceMappingURL=ledger-lane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger-lane.d.ts","sourceRoot":"","sources":["../../src/server/ledger-lane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAIL,KAAK,mBAAmB,EACxB,KAAK,aAAa,EACnB,MAAM,cAAc,CAAA;AAErB,OAAO,KAAK,EAAa,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5D,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAA;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,4FAA4F;IAC5F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAED,uGAAuG;AACvG,MAAM,WAAW,mBAAmB;IAClC,yGAAyG;IACzG,OAAO,CACL,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,oBAAoB,GAAG,SAAS,CAAA;IACnC,0DAA0D;IAC1D,MAAM,CAAC,QAAQ,EAAE,oBAAoB,GAAG,aAAa,CAAA;IACrD,kFAAkF;IAClF,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;IACpD,oFAAoF;IACpF,MAAM,CAAC,CAAC,EACN,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,oBAAoB,EAC9B,KAAK,EAAE,CAAC,EACR,WAAW,EAAE,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAC5E,OAAO,CAAC,CAAC,CAAC,CAAA;CACd;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,mBAAmB,GAAG,mBAAmB,CA6C3F;AAOD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,cAAc,CAOzE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The opt-in per-request effect ledger. Installed by `.use(effectLedger())` and reached only by routes
|
|
3
|
+
* that declare `schema.capabilities`, so the kernel never statically imports this code: a bare
|
|
4
|
+
* `server()` tree-shakes the ledger machinery out.
|
|
5
|
+
*
|
|
6
|
+
* The kernel resolves a capability-declaring route's ledger wiring at registration (via
|
|
7
|
+
* {@link EffectLedgerRuntime.resolve}) and, on the request path, creates + attaches a ledger before the
|
|
8
|
+
* handler and settles it after (via {@link EffectLedgerRuntime.create}/`attach`/`settle`). Nothing here
|
|
9
|
+
* depends on server internals; the sink-error log is supplied per settle.
|
|
10
|
+
*/
|
|
11
|
+
import { attachEffectLedger, createRequestLedger, DEFAULT_MAX_ENTRIES, } from "../ledger.js";
|
|
12
|
+
import { INSTALL_EFFECT_LEDGER } from "./install.js";
|
|
13
|
+
/**
|
|
14
|
+
* Build an effect-ledger runtime from the app-wide options. `sink` receives the sealed, token-only
|
|
15
|
+
* ledger when a capability-declaring route settles with recorded entries; `maxEntries` bounds a single
|
|
16
|
+
* request's ledger; `chain` opts into the per-entry hash chain.
|
|
17
|
+
*/
|
|
18
|
+
export function createEffectLedgerRuntime(options) {
|
|
19
|
+
if (typeof options.sink !== "function") {
|
|
20
|
+
throw new TypeError("effectLedger.sink must be a function");
|
|
21
|
+
}
|
|
22
|
+
const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
23
|
+
if (!Number.isSafeInteger(maxEntries) || maxEntries < 1) {
|
|
24
|
+
throw new RangeError("effectLedger.maxEntries must be a positive safe integer");
|
|
25
|
+
}
|
|
26
|
+
const chain = options.chain ?? false;
|
|
27
|
+
const sink = options.sink;
|
|
28
|
+
return {
|
|
29
|
+
resolve(capabilities, method, path) {
|
|
30
|
+
if (capabilities.length === 0)
|
|
31
|
+
return undefined;
|
|
32
|
+
return Object.freeze({ sink, maxEntries, chain, method, path, declared: capabilities });
|
|
33
|
+
},
|
|
34
|
+
create(resolved) {
|
|
35
|
+
return createRequestLedger({
|
|
36
|
+
method: resolved.method,
|
|
37
|
+
path: resolved.path,
|
|
38
|
+
declared: resolved.declared,
|
|
39
|
+
maxEntries: resolved.maxEntries,
|
|
40
|
+
chain: resolved.chain,
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
attach(context, ledger) {
|
|
44
|
+
attachEffectLedger(context, ledger);
|
|
45
|
+
},
|
|
46
|
+
async settle(ledger, resolved, value, onSinkError) {
|
|
47
|
+
const sealed = await ledger.seal();
|
|
48
|
+
if (sealed.entries.length === 0)
|
|
49
|
+
return value;
|
|
50
|
+
try {
|
|
51
|
+
await resolved.sink(sealed);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
// Token-only: the route pattern and the failure name - never entry payloads (there are none)
|
|
55
|
+
// and never the request. The ledger is observational, so a sink outage never fails the effect.
|
|
56
|
+
onSinkError({
|
|
57
|
+
method: resolved.method,
|
|
58
|
+
path: resolved.path,
|
|
59
|
+
name: err instanceof Error ? err.name : "Error",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Enable the per-request effect ledger. Each route that declares `schema.capabilities` gets a bounded,
|
|
68
|
+
* token-only ledger; `useCapability(c, id, …)` appends one entry per effect, and the sink receives the
|
|
69
|
+
* sealed ledger when the response settles (only when it recorded entries). Token-only by construction -
|
|
70
|
+
* capability ids, phases, counters, digests; never payloads, values, or the concrete URL (the route
|
|
71
|
+
* pattern is recorded). Without this plugin, capability-declaring routes simply carry no ledger.
|
|
72
|
+
*/
|
|
73
|
+
export function effectLedger(options) {
|
|
74
|
+
const runtime = createEffectLedgerRuntime(options);
|
|
75
|
+
const apply = (app) => {
|
|
76
|
+
;
|
|
77
|
+
app[INSTALL_EFFECT_LEDGER](runtime);
|
|
78
|
+
return app;
|
|
79
|
+
};
|
|
80
|
+
return Object.assign(apply, { pluginName: "nifra:effect-ledger" });
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=ledger-lane.js.map
|