@astrale-os/kernel-server 0.5.0-beta.9 → 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 +2 -2
- package/dist/invocation/context.js +2 -2
- package/dist/invocation/identity.d.ts +0 -1
- package/dist/invocation/identity.js +0 -1
- 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 +7 -6
- 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 +18 -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 +0 -1
- 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 -17
- package/dist/publication/snapshot.d.ts.map +0 -1
- package/dist/publication/snapshot.js +0 -38
- package/dist/publication/snapshot.js.map +0 -1
- package/dist/publication/table.d.ts +0 -5
- 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/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
package/dist/issuer/routes.js
CHANGED
|
@@ -2,7 +2,8 @@ import { configuration } from '@astrale-os/kernel-protocol/issuer/configuration'
|
|
|
2
2
|
import { hosted } from '@astrale-os/kernel-protocol/issuer/hosted';
|
|
3
3
|
import { keySet } from '@astrale-os/kernel-protocol/issuer/key-set';
|
|
4
4
|
import { paths as issuerPaths } from '@astrale-os/kernel-protocol/issuer/paths';
|
|
5
|
-
import {
|
|
5
|
+
import { workflowEngine } from '@astrale-os/kernel-protocol/issuer/workflow-engine';
|
|
6
|
+
import { admissionRefusal, IssuerKeysUnavailableError, retryAfterHeaders } from '../errors/index.js';
|
|
6
7
|
import { mount } from './exchange.js';
|
|
7
8
|
export function issuerRouteReservations(input, tokenExchange = false) {
|
|
8
9
|
const paths = issuerPaths(input.issuer);
|
|
@@ -13,6 +14,9 @@ export function issuerRouteReservations(input, tokenExchange = false) {
|
|
|
13
14
|
if (tokenExchange) {
|
|
14
15
|
reservations.push(Object.freeze({ path: paths.tokenExchange, methods: Object.freeze(['POST']) }));
|
|
15
16
|
}
|
|
17
|
+
if (input.workflowEngineKeys !== undefined) {
|
|
18
|
+
reservations.push(Object.freeze({ path: paths.workflowEngine, methods: Object.freeze(['GET']) }));
|
|
19
|
+
}
|
|
16
20
|
if (input.loadIssuerPublicKeys !== undefined) {
|
|
17
21
|
const base = new URL(input.issuer).pathname.replace(/\/+$/u, '');
|
|
18
22
|
reservations.push(Object.freeze({
|
|
@@ -32,11 +36,28 @@ export function mountIssuer(app, input, cors, active, timeoutMs, tokenExchange)
|
|
|
32
36
|
...cors.response(request),
|
|
33
37
|
'cache-control': 'no-cache',
|
|
34
38
|
});
|
|
39
|
+
// A lifecycle refusal names saturation or drain and presents its retry delay.
|
|
40
|
+
const refusal = (request) => {
|
|
41
|
+
const refused = admissionRefusal(active.isAccepting);
|
|
42
|
+
const retry = retryAfterHeaders(refused);
|
|
43
|
+
return {
|
|
44
|
+
body: { error: refused.message },
|
|
45
|
+
headers: {
|
|
46
|
+
...cors.response(request, Object.keys(retry)),
|
|
47
|
+
'cache-control': 'no-cache',
|
|
48
|
+
...retry,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
};
|
|
35
52
|
const algorithm = input.signingAlgorithm ?? 'ES256';
|
|
36
53
|
const discovery = configuration(issuer, [algorithm], {
|
|
37
54
|
tokenExchange: tokenExchange !== undefined,
|
|
38
55
|
});
|
|
39
56
|
app.get(paths.configuration, (context) => context.json(discovery, 200, headers(context.req.raw)));
|
|
57
|
+
if (input.workflowEngineKeys !== undefined) {
|
|
58
|
+
const engine = workflowEngine(issuer, input.workflowEngineKeys);
|
|
59
|
+
app.get(paths.workflowEngine, (context) => context.json(engine, 200, headers(context.req.raw)));
|
|
60
|
+
}
|
|
40
61
|
if (cors.enabled) {
|
|
41
62
|
app.options(paths.configuration, (context) => cors.preflight(context.req.raw, { methods: ['GET'] }));
|
|
42
63
|
app.options(paths.keys, (context) => cors.preflight(context.req.raw, { methods: ['GET'] }));
|
|
@@ -44,7 +65,8 @@ export function mountIssuer(app, input, cors, active, timeoutMs, tokenExchange)
|
|
|
44
65
|
app.get(paths.keys, async (context) => {
|
|
45
66
|
const operation = active.begin(timeoutMs, context.req.raw.signal);
|
|
46
67
|
if (operation === undefined) {
|
|
47
|
-
|
|
68
|
+
const refused = refusal(context.req.raw);
|
|
69
|
+
return context.json(refused.body, 503, refused.headers);
|
|
48
70
|
}
|
|
49
71
|
try {
|
|
50
72
|
const keys = await input.loadPublicKeys(operation.signal);
|
|
@@ -70,54 +92,37 @@ export function mountIssuer(app, input, cors, active, timeoutMs, tokenExchange)
|
|
|
70
92
|
app.options(`${base}/iss/:thumbprint/.well-known/openid-configuration`, (context) => cors.preflight(context.req.raw, { methods: ['GET'] }));
|
|
71
93
|
app.options(`${base}/iss/:thumbprint/.well-known/jwks.json`, (context) => cors.preflight(context.req.raw, { methods: ['GET'] }));
|
|
72
94
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const operation = active.begin(timeoutMs, context.req.raw.signal);
|
|
78
|
-
if (operation === undefined) {
|
|
79
|
-
return context.json({ error: 'Issuer is unavailable while draining.' }, 503, headers(context.req.raw));
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
const keys = await hostedKeys(loadIssuerPublicKeys, hostedIssuer, operation.signal, algorithm);
|
|
83
|
-
if (keys.length === 0)
|
|
95
|
+
const mountHostedDiscovery = (path, document) => {
|
|
96
|
+
app.get(path, async (context) => {
|
|
97
|
+
const hostedIssuer = hosted(issuer, context.req.param('thumbprint'));
|
|
98
|
+
if (hostedIssuer === undefined)
|
|
84
99
|
return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return context.json({ error: 'Issuer keys are unavailable.' }, 503, headers(context.req.raw));
|
|
100
|
+
const operation = active.begin(timeoutMs, context.req.raw.signal);
|
|
101
|
+
if (operation === undefined) {
|
|
102
|
+
const refused = refusal(context.req.raw);
|
|
103
|
+
return context.json(refused.body, 503, refused.headers);
|
|
90
104
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
});
|
|
97
|
-
app.get(`${base}/iss/:thumbprint/.well-known/jwks.json`, async (context) => {
|
|
98
|
-
const hostedIssuer = hosted(issuer, context.req.param('thumbprint'));
|
|
99
|
-
if (hostedIssuer === undefined)
|
|
100
|
-
return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
|
|
101
|
-
const operation = active.begin(timeoutMs, context.req.raw.signal);
|
|
102
|
-
if (operation === undefined) {
|
|
103
|
-
return context.json({ error: 'Issuer is unavailable while draining.' }, 503, headers(context.req.raw));
|
|
104
|
-
}
|
|
105
|
-
try {
|
|
106
|
-
const keys = await hostedKeys(loadIssuerPublicKeys, hostedIssuer, operation.signal, algorithm);
|
|
107
|
-
if (keys.length === 0)
|
|
108
|
-
return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
|
|
109
|
-
return context.json({ keys }, 200, headers(context.req.raw));
|
|
110
|
-
}
|
|
111
|
-
catch (cause) {
|
|
112
|
-
if (operation.signal.aborted || cause instanceof IssuerKeysUnavailableError) {
|
|
113
|
-
return context.json({ error: 'Issuer keys are unavailable.' }, 503, headers(context.req.raw));
|
|
105
|
+
try {
|
|
106
|
+
const keys = await hostedKeys(loadIssuerPublicKeys, hostedIssuer, operation.signal, algorithm);
|
|
107
|
+
if (keys.length === 0)
|
|
108
|
+
return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
|
|
109
|
+
return context.json(document(hostedIssuer, keys), 200, headers(context.req.raw));
|
|
114
110
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
111
|
+
catch (cause) {
|
|
112
|
+
if (operation.signal.aborted || cause instanceof IssuerKeysUnavailableError) {
|
|
113
|
+
return context.json({ error: 'Issuer keys are unavailable.' }, 503, headers(context.req.raw));
|
|
114
|
+
}
|
|
115
|
+
throw cause;
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
operation.settle();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
mountHostedDiscovery(`${base}/iss/:thumbprint/.well-known/openid-configuration`, (issuer) => configuration(issuer, [algorithm]));
|
|
123
|
+
mountHostedDiscovery(`${base}/iss/:thumbprint/.well-known/jwks.json`, (_issuer, keys) => ({
|
|
124
|
+
keys,
|
|
125
|
+
}));
|
|
121
126
|
}
|
|
122
127
|
}
|
|
123
128
|
async function hostedKeys(load, issuer, signal, algorithm) {
|
|
@@ -136,9 +141,14 @@ export function admitIssuer(input) {
|
|
|
136
141
|
}
|
|
137
142
|
const loadPublicKeys = input.loadPublicKeys;
|
|
138
143
|
const loadIssuerPublicKeys = input.loadIssuerPublicKeys;
|
|
144
|
+
// A Kernel that drives an engine publishes it; invalid keys fail closed at boot.
|
|
145
|
+
const workflowEngineKeys = input.workflowEngineKeys === undefined
|
|
146
|
+
? undefined
|
|
147
|
+
: workflowEngine(issuer, input.workflowEngineKeys).identity_keys;
|
|
139
148
|
return Object.freeze({
|
|
140
149
|
issuer,
|
|
141
150
|
signingAlgorithm,
|
|
151
|
+
...(workflowEngineKeys === undefined ? {} : { workflowEngineKeys }),
|
|
142
152
|
loadPublicKeys: (signal) => loadPublicKeys.call(input, signal),
|
|
143
153
|
...(loadIssuerPublicKeys === undefined
|
|
144
154
|
? {}
|
|
@@ -147,4 +157,3 @@ export function admitIssuer(input) {
|
|
|
147
157
|
}),
|
|
148
158
|
});
|
|
149
159
|
}
|
|
150
|
-
//# sourceMappingURL=routes.js.map
|
package/dist/issuer/types.d.ts
CHANGED
|
@@ -8,5 +8,9 @@ export interface Issuer {
|
|
|
8
8
|
loadPublicKeys(signal: AbortSignal): Promise<readonly JsonWebKey[]>;
|
|
9
9
|
/** Optional hosted-key discovery for issuers below `<issuer>/iss/:thumbprint`. */
|
|
10
10
|
readonly loadIssuerPublicKeys?: (issuer: IssuerId, signal: AbortSignal) => Promise<readonly JsonWebKey[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Request identity keys of the workflow engine this Kernel drives, published at
|
|
13
|
+
* `<issuer>/.well-known/astrale/workflow-engine.json` so Domains verify the engine's requests.
|
|
14
|
+
*/
|
|
15
|
+
readonly workflowEngineKeys?: readonly string[];
|
|
11
16
|
}
|
|
12
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/issuer/types.js
CHANGED
|
@@ -12,6 +12,8 @@ export interface ActiveOperation {
|
|
|
12
12
|
/** Register cooperative cleanup and return an idempotent unregistration function. */
|
|
13
13
|
onCancel(cancel: CancelWork): () => void;
|
|
14
14
|
cancel(reason: string): void;
|
|
15
|
+
/** Replace the remaining budget after a trusted transport binding is admitted. */
|
|
16
|
+
extend(timeoutMs: number): void;
|
|
15
17
|
settle(): void;
|
|
16
18
|
readonly finished: Promise<void>;
|
|
17
19
|
}
|
|
@@ -26,6 +28,8 @@ export declare class ActiveOperations {
|
|
|
26
28
|
private changed;
|
|
27
29
|
constructor(maximumConcurrentOperations: number);
|
|
28
30
|
get isAccepting(): boolean;
|
|
31
|
+
/** Whether a newly arriving operation can be admitted without semantic dispatch. */
|
|
32
|
+
get isAvailable(): boolean;
|
|
29
33
|
begin(timeoutMs: number, externalSignal?: AbortSignal): ActiveOperation | undefined;
|
|
30
34
|
attachSocket(socket: ActiveSocket): boolean;
|
|
31
35
|
detachSocket(socket: ActiveSocket): void;
|
|
@@ -37,4 +41,3 @@ export declare class ActiveOperations {
|
|
|
37
41
|
private closeSockets;
|
|
38
42
|
}
|
|
39
43
|
export {};
|
|
40
|
-
//# sourceMappingURL=active.d.ts.map
|
package/dist/lifecycle/active.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Streaming iterators can need several microtasks to return after cancellation. Keep the
|
|
2
|
+
// cleanup join bounded even when a producer ignores its cancellation signal.
|
|
3
|
+
const CANCELLATION_CLEANUP_GRACE_MS = 250;
|
|
1
4
|
export class ActiveOperations {
|
|
2
5
|
maximumConcurrentOperations;
|
|
3
6
|
accepting = true;
|
|
@@ -13,10 +16,13 @@ export class ActiveOperations {
|
|
|
13
16
|
get isAccepting() {
|
|
14
17
|
return this.accepting;
|
|
15
18
|
}
|
|
19
|
+
/** Whether a newly arriving operation can be admitted without semantic dispatch. */
|
|
20
|
+
get isAvailable() {
|
|
21
|
+
return this.accepting && this.operations.size < this.maximumConcurrentOperations;
|
|
22
|
+
}
|
|
16
23
|
begin(timeoutMs, externalSignal) {
|
|
17
|
-
if (!this.
|
|
24
|
+
if (!this.isAvailable)
|
|
18
25
|
return undefined;
|
|
19
|
-
}
|
|
20
26
|
const operation = new Operation(Date.now() + timeoutMs, () => {
|
|
21
27
|
this.operations.delete(operation);
|
|
22
28
|
this.changed?.();
|
|
@@ -63,19 +69,32 @@ export class ActiveOperations {
|
|
|
63
69
|
}
|
|
64
70
|
close(deadline) {
|
|
65
71
|
this.drain();
|
|
66
|
-
this.closing
|
|
72
|
+
if (this.closing === undefined) {
|
|
73
|
+
const flight = this.closeOnce(deadline);
|
|
74
|
+
this.closing = flight;
|
|
75
|
+
void flight.catch(() => {
|
|
76
|
+
// A failed close leaves admission terminal, but residual work may settle later.
|
|
77
|
+
// Let the same owner join that cleanup on its next close attempt.
|
|
78
|
+
if (this.closing === flight)
|
|
79
|
+
this.closing = undefined;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
67
82
|
return this.closing;
|
|
68
83
|
}
|
|
69
84
|
async closeOnce(deadline) {
|
|
70
85
|
await this.waitUntilIdleOr(deadline);
|
|
86
|
+
const hasCancellationWork = [...this.operations].some((operation) => operation.hasCancellationWork);
|
|
71
87
|
if (this.operations.size > 0) {
|
|
72
88
|
for (const operation of this.operations)
|
|
73
89
|
operation.cancel('server closing');
|
|
74
90
|
}
|
|
75
91
|
this.closeSockets();
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
if (hasCancellationWork) {
|
|
93
|
+
await this.waitUntilIdleOr(Date.now() + CANCELLATION_CLEANUP_GRACE_MS);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
await Promise.resolve();
|
|
97
|
+
}
|
|
79
98
|
if (this.operations.size > 0 || this.backgroundTasks.size > 0) {
|
|
80
99
|
this.closed = true;
|
|
81
100
|
throw new Error(`Server close deadline elapsed with ${this.operations.size} active operation(s) and ${this.backgroundTasks.size} background task(s).`);
|
|
@@ -110,11 +129,11 @@ export class ActiveOperations {
|
|
|
110
129
|
}
|
|
111
130
|
}
|
|
112
131
|
class Operation {
|
|
113
|
-
deadline;
|
|
114
132
|
onSettled;
|
|
115
133
|
controller = new AbortController();
|
|
116
134
|
cancelWork = new Set();
|
|
117
135
|
timer;
|
|
136
|
+
deadlineValue;
|
|
118
137
|
external;
|
|
119
138
|
settled = false;
|
|
120
139
|
cancelled = false;
|
|
@@ -122,15 +141,31 @@ class Operation {
|
|
|
122
141
|
resolveFinished;
|
|
123
142
|
finished;
|
|
124
143
|
constructor(deadline, onSettled) {
|
|
125
|
-
this.deadline = deadline;
|
|
126
144
|
this.onSettled = onSettled;
|
|
145
|
+
this.deadlineValue = deadline;
|
|
127
146
|
this.finished = new Promise((resolve) => {
|
|
128
147
|
this.resolveFinished = resolve;
|
|
129
148
|
});
|
|
130
|
-
this.timer =
|
|
149
|
+
this.timer = this.arm();
|
|
150
|
+
}
|
|
151
|
+
get deadline() {
|
|
152
|
+
return this.deadlineValue;
|
|
153
|
+
}
|
|
154
|
+
extend(timeoutMs) {
|
|
155
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2_147_483_647) {
|
|
156
|
+
throw new TypeError('Active operation timeout is invalid.');
|
|
157
|
+
}
|
|
158
|
+
if (this.cancelled || this.settled)
|
|
159
|
+
return;
|
|
160
|
+
clearTimeout(this.timer);
|
|
161
|
+
this.deadlineValue = Date.now() + timeoutMs;
|
|
162
|
+
this.timer = this.arm();
|
|
163
|
+
}
|
|
164
|
+
arm() {
|
|
165
|
+
return setTimeout(() => {
|
|
131
166
|
this.timeout = true;
|
|
132
167
|
this.cancel('invocation deadline exceeded');
|
|
133
|
-
}, Math.min(Math.max(0,
|
|
168
|
+
}, Math.min(Math.max(0, this.deadlineValue - Date.now()), 2_147_483_647));
|
|
134
169
|
}
|
|
135
170
|
link(signal) {
|
|
136
171
|
if (signal === undefined)
|
|
@@ -148,6 +183,9 @@ class Operation {
|
|
|
148
183
|
get timedOut() {
|
|
149
184
|
return this.timeout;
|
|
150
185
|
}
|
|
186
|
+
get hasCancellationWork() {
|
|
187
|
+
return this.cancelWork.size > 0;
|
|
188
|
+
}
|
|
151
189
|
onCancel(cancel) {
|
|
152
190
|
if (this.cancelled) {
|
|
153
191
|
void settleCancellation(cancel, abortReason(this.controller.signal));
|
|
@@ -197,4 +235,3 @@ async function settleCancellation(cancel, reason) {
|
|
|
197
235
|
function abortReason(signal) {
|
|
198
236
|
return signal.reason instanceof Error ? signal.reason.message : 'operation cancelled';
|
|
199
237
|
}
|
|
200
|
-
//# sourceMappingURL=active.js.map
|
package/dist/lifecycle/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ActiveOperations } from '../../lifecycle/index.js';
|
|
2
|
+
import type { Served } from '../served.js';
|
|
3
|
+
import type { Provider } from './types.js';
|
|
4
|
+
export interface Limits {
|
|
5
|
+
readonly maximumDeliveryBytes: number;
|
|
6
|
+
readonly invocationTimeoutMs: number;
|
|
7
|
+
}
|
|
8
|
+
/** Serve one version-two Delivery against the exact snapshot that selected its URL. */
|
|
9
|
+
export declare function handleDelivery(request: Request, snapshot: Served, provider: Provider | undefined, active: ActiveOperations, limits: Limits, corsFor: (exposedHeaders?: readonly string[]) => Readonly<Record<string, string>>): Promise<Response>;
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import * as codecs from '@astrale-os/kernel-protocol/codecs';
|
|
2
|
-
import * as
|
|
3
|
-
import { DeliveryUnavailableError } from '../../errors/index.js';
|
|
2
|
+
import * as release from '@astrale-os/kernel-protocol/release';
|
|
3
|
+
import { admissionRefusal, DeliveryUnavailableError, retryAfterHeaders, } from '../../errors/index.js';
|
|
4
4
|
import { RequestBodyInvalidError, RequestBodyTooLargeError } from '../../errors/index.js';
|
|
5
5
|
import { readBoundedBody } from '../../transport/index.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import { servedDocument } from '../served.js';
|
|
7
|
+
const delivery = release.delivery;
|
|
8
|
+
/** Serve one version-two Delivery against the exact snapshot that selected its URL. */
|
|
9
|
+
export async function handleDelivery(request, snapshot, provider, active, limits, corsFor) {
|
|
10
|
+
const cors = corsFor();
|
|
9
11
|
if (request.method !== 'POST') {
|
|
10
12
|
return response({ error: 'Delivery requires POST.' }, 405, cors);
|
|
11
13
|
}
|
|
12
14
|
if (provider === undefined) {
|
|
13
15
|
return response({ error: 'Delivery is unavailable.' }, 503, cors);
|
|
14
16
|
}
|
|
15
|
-
const advertised = snapshot.
|
|
17
|
+
const advertised = servedDocument(snapshot).delivery;
|
|
16
18
|
if (advertised === undefined) {
|
|
17
19
|
return response({ error: 'Delivery is unavailable.' }, 404, cors);
|
|
18
20
|
}
|
|
19
21
|
const operation = active.begin(limits.invocationTimeoutMs, request.signal);
|
|
20
|
-
if (operation === undefined)
|
|
21
|
-
|
|
22
|
+
if (operation === undefined) {
|
|
23
|
+
const refusal = admissionRefusal(active.isAccepting);
|
|
24
|
+
const retry = retryAfterHeaders(refusal);
|
|
25
|
+
return response({ error: refusal.message }, 503, { ...corsFor(Object.keys(retry)), ...retry });
|
|
26
|
+
}
|
|
22
27
|
try {
|
|
23
28
|
const authorization = request.headers.get('authorization');
|
|
24
29
|
const token = bearer(authorization);
|
|
@@ -64,8 +69,9 @@ export async function handleDelivery(request, snapshot, provider, active, limits
|
|
|
64
69
|
}
|
|
65
70
|
throw cause;
|
|
66
71
|
}
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
const served = snapshot.release.digest;
|
|
73
|
+
if (input.release !== served) {
|
|
74
|
+
return response({ error: 'Release digest differs from request.' }, 409, cors);
|
|
69
75
|
}
|
|
70
76
|
let candidate;
|
|
71
77
|
try {
|
|
@@ -90,8 +96,8 @@ export async function handleDelivery(request, snapshot, provider, active, limits
|
|
|
90
96
|
}
|
|
91
97
|
throw cause;
|
|
92
98
|
}
|
|
93
|
-
if (created.
|
|
94
|
-
return response({ error: 'Delivery provider returned a different
|
|
99
|
+
if (created.release.digest !== served) {
|
|
100
|
+
return response({ error: 'Delivery provider returned a different release.' }, 500, cors);
|
|
95
101
|
}
|
|
96
102
|
const encoded = codecs.json.encode(created);
|
|
97
103
|
if (encodedSize(encoded) > limits.maximumDeliveryBytes) {
|
|
@@ -125,4 +131,3 @@ function encodedSize(input) {
|
|
|
125
131
|
function toBody(input) {
|
|
126
132
|
return input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength);
|
|
127
133
|
}
|
|
128
|
-
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { Context, Provider } from './types.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import type * as
|
|
1
|
+
import type * as release from '@astrale-os/kernel-protocol/release';
|
|
2
2
|
export interface Context {
|
|
3
3
|
/** Omitted for a public endpoint; otherwise the exact admitted retrieval credential. */
|
|
4
|
-
readonly token?:
|
|
4
|
+
readonly token?: release.delivery.Token;
|
|
5
5
|
readonly signal: AbortSignal;
|
|
6
6
|
}
|
|
7
|
-
/** Provider-owned retrieval, construction, and attestation signing. */
|
|
7
|
+
/** Provider-owned retrieval, construction, and attestation signing for the served release. */
|
|
8
8
|
export interface Provider {
|
|
9
9
|
/** Throw DeliveryUnavailableError only for an expected dependency outage. */
|
|
10
|
-
create(request:
|
|
10
|
+
create(request: release.delivery.Request, context: Context): Promise<release.delivery.Delivery>;
|
|
11
11
|
}
|
|
12
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
|
|
2
|
+
import type { DomainRelease } from '@astrale-os/kernel-protocol/release';
|
|
3
|
+
import type { PreparedReleaseSnapshot } from './snapshot.js';
|
|
4
|
+
import type { ReleaseSnapshot, ReleaseSource } from './types.js';
|
|
5
|
+
export interface ServingSource {
|
|
6
|
+
readonly kind: 'release';
|
|
7
|
+
readonly source: ReleaseSource;
|
|
8
|
+
}
|
|
9
|
+
export type Served = PreparedReleaseSnapshot;
|
|
10
|
+
export interface RequestServing {
|
|
11
|
+
readonly kind: 'release';
|
|
12
|
+
/** The snapshot the request pins; undefined while none is ready or it is withheld. */
|
|
13
|
+
current(): Served | undefined;
|
|
14
|
+
/** The public serving origin named by a DEPLOYMENT_RETIRED answer. */
|
|
15
|
+
deployment(): IssuerId;
|
|
16
|
+
}
|
|
17
|
+
export declare function readServed(serving: ServingSource): Served | undefined;
|
|
18
|
+
export declare function servedDocument(served: Served): DomainRelease;
|
|
19
|
+
export declare function pinned(served: Served): {
|
|
20
|
+
readonly release: ReleaseSnapshot;
|
|
21
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { readPreparedReleaseSnapshot } from './snapshot.js';
|
|
2
|
+
export function readServed(serving) {
|
|
3
|
+
return readPreparedReleaseSnapshot(serving.source.current());
|
|
4
|
+
}
|
|
5
|
+
export function servedDocument(served) {
|
|
6
|
+
return served.release;
|
|
7
|
+
}
|
|
8
|
+
export function pinned(served) {
|
|
9
|
+
return { release: served.snapshot };
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Domain } from '@astrale-os/kernel-dsl/v1';
|
|
2
|
+
import type { ServedReferences, Table } from '@astrale-os/kernel-protocol/routes';
|
|
3
|
+
import { type DomainRelease } from '@astrale-os/kernel-protocol/release';
|
|
4
|
+
import type { ReleaseSnapshot, ReleaseSnapshotInput } from './types.js';
|
|
5
|
+
export interface PreparedReleaseSnapshot {
|
|
6
|
+
readonly kind: 'release';
|
|
7
|
+
readonly snapshot: ReleaseSnapshot;
|
|
8
|
+
readonly release: DomainRelease;
|
|
9
|
+
readonly releaseBytes: Uint8Array;
|
|
10
|
+
/** The exact release reference native-route If-Match names. */
|
|
11
|
+
readonly references: ServedReferences;
|
|
12
|
+
readonly bundle: Uint8Array;
|
|
13
|
+
readonly table: Table;
|
|
14
|
+
readonly domain: Domain;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Admit and detach one deployment's serving snapshot before it is served: the release, its exact
|
|
18
|
+
* bundle bytes, its resolved Domain and route Table.
|
|
19
|
+
*/
|
|
20
|
+
export declare function prepareReleaseSnapshot(input: ReleaseSnapshotInput): ReleaseSnapshot;
|
|
21
|
+
/** Internal proof that a ReleaseSource returned a Server-prepared snapshot without cloning it. */
|
|
22
|
+
export declare function readPreparedReleaseSnapshot(input: unknown): PreparedReleaseSnapshot | undefined;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { accept as acceptRelease, bundleUrl, encode as encodeRelease, verifyBundleBytes, } from '@astrale-os/kernel-protocol/release';
|
|
2
|
+
import { admitTable } from './table.js';
|
|
3
|
+
const snapshots = new WeakMap();
|
|
4
|
+
/**
|
|
5
|
+
* Admit and detach one deployment's serving snapshot before it is served: the release, its exact
|
|
6
|
+
* bundle bytes, its resolved Domain and route Table.
|
|
7
|
+
*/
|
|
8
|
+
export function prepareReleaseSnapshot(input) {
|
|
9
|
+
if (input === null || typeof input !== 'object' || !(input.bundle instanceof Uint8Array)) {
|
|
10
|
+
throw new TypeError('Release snapshot input is invalid.');
|
|
11
|
+
}
|
|
12
|
+
const release = acceptRelease(input.release);
|
|
13
|
+
const bundleBytes = new Uint8Array(input.bundle);
|
|
14
|
+
verifyBundleBytes(release, bundleBytes);
|
|
15
|
+
const domain = input.domain;
|
|
16
|
+
if (domain.origin !== release.origin || domain.revision !== release.schema.revision) {
|
|
17
|
+
throw new TypeError('Release snapshot Domain identity differs from its release.');
|
|
18
|
+
}
|
|
19
|
+
const table = admitTable(release, input.table, domain);
|
|
20
|
+
const addressed = new URL(bundleUrl(release.identity.issuer, release.schema.bundle.ref));
|
|
21
|
+
const addressedDirectory = addressed.pathname.slice(0, addressed.pathname.lastIndexOf('/') + 1);
|
|
22
|
+
const servingBundle = new URL(release.schema.bundle.href);
|
|
23
|
+
if (servingBundle.origin === addressed.origin &&
|
|
24
|
+
servingBundle.pathname.startsWith(addressedDirectory) &&
|
|
25
|
+
servingBundle.href !== addressed.href) {
|
|
26
|
+
throw new TypeError('Release bundle URL differs from its artifact digest.');
|
|
27
|
+
}
|
|
28
|
+
const snapshot = Object.freeze({ release });
|
|
29
|
+
const prepared = Object.freeze({
|
|
30
|
+
kind: 'release',
|
|
31
|
+
snapshot,
|
|
32
|
+
release,
|
|
33
|
+
releaseBytes: encodeRelease(release),
|
|
34
|
+
references: Object.freeze({
|
|
35
|
+
release: table.release,
|
|
36
|
+
}),
|
|
37
|
+
bundle: bundleBytes,
|
|
38
|
+
table,
|
|
39
|
+
domain,
|
|
40
|
+
});
|
|
41
|
+
snapshots.set(snapshot, prepared);
|
|
42
|
+
return snapshot;
|
|
43
|
+
}
|
|
44
|
+
/** Internal proof that a ReleaseSource returned a Server-prepared snapshot without cloning it. */
|
|
45
|
+
export function readPreparedReleaseSnapshot(input) {
|
|
46
|
+
if (input === null || typeof input !== 'object')
|
|
47
|
+
return undefined;
|
|
48
|
+
return snapshots.get(input);
|
|
49
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Domain } from '@astrale-os/kernel-dsl/v1';
|
|
2
|
+
import type { DomainRelease } from '@astrale-os/kernel-protocol/release';
|
|
3
|
+
import { type Table } from '@astrale-os/kernel-protocol/routes';
|
|
4
|
+
export declare function admitTable(deployed: DomainRelease, input: Table | undefined, domain: Domain): Table;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { reference } from '@astrale-os/kernel-protocol/release';
|
|
2
|
+
import { acceptTable, verifyTable } from '@astrale-os/kernel-protocol/routes';
|
|
3
|
+
export function admitTable(deployed, input, domain) {
|
|
4
|
+
if (input === undefined) {
|
|
5
|
+
if (deployed.routes.length !== 0) {
|
|
6
|
+
throw new TypeError('A release with native routes requires a Route Table.');
|
|
7
|
+
}
|
|
8
|
+
return verifyTable(Object.freeze({ release: reference(deployed), entries: Object.freeze([]) }), deployed, domain);
|
|
9
|
+
}
|
|
10
|
+
return verifyTable(acceptTable(input), deployed, domain);
|
|
11
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Domain } from '@astrale-os/kernel-dsl/v1';
|
|
2
|
+
import type * as release from '@astrale-os/kernel-protocol/release';
|
|
3
|
+
import type { Table } from '@astrale-os/kernel-protocol/routes';
|
|
4
|
+
declare const RELEASE_SNAPSHOT: unique symbol;
|
|
5
|
+
export interface ReleaseSnapshotInput {
|
|
6
|
+
readonly release: release.DomainRelease;
|
|
7
|
+
/** Exact artifact served at DomainRelease.schema.bundle.href. */
|
|
8
|
+
readonly bundle: Uint8Array;
|
|
9
|
+
/** Route Table bound to this release; required when it declares native routes. */
|
|
10
|
+
readonly table?: Table;
|
|
11
|
+
readonly domain: Domain;
|
|
12
|
+
}
|
|
13
|
+
/** Immutable admitted serving identity; bundle and compiled routes remain Server-private. */
|
|
14
|
+
export interface ReleaseSnapshot {
|
|
15
|
+
readonly release: release.DomainRelease;
|
|
16
|
+
readonly [RELEASE_SNAPSHOT]: true;
|
|
17
|
+
}
|
|
18
|
+
/** Atomically readable source swapped only after a complete snapshot is prepared. */
|
|
19
|
+
export interface ReleaseSource {
|
|
20
|
+
/** Undefined until the first snapshot is ready. */
|
|
21
|
+
current(): ReleaseSnapshot | undefined;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Match a resource path and query against its trusted serving origin. */
|
|
2
|
+
export function sameRoute(left, right, externalOrigin) {
|
|
3
|
+
const a = new URL(left);
|
|
4
|
+
const b = new URL(right);
|
|
5
|
+
return ((externalOrigin ?? a.origin) === b.origin && a.pathname === b.pathname && a.search === b.search);
|
|
6
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BinaryOutput, StreamingBinaryResult } from '@astrale-os/kernel-core/callable';
|
|
2
|
+
import type { CorsPolicy } from '../cors/index.js';
|
|
3
|
+
import type { ActiveOperation } from '../lifecycle/index.js';
|
|
4
|
+
/** Retain the native operation until its byte producer ends or is closed. */
|
|
5
|
+
export declare function streamingBinaryResponse(binary: StreamingBinaryResult, operation: ActiveOperation, cors: CorsPolicy, request: Request): Response;
|
|
6
|
+
/** Identify a producer-backed binary result before handing off operation ownership. */
|
|
7
|
+
export declare function isStreamingBinary(input: BinaryOutput): input is StreamingBinaryResult;
|
|
8
|
+
/** Render buffered application bytes with the native binary header manifest. */
|
|
9
|
+
export declare function binaryResponse(binary: Exclude<BinaryOutput, StreamingBinaryResult>, cors: CorsPolicy, request: Request): Response;
|