@astrale-os/kernel-server 0.5.0-beta.8 → 1.0.0-beta.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/capture.d.ts +0 -1
- package/dist/capture.js +0 -1
- package/dist/cors/cors.d.ts +0 -1
- package/dist/cors/cors.js +11 -4
- package/dist/cors/index.d.ts +0 -1
- package/dist/cors/index.js +0 -1
- package/dist/cors/types.d.ts +0 -1
- package/dist/cors/types.js +0 -1
- package/dist/errors/admission.d.ts +7 -0
- package/dist/errors/admission.js +22 -0
- package/dist/errors/availability.d.ts +0 -1
- package/dist/errors/availability.js +0 -1
- package/dist/errors/classify.d.ts +8 -1
- package/dist/errors/classify.js +15 -1
- package/dist/errors/index.d.ts +2 -2
- package/dist/errors/index.js +2 -2
- package/dist/errors/request-body.d.ts +0 -1
- package/dist/errors/request-body.js +0 -1
- package/dist/errors/response-body.d.ts +0 -1
- package/dist/errors/response-body.js +0 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.js +2 -3
- package/dist/invocation/context.d.ts +3 -3
- package/dist/invocation/context.js +2 -2
- package/dist/invocation/identity.d.ts +1 -2
- package/dist/invocation/identity.js +1 -2
- package/dist/invocation/index.d.ts +1 -2
- package/dist/invocation/index.js +1 -2
- package/dist/invocation/release-route.d.ts +22 -0
- package/dist/invocation/release-route.js +32 -0
- package/dist/invocation/types.d.ts +8 -7
- package/dist/invocation/types.js +0 -1
- package/dist/issuer/exchange.d.ts +0 -1
- package/dist/issuer/exchange.js +20 -10
- package/dist/issuer/index.d.ts +0 -1
- package/dist/issuer/index.js +0 -1
- package/dist/issuer/routes.d.ts +0 -1
- package/dist/issuer/routes.js +57 -48
- package/dist/issuer/types.d.ts +5 -1
- package/dist/issuer/types.js +0 -1
- package/dist/lifecycle/active.d.ts +4 -1
- package/dist/lifecycle/active.js +48 -11
- package/dist/lifecycle/index.d.ts +0 -1
- package/dist/lifecycle/index.js +0 -1
- package/dist/release/delivery/handler.d.ts +9 -0
- package/dist/{publication → release}/delivery/handler.js +19 -13
- package/dist/release/delivery/index.d.ts +1 -0
- package/dist/release/delivery/index.js +1 -0
- package/dist/{publication → release}/delivery/types.d.ts +4 -5
- package/dist/release/delivery/types.js +1 -0
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.js +2 -0
- package/dist/release/served.d.ts +21 -0
- package/dist/release/served.js +10 -0
- package/dist/release/snapshot.d.ts +22 -0
- package/dist/release/snapshot.js +49 -0
- package/dist/release/table.d.ts +4 -0
- package/dist/release/table.js +11 -0
- package/dist/release/types.d.ts +23 -0
- package/dist/release/types.js +1 -0
- package/dist/routes/address.d.ts +2 -0
- package/dist/routes/address.js +6 -0
- package/dist/routes/binary.d.ts +9 -0
- package/dist/routes/binary.js +110 -0
- package/dist/routes/bundle.d.ts +5 -0
- package/dist/routes/bundle.js +50 -0
- package/dist/routes/csrf.d.ts +16 -0
- package/dist/routes/csrf.js +35 -0
- package/dist/routes/document.d.ts +6 -0
- package/dist/routes/document.js +57 -0
- package/dist/routes/index.d.ts +3 -2
- package/dist/routes/index.js +2 -2
- package/dist/routes/model.d.ts +15 -0
- package/dist/routes/model.js +1 -0
- package/dist/routes/native.d.ts +17 -0
- package/dist/routes/native.js +126 -0
- package/dist/routes/preflight.d.ts +4 -0
- package/dist/routes/preflight.js +55 -0
- package/dist/routes/response.d.ts +14 -0
- package/dist/routes/response.js +33 -0
- package/dist/routes/routes.d.ts +7 -24
- package/dist/routes/routes.js +24 -439
- package/dist/routes/snapshot.d.ts +13 -0
- package/dist/routes/snapshot.js +39 -0
- package/dist/routes/stream.d.ts +6 -0
- package/dist/routes/stream.js +122 -0
- package/dist/server.d.ts +0 -1
- package/dist/server.js +95 -51
- package/dist/transport/body.d.ts +0 -1
- package/dist/transport/body.js +0 -1
- package/dist/transport/codecs.d.ts +2 -3
- package/dist/transport/codecs.js +4 -4
- package/dist/transport/credential.d.ts +0 -1
- package/dist/transport/credential.js +0 -1
- package/dist/transport/http/binary-request.d.ts +11 -0
- package/dist/transport/http/binary-request.js +102 -0
- package/dist/transport/http/binary.d.ts +6 -0
- package/dist/transport/http/binary.js +35 -0
- package/dist/transport/http/byte-stream.d.ts +4 -0
- package/dist/transport/http/byte-stream.js +123 -0
- package/dist/transport/http/control.d.ts +12 -0
- package/dist/transport/http/control.js +24 -0
- package/dist/transport/http/handler.d.ts +8 -3
- package/dist/transport/http/handler.js +97 -45
- package/dist/transport/http/index.d.ts +3 -2
- package/dist/transport/http/index.js +2 -2
- package/dist/transport/http/model.d.ts +20 -0
- package/dist/transport/http/model.js +1 -0
- package/dist/transport/http/render.d.ts +3 -31
- package/dist/transport/http/render.js +18 -269
- package/dist/transport/http/response.d.ts +14 -0
- package/dist/transport/http/response.js +77 -0
- package/dist/transport/http/stream.d.ts +5 -0
- package/dist/transport/http/stream.js +134 -0
- package/dist/transport/index.d.ts +2 -2
- package/dist/transport/index.js +2 -2
- package/dist/transport/result.d.ts +13 -0
- package/dist/transport/result.js +35 -0
- package/dist/transport/router.d.ts +0 -1
- package/dist/transport/router.js +0 -1
- package/dist/transport/stream-error.d.ts +0 -1
- package/dist/transport/stream-error.js +0 -1
- package/dist/transport/types.d.ts +1 -2
- package/dist/transport/types.js +0 -1
- package/dist/transport/websocket/index.d.ts +0 -1
- package/dist/transport/websocket/index.js +0 -1
- package/dist/transport/websocket/session.d.ts +3 -3
- package/dist/transport/websocket/session.js +30 -30
- package/dist/transport/websocket/upgrade.d.ts +2 -3
- package/dist/transport/websocket/upgrade.js +7 -7
- package/dist/types.d.ts +33 -10
- package/dist/types.js +0 -1
- package/package.json +11 -22
- package/dist/capture.d.ts.map +0 -1
- package/dist/capture.js.map +0 -1
- package/dist/cors/cors.d.ts.map +0 -1
- package/dist/cors/cors.js.map +0 -1
- package/dist/cors/index.d.ts.map +0 -1
- package/dist/cors/index.js.map +0 -1
- package/dist/cors/types.d.ts.map +0 -1
- package/dist/cors/types.js.map +0 -1
- package/dist/errors/availability.d.ts.map +0 -1
- package/dist/errors/availability.js.map +0 -1
- package/dist/errors/classify.d.ts.map +0 -1
- package/dist/errors/classify.js.map +0 -1
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/errors/index.js.map +0 -1
- package/dist/errors/request-body.d.ts.map +0 -1
- package/dist/errors/request-body.js.map +0 -1
- package/dist/errors/response-body.d.ts.map +0 -1
- package/dist/errors/response-body.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/invocation/context.d.ts.map +0 -1
- package/dist/invocation/context.js.map +0 -1
- package/dist/invocation/identity.d.ts.map +0 -1
- package/dist/invocation/identity.js.map +0 -1
- package/dist/invocation/index.d.ts.map +0 -1
- package/dist/invocation/index.js.map +0 -1
- package/dist/invocation/publication-route.d.ts +0 -14
- package/dist/invocation/publication-route.d.ts.map +0 -1
- package/dist/invocation/publication-route.js +0 -45
- package/dist/invocation/publication-route.js.map +0 -1
- package/dist/invocation/types.d.ts.map +0 -1
- package/dist/invocation/types.js.map +0 -1
- package/dist/issuer/exchange.d.ts.map +0 -1
- package/dist/issuer/exchange.js.map +0 -1
- package/dist/issuer/index.d.ts.map +0 -1
- package/dist/issuer/index.js.map +0 -1
- package/dist/issuer/routes.d.ts.map +0 -1
- package/dist/issuer/routes.js.map +0 -1
- package/dist/issuer/types.d.ts.map +0 -1
- package/dist/issuer/types.js.map +0 -1
- package/dist/lifecycle/active.d.ts.map +0 -1
- package/dist/lifecycle/active.js.map +0 -1
- package/dist/lifecycle/index.d.ts.map +0 -1
- package/dist/lifecycle/index.js.map +0 -1
- package/dist/publication/delivery/handler.d.ts +0 -10
- package/dist/publication/delivery/handler.d.ts.map +0 -1
- package/dist/publication/delivery/handler.js.map +0 -1
- package/dist/publication/delivery/index.d.ts +0 -3
- package/dist/publication/delivery/index.d.ts.map +0 -1
- package/dist/publication/delivery/index.js +0 -2
- package/dist/publication/delivery/index.js.map +0 -1
- package/dist/publication/delivery/types.d.ts.map +0 -1
- package/dist/publication/delivery/types.js +0 -2
- package/dist/publication/delivery/types.js.map +0 -1
- package/dist/publication/index.d.ts +0 -4
- package/dist/publication/index.d.ts.map +0 -1
- package/dist/publication/index.js +0 -3
- package/dist/publication/index.js.map +0 -1
- package/dist/publication/snapshot.d.ts +0 -16
- package/dist/publication/snapshot.d.ts.map +0 -1
- package/dist/publication/snapshot.js +0 -39
- package/dist/publication/snapshot.js.map +0 -1
- package/dist/publication/table.d.ts +0 -4
- package/dist/publication/table.d.ts.map +0 -1
- package/dist/publication/table.js +0 -17
- package/dist/publication/table.js.map +0 -1
- package/dist/publication/types.d.ts +0 -23
- package/dist/publication/types.d.ts.map +0 -1
- package/dist/publication/types.js +0 -2
- package/dist/publication/types.js.map +0 -1
- package/dist/publication/validator.d.ts +0 -4
- package/dist/publication/validator.d.ts.map +0 -1
- package/dist/publication/validator.js +0 -17
- package/dist/publication/validator.js.map +0 -1
- package/dist/routes/index.d.ts.map +0 -1
- package/dist/routes/index.js.map +0 -1
- package/dist/routes/routes.d.ts.map +0 -1
- package/dist/routes/routes.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/transport/body.d.ts.map +0 -1
- package/dist/transport/body.js.map +0 -1
- package/dist/transport/codecs.d.ts.map +0 -1
- package/dist/transport/codecs.js.map +0 -1
- package/dist/transport/credential.d.ts.map +0 -1
- package/dist/transport/credential.js.map +0 -1
- package/dist/transport/http/handler.d.ts.map +0 -1
- package/dist/transport/http/handler.js.map +0 -1
- package/dist/transport/http/index.d.ts.map +0 -1
- package/dist/transport/http/index.js.map +0 -1
- package/dist/transport/http/render.d.ts.map +0 -1
- package/dist/transport/http/render.js.map +0 -1
- package/dist/transport/index.d.ts.map +0 -1
- package/dist/transport/index.js.map +0 -1
- package/dist/transport/router.d.ts.map +0 -1
- package/dist/transport/router.js.map +0 -1
- package/dist/transport/stream-error.d.ts.map +0 -1
- package/dist/transport/stream-error.js.map +0 -1
- package/dist/transport/types.d.ts.map +0 -1
- package/dist/transport/types.js.map +0 -1
- package/dist/transport/websocket/index.d.ts.map +0 -1
- package/dist/transport/websocket/index.js.map +0 -1
- package/dist/transport/websocket/session.d.ts.map +0 -1
- package/dist/transport/websocket/session.js.map +0 -1
- package/dist/transport/websocket/upgrade.d.ts.map +0 -1
- package/dist/transport/websocket/upgrade.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as protocol from '@astrale-os/kernel-protocol/invocation';
|
|
2
|
+
import * as routes from '@astrale-os/kernel-protocol/routes';
|
|
3
|
+
import { toBody } from './response.js';
|
|
4
|
+
/** Retain the native operation until its byte producer ends or is closed. */
|
|
5
|
+
export function streamingBinaryResponse(binary, operation, cors, request) {
|
|
6
|
+
// Admit the reserved/invalid headers before acquiring the producer, so a refused render never
|
|
7
|
+
// captures the stream it would then have to abandon.
|
|
8
|
+
const applicationHeaders = applicationBinaryHeaders(binary.headers);
|
|
9
|
+
const iterator = binary.body[Symbol.asyncIterator]();
|
|
10
|
+
let terminal = false;
|
|
11
|
+
let controller;
|
|
12
|
+
let stopping;
|
|
13
|
+
const stop = (reason = 'Binary response was interrupted.') => {
|
|
14
|
+
if (terminal)
|
|
15
|
+
return stopping ?? Promise.resolve();
|
|
16
|
+
terminal = true;
|
|
17
|
+
controller?.error(new Error(reason));
|
|
18
|
+
stopping = Promise.resolve().then(async () => {
|
|
19
|
+
try {
|
|
20
|
+
await iterator.return?.();
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
operation.settle();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return stopping;
|
|
27
|
+
};
|
|
28
|
+
const body = new ReadableStream({
|
|
29
|
+
start(output) {
|
|
30
|
+
controller = output;
|
|
31
|
+
},
|
|
32
|
+
async pull(controller) {
|
|
33
|
+
if (terminal)
|
|
34
|
+
return;
|
|
35
|
+
try {
|
|
36
|
+
const next = await iterator.next();
|
|
37
|
+
if (terminal)
|
|
38
|
+
return;
|
|
39
|
+
if (next.done) {
|
|
40
|
+
terminal = true;
|
|
41
|
+
controller.close();
|
|
42
|
+
operation.settle();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
controller.enqueue(next.value);
|
|
46
|
+
}
|
|
47
|
+
catch (cause) {
|
|
48
|
+
if (terminal)
|
|
49
|
+
return;
|
|
50
|
+
terminal = true;
|
|
51
|
+
controller.error(cause);
|
|
52
|
+
operation.settle();
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
async cancel(reason) {
|
|
56
|
+
const message = typeof reason === 'string' ? reason : 'native binary response cancelled';
|
|
57
|
+
operation.cancel(message);
|
|
58
|
+
await stop();
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
operation.onCancel(stop);
|
|
62
|
+
try {
|
|
63
|
+
return new Response(body, {
|
|
64
|
+
status: binary.status ?? 200,
|
|
65
|
+
headers: {
|
|
66
|
+
...cors.response(request, Object.keys(applicationHeaders)),
|
|
67
|
+
...applicationHeaders,
|
|
68
|
+
'content-type': binary.mediaType,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch (cause) {
|
|
73
|
+
// A body-forbidding status (e.g. 204) rejects the Response after the producer was wired:
|
|
74
|
+
// close it and let the caller settle the operation rather than leaking its slot.
|
|
75
|
+
void stop('Binary response was refused.');
|
|
76
|
+
throw cause;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Identify a producer-backed binary result before handing off operation ownership. */
|
|
80
|
+
export function isStreamingBinary(input) {
|
|
81
|
+
return !(input.body instanceof Uint8Array);
|
|
82
|
+
}
|
|
83
|
+
function applicationBinaryHeaders(input) {
|
|
84
|
+
const output = {};
|
|
85
|
+
for (const [name, value] of Object.entries(input ?? {})) {
|
|
86
|
+
if (name.toLowerCase() === routes.HTTP_ROUTE_STALE ||
|
|
87
|
+
name.toLowerCase() === routes.HTTP_ROUTE_MISS) {
|
|
88
|
+
throw new TypeError(`${name.toLowerCase()} is reserved for native routing control.`);
|
|
89
|
+
}
|
|
90
|
+
if (protocol.http.isApplicationBinaryHeader(name))
|
|
91
|
+
output[name] = value;
|
|
92
|
+
}
|
|
93
|
+
return output;
|
|
94
|
+
}
|
|
95
|
+
/** Render buffered application bytes with the native binary header manifest. */
|
|
96
|
+
export function binaryResponse(binary, cors, request) {
|
|
97
|
+
const applicationHeaders = applicationBinaryHeaders(binary.headers);
|
|
98
|
+
return new Response(toBody(binary.body), {
|
|
99
|
+
status: binary.status ?? 200,
|
|
100
|
+
headers: {
|
|
101
|
+
...cors.response(request, [
|
|
102
|
+
protocol.http.BINARY_HEADERS_FIELD,
|
|
103
|
+
...Object.keys(applicationHeaders),
|
|
104
|
+
]),
|
|
105
|
+
...applicationHeaders,
|
|
106
|
+
'content-type': binary.mediaType,
|
|
107
|
+
[protocol.http.BINARY_HEADERS_FIELD]: protocol.http.encodeBinaryHeaderNames(applicationHeaders),
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ActiveOperations } from '../lifecycle/index.js';
|
|
2
|
+
import type { Served } from '../release/served.js';
|
|
3
|
+
import type { CorsHeaders } from './model.js';
|
|
4
|
+
/** Serve exact bundle bytes or an uncached miss inside its immutable artifact directory. */
|
|
5
|
+
export declare function bundleResponse(request: Request, snapshot: Served, active: ActiveOperations, responseCors: Readonly<Record<string, string>>, corsFor: CorsHeaders, externalOrigin?: string): Response | undefined;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { bundleUrl } from '@astrale-os/kernel-protocol/release';
|
|
2
|
+
import { servedDocument } from '../release/served.js';
|
|
3
|
+
import { sameRoute } from './address.js';
|
|
4
|
+
import { drained, quoteEtag, response, toBody } from './response.js';
|
|
5
|
+
/** Serve exact bundle bytes or an uncached miss inside its immutable artifact directory. */
|
|
6
|
+
export function bundleResponse(request, snapshot, active, responseCors, corsFor, externalOrigin) {
|
|
7
|
+
const document = servedDocument(snapshot);
|
|
8
|
+
const descriptor = document.schema.bundle;
|
|
9
|
+
const addressed = bundleUrl(document.identity.issuer, descriptor.ref);
|
|
10
|
+
const immutable = descriptor.href === addressed;
|
|
11
|
+
if (sameRoute(request.url, descriptor.href, externalOrigin)) {
|
|
12
|
+
if (!active.isAccepting) {
|
|
13
|
+
return drained('Release artifact is unavailable while draining.', corsFor);
|
|
14
|
+
}
|
|
15
|
+
if (request.method !== 'GET')
|
|
16
|
+
return response({ error: 'Bundle retrieval requires GET.' }, 405, responseCors);
|
|
17
|
+
const body = snapshot.bundle;
|
|
18
|
+
if (body.byteLength !== descriptor.ref.size) {
|
|
19
|
+
return response({
|
|
20
|
+
error: 'Bundle differs from its release.',
|
|
21
|
+
}, 500, responseCors);
|
|
22
|
+
}
|
|
23
|
+
const etag = quoteEtag(descriptor.ref.digest);
|
|
24
|
+
const unchanged = request.headers.get('if-none-match') === etag;
|
|
25
|
+
return new Response(unchanged ? null : toBody(body), {
|
|
26
|
+
status: unchanged ? 304 : 200,
|
|
27
|
+
headers: {
|
|
28
|
+
...responseCors,
|
|
29
|
+
'content-type': descriptor.ref.mediaType,
|
|
30
|
+
etag,
|
|
31
|
+
'cache-control': immutable
|
|
32
|
+
? 'public, max-age=31536000, immutable, no-transform'
|
|
33
|
+
: 'public, max-age=0, must-revalidate, no-transform',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (request.method === 'GET' && immutable) {
|
|
38
|
+
const pathname = new URL(addressed).pathname;
|
|
39
|
+
const directory = pathname.slice(0, pathname.lastIndexOf('/') + 1);
|
|
40
|
+
const requested = new URL(request.url);
|
|
41
|
+
if (requested.pathname.startsWith(directory) &&
|
|
42
|
+
/^[0-9a-f]{64}\.json$/u.test(requested.pathname.slice(directory.length))) {
|
|
43
|
+
return new Response(null, {
|
|
44
|
+
status: 404,
|
|
45
|
+
headers: { ...responseCors, 'cache-control': 'no-store' },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A request that mutates state or navigates to an effectful target. Non-safe HTTP methods always
|
|
3
|
+
* qualify; a GET or HEAD qualifies only as a top-level browser navigation (`Sec-Fetch-Mode:
|
|
4
|
+
* navigate`), the one GET shape that carries SameSite=Lax cookies cross-site. Programmatic GETs
|
|
5
|
+
* (`cors`/`no-cors`) are left untouched so read clients are never broken.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isStateChanging(request: Request): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Positive evidence that a request originates from another site. `Sec-Fetch-Site` is set by the
|
|
10
|
+
* browser and cannot be forged by page script, so when present it is authoritative: only
|
|
11
|
+
* `cross-site` is refused, and `same-origin`/`same-site`/`none` are trusted (a shared-domain
|
|
12
|
+
* subdomain deployment keeps working). When it is absent, a differing `Origin` is the next-best
|
|
13
|
+
* signal. Absence of both is treated as not-cross-site, so same-origin browsers and non-browser
|
|
14
|
+
* clients that omit these headers are never refused.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isClearlyCrossSite(request: Request, serviceOrigin: string): boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A request that mutates state or navigates to an effectful target. Non-safe HTTP methods always
|
|
3
|
+
* qualify; a GET or HEAD qualifies only as a top-level browser navigation (`Sec-Fetch-Mode:
|
|
4
|
+
* navigate`), the one GET shape that carries SameSite=Lax cookies cross-site. Programmatic GETs
|
|
5
|
+
* (`cors`/`no-cors`) are left untouched so read clients are never broken.
|
|
6
|
+
*/
|
|
7
|
+
export function isStateChanging(request) {
|
|
8
|
+
switch (request.method.toUpperCase()) {
|
|
9
|
+
case 'POST':
|
|
10
|
+
case 'PUT':
|
|
11
|
+
case 'PATCH':
|
|
12
|
+
case 'DELETE':
|
|
13
|
+
return true;
|
|
14
|
+
case 'GET':
|
|
15
|
+
case 'HEAD':
|
|
16
|
+
return request.headers.get('sec-fetch-mode') === 'navigate';
|
|
17
|
+
default:
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Positive evidence that a request originates from another site. `Sec-Fetch-Site` is set by the
|
|
23
|
+
* browser and cannot be forged by page script, so when present it is authoritative: only
|
|
24
|
+
* `cross-site` is refused, and `same-origin`/`same-site`/`none` are trusted (a shared-domain
|
|
25
|
+
* subdomain deployment keeps working). When it is absent, a differing `Origin` is the next-best
|
|
26
|
+
* signal. Absence of both is treated as not-cross-site, so same-origin browsers and non-browser
|
|
27
|
+
* clients that omit these headers are never refused.
|
|
28
|
+
*/
|
|
29
|
+
export function isClearlyCrossSite(request, serviceOrigin) {
|
|
30
|
+
const site = request.headers.get('sec-fetch-site');
|
|
31
|
+
if (site !== null)
|
|
32
|
+
return site === 'cross-site';
|
|
33
|
+
const origin = request.headers.get('origin');
|
|
34
|
+
return origin !== null && origin !== serviceOrigin;
|
|
35
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CorsPolicy } from '../cors/index.js';
|
|
2
|
+
import type { ActiveOperations } from '../lifecycle/index.js';
|
|
3
|
+
import type { Served } from '../release/served.js';
|
|
4
|
+
import type { ServerRouter } from '../transport/router.js';
|
|
5
|
+
/** Mount the Release document at its issuer path. */
|
|
6
|
+
export declare function mountDocuments(app: ServerRouter, cors: CorsPolicy, active: ActiveOperations, served: (request: Request) => Served | undefined, externalIssuer?: string): void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { MEDIA_TYPE as RELEASE_MEDIA_TYPE, PATH as RELEASE_PATH, url as releaseUrl, } from '@astrale-os/kernel-protocol/release';
|
|
2
|
+
import { drained, quoteEtag, response, toBody } from './response.js';
|
|
3
|
+
/** Mount the Release document at its issuer path. */
|
|
4
|
+
export function mountDocuments(app, cors, active, served, externalIssuer) {
|
|
5
|
+
mountDocument(app, documentPath(RELEASE_PATH, releaseUrl, externalIssuer), 'Release', {
|
|
6
|
+
cors,
|
|
7
|
+
active,
|
|
8
|
+
served,
|
|
9
|
+
read: (snapshot) => ({
|
|
10
|
+
bytes: snapshot.releaseBytes,
|
|
11
|
+
mediaType: RELEASE_MEDIA_TYPE,
|
|
12
|
+
etag: snapshot.release.digest,
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function documentPath(path, url, externalIssuer) {
|
|
17
|
+
return externalIssuer === undefined ? path : new URL(url(externalIssuer)).pathname;
|
|
18
|
+
}
|
|
19
|
+
function mountDocument(app, path, name, serving) {
|
|
20
|
+
const { cors, active } = serving;
|
|
21
|
+
if (cors.enabled) {
|
|
22
|
+
app.options(path, (context) => cors.preflight(context.req.raw, {
|
|
23
|
+
methods: ['GET'],
|
|
24
|
+
headers: ['if-none-match'],
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
app.get(path, (context) => {
|
|
28
|
+
const responseCors = cors.response(context.req.raw, ['etag']);
|
|
29
|
+
if (!active.isAccepting) {
|
|
30
|
+
return drained(`${name} is unavailable while draining.`, (exposed) => cors.response(context.req.raw, exposed));
|
|
31
|
+
}
|
|
32
|
+
const snapshot = serving.served(context.req.raw);
|
|
33
|
+
const document = snapshot === undefined ? undefined : serving.read(snapshot);
|
|
34
|
+
if (document === undefined)
|
|
35
|
+
return response({ error: `${name} is not ready.` }, 503, responseCors);
|
|
36
|
+
const etag = quoteEtag(document.etag);
|
|
37
|
+
if (context.req.raw.headers.get('if-none-match') === etag) {
|
|
38
|
+
return new Response(null, {
|
|
39
|
+
status: 304,
|
|
40
|
+
headers: {
|
|
41
|
+
...responseCors,
|
|
42
|
+
etag,
|
|
43
|
+
'cache-control': 'public, max-age=0, must-revalidate, no-transform',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return new Response(toBody(document.bytes), {
|
|
48
|
+
status: 200,
|
|
49
|
+
headers: {
|
|
50
|
+
...responseCors,
|
|
51
|
+
'content-type': document.mediaType,
|
|
52
|
+
etag,
|
|
53
|
+
'cache-control': 'public, max-age=0, must-revalidate, no-transform',
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
package/dist/routes/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
1
|
+
export { mountRoutes } from './routes.js';
|
|
2
|
+
export { currentServed } from './snapshot.js';
|
|
3
|
+
export type { RouteLimits, RouteReservation } from './model.js';
|
package/dist/routes/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
1
|
+
export { mountRoutes } from './routes.js';
|
|
2
|
+
export { currentServed } from './snapshot.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Budgets applied to advertised resources and native invocations. */
|
|
2
|
+
export interface RouteLimits {
|
|
3
|
+
readonly maximumRequestBytes: number;
|
|
4
|
+
readonly maximumReleaseBytes: number;
|
|
5
|
+
readonly maximumBundleBytes: number;
|
|
6
|
+
readonly maximumDeliveryBytes: number;
|
|
7
|
+
readonly invocationTimeoutMs: number;
|
|
8
|
+
}
|
|
9
|
+
/** Derive response headers with route-owned exposed names. */
|
|
10
|
+
export type CorsHeaders = (exposedHeaders?: readonly string[]) => Readonly<Record<string, string>>;
|
|
11
|
+
/** Server-owned method and path space unavailable to advertised native routes. */
|
|
12
|
+
export interface RouteReservation {
|
|
13
|
+
readonly path: string;
|
|
14
|
+
readonly methods: readonly string[];
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CorsPolicy } from '../cors/index.js';
|
|
2
|
+
import type { ActiveOperations } from '../lifecycle/index.js';
|
|
3
|
+
import type { Served } from '../release/served.js';
|
|
4
|
+
import type { RouteLimits } from './model.js';
|
|
5
|
+
import { type ApplicationHost, type Handler, type NextInvocationId } from '../invocation/index.js';
|
|
6
|
+
interface NativeOptions {
|
|
7
|
+
readonly handler: Handler;
|
|
8
|
+
readonly active: ActiveOperations;
|
|
9
|
+
readonly nextInvocation: NextInvocationId;
|
|
10
|
+
readonly limits: RouteLimits;
|
|
11
|
+
readonly cors: CorsPolicy;
|
|
12
|
+
readonly hostFor: (request: Request) => ApplicationHost | undefined;
|
|
13
|
+
readonly externalOrigin: string | undefined;
|
|
14
|
+
}
|
|
15
|
+
/** Gate the pinned native route before body admission and dispatch with one operation lifetime. */
|
|
16
|
+
export declare function nativeResponse(request: Request, snapshot: Served, options: NativeOptions, responseCors: Readonly<Record<string, string>>): Promise<Response | undefined>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as codecs from '@astrale-os/kernel-protocol/codecs';
|
|
2
|
+
import * as errors from '@astrale-os/kernel-protocol/errors';
|
|
3
|
+
import * as protocol from '@astrale-os/kernel-protocol/invocation';
|
|
4
|
+
import * as routes from '@astrale-os/kernel-protocol/routes';
|
|
5
|
+
import { admissionRefusal, classifyRequestError, RequestBodyInvalidError, RequestBodyTooLargeError, } from '../errors/index.js';
|
|
6
|
+
import { invocationContext, } from '../invocation/index.js';
|
|
7
|
+
import { pinned } from '../release/served.js';
|
|
8
|
+
import { readBoundedBody } from '../transport/body.js';
|
|
9
|
+
import { binaryResponse, isStreamingBinary, streamingBinaryResponse } from './binary.js';
|
|
10
|
+
import { isClearlyCrossSite, isStateChanging } from './csrf.js';
|
|
11
|
+
import { encodedBody, nativeError } from './response.js';
|
|
12
|
+
import { streamResponse } from './stream.js';
|
|
13
|
+
/** Gate the pinned native route before body admission and dispatch with one operation lifetime. */
|
|
14
|
+
export async function nativeResponse(request, snapshot, options, responseCors) {
|
|
15
|
+
const { handler, active, nextInvocation, limits, cors, hostFor, externalOrigin } = options;
|
|
16
|
+
const corsFor = (exposed) => cors.response(request, exposed);
|
|
17
|
+
let routing;
|
|
18
|
+
try {
|
|
19
|
+
const decoded = routes.decodeRouting(request, snapshot.references);
|
|
20
|
+
if (decoded?.kind === 'stale')
|
|
21
|
+
return stale(request, decoded.current, cors);
|
|
22
|
+
routing = decoded;
|
|
23
|
+
}
|
|
24
|
+
catch (cause) {
|
|
25
|
+
if (cause instanceof routes.RouteError) {
|
|
26
|
+
return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Native route routing is invalid.' }, 400, corsFor);
|
|
27
|
+
}
|
|
28
|
+
throw cause;
|
|
29
|
+
}
|
|
30
|
+
const route = routes.match(snapshot.table, request);
|
|
31
|
+
if (route === undefined)
|
|
32
|
+
return undefined;
|
|
33
|
+
const assigned = nextInvocation();
|
|
34
|
+
const operation = active.begin(limits.invocationTimeoutMs, request.signal);
|
|
35
|
+
if (operation === undefined)
|
|
36
|
+
return nativeError(admissionRefusal(active.isAccepting), 503, corsFor);
|
|
37
|
+
let handedOff = false;
|
|
38
|
+
try {
|
|
39
|
+
const body = await readBoundedBody(request, limits.maximumRequestBytes, operation.signal);
|
|
40
|
+
const native = { url: request.url, method: request.method, headers: request.headers, body };
|
|
41
|
+
const invocation = routes.decode(route, native, snapshot.domain, snapshot.references);
|
|
42
|
+
if (invocation.credentialIn === 'cookie' &&
|
|
43
|
+
isStateChanging(request) &&
|
|
44
|
+
isClearlyCrossSite(request, externalOrigin ?? new URL(request.url).origin)) {
|
|
45
|
+
const refusal = {
|
|
46
|
+
code: errors.CODES.ACCESS_DENIED,
|
|
47
|
+
message: 'Cross-site cookie-authenticated request to a native route is refused.',
|
|
48
|
+
};
|
|
49
|
+
return nativeError(refusal, errors.httpStatusOf(refusal), corsFor);
|
|
50
|
+
}
|
|
51
|
+
const idempotencyHeader = request.headers.get(protocol.HTTP_IDEMPOTENCY_KEY);
|
|
52
|
+
const idempotencyKey = idempotencyHeader === null
|
|
53
|
+
? undefined
|
|
54
|
+
: protocol.acceptIdempotencyKey(idempotencyHeader, protocol.HTTP_IDEMPOTENCY_KEY);
|
|
55
|
+
const result = await handler.dispatch(Object.freeze({
|
|
56
|
+
call: invocation.call,
|
|
57
|
+
...(invocation.credential === undefined ? {} : { credential: invocation.credential }),
|
|
58
|
+
...(routing === undefined ? {} : { via: routing }),
|
|
59
|
+
...pinned(snapshot),
|
|
60
|
+
context: invocationContext(assigned, operation, active, hostFor(request), body, idempotencyKey),
|
|
61
|
+
}));
|
|
62
|
+
if (result.kind === 'value') {
|
|
63
|
+
return new Response(encodedBody(codecs.json.encode(result.value)), {
|
|
64
|
+
headers: {
|
|
65
|
+
...responseCors,
|
|
66
|
+
'content-type': 'application/json',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (result.kind === 'binary') {
|
|
71
|
+
if (isStreamingBinary(result.value)) {
|
|
72
|
+
// Hand the operation off only once the streaming response is built. A refused render
|
|
73
|
+
// (reserved header or a body-forbidding status) throws before this point, so the
|
|
74
|
+
// `finally` below still settles the operation instead of leaking its slot.
|
|
75
|
+
const streaming = streamingBinaryResponse(result.value, operation, cors, request);
|
|
76
|
+
handedOff = true;
|
|
77
|
+
return streaming;
|
|
78
|
+
}
|
|
79
|
+
return binaryResponse(result.value, cors, request);
|
|
80
|
+
}
|
|
81
|
+
if (result.kind === 'stream') {
|
|
82
|
+
handedOff = true;
|
|
83
|
+
return await streamResponse(result.stream, operation, corsFor);
|
|
84
|
+
}
|
|
85
|
+
if (result.kind === 'miss') {
|
|
86
|
+
return new Response(null, {
|
|
87
|
+
status: routes.HTTP_ROUTE_MISS_STATUS,
|
|
88
|
+
headers: {
|
|
89
|
+
...cors.response(request, [routes.HTTP_ROUTE_MISS]),
|
|
90
|
+
...routes.missHeaders(),
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return nativeError({
|
|
95
|
+
code: errors.CODES.UNSUPPORTED,
|
|
96
|
+
message: 'Native routes do not forward Runtime redirects.',
|
|
97
|
+
}, 502, corsFor);
|
|
98
|
+
}
|
|
99
|
+
catch (cause) {
|
|
100
|
+
if (cause instanceof RequestBodyTooLargeError) {
|
|
101
|
+
return nativeError({ code: errors.CODES.LIMIT_EXCEEDED, message: 'Request body is too large.' }, 413, corsFor);
|
|
102
|
+
}
|
|
103
|
+
if (cause instanceof RequestBodyInvalidError) {
|
|
104
|
+
return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Request body is invalid.' }, 400, corsFor);
|
|
105
|
+
}
|
|
106
|
+
if (cause instanceof routes.RouteError) {
|
|
107
|
+
return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Native route request is invalid.' }, 400, corsFor);
|
|
108
|
+
}
|
|
109
|
+
const payload = classifyRequestError(cause, operation);
|
|
110
|
+
return nativeError(payload, errors.httpStatusOf(payload), corsFor);
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
if (!handedOff)
|
|
114
|
+
operation.settle();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Reserved native stale control naming the digest the destination serves (`StaleControl.current`). */
|
|
118
|
+
function stale(request, current, cors) {
|
|
119
|
+
return new Response(null, {
|
|
120
|
+
status: routes.HTTP_ROUTE_STALE_STATUS,
|
|
121
|
+
headers: {
|
|
122
|
+
...cors.response(request, [routes.HTTP_ROUTE_STALE]),
|
|
123
|
+
...routes.staleHeaders(current),
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CorsPolicy } from '../cors/index.js';
|
|
2
|
+
import type { Served } from '../release/served.js';
|
|
3
|
+
/** Answer resource or native-route preflight from the pinned serving snapshot. */
|
|
4
|
+
export declare function releasePreflight(cors: CorsPolicy, request: Request, snapshot: Served, externalOrigin?: string): Response;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as protocol from '@astrale-os/kernel-protocol/invocation';
|
|
2
|
+
import * as routes from '@astrale-os/kernel-protocol/routes';
|
|
3
|
+
import { servedDocument } from '../release/served.js';
|
|
4
|
+
import { sameRoute } from './address.js';
|
|
5
|
+
/** Answer resource or native-route preflight from the pinned serving snapshot. */
|
|
6
|
+
export function releasePreflight(cors, request, snapshot, externalOrigin) {
|
|
7
|
+
const document = servedDocument(snapshot);
|
|
8
|
+
if (sameRoute(request.url, document.schema.bundle.href, externalOrigin)) {
|
|
9
|
+
return cors.preflight(request, { methods: ['GET'], headers: ['if-none-match'] });
|
|
10
|
+
}
|
|
11
|
+
const deliveryUrl = document.delivery?.href;
|
|
12
|
+
if (deliveryUrl !== undefined && sameRoute(request.url, deliveryUrl, externalOrigin)) {
|
|
13
|
+
return cors.preflight(request, {
|
|
14
|
+
methods: ['POST'],
|
|
15
|
+
headers: ['authorization', 'content-type'],
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const requestedMethod = request.headers.get('access-control-request-method');
|
|
19
|
+
const route = requestedMethod === null
|
|
20
|
+
? undefined
|
|
21
|
+
: routes.match(snapshot.table, { url: request.url, method: requestedMethod });
|
|
22
|
+
return cors.preflight(request, route === undefined ? undefined : nativeCorsRoute(route, requestedMethod));
|
|
23
|
+
}
|
|
24
|
+
function nativeCorsRoute(entry, requestedMethod) {
|
|
25
|
+
const headers = new Set([
|
|
26
|
+
'accept',
|
|
27
|
+
routes.HTTP_ROUTE_DIGEST,
|
|
28
|
+
routes.HTTP_ROUTE_ISSUER,
|
|
29
|
+
protocol.HTTP_IDEMPOTENCY_KEY,
|
|
30
|
+
]);
|
|
31
|
+
if (entry.body !== undefined)
|
|
32
|
+
headers.add('content-type');
|
|
33
|
+
let allowAnyHeaders = false;
|
|
34
|
+
for (const pick of Object.values(entry.headers ?? {})) {
|
|
35
|
+
if (pick === '*')
|
|
36
|
+
allowAnyHeaders = true;
|
|
37
|
+
else
|
|
38
|
+
headers.add(pick.toLowerCase());
|
|
39
|
+
}
|
|
40
|
+
const credential = entry.credential;
|
|
41
|
+
const sources = credential === undefined
|
|
42
|
+
? [{ in: 'header' }]
|
|
43
|
+
: Array.isArray(credential)
|
|
44
|
+
? credential
|
|
45
|
+
: [credential];
|
|
46
|
+
for (const source of sources) {
|
|
47
|
+
if (source.in === 'header')
|
|
48
|
+
headers.add((source.name ?? 'authorization').toLowerCase());
|
|
49
|
+
}
|
|
50
|
+
return Object.freeze({
|
|
51
|
+
methods: Object.freeze([entry.method === '*' ? requestedMethod.toUpperCase() : entry.method]),
|
|
52
|
+
headers: Object.freeze([...headers]),
|
|
53
|
+
...(allowAnyHeaders ? { allowAnyHeaders: true } : {}),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type * as errors from '@astrale-os/kernel-protocol/errors';
|
|
2
|
+
import type { CorsHeaders } from './model.js';
|
|
3
|
+
/** Quote an exact digest as a strong HTTP validator. */
|
|
4
|
+
export declare function quoteEtag(digest: string): string;
|
|
5
|
+
/** Encode a JSON response with the admitted CORS headers. */
|
|
6
|
+
export declare function response(body: unknown, status: number, cors: Readonly<Record<string, string>>): Response;
|
|
7
|
+
/** A served document or artifact refused while draining presents the lifecycle retry delay. */
|
|
8
|
+
export declare function drained(message: string, cors: CorsHeaders): Response;
|
|
9
|
+
/** Every native error, including a capacity refusal raised by dispatch, presents its retry delay. */
|
|
10
|
+
export declare function nativeError(error: errors.ErrorPayload, status: number, cors: CorsHeaders): Response;
|
|
11
|
+
/** Preserve encoded JSON as text or exact detached bytes for Fetch. */
|
|
12
|
+
export declare function encodedBody(input: string | Uint8Array): string | ArrayBuffer;
|
|
13
|
+
/** Expose only the selected byte range as a Fetch response body. */
|
|
14
|
+
export declare function toBody(input: Uint8Array): ArrayBuffer;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as codecs from '@astrale-os/kernel-protocol/codecs';
|
|
2
|
+
import * as routes from '@astrale-os/kernel-protocol/routes';
|
|
3
|
+
import { admissionRefusal, retryAfterHeaders } from '../errors/index.js';
|
|
4
|
+
/** Quote an exact digest as a strong HTTP validator. */
|
|
5
|
+
export function quoteEtag(digest) {
|
|
6
|
+
return `"${digest}"`;
|
|
7
|
+
}
|
|
8
|
+
/** Encode a JSON response with the admitted CORS headers. */
|
|
9
|
+
export function response(body, status, cors) {
|
|
10
|
+
return new Response(encodedBody(codecs.json.encode(body)), {
|
|
11
|
+
status,
|
|
12
|
+
headers: { ...cors, 'content-type': 'application/json' },
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/** A served document or artifact refused while draining presents the lifecycle retry delay. */
|
|
16
|
+
export function drained(message, cors) {
|
|
17
|
+
const retry = retryAfterHeaders(admissionRefusal(false));
|
|
18
|
+
return response({ error: message }, 503, { ...cors(Object.keys(retry)), ...retry });
|
|
19
|
+
}
|
|
20
|
+
/** Every native error, including a capacity refusal raised by dispatch, presents its retry delay. */
|
|
21
|
+
export function nativeError(error, status, cors) {
|
|
22
|
+
const admitted = routes.decodeErrorResponse({ error });
|
|
23
|
+
const retry = retryAfterHeaders(error);
|
|
24
|
+
return response(admitted, status, { ...cors(Object.keys(retry)), ...retry });
|
|
25
|
+
}
|
|
26
|
+
/** Preserve encoded JSON as text or exact detached bytes for Fetch. */
|
|
27
|
+
export function encodedBody(input) {
|
|
28
|
+
return typeof input === 'string' ? input : toBody(input);
|
|
29
|
+
}
|
|
30
|
+
/** Expose only the selected byte range as a Fetch response body. */
|
|
31
|
+
export function toBody(input) {
|
|
32
|
+
return input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength);
|
|
33
|
+
}
|
package/dist/routes/routes.d.ts
CHANGED
|
@@ -1,26 +1,9 @@
|
|
|
1
1
|
import type { CorsPolicy } from '../cors/index.js';
|
|
2
|
-
import type { NextInvocationId } from '../invocation/index.js';
|
|
2
|
+
import type { ApplicationHost, Handler, NextInvocationId } from '../invocation/index.js';
|
|
3
3
|
import type { ActiveOperations } from '../lifecycle/index.js';
|
|
4
|
-
import type { Provider as DeliveryProvider } from '../
|
|
5
|
-
import type {
|
|
6
|
-
import {
|
|
7
|
-
import { type
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
readonly maximumRequestBytes: number;
|
|
11
|
-
readonly maximumPublicationBytes: number;
|
|
12
|
-
readonly maximumBundleBytes: number;
|
|
13
|
-
readonly maximumDeliveryBytes: number;
|
|
14
|
-
readonly invocationTimeoutMs: number;
|
|
15
|
-
}
|
|
16
|
-
export interface RouteReservation {
|
|
17
|
-
readonly path: string;
|
|
18
|
-
readonly methods: readonly string[];
|
|
19
|
-
}
|
|
20
|
-
export declare function mountRoutes(app: ServerRouter, source: PublicationSource, deliveryProvider: DeliveryProvider | undefined, handler: Handler, active: ActiveOperations, nextInvocation: NextInvocationId, limits: RouteLimits, cors: CorsPolicy, hostFor: (request: Request) => ApplicationHost | undefined, reservations: readonly RouteReservation[], externalIssuer?: string): void;
|
|
21
|
-
export declare function currentPublicationSnapshot(source: PublicationSource, limits: Pick<RouteLimits, 'maximumPublicationBytes' | 'maximumBundleBytes'>, serving?: {
|
|
22
|
-
readonly origin: string;
|
|
23
|
-
readonly issuer?: string;
|
|
24
|
-
readonly reservations: readonly RouteReservation[];
|
|
25
|
-
}): PreparedPublicationSnapshot | undefined;
|
|
26
|
-
//# sourceMappingURL=routes.d.ts.map
|
|
4
|
+
import type { Provider as DeliveryProvider } from '../release/delivery/index.js';
|
|
5
|
+
import type { ServerRouter } from '../transport/router.js';
|
|
6
|
+
import type { RouteLimits, RouteReservation } from './model.js';
|
|
7
|
+
import { type ServingSource } from '../release/served.js';
|
|
8
|
+
/** Mount the advertised HTTP resources and native routes from one pinned serving snapshot. */
|
|
9
|
+
export declare function mountRoutes(app: ServerRouter, source: ServingSource, deliveryProvider: DeliveryProvider | undefined, handler: Handler, active: ActiveOperations, nextInvocation: NextInvocationId, limits: RouteLimits, cors: CorsPolicy, hostFor: (request: Request) => ApplicationHost | undefined, reservations: readonly RouteReservation[], externalIssuer?: string, publicOrigin?: string): void;
|