@astrale-os/kernel-server 0.4.1
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/dist/app/create.d.ts +48 -0
- package/dist/app/create.d.ts.map +1 -0
- package/dist/app/create.js +107 -0
- package/dist/app/create.js.map +1 -0
- package/dist/app/index.d.ts +4 -0
- package/dist/app/index.d.ts.map +1 -0
- package/dist/app/index.js +4 -0
- package/dist/app/index.js.map +1 -0
- package/dist/app/multiplexed.d.ts +53 -0
- package/dist/app/multiplexed.d.ts.map +1 -0
- package/dist/app/multiplexed.js +63 -0
- package/dist/app/multiplexed.js.map +1 -0
- package/dist/app/ws-upgrade.d.ts +35 -0
- package/dist/app/ws-upgrade.d.ts.map +1 -0
- package/dist/app/ws-upgrade.js +93 -0
- package/dist/app/ws-upgrade.js.map +1 -0
- package/dist/cors.d.ts +21 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +17 -0
- package/dist/cors.js.map +1 -0
- package/dist/envelope/batch.d.ts +12 -0
- package/dist/envelope/batch.d.ts.map +1 -0
- package/dist/envelope/batch.js +90 -0
- package/dist/envelope/batch.js.map +1 -0
- package/dist/envelope/codec.d.ts +26 -0
- package/dist/envelope/codec.d.ts.map +1 -0
- package/dist/envelope/codec.js +52 -0
- package/dist/envelope/codec.js.map +1 -0
- package/dist/envelope/credential.d.ts +20 -0
- package/dist/envelope/credential.d.ts.map +1 -0
- package/dist/envelope/credential.js +32 -0
- package/dist/envelope/credential.js.map +1 -0
- package/dist/envelope/http.d.ts +30 -0
- package/dist/envelope/http.d.ts.map +1 -0
- package/dist/envelope/http.js +128 -0
- package/dist/envelope/http.js.map +1 -0
- package/dist/envelope/index.d.ts +6 -0
- package/dist/envelope/index.d.ts.map +1 -0
- package/dist/envelope/index.js +6 -0
- package/dist/envelope/index.js.map +1 -0
- package/dist/envelope/sse.d.ts +11 -0
- package/dist/envelope/sse.d.ts.map +1 -0
- package/dist/envelope/sse.js +73 -0
- package/dist/envelope/sse.js.map +1 -0
- package/dist/envelope/ws.d.ts +32 -0
- package/dist/envelope/ws.d.ts.map +1 -0
- package/dist/envelope/ws.js +136 -0
- package/dist/envelope/ws.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/jwks/discovery.d.ts +40 -0
- package/dist/jwks/discovery.d.ts.map +1 -0
- package/dist/jwks/discovery.js +113 -0
- package/dist/jwks/discovery.js.map +1 -0
- package/dist/jwks/index.d.ts +3 -0
- package/dist/jwks/index.d.ts.map +1 -0
- package/dist/jwks/index.js +3 -0
- package/dist/jwks/index.js.map +1 -0
- package/dist/jwks/issuers.d.ts +47 -0
- package/dist/jwks/issuers.d.ts.map +1 -0
- package/dist/jwks/issuers.js +76 -0
- package/dist/jwks/issuers.js.map +1 -0
- package/dist/jwks/keys.d.ts +44 -0
- package/dist/jwks/keys.d.ts.map +1 -0
- package/dist/jwks/keys.js +56 -0
- package/dist/jwks/keys.js.map +1 -0
- package/dist/plan/host-params.d.ts +13 -0
- package/dist/plan/host-params.d.ts.map +1 -0
- package/dist/plan/host-params.js +23 -0
- package/dist/plan/host-params.js.map +1 -0
- package/dist/plan/host.d.ts +24 -0
- package/dist/plan/host.d.ts.map +1 -0
- package/dist/plan/host.js +24 -0
- package/dist/plan/host.js.map +1 -0
- package/dist/plan/index.d.ts +4 -0
- package/dist/plan/index.d.ts.map +1 -0
- package/dist/plan/index.js +4 -0
- package/dist/plan/index.js.map +1 -0
- package/dist/plan/walk.d.ts +51 -0
- package/dist/plan/walk.d.ts.map +1 -0
- package/dist/plan/walk.js +110 -0
- package/dist/plan/walk.js.map +1 -0
- package/dist/render/binary.d.ts +19 -0
- package/dist/render/binary.d.ts.map +1 -0
- package/dist/render/binary.js +33 -0
- package/dist/render/binary.js.map +1 -0
- package/dist/render/body.d.ts +10 -0
- package/dist/render/body.d.ts.map +1 -0
- package/dist/render/body.js +17 -0
- package/dist/render/body.js.map +1 -0
- package/dist/render/index.d.ts +3 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.js +3 -0
- package/dist/render/index.js.map +1 -0
- package/dist/routed/credential.d.ts +10 -0
- package/dist/routed/credential.d.ts.map +1 -0
- package/dist/routed/credential.js +55 -0
- package/dist/routed/credential.js.map +1 -0
- package/dist/routed/handler.d.ts +36 -0
- package/dist/routed/handler.d.ts.map +1 -0
- package/dist/routed/handler.js +119 -0
- package/dist/routed/handler.js.map +1 -0
- package/dist/routed/index.d.ts +5 -0
- package/dist/routed/index.d.ts.map +1 -0
- package/dist/routed/index.js +5 -0
- package/dist/routed/index.js.map +1 -0
- package/dist/routed/params.d.ts +40 -0
- package/dist/routed/params.d.ts.map +1 -0
- package/dist/routed/params.js +146 -0
- package/dist/routed/params.js.map +1 -0
- package/dist/routed/stream.d.ts +21 -0
- package/dist/routed/stream.d.ts.map +1 -0
- package/dist/routed/stream.js +69 -0
- package/dist/routed/stream.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain walker — turns a list of MountableContract into a mount plan.
|
|
3
|
+
*
|
|
4
|
+
* For each contract, decide whether it mounts on THIS server:
|
|
5
|
+
* - ambient (no binding.remoteUrl) → always mount
|
|
6
|
+
* - anchored (binding.remoteUrl set) → match host pattern against host.url
|
|
7
|
+
*
|
|
8
|
+
* Then bucket by base URL to compute one kernel mount per unique (host+basePath).
|
|
9
|
+
* Collect route bindings and check for path collisions.
|
|
10
|
+
*
|
|
11
|
+
* See docs/protocol-redesign.md §6 ("Server").
|
|
12
|
+
*/
|
|
13
|
+
import type { MountableContract, RouteBinding } from '@astrale-os/kernel-api/routed';
|
|
14
|
+
import { type ServerHost } from './host';
|
|
15
|
+
export type MountedFunction = {
|
|
16
|
+
contract: MountableContract;
|
|
17
|
+
/**
|
|
18
|
+
* Base URL template for this function's kernel mount.
|
|
19
|
+
*
|
|
20
|
+
* Ambient functions share the server host URL. Anchored functions use
|
|
21
|
+
* their own `binding.remoteUrl` (verified to match the server host).
|
|
22
|
+
*/
|
|
23
|
+
baseUrl: string;
|
|
24
|
+
/** Anchored functions only — the parsed base path from remoteUrl. */
|
|
25
|
+
basePath: string;
|
|
26
|
+
};
|
|
27
|
+
export type MountedRoute = {
|
|
28
|
+
contract: MountableContract;
|
|
29
|
+
route: RouteBinding;
|
|
30
|
+
/** Full path from base URL (host stripped) + route.path. */
|
|
31
|
+
fullPath: string;
|
|
32
|
+
/** Normalized pattern: `{name}` → `:p`, `{name+}` → `*` — used for collision detection. */
|
|
33
|
+
normalizedKey: string;
|
|
34
|
+
};
|
|
35
|
+
export type MountPlan = {
|
|
36
|
+
/** Kernel mounts keyed by their base path (relative to the server host URL). */
|
|
37
|
+
kernelMounts: Map<string, MountedFunction[]>;
|
|
38
|
+
/** Route mounts. */
|
|
39
|
+
routes: MountedRoute[];
|
|
40
|
+
/** Fully-qualified method name → mounted function (for dispatch lookup). */
|
|
41
|
+
methodIndex: Map<string, MountedFunction>;
|
|
42
|
+
/**
|
|
43
|
+
* Compiled Host header matcher when the server host URL contains placeholders.
|
|
44
|
+
* Used at request time to extract host params and merge into params.
|
|
45
|
+
*/
|
|
46
|
+
hostMatcher: RegExp | null;
|
|
47
|
+
/** Names of host placeholders, in source order. */
|
|
48
|
+
hostPlaceholders: string[];
|
|
49
|
+
};
|
|
50
|
+
export declare function walkDomain(contracts: MountableContract[], host: ServerHost): MountPlan;
|
|
51
|
+
//# sourceMappingURL=walk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../../plan/walk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAIpF,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAA;AAIzD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,iBAAiB,CAAA;IAC3B;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,KAAK,EAAE,YAAY,CAAA;IACnB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAA;IAChB,2FAA2F;IAC3F,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,gFAAgF;IAChF,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC,CAAA;IAC5C,oBAAoB;IACpB,MAAM,EAAE,YAAY,EAAE,CAAA;IACtB,4EAA4E;IAC5E,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IACzC;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,mDAAmD;IACnD,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAC3B,CAAA;AAID,wBAAgB,UAAU,CAAC,SAAS,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS,CAgGtF"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain walker — turns a list of MountableContract into a mount plan.
|
|
3
|
+
*
|
|
4
|
+
* For each contract, decide whether it mounts on THIS server:
|
|
5
|
+
* - ambient (no binding.remoteUrl) → always mount
|
|
6
|
+
* - anchored (binding.remoteUrl set) → match host pattern against host.url
|
|
7
|
+
*
|
|
8
|
+
* Then bucket by base URL to compute one kernel mount per unique (host+basePath).
|
|
9
|
+
* Collect route bindings and check for path collisions.
|
|
10
|
+
*
|
|
11
|
+
* See docs/protocol-redesign.md §6 ("Server").
|
|
12
|
+
*/
|
|
13
|
+
import { isSubdomainOf, parseUrlTemplate } from '@astrale-os/kernel-api/routed';
|
|
14
|
+
import { parseServerHost } from './host';
|
|
15
|
+
// ─── Walk ────────────────────────────────────────────────────────────────────
|
|
16
|
+
export function walkDomain(contracts, host) {
|
|
17
|
+
const parsed = parseServerHost(host);
|
|
18
|
+
const kernelMounts = new Map();
|
|
19
|
+
const routes = [];
|
|
20
|
+
const methodIndex = new Map();
|
|
21
|
+
const routeCollisionKeys = new Map();
|
|
22
|
+
for (const contract of contracts) {
|
|
23
|
+
const binding = contract.binding;
|
|
24
|
+
const remoteUrl = binding?.remoteUrl;
|
|
25
|
+
let basePath;
|
|
26
|
+
let baseUrl;
|
|
27
|
+
if (!remoteUrl) {
|
|
28
|
+
// Ambient — mounts at the server host URL.
|
|
29
|
+
basePath = parsed.basePath;
|
|
30
|
+
baseUrl = host.url;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// Anchored — must match or be a subdomain of the server's host pattern.
|
|
34
|
+
const fnParsed = parseUrlTemplate(remoteUrl);
|
|
35
|
+
if (!isSubdomainOf(fnParsed.hostPattern, parsed.hostPattern)) {
|
|
36
|
+
// Not this server's responsibility — skip (debug log).
|
|
37
|
+
// oxlint-disable-next-line no-console
|
|
38
|
+
console.debug(`[server] Skipping ${contract.ref}: remoteUrl host "${fnParsed.hostPattern}" does not match server host "${parsed.hostPattern}"`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
basePath = stripTrailingSlash(fnParsed.basePath);
|
|
42
|
+
baseUrl = remoteUrl;
|
|
43
|
+
}
|
|
44
|
+
const mounted = { contract, baseUrl, basePath };
|
|
45
|
+
methodIndex.set(contract.ref, mounted);
|
|
46
|
+
// Add to the kernel mount bucket for this basePath.
|
|
47
|
+
const bucket = kernelMounts.get(basePath) ?? [];
|
|
48
|
+
bucket.push(mounted);
|
|
49
|
+
kernelMounts.set(basePath, bucket);
|
|
50
|
+
// Route binding?
|
|
51
|
+
if (binding?.route) {
|
|
52
|
+
const route = binding.route;
|
|
53
|
+
const fullPath = joinPath(basePath, route.path);
|
|
54
|
+
const normalizedPath = normalizePath(fullPath);
|
|
55
|
+
const normalizedKey = `${route.method} ${normalizedPath}`;
|
|
56
|
+
if (route.method === '*') {
|
|
57
|
+
// Wildcard: collides with ANY existing route on this path.
|
|
58
|
+
const wildcardKey = `* ${normalizedPath}`;
|
|
59
|
+
const existingWild = routeCollisionKeys.get(wildcardKey);
|
|
60
|
+
if (existingWild) {
|
|
61
|
+
throw new Error(`Route collision: ${contract.ref} and ${existingWild} both mount at ${wildcardKey}`);
|
|
62
|
+
}
|
|
63
|
+
for (const m of ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
|
|
64
|
+
const specificKey = `${m} ${normalizedPath}`;
|
|
65
|
+
const existingSpecific = routeCollisionKeys.get(specificKey);
|
|
66
|
+
if (existingSpecific) {
|
|
67
|
+
throw new Error(`Route collision: ${contract.ref} (wildcard) and ${existingSpecific} both mount at ${specificKey}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
routeCollisionKeys.set(wildcardKey, contract.ref);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// Specific method: check same method AND existing wildcard.
|
|
74
|
+
const existing = routeCollisionKeys.get(normalizedKey);
|
|
75
|
+
if (existing) {
|
|
76
|
+
throw new Error(`Route collision: ${contract.ref} and ${existing} both mount at ${normalizedKey}`);
|
|
77
|
+
}
|
|
78
|
+
const existingWild = routeCollisionKeys.get(`* ${normalizedPath}`);
|
|
79
|
+
if (existingWild) {
|
|
80
|
+
throw new Error(`Route collision: ${contract.ref} and ${existingWild} (wildcard) both mount at * ${normalizedPath}`);
|
|
81
|
+
}
|
|
82
|
+
routeCollisionKeys.set(normalizedKey, contract.ref);
|
|
83
|
+
}
|
|
84
|
+
routes.push({ contract, route, fullPath, normalizedKey });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
kernelMounts,
|
|
89
|
+
routes,
|
|
90
|
+
methodIndex,
|
|
91
|
+
hostMatcher: parsed.hostMatcher,
|
|
92
|
+
hostPlaceholders: parsed.hostPlaceholders,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
96
|
+
function stripTrailingSlash(p) {
|
|
97
|
+
if (p.length > 1 && p.endsWith('/'))
|
|
98
|
+
return p.slice(0, -1);
|
|
99
|
+
return p;
|
|
100
|
+
}
|
|
101
|
+
function joinPath(base, child) {
|
|
102
|
+
const b = stripTrailingSlash(base);
|
|
103
|
+
// child is guaranteed to start with '/'
|
|
104
|
+
return `${b}${child}` || '/';
|
|
105
|
+
}
|
|
106
|
+
/** Normalize a path template for collision detection. */
|
|
107
|
+
function normalizePath(path) {
|
|
108
|
+
return path.replace(/\{[a-zA-Z_]\w*[+*]\}/g, '*').replace(/\{[a-zA-Z_]\w*\}/g, ':p');
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=walk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.js","sourceRoot":"","sources":["../../plan/walk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary HTTP response — `BinaryResult` → `Response`.
|
|
3
|
+
*
|
|
4
|
+
* Tags binary responses with `x-binary-result: true` so kernel-client can
|
|
5
|
+
* distinguish them from accidental opaque blobs (the kernel envelope is
|
|
6
|
+
* bypassed for binary results).
|
|
7
|
+
*
|
|
8
|
+
* Header precedence (low → high, later wins):
|
|
9
|
+
* CORS headers < `br.headers` < canonical `content-type` + `x-binary-result`
|
|
10
|
+
*
|
|
11
|
+
* This lets a handler pass through any framing-specific headers its body
|
|
12
|
+
* needs (e.g. `x-vercel-ai-ui-message-stream`, `x-accel-buffering`, a tuned
|
|
13
|
+
* `cache-control`) while guaranteeing the kernel's own invariants
|
|
14
|
+
* (`content-type` matches `br.contentType`, `x-binary-result` is always
|
|
15
|
+
* set) cannot be accidentally clobbered.
|
|
16
|
+
*/
|
|
17
|
+
import type { BinaryResult } from '@astrale-os/kernel-api';
|
|
18
|
+
export declare function buildBinaryResponse(br: BinaryResult, corsHeaders: Record<string, string>): Response;
|
|
19
|
+
//# sourceMappingURL=binary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary.d.ts","sourceRoot":"","sources":["../../render/binary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAM1D,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,YAAY,EAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAClC,QAAQ,CAcV"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary HTTP response — `BinaryResult` → `Response`.
|
|
3
|
+
*
|
|
4
|
+
* Tags binary responses with `x-binary-result: true` so kernel-client can
|
|
5
|
+
* distinguish them from accidental opaque blobs (the kernel envelope is
|
|
6
|
+
* bypassed for binary results).
|
|
7
|
+
*
|
|
8
|
+
* Header precedence (low → high, later wins):
|
|
9
|
+
* CORS headers < `br.headers` < canonical `content-type` + `x-binary-result`
|
|
10
|
+
*
|
|
11
|
+
* This lets a handler pass through any framing-specific headers its body
|
|
12
|
+
* needs (e.g. `x-vercel-ai-ui-message-stream`, `x-accel-buffering`, a tuned
|
|
13
|
+
* `cache-control`) while guaranteeing the kernel's own invariants
|
|
14
|
+
* (`content-type` matches `br.contentType`, `x-binary-result` is always
|
|
15
|
+
* set) cannot be accidentally clobbered.
|
|
16
|
+
*/
|
|
17
|
+
import { BINARY_RESULT_HEADER } from '@astrale-os/kernel-api';
|
|
18
|
+
import { toBody } from './body';
|
|
19
|
+
export function buildBinaryResponse(br, corsHeaders) {
|
|
20
|
+
const body = br.body instanceof Uint8Array
|
|
21
|
+
? toBody(br.body)
|
|
22
|
+
: br.body;
|
|
23
|
+
return new Response(body, {
|
|
24
|
+
status: br.status ?? 200,
|
|
25
|
+
headers: {
|
|
26
|
+
...corsHeaders,
|
|
27
|
+
...br.headers,
|
|
28
|
+
'content-type': br.contentType,
|
|
29
|
+
[BINARY_RESULT_HEADER]: 'true',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=binary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary.js","sourceRoot":"","sources":["../../render/binary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAE7D,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B,MAAM,UAAU,mBAAmB,CACjC,EAAgB,EAChB,WAAmC;IAEnC,MAAM,IAAI,GACR,EAAE,CAAC,IAAI,YAAY,UAAU;QAC3B,CAAC,CAAE,MAAM,CAAC,EAAE,CAAC,IAAI,CAAiB;QAClC,CAAC,CAAE,EAAE,CAAC,IAAmC,CAAA;IAC7C,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACxB,MAAM,EAAE,EAAE,CAAC,MAAM,IAAI,GAAG;QACxB,OAAO,EAAE;YACP,GAAG,WAAW;YACd,GAAG,EAAE,CAAC,OAAO;YACb,cAAc,EAAE,EAAE,CAAC,WAAW;YAC9B,CAAC,oBAAoB,CAAC,EAAE,MAAM;SAC/B;KACF,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert codec output (string or Uint8Array) to a Response-compatible body.
|
|
3
|
+
*
|
|
4
|
+
* Text codecs (JSON) return strings — passed through.
|
|
5
|
+
* Binary codecs (MsgPack) return Uint8Array — narrowed to ArrayBuffer for
|
|
6
|
+
* tsgo's stricter `BodyInit` typing. The fast path avoids a copy when the
|
|
7
|
+
* Uint8Array is a full view of its backing buffer.
|
|
8
|
+
*/
|
|
9
|
+
export declare function toBody(data: string | Uint8Array): string | ArrayBuffer;
|
|
10
|
+
//# sourceMappingURL=body.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body.d.ts","sourceRoot":"","sources":["../../render/body.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,WAAW,CAMtE"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert codec output (string or Uint8Array) to a Response-compatible body.
|
|
3
|
+
*
|
|
4
|
+
* Text codecs (JSON) return strings — passed through.
|
|
5
|
+
* Binary codecs (MsgPack) return Uint8Array — narrowed to ArrayBuffer for
|
|
6
|
+
* tsgo's stricter `BodyInit` typing. The fast path avoids a copy when the
|
|
7
|
+
* Uint8Array is a full view of its backing buffer.
|
|
8
|
+
*/
|
|
9
|
+
export function toBody(data) {
|
|
10
|
+
if (typeof data === 'string')
|
|
11
|
+
return data;
|
|
12
|
+
if (data.byteOffset === 0 && data.byteLength === data.buffer.byteLength) {
|
|
13
|
+
return data.buffer;
|
|
14
|
+
}
|
|
15
|
+
return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=body.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body.js","sourceRoot":"","sources":["../../render/body.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,MAAM,CAAC,IAAyB;IAC9C,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IACzC,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACxE,OAAO,IAAI,CAAC,MAAqB,CAAA;IACnC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAgB,CAAA;AAC7F,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../render/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../render/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential extraction for route bindings.
|
|
3
|
+
*
|
|
4
|
+
* A route binding declares where to find the request credential — header,
|
|
5
|
+
* cookie, or query parameter — via its `route.credential` field. This module
|
|
6
|
+
* implements the lookup, including cookie splitting and URL decoding.
|
|
7
|
+
*/
|
|
8
|
+
import type { CredentialSource } from '@astrale-os/kernel-api';
|
|
9
|
+
export declare function readCredential(req: Request, source: CredentialSource): string;
|
|
10
|
+
//# sourceMappingURL=credential.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../../routed/credential.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAA0B,MAAM,wBAAwB,CAAA;AAEtF,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAS7E"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential extraction for route bindings.
|
|
3
|
+
*
|
|
4
|
+
* A route binding declares where to find the request credential — header,
|
|
5
|
+
* cookie, or query parameter — via its `route.credential` field. This module
|
|
6
|
+
* implements the lookup, including cookie splitting and URL decoding.
|
|
7
|
+
*/
|
|
8
|
+
export function readCredential(req, source) {
|
|
9
|
+
if (Array.isArray(source)) {
|
|
10
|
+
for (const s of source) {
|
|
11
|
+
const value = readSingle(req, s);
|
|
12
|
+
if (value)
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
17
|
+
return readSingle(req, source);
|
|
18
|
+
}
|
|
19
|
+
function readSingle(req, source) {
|
|
20
|
+
switch (source.in) {
|
|
21
|
+
case 'header': {
|
|
22
|
+
const raw = req.headers.get(source.name ?? 'authorization') ?? '';
|
|
23
|
+
return stripBearerPrefix(raw);
|
|
24
|
+
}
|
|
25
|
+
case 'query':
|
|
26
|
+
return new URL(req.url).searchParams.get(source.name ?? 'credential') ?? '';
|
|
27
|
+
case 'cookie': {
|
|
28
|
+
const header = req.headers.get('cookie');
|
|
29
|
+
if (!header || !source.name)
|
|
30
|
+
return '';
|
|
31
|
+
const cookies = header.split(';').map((c) => c.trim());
|
|
32
|
+
for (const c of cookies) {
|
|
33
|
+
const [k, v] = c.split('=', 2);
|
|
34
|
+
if (k === source.name)
|
|
35
|
+
return decodeURIComponent(v ?? '');
|
|
36
|
+
}
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Strip an optional `Bearer ` (case-insensitive) prefix from an
|
|
43
|
+
* Authorization-header value. Other values pass through as-is —
|
|
44
|
+
* validation is the auth layer's responsibility, not extraction's.
|
|
45
|
+
*
|
|
46
|
+
* Header-source credentials are interpreted per RFC 7235: the header value is
|
|
47
|
+
* `<scheme> <credentials>`, so an optional leading `Bearer ` prefix is
|
|
48
|
+
* stripped. This keeps the reader HTTP-standard-compliant and lets route
|
|
49
|
+
* bindings interoperate with any client that sends `Authorization: Bearer
|
|
50
|
+
* <token>`
|
|
51
|
+
*/
|
|
52
|
+
function stripBearerPrefix(value) {
|
|
53
|
+
return value.trim().replace(/^Bearer\s+/i, '');
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=credential.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential.js","sourceRoot":"","sources":["../../routed/credential.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,UAAU,cAAc,CAAC,GAAY,EAAE,MAAwB;IACnE,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YAChC,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAA;QACzB,CAAC;QACD,OAAO,EAAE,CAAA;IACX,CAAC;IACD,OAAO,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AAChC,CAAC;AAED,SAAS,UAAU,CAAC,GAAY,EAAE,MAA8B;IAC9D,QAAQ,MAAM,CAAC,EAAE,EAAE,CAAC;QAClB,KAAK,QAAQ,EAAE,CAAC;YACd,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,eAAe,CAAC,IAAI,EAAE,CAAA;YACjE,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAA;QAC/B,CAAC;QACD,KAAK,OAAO;YACV,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,YAAY,CAAC,IAAI,EAAE,CAAA;QAC7E,KAAK,QAAQ,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACxC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI;gBAAE,OAAO,EAAE,CAAA;YACtC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;YACtD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;gBACxB,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;gBAC9B,IAAI,CAAC,KAAK,MAAM,CAAC,IAAI;oBAAE,OAAO,kBAAkB,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;YAC3D,CAAC;YACD,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,iBAAiB,CAAC,KAAa;IACtC,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAA;AAChD,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route binding HTTP handler — native HTTP shape (REST-like).
|
|
3
|
+
*
|
|
4
|
+
* One handler per mounted route. Decodes params from the request, dispatches
|
|
5
|
+
* via the kernel API, encodes the response per output mode:
|
|
6
|
+
* - value → JSON body, HTTP status from error code table on error
|
|
7
|
+
* - stream → SSE with one event per yielded chunk (raw, NOT kernel envelope)
|
|
8
|
+
* - binary → raw bytes with BinaryResult contentType + status
|
|
9
|
+
*
|
|
10
|
+
* Errors use the kernel envelope's `{ error: { code, message, data? } }` shape
|
|
11
|
+
* for consistency with kernel HTTP. The HTTP status comes from the error code
|
|
12
|
+
* table (`kernelErrorHttpStatus`).
|
|
13
|
+
*/
|
|
14
|
+
import type { KernelAPI, RouteBinding } from '@astrale-os/kernel-api';
|
|
15
|
+
import type { AuthPolicy } from '@astrale-os/kernel-api/routed';
|
|
16
|
+
import type { CorsConfig } from '../cors';
|
|
17
|
+
export type RouteHandlerConfig = {
|
|
18
|
+
/** The full kernel method name to dispatch (e.g. `Task.read`). */
|
|
19
|
+
method: string;
|
|
20
|
+
route: RouteBinding;
|
|
21
|
+
kernel: KernelAPI;
|
|
22
|
+
cors: CorsConfig;
|
|
23
|
+
/**
|
|
24
|
+
* Optional host placeholder extractor — returned values are merged into
|
|
25
|
+
* params at the lowest priority (host < path < query < header < body).
|
|
26
|
+
* Return null to reject the request with 404.
|
|
27
|
+
*/
|
|
28
|
+
hostParams?: (req: Request) => Record<string, string> | null;
|
|
29
|
+
/** Authentication policy from the method definition. Defaults to `'required'`. */
|
|
30
|
+
auth?: AuthPolicy;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Build a Hono-compatible handler for a single route binding.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createRouteHandler(config: RouteHandlerConfig): (req: Request, pathParams: Record<string, string>) => Promise<Response>;
|
|
36
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../routed/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiC,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACpG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAU/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AA0BzC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,YAAY,CAAA;IACnB,MAAM,EAAE,SAAS,CAAA;IACjB,IAAI,EAAE,UAAU,CAAA;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;IAC5D,kFAAkF;IAClF,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB,CAAA;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,kBAAkB,GACzB,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAuGzE"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route binding HTTP handler — native HTTP shape (REST-like).
|
|
3
|
+
*
|
|
4
|
+
* One handler per mounted route. Decodes params from the request, dispatches
|
|
5
|
+
* via the kernel API, encodes the response per output mode:
|
|
6
|
+
* - value → JSON body, HTTP status from error code table on error
|
|
7
|
+
* - stream → SSE with one event per yielded chunk (raw, NOT kernel envelope)
|
|
8
|
+
* - binary → raw bytes with BinaryResult contentType + status
|
|
9
|
+
*
|
|
10
|
+
* Errors use the kernel envelope's `{ error: { code, message, data? } }` shape
|
|
11
|
+
* for consistency with kernel HTTP. The HTTP status comes from the error code
|
|
12
|
+
* table (`kernelErrorHttpStatus`).
|
|
13
|
+
*/
|
|
14
|
+
import { KERNEL_ERROR_CODES, dispatch, isBinaryResult, jsonCodec, kernelErrorHttpStatus, } from '@astrale-os/kernel-api';
|
|
15
|
+
import { buildCorsHeaders } from '../cors';
|
|
16
|
+
import { buildBinaryResponse, toBody } from '../render';
|
|
17
|
+
import { readCredential } from './credential';
|
|
18
|
+
import { decodeRouteRequest } from './params';
|
|
19
|
+
import { buildRouteStreamResponse } from './stream';
|
|
20
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
21
|
+
function errorResponse(error, corsHeaders) {
|
|
22
|
+
const status = kernelErrorHttpStatus(error.code);
|
|
23
|
+
const body = jsonCodec.encode({
|
|
24
|
+
error: error.data === undefined
|
|
25
|
+
? { code: error.code, message: error.message }
|
|
26
|
+
: { code: error.code, message: error.message, data: error.data },
|
|
27
|
+
});
|
|
28
|
+
return new Response(toBody(body), {
|
|
29
|
+
status,
|
|
30
|
+
headers: { ...corsHeaders, 'content-type': 'application/json' },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build a Hono-compatible handler for a single route binding.
|
|
35
|
+
*/
|
|
36
|
+
export function createRouteHandler(config) {
|
|
37
|
+
const corsHeaders = buildCorsHeaders(config.cors);
|
|
38
|
+
const hostParamsFn = config.hostParams;
|
|
39
|
+
return async (req, pathParams) => {
|
|
40
|
+
if (req.method === 'OPTIONS') {
|
|
41
|
+
return new Response(null, { status: 204, headers: corsHeaders });
|
|
42
|
+
}
|
|
43
|
+
// 0. Host params (lowest priority).
|
|
44
|
+
let hostParams = {};
|
|
45
|
+
if (hostParamsFn) {
|
|
46
|
+
const extracted = hostParamsFn(req);
|
|
47
|
+
if (extracted === null) {
|
|
48
|
+
return new Response('Host not recognised', { status: 404, headers: corsHeaders });
|
|
49
|
+
}
|
|
50
|
+
hostParams = extracted;
|
|
51
|
+
}
|
|
52
|
+
// 1. Decode params + self
|
|
53
|
+
let decoded;
|
|
54
|
+
try {
|
|
55
|
+
decoded = await decodeRouteRequest({
|
|
56
|
+
route: config.route,
|
|
57
|
+
pathParams,
|
|
58
|
+
hostParams,
|
|
59
|
+
request: req,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
return errorResponse({
|
|
64
|
+
code: KERNEL_ERROR_CODES.PARSE_ERROR,
|
|
65
|
+
message: err instanceof Error ? err.message : 'Parse error',
|
|
66
|
+
}, corsHeaders);
|
|
67
|
+
}
|
|
68
|
+
const params = decoded.params;
|
|
69
|
+
// 1b. Inject actual HTTP method for wildcard routes.
|
|
70
|
+
if (config.route.method === '*') {
|
|
71
|
+
params._method = req.method;
|
|
72
|
+
}
|
|
73
|
+
// 2. Resolve credential.
|
|
74
|
+
// Configurable via route.credential. Defaults to Authorization header.
|
|
75
|
+
const credSource = config.route.credential ?? { in: 'header', name: 'authorization' };
|
|
76
|
+
const credential = readCredential(req, credSource);
|
|
77
|
+
// 3. Stream detection (Accept header opt-in)
|
|
78
|
+
const isStream = req.headers.get('accept')?.includes('text/event-stream') === true;
|
|
79
|
+
// 4. Dispatch
|
|
80
|
+
const requestId = crypto.randomUUID();
|
|
81
|
+
const result = await dispatch(config.method, params, credential, config.kernel, { id: requestId, isStream }, decoded.self);
|
|
82
|
+
// 5. Render
|
|
83
|
+
switch (result.kind) {
|
|
84
|
+
case 'ack':
|
|
85
|
+
return new Response(null, { status: 204, headers: corsHeaders });
|
|
86
|
+
case 'success': {
|
|
87
|
+
if (isBinaryResult(result.data)) {
|
|
88
|
+
return buildBinaryResponse(result.data, corsHeaders);
|
|
89
|
+
}
|
|
90
|
+
const body = jsonCodec.encode(result.data);
|
|
91
|
+
return new Response(toBody(body), {
|
|
92
|
+
status: 200,
|
|
93
|
+
headers: { ...corsHeaders, 'content-type': 'application/json' },
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
case 'error':
|
|
97
|
+
return errorResponse(result.error, corsHeaders);
|
|
98
|
+
case 'redirect': {
|
|
99
|
+
const body = jsonCodec.encode({ redirect: result.redirection });
|
|
100
|
+
return new Response(toBody(body), {
|
|
101
|
+
status: 307,
|
|
102
|
+
headers: {
|
|
103
|
+
...corsHeaders,
|
|
104
|
+
'content-type': 'application/json',
|
|
105
|
+
location: result.redirection.url,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
case 'stream': {
|
|
110
|
+
const streamResult = await buildRouteStreamResponse(result.generator, corsHeaders);
|
|
111
|
+
if (streamResult.kind === 'error') {
|
|
112
|
+
return errorResponse(streamResult.error, corsHeaders);
|
|
113
|
+
}
|
|
114
|
+
return streamResult.response;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../routed/handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createRouteHandler, type RouteHandlerConfig } from './handler';
|
|
2
|
+
export { decodeRouteRequest, type DecodedRouteParams, type DecodeRouteInput } from './params';
|
|
3
|
+
export { readCredential } from './credential';
|
|
4
|
+
export { buildRouteStreamResponse, type StreamResponseResult } from './stream';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../routed/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAA;AACvE,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC7F,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,wBAAwB,EAAE,KAAK,oBAAoB,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../routed/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA2B,MAAM,WAAW,CAAA;AACvE,OAAO,EAAE,kBAAkB,EAAkD,MAAM,UAAU,CAAA;AAC7F,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,wBAAwB,EAA6B,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route binding parameter decoder.
|
|
3
|
+
*
|
|
4
|
+
* Extracts params from all sources (host, path, query, header, body) per
|
|
5
|
+
* spec §3. Merge order: host < path < query < header < body — later wins.
|
|
6
|
+
* `route.self`, if set, is lifted to a separate top-level `self` channel
|
|
7
|
+
* AND removed from `params` — handlers read it via the `self` sibling in
|
|
8
|
+
* their execute context (`self.path.raw`), never from `params`. Leaving
|
|
9
|
+
* a copy in `params` would trip strict Zod validation of `inputSchema`
|
|
10
|
+
* (`route-self.ts` explicitly allows the placeholder to be absent from
|
|
11
|
+
* the schema, so the runtime must not smuggle it through anyway).
|
|
12
|
+
*
|
|
13
|
+
* Body modes:
|
|
14
|
+
* - `'*'` parse JSON body, merge top-level fields into params.
|
|
15
|
+
* - `'raw'` skip parsing; raw bytes at `params._body`.
|
|
16
|
+
* - `Record` parse JSON, pick mapped fields with wire-name remapping.
|
|
17
|
+
*/
|
|
18
|
+
import type { RouteBinding } from '@astrale-os/kernel-api';
|
|
19
|
+
export type DecodedRouteParams = Record<string, unknown>;
|
|
20
|
+
export type DecodedRoute = {
|
|
21
|
+
params: DecodedRouteParams;
|
|
22
|
+
/** Lifted from the placeholder declared by `route.self`. Sibling of params. */
|
|
23
|
+
self?: string;
|
|
24
|
+
};
|
|
25
|
+
export type DecodeRouteInput = {
|
|
26
|
+
route: RouteBinding;
|
|
27
|
+
/** Pre-extracted path params (from path matcher). */
|
|
28
|
+
pathParams: Record<string, string>;
|
|
29
|
+
/** Pre-extracted host params (from server-side host matcher). May be empty. */
|
|
30
|
+
hostParams?: Record<string, string>;
|
|
31
|
+
/** The raw request. */
|
|
32
|
+
request: Request;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Decode params from a route + request.
|
|
36
|
+
*
|
|
37
|
+
* Throws on body parse errors — the caller should map to PARSE_ERROR.
|
|
38
|
+
*/
|
|
39
|
+
export declare function decodeRouteRequest(input: DecodeRouteInput): Promise<DecodedRoute>;
|
|
40
|
+
//# sourceMappingURL=params.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../../routed/params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,wBAAwB,CAAA;AASpE,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAExD,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,kBAAkB,CAAA;IAC1B,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,YAAY,CAAA;IACnB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,uBAAuB;IACvB,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAqGvF"}
|