@astrale-os/kernel-server 0.5.0-beta.7 → 0.5.0-beta.70
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 +8 -4
- package/dist/index.js +7 -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/legacy/index.d.ts +20 -0
- package/dist/invocation/legacy/index.js +20 -0
- package/dist/invocation/{publication-route.d.ts → legacy/publication-route.d.ts} +7 -5
- package/dist/invocation/{publication-route.js → legacy/publication-route.js} +7 -13
- package/dist/invocation/release-route.d.ts +26 -0
- package/dist/invocation/release-route.js +55 -0
- package/dist/invocation/types.d.ts +20 -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 +33 -5
- 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 +13 -0
- package/dist/{publication → release}/delivery/handler.js +42 -15
- package/dist/{publication → release}/delivery/index.d.ts +0 -1
- package/dist/{publication → release}/delivery/index.js +0 -1
- package/dist/release/delivery/types.d.ts +21 -0
- 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/legacy/delivery.d.ts +18 -0
- package/dist/release/legacy/delivery.js +2 -0
- package/dist/release/legacy/index.d.ts +39 -0
- package/dist/release/legacy/index.js +38 -0
- package/dist/release/legacy/snapshot.d.ts +26 -0
- package/dist/release/legacy/snapshot.js +55 -0
- package/dist/release/legacy/table.d.ts +5 -0
- package/dist/{publication → release/legacy}/table.js +5 -6
- package/dist/release/legacy/types.d.ts +31 -0
- package/dist/release/legacy/types.js +1 -0
- package/dist/release/served.d.ts +30 -0
- package/dist/release/served.js +14 -0
- package/dist/release/snapshot.d.ts +26 -0
- package/dist/release/snapshot.js +54 -0
- package/dist/release/table.d.ts +4 -0
- package/dist/release/table.js +11 -0
- package/dist/release/types.d.ts +33 -0
- package/dist/release/types.js +1 -0
- package/dist/routes/index.d.ts +1 -2
- package/dist/routes/index.js +1 -2
- package/dist/routes/routes.d.ts +9 -7
- package/dist/routes/routes.js +256 -123
- package/dist/server.d.ts +0 -1
- package/dist/server.js +110 -48
- 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/handler.d.ts +8 -3
- package/dist/transport/http/handler.js +95 -45
- package/dist/transport/http/index.d.ts +0 -1
- package/dist/transport/http/index.js +0 -1
- package/dist/transport/http/render.d.ts +9 -4
- package/dist/transport/http/render.js +150 -5
- package/dist/transport/index.d.ts +0 -1
- package/dist/transport/index.js +0 -1
- 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 +32 -6
- package/dist/types.js +0 -1
- package/package.json +12 -15
- 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.map +0 -1
- 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.map +0 -1
- package/dist/publication/delivery/index.js.map +0 -1
- package/dist/publication/delivery/types.d.ts +0 -12
- 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.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
package/dist/routes/routes.js
CHANGED
|
@@ -1,57 +1,46 @@
|
|
|
1
1
|
import * as codecs from '@astrale-os/kernel-protocol/codecs';
|
|
2
2
|
import * as errors from '@astrale-os/kernel-protocol/errors';
|
|
3
3
|
import * as protocol from '@astrale-os/kernel-protocol/invocation';
|
|
4
|
-
import { PATH as
|
|
5
|
-
import { reference as publicationReference } from '@astrale-os/kernel-protocol/
|
|
4
|
+
import { bundleUrl, MEDIA_TYPE as RELEASE_MEDIA_TYPE, PATH as RELEASE_PATH, url as releaseUrl, } from '@astrale-os/kernel-protocol/release';
|
|
5
|
+
import { mediaType as publicationMediaType, PATH as PUBLICATION_PATH, reference as publicationReference, url as publicationUrl, } from '@astrale-os/kernel-protocol/release/legacy';
|
|
6
6
|
import * as routes from '@astrale-os/kernel-protocol/routes';
|
|
7
|
-
import { classifyError, RequestBodyInvalidError, RequestBodyTooLargeError, } from '../errors/index.js';
|
|
7
|
+
import { admissionRefusal, classifyError, classifyRequestError, RequestBodyInvalidError, RequestBodyTooLargeError, retryAfterHeaders, } from '../errors/index.js';
|
|
8
8
|
import { invocationContext } from '../invocation/index.js';
|
|
9
|
-
import { handleDelivery } from '../
|
|
10
|
-
import {
|
|
9
|
+
import { handleDelivery } from '../release/delivery/index.js';
|
|
10
|
+
import { pinned, readServed, servedDocument } from '../release/served.js';
|
|
11
11
|
import { readBoundedBody } from '../transport/body.js';
|
|
12
12
|
import {} from '../transport/router.js';
|
|
13
13
|
import { interruptedStreamError } from '../transport/stream-error.js';
|
|
14
|
-
export function mountRoutes(app, source, deliveryProvider, handler, active, nextInvocation, limits, cors, hostFor, reservations, externalIssuer) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const snapshot = servingSnapshot(source, limits, context.req.raw, reservations, externalIssuer);
|
|
30
|
-
if (snapshot === undefined)
|
|
31
|
-
return response({ error: 'Publication is not ready.' }, 503, responseCors);
|
|
32
|
-
const body = snapshot.publicationBytes;
|
|
33
|
-
if (context.req.raw.headers.get('if-none-match') === quoteEtag(snapshot.publication.etag)) {
|
|
34
|
-
return new Response(null, {
|
|
35
|
-
status: 304,
|
|
36
|
-
headers: {
|
|
37
|
-
...responseCors,
|
|
38
|
-
etag: quoteEtag(snapshot.publication.etag),
|
|
39
|
-
'cache-control': 'public, max-age=0, must-revalidate, no-transform',
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
return new Response(toBody(body), {
|
|
44
|
-
status: 200,
|
|
45
|
-
headers: {
|
|
46
|
-
...responseCors,
|
|
47
|
-
'content-type': 'application/json',
|
|
48
|
-
etag: quoteEtag(snapshot.publication.etag),
|
|
49
|
-
'cache-control': 'public, max-age=0, must-revalidate, no-transform',
|
|
50
|
-
},
|
|
14
|
+
export function mountRoutes(app, source, deliveryProvider, handler, active, nextInvocation, limits, cors, hostFor, reservations, externalIssuer, publicOrigin) {
|
|
15
|
+
const externalOrigin = publicOrigin ?? (externalIssuer === undefined ? undefined : new URL(externalIssuer).origin);
|
|
16
|
+
const served = (request) => servingSnapshot(source, limits, request, reservations, externalIssuer, externalOrigin);
|
|
17
|
+
if (source.kind === 'release') {
|
|
18
|
+
mountDocument(app, documentPath(RELEASE_PATH, releaseUrl, externalIssuer), 'Release', {
|
|
19
|
+
cors,
|
|
20
|
+
active,
|
|
21
|
+
served,
|
|
22
|
+
read: (snapshot) => snapshot.kind === 'release'
|
|
23
|
+
? {
|
|
24
|
+
bytes: snapshot.releaseBytes,
|
|
25
|
+
mediaType: RELEASE_MEDIA_TYPE,
|
|
26
|
+
etag: snapshot.release.digest,
|
|
27
|
+
}
|
|
28
|
+
: undefined,
|
|
51
29
|
});
|
|
30
|
+
}
|
|
31
|
+
// At a release-backed destination `domain.json` serves the frozen v3 projection.
|
|
32
|
+
mountDocument(app, documentPath(PUBLICATION_PATH, publicationUrl, externalIssuer), 'Publication', {
|
|
33
|
+
cors,
|
|
34
|
+
active,
|
|
35
|
+
served,
|
|
36
|
+
read: (snapshot) => ({
|
|
37
|
+
bytes: snapshot.publicationBytes,
|
|
38
|
+
mediaType: publicationMediaType(snapshot.publication),
|
|
39
|
+
etag: snapshot.publication.etag,
|
|
40
|
+
}),
|
|
52
41
|
});
|
|
53
42
|
app.all('*', async (context) => {
|
|
54
|
-
const snapshot =
|
|
43
|
+
const snapshot = served(context.req.raw);
|
|
55
44
|
if (snapshot === undefined)
|
|
56
45
|
return context.notFound();
|
|
57
46
|
const request = context.req.raw;
|
|
@@ -59,48 +48,80 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
|
|
|
59
48
|
return publicationPreflight(cors, request, snapshot, externalOrigin);
|
|
60
49
|
}
|
|
61
50
|
const responseCors = cors.response(request);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
51
|
+
const corsFor = (exposed) => cors.response(request, exposed);
|
|
52
|
+
const document = servedDocument(snapshot);
|
|
53
|
+
const descriptor = document.schema.bundle;
|
|
54
|
+
const addressed = bundleUrl(document.identity.issuer, descriptor.ref);
|
|
55
|
+
const immutable = descriptor.href === addressed;
|
|
56
|
+
if (sameRoute(request.url, descriptor.href, externalOrigin)) {
|
|
57
|
+
// A release names its own document; a v3-input destination keeps its recorded bytes.
|
|
58
|
+
const releaseBacked = snapshot.kind === 'release';
|
|
59
|
+
if (!active.isAccepting) {
|
|
60
|
+
return drained(releaseBacked
|
|
61
|
+
? 'Release artifact is unavailable while draining.'
|
|
62
|
+
: 'Publication artifact is unavailable while draining.', corsFor);
|
|
63
|
+
}
|
|
65
64
|
if (request.method !== 'GET')
|
|
66
65
|
return response({ error: 'Bundle retrieval requires GET.' }, 405, responseCors);
|
|
67
66
|
const body = snapshot.bundle;
|
|
68
|
-
if (body.byteLength !==
|
|
69
|
-
return response({
|
|
67
|
+
if (body.byteLength !== descriptor.ref.size) {
|
|
68
|
+
return response({
|
|
69
|
+
error: releaseBacked
|
|
70
|
+
? 'Bundle differs from its release.'
|
|
71
|
+
: 'Bundle differs from its Publication.',
|
|
72
|
+
}, 500, responseCors);
|
|
70
73
|
}
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
const etag = quoteEtag(descriptor.ref.digest);
|
|
75
|
+
const unchanged = request.headers.get('if-none-match') === etag;
|
|
76
|
+
return new Response(unchanged ? null : toBody(body), {
|
|
77
|
+
status: unchanged ? 304 : 200,
|
|
73
78
|
headers: {
|
|
74
79
|
...responseCors,
|
|
75
|
-
'content-type':
|
|
76
|
-
etag
|
|
77
|
-
'cache-control':
|
|
80
|
+
'content-type': descriptor.ref.mediaType,
|
|
81
|
+
etag,
|
|
82
|
+
'cache-control': immutable
|
|
83
|
+
? 'public, max-age=31536000, immutable, no-transform'
|
|
84
|
+
: 'public, max-age=0, must-revalidate, no-transform',
|
|
78
85
|
},
|
|
79
86
|
});
|
|
80
87
|
}
|
|
81
|
-
|
|
88
|
+
if (request.method === 'GET' && immutable) {
|
|
89
|
+
const pathname = new URL(addressed).pathname;
|
|
90
|
+
const directory = pathname.slice(0, pathname.lastIndexOf('/') + 1);
|
|
91
|
+
const requested = new URL(request.url);
|
|
92
|
+
if (requested.pathname.startsWith(directory) &&
|
|
93
|
+
/^[0-9a-f]{64}\.json$/u.test(requested.pathname.slice(directory.length))) {
|
|
94
|
+
return new Response(null, {
|
|
95
|
+
status: 404,
|
|
96
|
+
headers: { ...responseCors, 'cache-control': 'no-store' },
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const deliveryUrl = document.delivery?.href;
|
|
82
101
|
if (deliveryUrl !== undefined && sameRoute(request.url, deliveryUrl, externalOrigin)) {
|
|
83
|
-
return handleDelivery(request, snapshot, deliveryProvider, active, limits,
|
|
102
|
+
return handleDelivery(request, snapshot, deliveryProvider, active, limits, corsFor);
|
|
84
103
|
}
|
|
85
104
|
let routing;
|
|
86
105
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
106
|
+
if (snapshot.kind === 'release') {
|
|
107
|
+
const decoded = routes.decodeRouting(request, snapshot.references);
|
|
108
|
+
if (decoded?.kind === 'stale')
|
|
109
|
+
return stale(request, decoded.current, cors);
|
|
110
|
+
routing = decoded;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
const ifMatch = request.headers.get(routes.HTTP_ROUTE_DIGEST);
|
|
114
|
+
if (ifMatch !== null &&
|
|
115
|
+
/^"sha256:[0-9a-f]{64}"$/u.test(ifMatch) &&
|
|
116
|
+
ifMatch !== quoteEtag(snapshot.publication.etag)) {
|
|
117
|
+
return stale(request, snapshot.publication.etag, cors);
|
|
118
|
+
}
|
|
119
|
+
routing = routes.legacy.decodeRouting(request, publicationReference(snapshot.publication));
|
|
98
120
|
}
|
|
99
|
-
routing = routes.decodeRouting(request, publicationReference(snapshot.publication));
|
|
100
121
|
}
|
|
101
122
|
catch (cause) {
|
|
102
123
|
if (cause instanceof routes.RouteError) {
|
|
103
|
-
return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Native route routing is invalid.' }, 400,
|
|
124
|
+
return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Native route routing is invalid.' }, 400, corsFor);
|
|
104
125
|
}
|
|
105
126
|
throw cause;
|
|
106
127
|
}
|
|
@@ -110,14 +131,14 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
|
|
|
110
131
|
const assigned = nextInvocation();
|
|
111
132
|
const operation = active.begin(limits.invocationTimeoutMs, request.signal);
|
|
112
133
|
if (operation === undefined)
|
|
113
|
-
return nativeError(
|
|
114
|
-
code: errors.CODES.BACKEND_UNAVAILABLE,
|
|
115
|
-
message: 'Server is draining.',
|
|
116
|
-
}, 503, responseCors);
|
|
134
|
+
return nativeError(admissionRefusal(active.isAccepting), 503, corsFor);
|
|
117
135
|
let handedOff = false;
|
|
118
136
|
try {
|
|
119
137
|
const body = await readBoundedBody(request, limits.maximumRequestBytes, operation.signal);
|
|
120
|
-
const
|
|
138
|
+
const native = { url: request.url, method: request.method, headers: request.headers, body };
|
|
139
|
+
const invocation = snapshot.kind === 'release'
|
|
140
|
+
? routes.decode(route, native, snapshot.domain, snapshot.references)
|
|
141
|
+
: routes.legacy.decode(route, native, snapshot.domain, snapshot.table.publication);
|
|
121
142
|
const idempotencyHeader = request.headers.get(protocol.HTTP_IDEMPOTENCY_KEY);
|
|
122
143
|
const idempotencyKey = idempotencyHeader === null
|
|
123
144
|
? undefined
|
|
@@ -126,7 +147,7 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
|
|
|
126
147
|
call: invocation.call,
|
|
127
148
|
...(invocation.credential === undefined ? {} : { credential: invocation.credential }),
|
|
128
149
|
...(routing === undefined ? {} : { via: routing }),
|
|
129
|
-
|
|
150
|
+
...pinned(snapshot),
|
|
130
151
|
context: invocationContext(assigned, operation, active, hostFor(request), body, idempotencyKey),
|
|
131
152
|
}));
|
|
132
153
|
if (result.kind === 'value') {
|
|
@@ -138,8 +159,13 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
|
|
|
138
159
|
});
|
|
139
160
|
}
|
|
140
161
|
if (result.kind === 'binary') {
|
|
162
|
+
if (isStreamingBinary(result.value)) {
|
|
163
|
+
handedOff = true;
|
|
164
|
+
return streamingBinaryResponse(result.value, operation, cors, request);
|
|
165
|
+
}
|
|
141
166
|
const applicationHeaders = applicationBinaryHeaders(result.value.headers);
|
|
142
167
|
return new Response(toBody(result.value.body), {
|
|
168
|
+
status: result.value.status ?? 200,
|
|
143
169
|
headers: {
|
|
144
170
|
...cors.response(request, [
|
|
145
171
|
protocol.http.BINARY_HEADERS_FIELD,
|
|
@@ -153,7 +179,7 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
|
|
|
153
179
|
}
|
|
154
180
|
if (result.kind === 'stream') {
|
|
155
181
|
handedOff = true;
|
|
156
|
-
return await streamResponse(result.stream, operation,
|
|
182
|
+
return await streamResponse(result.stream, operation, corsFor);
|
|
157
183
|
}
|
|
158
184
|
if (result.kind === 'miss') {
|
|
159
185
|
return new Response(null, {
|
|
@@ -167,29 +193,20 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
|
|
|
167
193
|
return nativeError({
|
|
168
194
|
code: errors.CODES.UNSUPPORTED,
|
|
169
195
|
message: 'Native routes do not forward Runtime redirects.',
|
|
170
|
-
}, 502,
|
|
196
|
+
}, 502, corsFor);
|
|
171
197
|
}
|
|
172
198
|
catch (cause) {
|
|
173
199
|
if (cause instanceof RequestBodyTooLargeError) {
|
|
174
|
-
return nativeError({ code: errors.CODES.LIMIT_EXCEEDED, message: 'Request body is too large.' }, 413,
|
|
200
|
+
return nativeError({ code: errors.CODES.LIMIT_EXCEEDED, message: 'Request body is too large.' }, 413, corsFor);
|
|
175
201
|
}
|
|
176
202
|
if (cause instanceof RequestBodyInvalidError) {
|
|
177
|
-
return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Request body is invalid.' }, 400,
|
|
203
|
+
return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Request body is invalid.' }, 400, corsFor);
|
|
178
204
|
}
|
|
179
205
|
if (cause instanceof routes.RouteError) {
|
|
180
|
-
return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Native route request is invalid.' }, 400,
|
|
181
|
-
}
|
|
182
|
-
if (operation.timedOut) {
|
|
183
|
-
return nativeError({
|
|
184
|
-
code: errors.CODES.DEADLINE_EXCEEDED,
|
|
185
|
-
message: 'Invocation deadline exceeded.',
|
|
186
|
-
}, 504, responseCors);
|
|
206
|
+
return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Native route request is invalid.' }, 400, corsFor);
|
|
187
207
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
const payload = classifyError(cause);
|
|
192
|
-
return nativeError(payload, errors.httpStatusOf(payload), responseCors);
|
|
208
|
+
const payload = classifyRequestError(cause, operation);
|
|
209
|
+
return nativeError(payload, errors.httpStatusOf(payload), corsFor);
|
|
193
210
|
}
|
|
194
211
|
finally {
|
|
195
212
|
if (!handedOff)
|
|
@@ -197,11 +214,79 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
|
|
|
197
214
|
}
|
|
198
215
|
});
|
|
199
216
|
}
|
|
217
|
+
function streamingBinaryResponse(binary, operation, cors, request) {
|
|
218
|
+
const iterator = binary.body[Symbol.asyncIterator]();
|
|
219
|
+
const applicationHeaders = applicationBinaryHeaders(binary.headers);
|
|
220
|
+
let terminal = false;
|
|
221
|
+
let controller;
|
|
222
|
+
let stopping;
|
|
223
|
+
const stop = (reason = 'Binary response was interrupted.') => {
|
|
224
|
+
if (terminal)
|
|
225
|
+
return stopping ?? Promise.resolve();
|
|
226
|
+
terminal = true;
|
|
227
|
+
controller?.error(new Error(reason));
|
|
228
|
+
stopping = Promise.resolve().then(async () => {
|
|
229
|
+
try {
|
|
230
|
+
await iterator.return?.();
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
operation.settle();
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
return stopping;
|
|
237
|
+
};
|
|
238
|
+
const body = new ReadableStream({
|
|
239
|
+
start(output) {
|
|
240
|
+
controller = output;
|
|
241
|
+
},
|
|
242
|
+
async pull(controller) {
|
|
243
|
+
if (terminal)
|
|
244
|
+
return;
|
|
245
|
+
try {
|
|
246
|
+
const next = await iterator.next();
|
|
247
|
+
if (terminal)
|
|
248
|
+
return;
|
|
249
|
+
if (next.done) {
|
|
250
|
+
terminal = true;
|
|
251
|
+
controller.close();
|
|
252
|
+
operation.settle();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
controller.enqueue(next.value);
|
|
256
|
+
}
|
|
257
|
+
catch (cause) {
|
|
258
|
+
if (terminal)
|
|
259
|
+
return;
|
|
260
|
+
terminal = true;
|
|
261
|
+
controller.error(cause);
|
|
262
|
+
operation.settle();
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
async cancel(reason) {
|
|
266
|
+
const message = typeof reason === 'string' ? reason : 'native binary response cancelled';
|
|
267
|
+
operation.cancel(message);
|
|
268
|
+
await stop();
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
operation.onCancel(stop);
|
|
272
|
+
return new Response(body, {
|
|
273
|
+
status: binary.status ?? 200,
|
|
274
|
+
headers: {
|
|
275
|
+
...cors.response(request, Object.keys(applicationHeaders)),
|
|
276
|
+
...applicationHeaders,
|
|
277
|
+
'content-type': binary.mediaType,
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
function isStreamingBinary(input) {
|
|
282
|
+
return !(input.body instanceof Uint8Array);
|
|
283
|
+
}
|
|
200
284
|
function publicationPreflight(cors, request, snapshot, externalOrigin) {
|
|
201
|
-
|
|
285
|
+
const document = servedDocument(snapshot);
|
|
286
|
+
if (sameRoute(request.url, document.schema.bundle.href, externalOrigin)) {
|
|
202
287
|
return cors.preflight(request, { methods: ['GET'], headers: ['if-none-match'] });
|
|
203
288
|
}
|
|
204
|
-
const deliveryUrl =
|
|
289
|
+
const deliveryUrl = document.delivery?.href;
|
|
205
290
|
if (deliveryUrl !== undefined && sameRoute(request.url, deliveryUrl, externalOrigin)) {
|
|
206
291
|
return cors.preflight(request, {
|
|
207
292
|
methods: ['POST'],
|
|
@@ -217,7 +302,7 @@ function publicationPreflight(cors, request, snapshot, externalOrigin) {
|
|
|
217
302
|
function nativeCorsRoute(entry, requestedMethod) {
|
|
218
303
|
const headers = new Set([
|
|
219
304
|
'accept',
|
|
220
|
-
routes.
|
|
305
|
+
routes.HTTP_ROUTE_DIGEST,
|
|
221
306
|
routes.HTTP_ROUTE_ISSUER,
|
|
222
307
|
protocol.HTTP_IDEMPOTENCY_KEY,
|
|
223
308
|
]);
|
|
@@ -246,23 +331,28 @@ function nativeCorsRoute(entry, requestedMethod) {
|
|
|
246
331
|
...(allowAnyHeaders ? { allowAnyHeaders: true } : {}),
|
|
247
332
|
});
|
|
248
333
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
334
|
+
/**
|
|
335
|
+
* Read the one snapshot a request pins, withheld while it exceeds a serving limit, names another
|
|
336
|
+
* serving issuer, or claims a route this Server reserves.
|
|
337
|
+
*/
|
|
338
|
+
export function currentServed(source, limits, serving) {
|
|
339
|
+
const prepared = readServed(source);
|
|
254
340
|
if (prepared === undefined ||
|
|
341
|
+
(prepared.kind === 'release' &&
|
|
342
|
+
prepared.releaseBytes.byteLength > limits.maximumPublicationBytes) ||
|
|
255
343
|
prepared.publicationBytes.byteLength > limits.maximumPublicationBytes ||
|
|
256
344
|
prepared.bundle.byteLength > limits.maximumBundleBytes ||
|
|
257
|
-
(serving?.issuer !== undefined &&
|
|
345
|
+
(serving?.issuer !== undefined &&
|
|
346
|
+
servedDocument(prepared).identity.issuer !== serving.issuer) ||
|
|
258
347
|
(serving !== undefined && conflictsWithServingRoutes(prepared, serving))) {
|
|
259
348
|
return undefined;
|
|
260
349
|
}
|
|
261
350
|
return prepared;
|
|
262
351
|
}
|
|
263
|
-
function servingSnapshot(source, limits, request, reservations, externalIssuer) {
|
|
264
|
-
return
|
|
265
|
-
origin:
|
|
352
|
+
function servingSnapshot(source, limits, request, reservations, externalIssuer, publicOrigin) {
|
|
353
|
+
return currentServed(source, limits, {
|
|
354
|
+
origin: publicOrigin ??
|
|
355
|
+
(externalIssuer === undefined ? new URL(request.url).origin : new URL(externalIssuer).origin),
|
|
266
356
|
...(externalIssuer === undefined ? {} : { issuer: externalIssuer }),
|
|
267
357
|
reservations,
|
|
268
358
|
});
|
|
@@ -279,6 +369,58 @@ function conflictsWithServingRoutes(snapshot, serving) {
|
|
|
279
369
|
}
|
|
280
370
|
return false;
|
|
281
371
|
}
|
|
372
|
+
function documentPath(path, url, externalIssuer) {
|
|
373
|
+
return externalIssuer === undefined ? path : new URL(url(externalIssuer)).pathname;
|
|
374
|
+
}
|
|
375
|
+
function mountDocument(app, path, name, serving) {
|
|
376
|
+
const { cors, active } = serving;
|
|
377
|
+
if (cors.enabled) {
|
|
378
|
+
app.options(path, (context) => cors.preflight(context.req.raw, {
|
|
379
|
+
methods: ['GET'],
|
|
380
|
+
headers: ['if-none-match'],
|
|
381
|
+
}));
|
|
382
|
+
}
|
|
383
|
+
app.get(path, (context) => {
|
|
384
|
+
const responseCors = cors.response(context.req.raw, ['etag']);
|
|
385
|
+
if (!active.isAccepting) {
|
|
386
|
+
return drained(`${name} is unavailable while draining.`, (exposed) => cors.response(context.req.raw, exposed));
|
|
387
|
+
}
|
|
388
|
+
const snapshot = serving.served(context.req.raw);
|
|
389
|
+
const document = snapshot === undefined ? undefined : serving.read(snapshot);
|
|
390
|
+
if (document === undefined)
|
|
391
|
+
return response({ error: `${name} is not ready.` }, 503, responseCors);
|
|
392
|
+
const etag = quoteEtag(document.etag);
|
|
393
|
+
if (context.req.raw.headers.get('if-none-match') === etag) {
|
|
394
|
+
return new Response(null, {
|
|
395
|
+
status: 304,
|
|
396
|
+
headers: {
|
|
397
|
+
...responseCors,
|
|
398
|
+
etag,
|
|
399
|
+
'cache-control': 'public, max-age=0, must-revalidate, no-transform',
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
return new Response(toBody(document.bytes), {
|
|
404
|
+
status: 200,
|
|
405
|
+
headers: {
|
|
406
|
+
...responseCors,
|
|
407
|
+
'content-type': document.mediaType,
|
|
408
|
+
etag,
|
|
409
|
+
'cache-control': 'public, max-age=0, must-revalidate, no-transform',
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
/** Reserved native stale control naming the digest the destination serves (`StaleControl.current`). */
|
|
415
|
+
function stale(request, current, cors) {
|
|
416
|
+
return new Response(null, {
|
|
417
|
+
status: routes.HTTP_ROUTE_STALE_STATUS,
|
|
418
|
+
headers: {
|
|
419
|
+
...cors.response(request, [routes.HTTP_ROUTE_STALE]),
|
|
420
|
+
...routes.staleHeaders(current),
|
|
421
|
+
},
|
|
422
|
+
});
|
|
423
|
+
}
|
|
282
424
|
async function streamResponse(stream, operation, cors) {
|
|
283
425
|
const iterator = stream[Symbol.asyncIterator]();
|
|
284
426
|
let rejectSetup;
|
|
@@ -304,14 +446,14 @@ async function streamResponse(stream, operation, cors) {
|
|
|
304
446
|
const cleanup = cancelSetup('native stream setup failed');
|
|
305
447
|
if (!operation.signal.aborted)
|
|
306
448
|
await cleanup;
|
|
307
|
-
const payload =
|
|
449
|
+
const payload = classifyRequestError(cause, operation);
|
|
308
450
|
return nativeError(payload, errors.httpStatusOf(payload), cors);
|
|
309
451
|
}
|
|
310
452
|
unregisterSetupCancellation();
|
|
311
453
|
if (operation.signal.aborted) {
|
|
312
454
|
void cancelSetup('native stream setup cancelled');
|
|
313
|
-
const payload =
|
|
314
|
-
return response({ error: payload }, errors.httpStatusOf(payload), cors);
|
|
455
|
+
const payload = classifyRequestError(operation.signal.reason, operation);
|
|
456
|
+
return response({ error: payload }, errors.httpStatusOf(payload), cors());
|
|
315
457
|
}
|
|
316
458
|
const encoder = new TextEncoder();
|
|
317
459
|
let pending = first;
|
|
@@ -389,27 +531,12 @@ async function streamResponse(stream, operation, cors) {
|
|
|
389
531
|
operation.onCancel((reason) => stop(reason, interruptedStreamError(operation.timedOut)));
|
|
390
532
|
return new Response(body, {
|
|
391
533
|
headers: {
|
|
392
|
-
...cors,
|
|
534
|
+
...cors(),
|
|
393
535
|
'content-type': routes.STREAM_MEDIA_TYPE,
|
|
394
536
|
'cache-control': 'no-cache',
|
|
395
537
|
},
|
|
396
538
|
});
|
|
397
539
|
}
|
|
398
|
-
function preStreamError(cause, operation) {
|
|
399
|
-
if (operation.timedOut) {
|
|
400
|
-
return {
|
|
401
|
-
code: errors.CODES.DEADLINE_EXCEEDED,
|
|
402
|
-
message: 'Invocation deadline exceeded before the native stream began.',
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
if (operation.signal.aborted) {
|
|
406
|
-
return {
|
|
407
|
-
code: errors.CODES.CANCELLED,
|
|
408
|
-
message: 'Invocation was cancelled before the native stream began.',
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
return classifyError(cause);
|
|
412
|
-
}
|
|
413
540
|
function sameRoute(left, right, externalOrigin) {
|
|
414
541
|
const a = new URL(left);
|
|
415
542
|
const b = new URL(right);
|
|
@@ -436,9 +563,16 @@ function response(body, status, cors) {
|
|
|
436
563
|
headers: { ...cors, 'content-type': 'application/json' },
|
|
437
564
|
});
|
|
438
565
|
}
|
|
566
|
+
/** A served document or artifact refused while draining presents the lifecycle retry delay. */
|
|
567
|
+
function drained(message, cors) {
|
|
568
|
+
const retry = retryAfterHeaders(admissionRefusal(false));
|
|
569
|
+
return response({ error: message }, 503, { ...cors(Object.keys(retry)), ...retry });
|
|
570
|
+
}
|
|
571
|
+
/** Every native error, including a capacity refusal raised by dispatch, presents its retry delay. */
|
|
439
572
|
function nativeError(error, status, cors) {
|
|
440
573
|
const admitted = routes.decodeErrorResponse({ error });
|
|
441
|
-
|
|
574
|
+
const retry = retryAfterHeaders(error);
|
|
575
|
+
return response(admitted, status, { ...cors(Object.keys(retry)), ...retry });
|
|
442
576
|
}
|
|
443
577
|
function encodedBody(input) {
|
|
444
578
|
return typeof input === 'string' ? input : toBody(input);
|
|
@@ -446,4 +580,3 @@ function encodedBody(input) {
|
|
|
446
580
|
function toBody(input) {
|
|
447
581
|
return input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength);
|
|
448
582
|
}
|
|
449
|
-
//# sourceMappingURL=routes.js.map
|
package/dist/server.d.ts
CHANGED
|
@@ -2,4 +2,3 @@ import type { Server, ServerConfig, ServerOptions, PreparedServer } from './type
|
|
|
2
2
|
export declare function createServer(config: ServerConfig): Server;
|
|
3
3
|
/** Validate all static transport configuration before a Host starts Runtime recovery/bootstrap. */
|
|
4
4
|
export declare function prepareServer(options: ServerOptions): PreparedServer;
|
|
5
|
-
//# sourceMappingURL=server.d.ts.map
|