@astrale-os/kernel-server 0.4.17 → 0.5.0-beta.10
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 +2 -0
- package/dist/capture.js +9 -0
- package/dist/cors/cors.d.ts +21 -0
- package/dist/cors/cors.js +135 -0
- package/dist/cors/index.d.ts +2 -0
- package/dist/cors/index.js +1 -0
- package/dist/cors/types.d.ts +11 -0
- package/dist/cors/types.js +1 -0
- package/dist/errors/availability.d.ts +10 -0
- package/dist/errors/availability.js +14 -0
- package/dist/errors/classify.d.ts +3 -0
- package/dist/errors/classify.js +41 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/request-body.d.ts +7 -0
- package/dist/errors/request-body.js +14 -0
- package/dist/errors/response-body.d.ts +4 -0
- package/dist/errors/response-body.js +8 -0
- package/dist/index.d.ts +5 -12
- package/dist/index.js +4 -19
- package/dist/invocation/context.d.ts +6 -0
- package/dist/invocation/context.js +60 -0
- package/dist/invocation/identity.d.ts +9 -0
- package/dist/invocation/identity.js +11 -0
- package/dist/invocation/index.d.ts +4 -3
- package/dist/invocation/index.js +3 -3
- package/dist/invocation/publication-route.d.ts +13 -0
- package/dist/invocation/publication-route.js +44 -0
- package/dist/invocation/types.d.ts +62 -0
- package/dist/invocation/types.js +1 -0
- package/dist/issuer/exchange.d.ts +12 -0
- package/dist/issuer/exchange.js +136 -0
- package/dist/issuer/index.d.ts +3 -0
- package/dist/issuer/index.js +1 -0
- package/dist/issuer/routes.d.ts +13 -0
- package/dist/issuer/routes.js +149 -0
- package/dist/issuer/types.d.ts +11 -0
- package/dist/issuer/types.js +1 -0
- package/dist/lifecycle/active.d.ts +39 -0
- package/dist/lifecycle/active.js +199 -0
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.js +1 -0
- package/dist/publication/delivery/handler.d.ts +9 -0
- package/dist/publication/delivery/handler.js +127 -0
- package/dist/publication/delivery/index.d.ts +2 -0
- package/dist/publication/delivery/index.js +1 -0
- package/dist/publication/delivery/types.d.ts +11 -0
- package/dist/publication/delivery/types.js +1 -0
- package/dist/publication/index.d.ts +3 -0
- package/dist/publication/index.js +2 -0
- package/dist/publication/snapshot.d.ts +16 -0
- package/dist/publication/snapshot.js +37 -0
- package/dist/publication/table.d.ts +4 -0
- package/dist/publication/table.js +16 -0
- package/dist/publication/types.d.ts +22 -0
- package/dist/publication/types.js +1 -0
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.js +1 -0
- package/dist/routes/routes.d.ts +25 -0
- package/dist/routes/routes.js +448 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +266 -0
- package/dist/transport/body.d.ts +4 -0
- package/dist/transport/body.js +50 -0
- package/dist/transport/codecs.d.ts +4 -0
- package/dist/transport/codecs.js +5 -0
- package/dist/transport/credential.d.ts +10 -0
- package/dist/transport/credential.js +28 -0
- package/dist/transport/http/handler.d.ts +17 -0
- package/dist/transport/http/handler.js +180 -0
- package/dist/transport/http/index.d.ts +2 -0
- package/dist/transport/http/index.js +2 -0
- package/dist/transport/http/render.d.ts +31 -0
- package/dist/transport/http/render.js +288 -0
- package/dist/transport/index.d.ts +9 -0
- package/dist/transport/index.js +8 -0
- package/dist/transport/router.d.ts +4 -0
- package/dist/transport/router.js +4 -0
- package/dist/transport/stream-error.d.ts +3 -0
- package/dist/transport/stream-error.js +13 -0
- package/dist/transport/types.d.ts +28 -0
- package/dist/transport/types.js +1 -0
- package/dist/transport/websocket/index.d.ts +2 -0
- package/dist/transport/websocket/index.js +2 -0
- package/dist/transport/websocket/session.d.ts +20 -0
- package/dist/transport/websocket/session.js +335 -0
- package/dist/transport/websocket/upgrade.d.ts +20 -0
- package/dist/transport/websocket/upgrade.js +46 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.js +1 -0
- package/package.json +24 -14
- package/dist/app/create.d.ts +0 -48
- package/dist/app/create.d.ts.map +0 -1
- package/dist/app/create.js +0 -108
- package/dist/app/create.js.map +0 -1
- package/dist/app/index.d.ts +0 -4
- package/dist/app/index.d.ts.map +0 -1
- package/dist/app/index.js +0 -4
- package/dist/app/index.js.map +0 -1
- package/dist/app/multiplexed.d.ts +0 -53
- package/dist/app/multiplexed.d.ts.map +0 -1
- package/dist/app/multiplexed.js +0 -64
- package/dist/app/multiplexed.js.map +0 -1
- package/dist/app/ws-upgrade.d.ts +0 -34
- package/dist/app/ws-upgrade.d.ts.map +0 -1
- package/dist/app/ws-upgrade.js +0 -94
- package/dist/app/ws-upgrade.js.map +0 -1
- package/dist/capabilities/contract.d.ts +0 -8
- package/dist/capabilities/contract.d.ts.map +0 -1
- package/dist/capabilities/contract.js +0 -2
- package/dist/capabilities/contract.js.map +0 -1
- package/dist/capabilities/defer.d.ts +0 -4
- package/dist/capabilities/defer.d.ts.map +0 -1
- package/dist/capabilities/defer.js +0 -2
- package/dist/capabilities/defer.js.map +0 -1
- package/dist/capabilities/hono.d.ts +0 -8
- package/dist/capabilities/hono.d.ts.map +0 -1
- package/dist/capabilities/hono.js +0 -13
- package/dist/capabilities/hono.js.map +0 -1
- package/dist/capabilities/index.d.ts +0 -6
- package/dist/capabilities/index.d.ts.map +0 -1
- package/dist/capabilities/index.js +0 -3
- package/dist/capabilities/index.js.map +0 -1
- package/dist/capabilities/lifecycle.d.ts +0 -17
- package/dist/capabilities/lifecycle.d.ts.map +0 -1
- package/dist/capabilities/lifecycle.js +0 -37
- package/dist/capabilities/lifecycle.js.map +0 -1
- package/dist/capabilities/sleep.d.ts +0 -4
- package/dist/capabilities/sleep.d.ts.map +0 -1
- package/dist/capabilities/sleep.js +0 -8
- package/dist/capabilities/sleep.js.map +0 -1
- package/dist/cors.d.ts +0 -21
- package/dist/cors.d.ts.map +0 -1
- package/dist/cors.js +0 -17
- package/dist/cors.js.map +0 -1
- package/dist/envelope/batch.d.ts +0 -13
- package/dist/envelope/batch.d.ts.map +0 -1
- package/dist/envelope/batch.js +0 -96
- package/dist/envelope/batch.js.map +0 -1
- package/dist/envelope/codec.d.ts +0 -26
- package/dist/envelope/codec.d.ts.map +0 -1
- package/dist/envelope/codec.js +0 -52
- package/dist/envelope/codec.js.map +0 -1
- package/dist/envelope/credential.d.ts +0 -20
- package/dist/envelope/credential.d.ts.map +0 -1
- package/dist/envelope/credential.js +0 -32
- package/dist/envelope/credential.js.map +0 -1
- package/dist/envelope/http.d.ts +0 -31
- package/dist/envelope/http.d.ts.map +0 -1
- package/dist/envelope/http.js +0 -132
- package/dist/envelope/http.js.map +0 -1
- package/dist/envelope/sse.d.ts +0 -11
- package/dist/envelope/sse.d.ts.map +0 -1
- package/dist/envelope/sse.js +0 -73
- package/dist/envelope/sse.js.map +0 -1
- package/dist/envelope/ws.d.ts +0 -35
- package/dist/envelope/ws.d.ts.map +0 -1
- package/dist/envelope/ws.js +0 -142
- package/dist/envelope/ws.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/invocation/dispatch.d.ts +0 -32
- package/dist/invocation/dispatch.d.ts.map +0 -1
- package/dist/invocation/dispatch.js +0 -68
- package/dist/invocation/dispatch.js.map +0 -1
- package/dist/invocation/dispatcher.d.ts +0 -19
- package/dist/invocation/dispatcher.d.ts.map +0 -1
- package/dist/invocation/dispatcher.js +0 -9
- package/dist/invocation/dispatcher.js.map +0 -1
- package/dist/invocation/index.d.ts.map +0 -1
- package/dist/invocation/index.js.map +0 -1
- package/dist/jwks/discovery.d.ts +0 -40
- package/dist/jwks/discovery.d.ts.map +0 -1
- package/dist/jwks/discovery.js +0 -113
- package/dist/jwks/discovery.js.map +0 -1
- package/dist/jwks/index.d.ts +0 -3
- package/dist/jwks/index.d.ts.map +0 -1
- package/dist/jwks/index.js +0 -3
- package/dist/jwks/index.js.map +0 -1
- package/dist/jwks/issuers.d.ts +0 -47
- package/dist/jwks/issuers.d.ts.map +0 -1
- package/dist/jwks/issuers.js +0 -76
- package/dist/jwks/issuers.js.map +0 -1
- package/dist/jwks/keys.d.ts +0 -44
- package/dist/jwks/keys.d.ts.map +0 -1
- package/dist/jwks/keys.js +0 -56
- package/dist/jwks/keys.js.map +0 -1
- package/dist/plan/host-params.d.ts +0 -13
- package/dist/plan/host-params.d.ts.map +0 -1
- package/dist/plan/host-params.js +0 -23
- package/dist/plan/host-params.js.map +0 -1
- package/dist/plan/host.d.ts +0 -24
- package/dist/plan/host.d.ts.map +0 -1
- package/dist/plan/host.js +0 -24
- package/dist/plan/host.js.map +0 -1
- package/dist/plan/index.d.ts +0 -4
- package/dist/plan/index.d.ts.map +0 -1
- package/dist/plan/index.js +0 -4
- package/dist/plan/index.js.map +0 -1
- package/dist/plan/walk.d.ts +0 -51
- package/dist/plan/walk.d.ts.map +0 -1
- package/dist/plan/walk.js +0 -110
- package/dist/plan/walk.js.map +0 -1
- package/dist/render/binary.d.ts +0 -19
- package/dist/render/binary.d.ts.map +0 -1
- package/dist/render/binary.js +0 -33
- package/dist/render/binary.js.map +0 -1
- package/dist/render/body.d.ts +0 -10
- package/dist/render/body.d.ts.map +0 -1
- package/dist/render/body.js +0 -17
- package/dist/render/body.js.map +0 -1
- package/dist/render/index.d.ts +0 -3
- package/dist/render/index.d.ts.map +0 -1
- package/dist/render/index.js +0 -3
- package/dist/render/index.js.map +0 -1
- package/dist/routed/credential.d.ts +0 -10
- package/dist/routed/credential.d.ts.map +0 -1
- package/dist/routed/credential.js +0 -55
- package/dist/routed/credential.js.map +0 -1
- package/dist/routed/handler.d.ts +0 -38
- package/dist/routed/handler.d.ts.map +0 -1
- package/dist/routed/handler.js +0 -123
- package/dist/routed/handler.js.map +0 -1
- package/dist/routed/index.d.ts +0 -5
- package/dist/routed/index.d.ts.map +0 -1
- package/dist/routed/index.js +0 -5
- package/dist/routed/index.js.map +0 -1
- package/dist/routed/params.d.ts +0 -40
- package/dist/routed/params.d.ts.map +0 -1
- package/dist/routed/params.js +0 -146
- package/dist/routed/params.js.map +0 -1
- package/dist/routed/stream.d.ts +0 -21
- package/dist/routed/stream.d.ts.map +0 -1
- package/dist/routed/stream.js +0 -69
- package/dist/routed/stream.js.map +0 -1
package/dist/server.js
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { PATH as PUBLICATION_PATH, url as publicationUrl, } from '@astrale-os/kernel-protocol/publication/publication';
|
|
2
|
+
import { captureMethod } from './capture.js';
|
|
3
|
+
import { prepareCors } from './cors/index.js';
|
|
4
|
+
import { captureInvocationSource } from './invocation/index.js';
|
|
5
|
+
import { capture as captureExchangeProvider } from './issuer/exchange.js';
|
|
6
|
+
import { admitIssuer, issuerRouteReservations, mountIssuer } from './issuer/index.js';
|
|
7
|
+
import { ActiveOperations } from './lifecycle/index.js';
|
|
8
|
+
import { currentPublicationSnapshot, mountRoutes } from './routes/index.js';
|
|
9
|
+
import { createHttpHandler } from './transport/http/handler.js';
|
|
10
|
+
import { createServerRouter } from './transport/router.js';
|
|
11
|
+
import { handleWebSocketUpgrade } from './transport/websocket/upgrade.js';
|
|
12
|
+
export function createServer(config) {
|
|
13
|
+
const { handler, ...options } = config;
|
|
14
|
+
return prepareServer(options).create(handler);
|
|
15
|
+
}
|
|
16
|
+
/** Validate all static transport configuration before a Host starts Runtime recovery/bootstrap. */
|
|
17
|
+
export function prepareServer(options) {
|
|
18
|
+
const prepared = admitOptions(options);
|
|
19
|
+
return Object.freeze({
|
|
20
|
+
create: (handler) => createPreparedServer(handler, prepared),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function createPreparedServer(input, config) {
|
|
24
|
+
const handler = captureHandler(input);
|
|
25
|
+
const active = new ActiveOperations(config.limits.maximumConcurrentOperations);
|
|
26
|
+
const app = createServerRouter();
|
|
27
|
+
const hosts = new WeakMap();
|
|
28
|
+
const externalIssuer = config.issuer?.issuer;
|
|
29
|
+
const externalOrigin = externalIssuer === undefined ? undefined : new URL(externalIssuer).origin;
|
|
30
|
+
const handleHttp = createHttpHandler({
|
|
31
|
+
handler,
|
|
32
|
+
active,
|
|
33
|
+
invocation: config.invocation,
|
|
34
|
+
...config.limits,
|
|
35
|
+
cors: config.cors,
|
|
36
|
+
websocket: config.websocket !== undefined,
|
|
37
|
+
...(config.publication === undefined
|
|
38
|
+
? {}
|
|
39
|
+
: {
|
|
40
|
+
currentPublication: (request) => currentPublicationSnapshot(config.publication, config.limits, {
|
|
41
|
+
origin: externalOrigin ?? new URL(request.url).origin,
|
|
42
|
+
...(externalIssuer === undefined ? {} : { issuer: externalIssuer }),
|
|
43
|
+
reservations: config.routeReservations,
|
|
44
|
+
})?.snapshot,
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
if (config.websocket !== undefined) {
|
|
48
|
+
app.get(config.path, (context) => handleWebSocketUpgrade({
|
|
49
|
+
request: context.req.raw,
|
|
50
|
+
adapter: config.websocket,
|
|
51
|
+
handler,
|
|
52
|
+
active,
|
|
53
|
+
invocation: config.invocation,
|
|
54
|
+
...config.limits,
|
|
55
|
+
cors: config.cors,
|
|
56
|
+
...(config.publication === undefined
|
|
57
|
+
? {}
|
|
58
|
+
: {
|
|
59
|
+
currentPublication: () => currentPublicationSnapshot(config.publication, config.limits, {
|
|
60
|
+
origin: externalOrigin ?? new URL(context.req.raw.url).origin,
|
|
61
|
+
...(externalIssuer === undefined ? {} : { issuer: externalIssuer }),
|
|
62
|
+
reservations: config.routeReservations,
|
|
63
|
+
})?.snapshot,
|
|
64
|
+
}),
|
|
65
|
+
host: hosts.get(context.req.raw),
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
app.all(config.path, (context) => handleHttp(context.req.raw, hosts.get(context.req.raw)));
|
|
69
|
+
if (config.health !== undefined) {
|
|
70
|
+
app.get(config.health.path, (context) => {
|
|
71
|
+
const ready = active.isAccepting && config.health.probe.check();
|
|
72
|
+
return context.json({ status: ready ? 'ready' : 'not-ready' }, ready ? 200 : 503, {
|
|
73
|
+
...config.cors.response(context.req.raw),
|
|
74
|
+
'cache-control': 'no-store',
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
if (config.cors.enabled) {
|
|
78
|
+
app.options(config.health.path, (context) => config.cors.preflight(context.req.raw, { methods: ['GET'] }));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (config.issuer !== undefined) {
|
|
82
|
+
mountIssuer(app, config.issuer, config.cors, active, config.limits.invocationTimeoutMs, config.tokenExchange);
|
|
83
|
+
}
|
|
84
|
+
if (config.publication !== undefined) {
|
|
85
|
+
mountRoutes(app, config.publication, config.delivery, handler, active, config.invocation, config.limits, config.cors, (request) => hosts.get(request), config.routeReservations, externalIssuer);
|
|
86
|
+
}
|
|
87
|
+
app.onError((_error, context) => new Response('{"error":"Internal server error."}', {
|
|
88
|
+
status: 500,
|
|
89
|
+
headers: {
|
|
90
|
+
...config.cors.response(context.req.raw),
|
|
91
|
+
'content-type': 'application/json',
|
|
92
|
+
},
|
|
93
|
+
}));
|
|
94
|
+
return Object.freeze({
|
|
95
|
+
app: Object.freeze({
|
|
96
|
+
async fetch(request, host) {
|
|
97
|
+
const admitted = host === undefined ? undefined : captureApplicationHost(host);
|
|
98
|
+
if (admitted !== undefined)
|
|
99
|
+
hosts.set(request, admitted);
|
|
100
|
+
try {
|
|
101
|
+
return await app.fetch(request);
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
hosts.delete(request);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
drain() {
|
|
109
|
+
active.drain();
|
|
110
|
+
},
|
|
111
|
+
close(options) {
|
|
112
|
+
if (!Number.isFinite(options.deadline)) {
|
|
113
|
+
return Promise.reject(new TypeError('Server close deadline must be finite.'));
|
|
114
|
+
}
|
|
115
|
+
return active.close(options.deadline);
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function captureApplicationHost(input) {
|
|
120
|
+
const waitUntil = captureMethod(input, 'waitUntil', 'Application host is invalid.');
|
|
121
|
+
const sleep = input.sleep === undefined
|
|
122
|
+
? undefined
|
|
123
|
+
: captureMethod(input, 'sleep', 'Application host is invalid.');
|
|
124
|
+
return Object.freeze({
|
|
125
|
+
waitUntil,
|
|
126
|
+
...(sleep === undefined ? {} : { sleep }),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function captureHandler(input) {
|
|
130
|
+
const dispatch = captureMethod(input, 'dispatch', 'Server handler is invalid.');
|
|
131
|
+
return Object.freeze({ dispatch });
|
|
132
|
+
}
|
|
133
|
+
function admitOptions(config) {
|
|
134
|
+
const invocation = captureInvocationSource(config.invocation);
|
|
135
|
+
const limits = admitLimits(config.limits);
|
|
136
|
+
const path = admitRoute(config.path ?? '/', 'invocation path');
|
|
137
|
+
const issuer = config.issuer === undefined ? undefined : admitIssuer(config.issuer);
|
|
138
|
+
const tokenExchange = config.tokenExchange === undefined ? undefined : captureExchangeProvider(config.tokenExchange);
|
|
139
|
+
if (tokenExchange !== undefined && issuer === undefined) {
|
|
140
|
+
throw new TypeError('Token exchange requires issuer configuration.');
|
|
141
|
+
}
|
|
142
|
+
const health = config.health === undefined || config.health === false
|
|
143
|
+
? undefined
|
|
144
|
+
: Object.freeze({
|
|
145
|
+
path: admitRoute(config.health.path ?? '/health', 'Health path'),
|
|
146
|
+
probe: captureHealthProbe(config.health.probe),
|
|
147
|
+
});
|
|
148
|
+
const websocket = config.websocket === undefined ? undefined : captureWebSocket(config.websocket);
|
|
149
|
+
const publication = config.publication === undefined ? undefined : capturePublicationSource(config.publication);
|
|
150
|
+
const delivery = config.delivery === undefined ? undefined : captureDeliveryProvider(config.delivery);
|
|
151
|
+
if (delivery !== undefined && publication === undefined) {
|
|
152
|
+
throw new TypeError('Publication Delivery requires a Publication source.');
|
|
153
|
+
}
|
|
154
|
+
const publicationPath = publication === undefined
|
|
155
|
+
? undefined
|
|
156
|
+
: issuer === undefined
|
|
157
|
+
? protocolPublicationPath()
|
|
158
|
+
: protocolPublicationPath(issuer.issuer);
|
|
159
|
+
if (health?.path === path)
|
|
160
|
+
throw new TypeError('Health and invocation paths must be distinct.');
|
|
161
|
+
if (publicationPath === path) {
|
|
162
|
+
throw new TypeError('Publication and invocation paths must be distinct.');
|
|
163
|
+
}
|
|
164
|
+
if (health !== undefined && publicationPath === health.path) {
|
|
165
|
+
throw new TypeError('Health and Publication paths must be distinct.');
|
|
166
|
+
}
|
|
167
|
+
if (issuer !== undefined) {
|
|
168
|
+
const issuerReservations = issuerRouteReservations(issuer, tokenExchange !== undefined);
|
|
169
|
+
const issuerPaths = issuerReservations.map(({ path }) => path);
|
|
170
|
+
if (health !== undefined && issuerPaths.includes(health.path)) {
|
|
171
|
+
throw new TypeError('Health and issuer paths must be distinct.');
|
|
172
|
+
}
|
|
173
|
+
if (websocket !== undefined && issuerPaths.includes(path)) {
|
|
174
|
+
throw new TypeError('WebSocket invocation and issuer paths must be distinct.');
|
|
175
|
+
}
|
|
176
|
+
if (websocket === undefined &&
|
|
177
|
+
issuerReservations.some((reservation) => reservation.path === path && reservation.methods.includes('POST'))) {
|
|
178
|
+
throw new TypeError('Invocation and token-exchange paths must be distinct.');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return Object.freeze({
|
|
182
|
+
invocation,
|
|
183
|
+
limits,
|
|
184
|
+
path,
|
|
185
|
+
cors: prepareCors(config.cors),
|
|
186
|
+
...(health === undefined ? {} : { health }),
|
|
187
|
+
...(issuer === undefined ? {} : { issuer }),
|
|
188
|
+
...(tokenExchange === undefined ? {} : { tokenExchange }),
|
|
189
|
+
...(publication === undefined ? {} : { publication }),
|
|
190
|
+
...(delivery === undefined ? {} : { delivery }),
|
|
191
|
+
routeReservations: routeReservations(path, health?.path, issuer, websocket !== undefined, tokenExchange !== undefined, publicationPath),
|
|
192
|
+
...(websocket === undefined ? {} : { websocket }),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
function routeReservations(invocationPath, healthPath, issuer, websocket, tokenExchange, publicationPath) {
|
|
196
|
+
const reservations = [
|
|
197
|
+
Object.freeze({
|
|
198
|
+
path: invocationPath,
|
|
199
|
+
methods: Object.freeze(websocket ? ['GET', 'POST'] : ['POST']),
|
|
200
|
+
}),
|
|
201
|
+
];
|
|
202
|
+
if (healthPath !== undefined) {
|
|
203
|
+
reservations.push(Object.freeze({ path: healthPath, methods: Object.freeze(['GET']) }));
|
|
204
|
+
}
|
|
205
|
+
if (issuer !== undefined) {
|
|
206
|
+
reservations.push(...issuerRouteReservations(issuer, tokenExchange));
|
|
207
|
+
}
|
|
208
|
+
if (publicationPath !== undefined) {
|
|
209
|
+
reservations.push(Object.freeze({ path: publicationPath, methods: Object.freeze(['GET']) }));
|
|
210
|
+
}
|
|
211
|
+
return Object.freeze(reservations);
|
|
212
|
+
}
|
|
213
|
+
function protocolPublicationPath(issuer) {
|
|
214
|
+
if (issuer === undefined)
|
|
215
|
+
return PUBLICATION_PATH;
|
|
216
|
+
return new URL(publicationUrl(issuer)).pathname;
|
|
217
|
+
}
|
|
218
|
+
function captureHealthProbe(probe) {
|
|
219
|
+
const check = captureMethod(probe, 'check', 'Health probe is invalid.');
|
|
220
|
+
return Object.freeze({ check });
|
|
221
|
+
}
|
|
222
|
+
function captureWebSocket(adapter) {
|
|
223
|
+
const upgrade = captureMethod(adapter, 'upgrade', 'WebSocket adapter is invalid.');
|
|
224
|
+
return Object.freeze({ upgrade });
|
|
225
|
+
}
|
|
226
|
+
function capturePublicationSource(input) {
|
|
227
|
+
const current = captureMethod(input, 'current', 'Publication source is invalid.');
|
|
228
|
+
return Object.freeze({ current });
|
|
229
|
+
}
|
|
230
|
+
function captureDeliveryProvider(input) {
|
|
231
|
+
const create = captureMethod(input, 'create', 'Publication Delivery provider is invalid.');
|
|
232
|
+
return Object.freeze({ create });
|
|
233
|
+
}
|
|
234
|
+
function admitLimits(limits) {
|
|
235
|
+
if (!Number.isSafeInteger(limits.maximumRequestBytes) || limits.maximumRequestBytes <= 0) {
|
|
236
|
+
throw new TypeError('maximumRequestBytes must be a positive safe integer.');
|
|
237
|
+
}
|
|
238
|
+
if (!Number.isSafeInteger(limits.invocationTimeoutMs) || limits.invocationTimeoutMs <= 0) {
|
|
239
|
+
throw new TypeError('invocationTimeoutMs must be a positive safe integer.');
|
|
240
|
+
}
|
|
241
|
+
if (limits.invocationTimeoutMs > 2_147_483_647) {
|
|
242
|
+
throw new TypeError('invocationTimeoutMs exceeds the platform timer range.');
|
|
243
|
+
}
|
|
244
|
+
return Object.freeze({
|
|
245
|
+
maximumRequestBytes: limits.maximumRequestBytes,
|
|
246
|
+
maximumResponseBytes: admitOptionalLimit(limits.maximumResponseBytes, 16 * 1024 * 1024, 'maximumResponseBytes'),
|
|
247
|
+
maximumConcurrentOperations: admitOptionalLimit(limits.maximumConcurrentOperations, 1_024, 'maximumConcurrentOperations'),
|
|
248
|
+
maximumPublicationBytes: admitOptionalLimit(limits.maximumPublicationBytes, limits.maximumRequestBytes, 'maximumPublicationBytes'),
|
|
249
|
+
maximumBundleBytes: admitOptionalLimit(limits.maximumBundleBytes, 16 * 1024 * 1024, 'maximumBundleBytes'),
|
|
250
|
+
maximumDeliveryBytes: admitOptionalLimit(limits.maximumDeliveryBytes, limits.maximumRequestBytes, 'maximumDeliveryBytes'),
|
|
251
|
+
invocationTimeoutMs: limits.invocationTimeoutMs,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
function admitOptionalLimit(input, fallback, label) {
|
|
255
|
+
const value = input ?? fallback;
|
|
256
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
257
|
+
throw new TypeError(`${label} must be a positive safe integer.`);
|
|
258
|
+
}
|
|
259
|
+
return value;
|
|
260
|
+
}
|
|
261
|
+
function admitRoute(path, label) {
|
|
262
|
+
if (!path.startsWith('/') || path.includes('?') || path.includes('#')) {
|
|
263
|
+
throw new TypeError(`${label} must be an absolute path without query or fragment.`);
|
|
264
|
+
}
|
|
265
|
+
return path;
|
|
266
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { RequestBodyInvalidError, RequestBodyTooLargeError } from '../errors/index.js';
|
|
2
|
+
/** Read a request body while enforcing the limit before retaining excess bytes. */
|
|
3
|
+
export declare function readBoundedBody(request: Request, maximumBytes: number, signal: AbortSignal): Promise<Uint8Array>;
|
|
4
|
+
export declare function byteLength(input: string | Uint8Array): number;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { RequestBodyInvalidError, RequestBodyTooLargeError } from '../errors/index.js';
|
|
2
|
+
export { RequestBodyInvalidError, RequestBodyTooLargeError } from '../errors/index.js';
|
|
3
|
+
/** Read a request body while enforcing the limit before retaining excess bytes. */
|
|
4
|
+
export async function readBoundedBody(request, maximumBytes, signal) {
|
|
5
|
+
const declared = request.headers.get('content-length');
|
|
6
|
+
if (declared !== null) {
|
|
7
|
+
const length = Number(declared);
|
|
8
|
+
if (!Number.isSafeInteger(length) || length < 0) {
|
|
9
|
+
throw new RequestBodyInvalidError('Content-Length is invalid.');
|
|
10
|
+
}
|
|
11
|
+
if (length > maximumBytes)
|
|
12
|
+
throw new RequestBodyTooLargeError(maximumBytes);
|
|
13
|
+
}
|
|
14
|
+
const reader = request.body?.getReader();
|
|
15
|
+
if (reader === undefined)
|
|
16
|
+
return new Uint8Array();
|
|
17
|
+
const chunks = [];
|
|
18
|
+
let total = 0;
|
|
19
|
+
const abort = () => void reader.cancel(signal.reason).catch(() => undefined);
|
|
20
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
21
|
+
try {
|
|
22
|
+
while (true) {
|
|
23
|
+
if (signal.aborted)
|
|
24
|
+
throw signal.reason;
|
|
25
|
+
const { done, value } = await reader.read();
|
|
26
|
+
if (done)
|
|
27
|
+
break;
|
|
28
|
+
total += value.byteLength;
|
|
29
|
+
if (total > maximumBytes) {
|
|
30
|
+
await reader.cancel('request body too large').catch(() => undefined);
|
|
31
|
+
throw new RequestBodyTooLargeError(maximumBytes);
|
|
32
|
+
}
|
|
33
|
+
chunks.push(value);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
signal.removeEventListener('abort', abort);
|
|
38
|
+
reader.releaseLock();
|
|
39
|
+
}
|
|
40
|
+
const body = new Uint8Array(total);
|
|
41
|
+
let offset = 0;
|
|
42
|
+
for (const chunk of chunks) {
|
|
43
|
+
body.set(chunk, offset);
|
|
44
|
+
offset += chunk.byteLength;
|
|
45
|
+
}
|
|
46
|
+
return body;
|
|
47
|
+
}
|
|
48
|
+
export function byteLength(input) {
|
|
49
|
+
return typeof input === 'string' ? new TextEncoder().encode(input).byteLength : input.byteLength;
|
|
50
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import * as codecs from '@astrale-os/kernel-protocol/codecs';
|
|
2
|
+
export type LoadedCodec = codecs.Codec;
|
|
3
|
+
/** Load the built-in Server representation set before pure Protocol binding selection. */
|
|
4
|
+
export declare function loadCodecs(): Promise<readonly [codecs.Codec, codecs.Codec]>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as errors from '@astrale-os/kernel-protocol/errors';
|
|
2
|
+
export type CredentialResult = {
|
|
3
|
+
readonly ok: true;
|
|
4
|
+
readonly credential: string | undefined;
|
|
5
|
+
} | {
|
|
6
|
+
readonly ok: false;
|
|
7
|
+
readonly error: errors.ErrorPayload;
|
|
8
|
+
};
|
|
9
|
+
/** Resolve exactly zero or one untrusted credential channel without authenticating it. */
|
|
10
|
+
export declare function resolveCredential(envelope: string | undefined, authorization: string | null): CredentialResult;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as errors from '@astrale-os/kernel-protocol/errors';
|
|
2
|
+
/** Resolve exactly zero or one untrusted credential channel without authenticating it. */
|
|
3
|
+
export function resolveCredential(envelope, authorization) {
|
|
4
|
+
if (envelope !== undefined && authorization !== null) {
|
|
5
|
+
return failure('Credential must be supplied in either the envelope or Authorization, not both.');
|
|
6
|
+
}
|
|
7
|
+
if (envelope !== undefined)
|
|
8
|
+
return { ok: true, credential: envelope };
|
|
9
|
+
if (authorization === null)
|
|
10
|
+
return { ok: true, credential: undefined };
|
|
11
|
+
const match = /^Bearer[ \t]+([^\s]+)$/i.exec(authorization.trim());
|
|
12
|
+
if (match === null) {
|
|
13
|
+
return {
|
|
14
|
+
ok: false,
|
|
15
|
+
error: {
|
|
16
|
+
code: errors.CODES.AUTH_INVALID,
|
|
17
|
+
message: 'Authorization must use one Bearer credential.',
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return { ok: true, credential: match[1] };
|
|
22
|
+
}
|
|
23
|
+
function failure(message) {
|
|
24
|
+
return {
|
|
25
|
+
ok: false,
|
|
26
|
+
error: { code: errors.CODES.INVALID_REQUEST, message },
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CorsPolicy } from '../../cors/index.js';
|
|
2
|
+
import type { NextInvocationId } from '../../invocation/index.js';
|
|
3
|
+
import type { ActiveOperations } from '../../lifecycle/index.js';
|
|
4
|
+
import type { PublicationSnapshot } from '../../publication/index.js';
|
|
5
|
+
import { type ApplicationHost, type Handler } from '../../invocation/index.js';
|
|
6
|
+
export interface HttpHandlerConfig {
|
|
7
|
+
readonly handler: Handler;
|
|
8
|
+
readonly active: ActiveOperations;
|
|
9
|
+
readonly invocation: NextInvocationId;
|
|
10
|
+
readonly maximumRequestBytes: number;
|
|
11
|
+
readonly maximumResponseBytes: number;
|
|
12
|
+
readonly invocationTimeoutMs: number;
|
|
13
|
+
readonly cors: CorsPolicy;
|
|
14
|
+
readonly websocket: boolean;
|
|
15
|
+
readonly currentPublication?: (request: Request) => PublicationSnapshot | undefined;
|
|
16
|
+
}
|
|
17
|
+
export declare function createHttpHandler(config: HttpHandlerConfig): (request: Request, host?: ApplicationHost) => Promise<Response>;
|
|
@@ -0,0 +1,180 @@
|
|
|
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 { classifyError, RequestBodyInvalidError, RequestBodyTooLargeError, } from '../../errors/index.js';
|
|
5
|
+
import { invocationContext, publicationRouteDecision, } from '../../invocation/index.js';
|
|
6
|
+
import { readBoundedBody } from '../body.js';
|
|
7
|
+
import { loadCodecs } from '../codecs.js';
|
|
8
|
+
import { resolveCredential } from '../credential.js';
|
|
9
|
+
import { renderHttpError, renderHttpResult } from './render.js';
|
|
10
|
+
export function createHttpHandler(config) {
|
|
11
|
+
return async (request, host) => {
|
|
12
|
+
const cors = (exposedHeaders) => config.cors.response(request, exposedHeaders);
|
|
13
|
+
const allow = [
|
|
14
|
+
...(config.websocket ? ['GET'] : []),
|
|
15
|
+
'POST',
|
|
16
|
+
...(config.cors.enabled ? ['OPTIONS'] : []),
|
|
17
|
+
];
|
|
18
|
+
if (request.method === 'OPTIONS' && config.cors.enabled) {
|
|
19
|
+
return noStore(config.cors.preflight(request, {
|
|
20
|
+
methods: ['POST'],
|
|
21
|
+
headers: [
|
|
22
|
+
'authorization',
|
|
23
|
+
'content-type',
|
|
24
|
+
protocol.http.TRACEPARENT_FIELD,
|
|
25
|
+
protocol.http.TRACESTATE_FIELD,
|
|
26
|
+
],
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
if (request.method !== 'POST') {
|
|
30
|
+
return new Response(null, {
|
|
31
|
+
status: 405,
|
|
32
|
+
headers: { ...cors(), allow: allow.join(', '), 'cache-control': 'no-store' },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const assigned = config.invocation();
|
|
36
|
+
const renderError = (requestId, error, codec, options = {}) => renderHttpError(requestId, assigned, error, codec, cors, {
|
|
37
|
+
...options,
|
|
38
|
+
maximumResponseBytes: config.maximumResponseBytes,
|
|
39
|
+
});
|
|
40
|
+
const active = config.active.begin(config.invocationTimeoutMs, request.signal);
|
|
41
|
+
if (active === undefined) {
|
|
42
|
+
return renderError(null, {
|
|
43
|
+
code: errors.CODES.BACKEND_UNAVAILABLE,
|
|
44
|
+
message: 'server is unavailable.',
|
|
45
|
+
}, codecs.json, { status: 503, negotiated: false });
|
|
46
|
+
}
|
|
47
|
+
let handedOff = false;
|
|
48
|
+
try {
|
|
49
|
+
const supported = await loadCodecs();
|
|
50
|
+
const contentType = request.headers.get('content-type');
|
|
51
|
+
const incoming = contentType === null || contentType.trim() === ''
|
|
52
|
+
? codecs.json
|
|
53
|
+
: protocol.http.codecForContentType(contentType, supported);
|
|
54
|
+
if (incoming === undefined) {
|
|
55
|
+
return renderError(null, {
|
|
56
|
+
code: errors.CODES.UNSUPPORTED,
|
|
57
|
+
message: 'Unsupported request Content-Type.',
|
|
58
|
+
}, codecs.json, { status: 415, negotiated: false });
|
|
59
|
+
}
|
|
60
|
+
const outgoing = protocol.http.negotiateResponseCodec(request.headers.get('accept'), incoming, supported);
|
|
61
|
+
if (outgoing === undefined) {
|
|
62
|
+
return renderError(null, {
|
|
63
|
+
code: errors.CODES.UNSUPPORTED,
|
|
64
|
+
message: 'No acceptable response codec is available.',
|
|
65
|
+
}, incoming, { status: 406 });
|
|
66
|
+
}
|
|
67
|
+
let body;
|
|
68
|
+
try {
|
|
69
|
+
body = await readBoundedBody(request, config.maximumRequestBytes, active.signal);
|
|
70
|
+
}
|
|
71
|
+
catch (input) {
|
|
72
|
+
const payload = input instanceof RequestBodyTooLargeError
|
|
73
|
+
? classifyError(input)
|
|
74
|
+
: input instanceof RequestBodyInvalidError
|
|
75
|
+
? { code: errors.CODES.PARSE_ERROR, message: 'Request body is invalid.' }
|
|
76
|
+
: active.timedOut
|
|
77
|
+
? {
|
|
78
|
+
code: errors.CODES.DEADLINE_EXCEEDED,
|
|
79
|
+
message: 'Invocation deadline exceeded.',
|
|
80
|
+
}
|
|
81
|
+
: active.signal.aborted
|
|
82
|
+
? { code: errors.CODES.CANCELLED, message: 'Invocation was cancelled.' }
|
|
83
|
+
: undefined;
|
|
84
|
+
if (payload === undefined)
|
|
85
|
+
throw input;
|
|
86
|
+
return renderError(null, payload, outgoing, {
|
|
87
|
+
status: input instanceof RequestBodyTooLargeError ? 413 : errors.httpStatusOf(payload),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
let decoded;
|
|
91
|
+
try {
|
|
92
|
+
decoded = incoming.decode(body);
|
|
93
|
+
}
|
|
94
|
+
catch (input) {
|
|
95
|
+
if (!(input instanceof codecs.DecodeError))
|
|
96
|
+
throw input;
|
|
97
|
+
return renderError(null, { code: errors.CODES.PARSE_ERROR, message: 'Request body is invalid.' }, outgoing);
|
|
98
|
+
}
|
|
99
|
+
let envelope;
|
|
100
|
+
try {
|
|
101
|
+
envelope = protocol.decodeRequest(decoded);
|
|
102
|
+
}
|
|
103
|
+
catch (input) {
|
|
104
|
+
return renderError(null, classifyError(input), outgoing);
|
|
105
|
+
}
|
|
106
|
+
const publication = config.currentPublication?.(request);
|
|
107
|
+
if (config.currentPublication !== undefined && publication === undefined) {
|
|
108
|
+
return renderError(envelope.requestId, {
|
|
109
|
+
code: errors.CODES.BACKEND_UNAVAILABLE,
|
|
110
|
+
message: 'Publication is unavailable.',
|
|
111
|
+
}, outgoing, { status: 503 });
|
|
112
|
+
}
|
|
113
|
+
if (envelope.routing?.kind === 'via') {
|
|
114
|
+
if (publication === undefined) {
|
|
115
|
+
return renderError(envelope.requestId, {
|
|
116
|
+
code: errors.CODES.INVALID_REQUEST,
|
|
117
|
+
message: 'Publication routing requires a Publication-backed destination.',
|
|
118
|
+
}, outgoing);
|
|
119
|
+
}
|
|
120
|
+
const decision = publicationRouteDecision(publication.publication, envelope.routing.publication);
|
|
121
|
+
if (decision.kind === 'rejected') {
|
|
122
|
+
return renderError(envelope.requestId, decision.error, outgoing);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const credential = resolveCredential(envelope.credential, request.headers.get('authorization'));
|
|
126
|
+
if (!credential.ok) {
|
|
127
|
+
return renderError(envelope.requestId, credential.error, outgoing);
|
|
128
|
+
}
|
|
129
|
+
const trace = protocol.http.decodeTraceHeaders(request.headers.get(protocol.http.TRACEPARENT_FIELD), request.headers.get(protocol.http.TRACESTATE_FIELD));
|
|
130
|
+
try {
|
|
131
|
+
const result = await config.handler.dispatch(Object.freeze({
|
|
132
|
+
call: envelope.call,
|
|
133
|
+
...(trace === undefined ? {} : { trace }),
|
|
134
|
+
...(credential.credential === undefined ? {} : { credential: credential.credential }),
|
|
135
|
+
...(envelope.delegate === undefined ? {} : { delegate: envelope.delegate }),
|
|
136
|
+
...(envelope.routing?.kind === 'via' ? { via: envelope.routing } : {}),
|
|
137
|
+
...(envelope.schema === undefined ? {} : { schema: envelope.schema }),
|
|
138
|
+
...(publication === undefined ? {} : { publication }),
|
|
139
|
+
context: invocationContext(assigned, active, config.active, host, undefined, envelope.idempotencyKey),
|
|
140
|
+
}));
|
|
141
|
+
const response = renderHttpResult(result, {
|
|
142
|
+
requestId: envelope.requestId,
|
|
143
|
+
codec: outgoing,
|
|
144
|
+
cors,
|
|
145
|
+
invocation: assigned,
|
|
146
|
+
active,
|
|
147
|
+
maximumResponseBytes: config.maximumResponseBytes,
|
|
148
|
+
});
|
|
149
|
+
handedOff = result.kind === 'stream';
|
|
150
|
+
return response;
|
|
151
|
+
}
|
|
152
|
+
catch (input) {
|
|
153
|
+
if (active.timedOut) {
|
|
154
|
+
return renderError(envelope.requestId, {
|
|
155
|
+
code: errors.CODES.DEADLINE_EXCEEDED,
|
|
156
|
+
message: 'Invocation deadline exceeded.',
|
|
157
|
+
}, outgoing, { status: 504 });
|
|
158
|
+
}
|
|
159
|
+
if (active.signal.aborted) {
|
|
160
|
+
return renderError(envelope.requestId, { code: errors.CODES.CANCELLED, message: 'Invocation was cancelled.' }, outgoing, { status: 408 });
|
|
161
|
+
}
|
|
162
|
+
return renderError(envelope.requestId, classifyError(input), outgoing);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (input) {
|
|
166
|
+
return renderError(null, classifyError(input), codecs.json, {
|
|
167
|
+
negotiated: false,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
if (!handedOff)
|
|
172
|
+
active.settle();
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function noStore(response) {
|
|
177
|
+
const headers = new Headers(response.headers);
|
|
178
|
+
headers.set('cache-control', 'no-store');
|
|
179
|
+
return new Response(response.body, { status: response.status, headers });
|
|
180
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Codec } 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 type { Handler } from '../../invocation/index.js';
|
|
5
|
+
import type { ActiveOperation } from '../../lifecycle/index.js';
|
|
6
|
+
export interface RenderContext {
|
|
7
|
+
readonly requestId: protocol.RequestId;
|
|
8
|
+
readonly codec: Codec;
|
|
9
|
+
readonly cors: CorsHeaders;
|
|
10
|
+
readonly invocation: protocol.InvocationId;
|
|
11
|
+
readonly active: ActiveOperation;
|
|
12
|
+
readonly maximumResponseBytes: number;
|
|
13
|
+
}
|
|
14
|
+
export type CorsHeaders = (exposedHeaders?: readonly string[]) => Readonly<Record<string, string>>;
|
|
15
|
+
export interface ErrorRenderOptions {
|
|
16
|
+
readonly status?: number;
|
|
17
|
+
readonly negotiated?: boolean;
|
|
18
|
+
readonly maximumResponseBytes?: number;
|
|
19
|
+
}
|
|
20
|
+
type HandlerResult = Awaited<ReturnType<Handler['dispatch']>>;
|
|
21
|
+
export declare function renderHttpResult(result: HandlerResult, context: RenderContext): Response;
|
|
22
|
+
export declare function renderBinary(requestId: protocol.RequestId, invocation: protocol.InvocationId, binary: Extract<HandlerResult, {
|
|
23
|
+
readonly kind: 'binary';
|
|
24
|
+
}>['value']): protocol.BinaryResponse;
|
|
25
|
+
export declare function renderRedirect(requestId: protocol.RequestId, invocation: protocol.InvocationId, redirect: Extract<HandlerResult, {
|
|
26
|
+
readonly kind: 'redirect';
|
|
27
|
+
}>['redirect']): protocol.RedirectResponse;
|
|
28
|
+
export declare function renderHttpError(requestId: protocol.RequestId, invocation: protocol.InvocationId, error: errors.ErrorPayload, codec: Codec, cors: CorsHeaders, options?: ErrorRenderOptions): Response;
|
|
29
|
+
export declare function renderHttpStale(requestId: protocol.RequestId, invocation: protocol.InvocationId, current: protocol.Stale['current'], codec: Codec, cors: CorsHeaders, maximumResponseBytes?: number): Response;
|
|
30
|
+
export declare function encodeEnvelope(codec: Codec, input: unknown): string | Uint8Array;
|
|
31
|
+
export {};
|