@bleedingdev/modern-js-renderer-core 0.0.0-bootstrap → 3.9.3-ultramodern.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.
Files changed (147) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/abort.js +65 -0
  3. package/dist/cjs/adapter.js +62 -0
  4. package/dist/cjs/data/body.js +78 -0
  5. package/dist/cjs/data/client.js +409 -0
  6. package/dist/cjs/data/codec.js +168 -0
  7. package/dist/cjs/data/index.js +125 -0
  8. package/dist/cjs/data/private.js +77 -0
  9. package/dist/cjs/data/routes.js +149 -0
  10. package/dist/cjs/data/server.js +791 -0
  11. package/dist/cjs/data/static.js +110 -0
  12. package/dist/cjs/data/tree.js +231 -0
  13. package/dist/cjs/data/types.js +56 -0
  14. package/dist/cjs/document.js +184 -0
  15. package/dist/cjs/entry-client.js +142 -0
  16. package/dist/cjs/entry-server.js +253 -0
  17. package/dist/cjs/federation.js +80 -0
  18. package/dist/cjs/identity.js +120 -0
  19. package/dist/cjs/index.js +46 -0
  20. package/dist/cjs/localization.js +48 -0
  21. package/dist/cjs/router.js +293 -0
  22. package/dist/cjs/server/assets.js +56 -0
  23. package/dist/cjs/server/dispatch.js +351 -0
  24. package/dist/cjs/server/index.js +57 -0
  25. package/dist/cjs/server/types.js +18 -0
  26. package/dist/cjs/server/worker.js +73 -0
  27. package/dist/cjs/session/cache.js +172 -0
  28. package/dist/cjs/session/index.js +63 -0
  29. package/dist/cjs/session/request.js +269 -0
  30. package/dist/cjs/session/types.js +18 -0
  31. package/dist/esm/abort.mjs +27 -0
  32. package/dist/esm/adapter.mjs +21 -0
  33. package/dist/esm/data/body.mjs +40 -0
  34. package/dist/esm/data/client.mjs +368 -0
  35. package/dist/esm/data/codec.mjs +112 -0
  36. package/dist/esm/data/index.mjs +7 -0
  37. package/dist/esm/data/private.mjs +39 -0
  38. package/dist/esm/data/routes.mjs +108 -0
  39. package/dist/esm/data/server.mjs +723 -0
  40. package/dist/esm/data/static.mjs +51 -0
  41. package/dist/esm/data/tree.mjs +193 -0
  42. package/dist/esm/data/types.mjs +6 -0
  43. package/dist/esm/document.mjs +131 -0
  44. package/dist/esm/entry-client.mjs +104 -0
  45. package/dist/esm/entry-server.mjs +215 -0
  46. package/dist/esm/federation.mjs +36 -0
  47. package/dist/esm/identity.mjs +63 -0
  48. package/dist/esm/index.mjs +1 -0
  49. package/dist/esm/localization.mjs +10 -0
  50. package/dist/esm/router.mjs +225 -0
  51. package/dist/esm/server/assets.mjs +15 -0
  52. package/dist/esm/server/dispatch.mjs +307 -0
  53. package/dist/esm/server/index.mjs +3 -0
  54. package/dist/esm/server/types.mjs +0 -0
  55. package/dist/esm/server/worker.mjs +35 -0
  56. package/dist/esm/session/cache.mjs +116 -0
  57. package/dist/esm/session/index.mjs +3 -0
  58. package/dist/esm/session/request.mjs +231 -0
  59. package/dist/esm/session/types.mjs +0 -0
  60. package/dist/esm-node/abort.mjs +28 -0
  61. package/dist/esm-node/adapter.mjs +22 -0
  62. package/dist/esm-node/data/body.mjs +41 -0
  63. package/dist/esm-node/data/client.mjs +369 -0
  64. package/dist/esm-node/data/codec.mjs +113 -0
  65. package/dist/esm-node/data/index.mjs +8 -0
  66. package/dist/esm-node/data/private.mjs +40 -0
  67. package/dist/esm-node/data/routes.mjs +109 -0
  68. package/dist/esm-node/data/server.mjs +724 -0
  69. package/dist/esm-node/data/static.mjs +52 -0
  70. package/dist/esm-node/data/tree.mjs +194 -0
  71. package/dist/esm-node/data/types.mjs +7 -0
  72. package/dist/esm-node/document.mjs +132 -0
  73. package/dist/esm-node/entry-client.mjs +105 -0
  74. package/dist/esm-node/entry-server.mjs +216 -0
  75. package/dist/esm-node/federation.mjs +37 -0
  76. package/dist/esm-node/identity.mjs +64 -0
  77. package/dist/esm-node/index.mjs +2 -0
  78. package/dist/esm-node/localization.mjs +11 -0
  79. package/dist/esm-node/router.mjs +226 -0
  80. package/dist/esm-node/server/assets.mjs +16 -0
  81. package/dist/esm-node/server/dispatch.mjs +308 -0
  82. package/dist/esm-node/server/index.mjs +4 -0
  83. package/dist/esm-node/server/types.mjs +1 -0
  84. package/dist/esm-node/server/worker.mjs +36 -0
  85. package/dist/esm-node/session/cache.mjs +117 -0
  86. package/dist/esm-node/session/index.mjs +4 -0
  87. package/dist/esm-node/session/request.mjs +232 -0
  88. package/dist/esm-node/session/types.mjs +1 -0
  89. package/dist/types/abort.d.ts +6 -0
  90. package/dist/types/adapter.d.ts +195 -0
  91. package/dist/types/data/body.d.ts +2 -0
  92. package/dist/types/data/client.d.ts +17 -0
  93. package/dist/types/data/codec.d.ts +14 -0
  94. package/dist/types/data/index.d.ts +8 -0
  95. package/dist/types/data/private.d.ts +6 -0
  96. package/dist/types/data/routes.d.ts +28 -0
  97. package/dist/types/data/server.d.ts +59 -0
  98. package/dist/types/data/static.d.ts +31 -0
  99. package/dist/types/data/tree.d.ts +3 -0
  100. package/dist/types/data/types.d.ts +104 -0
  101. package/dist/types/document.d.ts +76 -0
  102. package/dist/types/entry-client.d.ts +53 -0
  103. package/dist/types/entry-server.d.ts +63 -0
  104. package/dist/types/federation.d.ts +56 -0
  105. package/dist/types/identity.d.ts +27 -0
  106. package/dist/types/index.d.ts +1 -0
  107. package/dist/types/localization.d.ts +58 -0
  108. package/dist/types/router.d.ts +201 -0
  109. package/dist/types/server/assets.d.ts +14 -0
  110. package/dist/types/server/dispatch.d.ts +11 -0
  111. package/dist/types/server/index.d.ts +4 -0
  112. package/dist/types/server/types.d.ts +64 -0
  113. package/dist/types/server/worker.d.ts +25 -0
  114. package/dist/types/session/cache.d.ts +25 -0
  115. package/dist/types/session/index.d.ts +4 -0
  116. package/dist/types/session/request.d.ts +8 -0
  117. package/dist/types/session/types.d.ts +48 -0
  118. package/package.json +159 -4
  119. package/src/abort.ts +40 -0
  120. package/src/adapter.ts +255 -0
  121. package/src/data/body.ts +43 -0
  122. package/src/data/client.ts +501 -0
  123. package/src/data/codec.ts +247 -0
  124. package/src/data/index.ts +8 -0
  125. package/src/data/private.ts +68 -0
  126. package/src/data/routes.ts +201 -0
  127. package/src/data/server.ts +977 -0
  128. package/src/data/static.ts +105 -0
  129. package/src/data/tree.ts +244 -0
  130. package/src/data/types.ts +98 -0
  131. package/src/document.ts +338 -0
  132. package/src/entry-client.ts +236 -0
  133. package/src/entry-server.ts +393 -0
  134. package/src/federation.ts +111 -0
  135. package/src/identity.ts +128 -0
  136. package/src/index.ts +7 -0
  137. package/src/localization.ts +77 -0
  138. package/src/router.ts +475 -0
  139. package/src/server/assets.ts +60 -0
  140. package/src/server/dispatch.ts +523 -0
  141. package/src/server/index.ts +16 -0
  142. package/src/server/types.ts +94 -0
  143. package/src/server/worker.ts +94 -0
  144. package/src/session/cache.ts +177 -0
  145. package/src/session/index.ts +11 -0
  146. package/src/session/request.ts +324 -0
  147. package/src/session/types.ts +60 -0
@@ -0,0 +1,308 @@
1
+ import "node:module";
2
+ import { untilAborted } from "../abort.mjs";
3
+ import { assertRendererIdentity, identityCacheKey } from "../identity.mjs";
4
+ import { createRequestSession, documentCacheKey, headerMaxAgeSeconds, permitsDocumentCache, policyHeaders, responseHeaders } from "../session/index.mjs";
5
+ function rejectNativeRscRequest(request) {
6
+ if (request.headers.has('x-rsc-tree') || request.headers.has('x-rsc-action')) return Response.json({
7
+ code: 'unsupported-renderer-capability',
8
+ capability: 'rsc'
9
+ }, {
10
+ status: 400,
11
+ headers: {
12
+ 'cache-control': 'no-store'
13
+ }
14
+ });
15
+ }
16
+ function permitsCacheLookup(request) {
17
+ const url = new URL(request.url);
18
+ const cacheControl = request.headers.get('cache-control') ?? '';
19
+ return 'GET' === request.method && !request.headers.has('authorization') && !request.headers.has('cookie') && !request.headers.has('range') && !request.headers.has('if-none-match') && !request.headers.has('if-modified-since') && !request.headers.has('if-match') && !request.headers.has('if-unmodified-since') && !/(?:^|,)\s*(?:private|no-store|no-cache)(?:\s|,|=|$)/i.test(cacheControl) && !/(?:^|,)\s*max-age\s*=\s*(?:0+|"0+")\s*(?:,|$)/i.test(cacheControl) && !/(?:^|,)\s*min-fresh(?:\s|,|=|$)/i.test(cacheControl) && (cacheControl.match(/(?:^|,)\s*max-age\s*=/gi)?.length ?? 0) <= 1 && !request.headers.get('pragma')?.split(',').some((directive)=>'no-cache' === directive.trim().toLowerCase()) && !url.searchParams.has('__loader') && !url.searchParams.has('__ssrDirect');
20
+ }
21
+ function requestMaxAgeSeconds(request) {
22
+ const match = /(?:^|,)\s*max-age\s*=\s*(?:(\d+)|"(\d+)")\s*(?:,|$)/i.exec(request.headers.get('cache-control') ?? '');
23
+ return match ? Number(match[1] ?? match[2]) : void 0;
24
+ }
25
+ function documentAgeSeconds(document) {
26
+ return Math.floor((Date.now() - document.storedAt) / 1000);
27
+ }
28
+ function isReusableDocument(document, key, maxAgeSeconds) {
29
+ return document.identityKey === key && Number.isFinite(document.storedAt) && document.storedAt <= Date.now() && (void 0 === maxAgeSeconds || Date.now() - document.storedAt < 1000 * maxAgeSeconds) && document.expiresAt > Date.now() && 200 === document.status && document.bytes instanceof Uint8Array && permitsDocumentCache({
30
+ kind: 'document',
31
+ status: document.status,
32
+ headers: document.headers,
33
+ cache: {
34
+ mode: 'public',
35
+ maxAgeSeconds: 1
36
+ }
37
+ }) && !policyHeaders(document.headers).has('vary');
38
+ }
39
+ function defaultStatusText(status) {
40
+ return new Response(null, {
41
+ status
42
+ }).statusText;
43
+ }
44
+ function normalizeTerminalResponse(response, session) {
45
+ if (session.committedPolicy) {
46
+ if (response.status !== session.committedPolicy.status) throw new Error('Native handler changed HTTP status after committing its response policy.');
47
+ if (response.statusText !== (session.committedPolicy.statusText ?? defaultStatusText(session.committedPolicy.status))) throw new Error('Native handler changed HTTP status text after committing its response policy.');
48
+ if (!session.ownsResponseBody(response)) throw new Error('Native handler returned a body outside its committed request session.');
49
+ return response;
50
+ }
51
+ session.resolveResponse({
52
+ kind: 'terminal',
53
+ status: response.status,
54
+ statusText: response.statusText,
55
+ headers: responseHeaders(response.headers),
56
+ cache: {
57
+ mode: 'no-store'
58
+ }
59
+ });
60
+ return session.respond(response.body);
61
+ }
62
+ async function chooseCSR(request, manifest, context) {
63
+ const url = new URL(request.url);
64
+ if ('GET' !== request.method && 'HEAD' !== request.method || url.searchParams.has('__loader') || url.searchParams.has('__ssrDirect')) return false;
65
+ const config = context.serverConfig;
66
+ if (config?.ssr === false || config?.forceCSR) return true;
67
+ const selectedIds = config?.ssrByRouteIds;
68
+ if (!selectedIds?.length) return false;
69
+ if (!manifest.nativeMatchRouteIds) throw new Error('unsupported-renderer-capability: ssrByRouteIds requires native route matching.');
70
+ const matchedIds = await untilAborted(manifest.nativeMatchRouteIds(request, context), context.session.signal);
71
+ const leafId = matchedIds.at(-1);
72
+ return !leafId || !selectedIds.includes(leafId);
73
+ }
74
+ function captureDocument(response, session, options, key, identityKey) {
75
+ if (!response.body || !options.cache) return response;
76
+ const reader = response.body.getReader();
77
+ const chunks = [];
78
+ const maximum = options.maxCacheBytes ?? 2097152;
79
+ let total = 0;
80
+ let canCapture = Number.isSafeInteger(maximum) && maximum > 0;
81
+ let delivered;
82
+ const nativeHeaders = responseHeaders(response.headers);
83
+ const body = new ReadableStream({
84
+ async pull (controller) {
85
+ try {
86
+ const part = await reader.read();
87
+ if (!part.done) {
88
+ if (canCapture) {
89
+ total += part.value.byteLength;
90
+ if (total <= maximum) chunks.push(part.value.slice());
91
+ else {
92
+ canCapture = false;
93
+ chunks.length = 0;
94
+ }
95
+ }
96
+ controller.enqueue(part.value);
97
+ return;
98
+ }
99
+ reader.releaseLock();
100
+ const completion = await session.completion;
101
+ const policy = session.committedPolicy;
102
+ if (canCapture && completion.cacheEligible && policy?.cache.mode === 'public' && policy.cache.maxAgeSeconds > 0 && 200 === delivered.status && !delivered.headers.has('vary') && permitsDocumentCache({
103
+ ...policy,
104
+ status: delivered.status,
105
+ headers: responseHeaders(delivered.headers)
106
+ })) {
107
+ const maxAgeSeconds = policy.cache.maxAgeSeconds;
108
+ const bytes = new Uint8Array(total);
109
+ let offset = 0;
110
+ for (const chunk of chunks){
111
+ bytes.set(chunk, offset);
112
+ offset += chunk.byteLength;
113
+ }
114
+ const commit = async (headers)=>{
115
+ if (!headers || headers.has('vary') || !permitsDocumentCache({
116
+ ...policy,
117
+ status: delivered.status,
118
+ headers: responseHeaders(headers)
119
+ })) return;
120
+ const lifetimeSeconds = Math.min(maxAgeSeconds, ...headerMaxAgeSeconds(headers));
121
+ const age = headers.get('age')?.trim() ?? '0';
122
+ const date = Date.parse(headers.get('date') ?? '');
123
+ const ageSeconds = Math.max(/^\d+$/u.test(age) ? Number(age) : 0, Number.isFinite(date) ? Math.floor((Date.now() - date) / 1000) : 0);
124
+ if (lifetimeSeconds - ageSeconds <= 0) return;
125
+ try {
126
+ const storedAt = Date.now() - 1000 * ageSeconds;
127
+ await options.cache?.set(key, {
128
+ identityKey,
129
+ storedAt,
130
+ expiresAt: storedAt + 1000 * lifetimeSeconds,
131
+ status: 200,
132
+ statusText: delivered.statusText,
133
+ headers: nativeHeaders,
134
+ bytes
135
+ });
136
+ } catch (error) {
137
+ options.onCacheError?.(error);
138
+ }
139
+ };
140
+ if (options.confirmDelivery) {
141
+ chunks.length = 0;
142
+ controller.close();
143
+ options.confirmDelivery(delivered).then(commit).catch((error)=>{
144
+ options.onCacheError?.(error);
145
+ });
146
+ return;
147
+ }
148
+ commit(delivered.headers).catch((error)=>{
149
+ options.onCacheError?.(error);
150
+ });
151
+ }
152
+ chunks.length = 0;
153
+ controller.close();
154
+ } catch (error) {
155
+ chunks.length = 0;
156
+ session.fail(error);
157
+ await session.completion;
158
+ controller.error(error);
159
+ }
160
+ },
161
+ async cancel (reason) {
162
+ chunks.length = 0;
163
+ reader.cancel(reason).catch(()=>void 0);
164
+ session.abort(reason);
165
+ await session.completion;
166
+ reader.releaseLock();
167
+ }
168
+ }, {
169
+ highWaterMark: 0
170
+ });
171
+ delivered = new Response(body, {
172
+ status: response.status,
173
+ statusText: response.statusText,
174
+ headers: response.headers
175
+ });
176
+ return delivered;
177
+ }
178
+ function openSession(request, options) {
179
+ const session = createRequestSession({
180
+ request,
181
+ identity: options.identity,
182
+ platform: {
183
+ kind: options.platform ?? 'node',
184
+ bindings: options.context.bindings
185
+ }
186
+ });
187
+ options.executionContext?.waitUntil(session.completion);
188
+ return session;
189
+ }
190
+ async function dispatchNativeRequest(request, options) {
191
+ const rscRejection = rejectNativeRscRequest(request);
192
+ if (rscRejection) return rscRejection;
193
+ const identityKey = identityCacheKey(options.identity);
194
+ if ('react' === options.identity.renderer) throw new Error('Native dispatch requires Solid or Octane.');
195
+ let session = openSession(request, options);
196
+ const { bindings: _bindings, ...renderContext } = options.context;
197
+ let context = {
198
+ ...renderContext,
199
+ session,
200
+ entry: session.identity
201
+ };
202
+ let handlerStarted = false;
203
+ try {
204
+ session.signal.throwIfAborted();
205
+ const manifest = await untilAborted(options.loadManifest(), session.signal);
206
+ assertRendererIdentity(manifest.rendererIdentity, options.identity);
207
+ if ('function' != typeof manifest.nativeRequestHandler) throw new Error('Native renderer manifest has no request handler.');
208
+ const csr = await chooseCSR(request, manifest, context);
209
+ session.signal.throwIfAborted();
210
+ if (csr && !manifest.nativeCSRRequestHandler) throw new Error('unsupported-renderer-capability: selected CSR requires a native document handler.');
211
+ if (csr) session.markFallback();
212
+ if (void 0 !== options.hydrationBuildId && ('string' != typeof options.hydrationBuildId || !options.hydrationBuildId.trim())) throw new Error('Native hydration cache identity requires a nonempty build ID.');
213
+ const cacheKey = documentCacheKey(options.identity, JSON.stringify([
214
+ request.url,
215
+ options.hydrationBuildId ?? options.identity.buildId,
216
+ options.context.assets ?? [],
217
+ options.context.nonce ?? null
218
+ ]));
219
+ const cacheAllowed = !csr && permitsCacheLookup(request);
220
+ if (cacheAllowed && options.cache) try {
221
+ const cached = await untilAborted(options.cache.get(cacheKey), session.signal);
222
+ if (cached && isReusableDocument(cached, identityKey, requestMaxAgeSeconds(request))) {
223
+ const age = documentAgeSeconds(cached);
224
+ session.resolveResponse({
225
+ kind: 'terminal',
226
+ status: cached.status,
227
+ statusText: cached.statusText,
228
+ headers: [
229
+ ...cached.headers.filter(([name])=>'age' !== name.toLowerCase()),
230
+ [
231
+ 'age',
232
+ String(age)
233
+ ]
234
+ ],
235
+ cache: {
236
+ mode: 'no-store'
237
+ }
238
+ });
239
+ return session.respond(new ReadableStream({
240
+ start (controller) {
241
+ controller.enqueue(cached.bytes.slice());
242
+ controller.close();
243
+ }
244
+ }));
245
+ }
246
+ } catch (error) {
247
+ if (session.signal.aborted) throw error;
248
+ options.onCacheError?.(error);
249
+ }
250
+ session.signal.throwIfAborted();
251
+ handlerStarted = true;
252
+ let response = await untilAborted((csr ? manifest.nativeCSRRequestHandler : manifest.nativeRequestHandler)(request, context), session.signal);
253
+ if (!(response instanceof Response)) throw new TypeError('Native renderer handler must return a Fetch Response.');
254
+ response = normalizeTerminalResponse(response, session);
255
+ if ('HEAD' === request.method && response.body) {
256
+ response.body.cancel().catch(()=>{});
257
+ return new Response(null, {
258
+ status: response.status,
259
+ statusText: response.statusText,
260
+ headers: response.headers
261
+ });
262
+ }
263
+ return cacheAllowed ? captureDocument(response, session, options, cacheKey, identityKey) : response;
264
+ } catch (error) {
265
+ const committed = void 0 !== session.committedPolicy;
266
+ session.fail(error);
267
+ await session.completion;
268
+ if (!handlerStarted || committed || request.signal.aborted || !options.onError) throw error;
269
+ session = openSession(request, options);
270
+ session.markFallback();
271
+ context = {
272
+ ...context,
273
+ session
274
+ };
275
+ try {
276
+ const response = await untilAborted(options.onError(error, request, context), session.signal);
277
+ if (!(response instanceof Response)) throw new TypeError('Native fallback must return a Fetch Response.');
278
+ const owned = normalizeTerminalResponse(response, session);
279
+ if ('HEAD' === request.method && owned.body) {
280
+ owned.body.cancel().catch(()=>{});
281
+ return new Response(null, {
282
+ status: owned.status,
283
+ statusText: owned.statusText,
284
+ headers: owned.headers
285
+ });
286
+ }
287
+ return owned;
288
+ } catch (fallbackError) {
289
+ session.fail(fallbackError);
290
+ await session.completion;
291
+ throw fallbackError;
292
+ }
293
+ }
294
+ }
295
+ function dispatchNativeNodeRequest(request, options) {
296
+ const fetchRequest = null === request.body ? request : new Request(request.url, {
297
+ method: request.method,
298
+ headers: request.headers,
299
+ body: request.body,
300
+ signal: request.signal,
301
+ duplex: 'half'
302
+ });
303
+ return dispatchNativeRequest(fetchRequest, {
304
+ ...options,
305
+ platform: 'node'
306
+ });
307
+ }
308
+ export { dispatchNativeNodeRequest, dispatchNativeRequest, rejectNativeRscRequest };
@@ -0,0 +1,4 @@
1
+ import "node:module";
2
+ export { RENDERER_ASSET_MANIFEST_FILE, validateNativeClientAssetManifest } from "./assets.mjs";
3
+ export { dispatchNativeNodeRequest, dispatchNativeRequest, rejectNativeRscRequest } from "./dispatch.mjs";
4
+ export { dispatchNativeWorkerRequest } from "./worker.mjs";
@@ -0,0 +1 @@
1
+ import "node:module";
@@ -0,0 +1,36 @@
1
+ import "node:module";
2
+ import { collectDocumentAssets } from "../document.mjs";
3
+ import { assertRendererIdentity } from "../identity.mjs";
4
+ import { dispatchNativeRequest, rejectNativeRscRequest } from "./dispatch.mjs";
5
+ function workerManifest(bundle, identity) {
6
+ const candidate = bundle && 'object' == typeof bundle && !('rendererIdentity' in bundle) && 'default' in bundle ? bundle.default : bundle;
7
+ if (!candidate || 'object' != typeof candidate && 'function' != typeof candidate || !('rendererIdentity' in candidate) || !('nativeRequestHandler' in candidate) || 'function' != typeof candidate.nativeRequestHandler) throw new Error('Native worker bundle requires its renderer identity and request handler.');
8
+ assertRendererIdentity(candidate.rendererIdentity, identity);
9
+ return candidate;
10
+ }
11
+ function dispatchNativeWorkerRequest(request, options) {
12
+ const rejection = rejectNativeRscRequest(request);
13
+ if (rejection) return Promise.resolve(rejection);
14
+ const { resources } = options;
15
+ const query = new URL(request.url).searchParams;
16
+ const forceCSR = Boolean(resources.csrFallbackHeader && (query.get('csr') || request.headers.get(resources.csrFallbackHeader)));
17
+ const bindings = null !== options.bindings && 'object' == typeof options.bindings ? options.bindings : {};
18
+ return dispatchNativeRequest(request, {
19
+ identity: options.identity,
20
+ platform: 'worker',
21
+ executionContext: options.executionContext,
22
+ hydrationBuildId: resources.hydrationBuildId,
23
+ loadManifest: ()=>workerManifest(options.bundle, options.identity),
24
+ context: {
25
+ assets: collectDocumentAssets(resources.assets),
26
+ nativeManifest: resources.nativeManifest,
27
+ bindings,
28
+ serverConfig: {
29
+ ...resources.serverConfig,
30
+ forceCSR
31
+ },
32
+ nonce: resources.nonce
33
+ }
34
+ });
35
+ }
36
+ export { dispatchNativeWorkerRequest };
@@ -0,0 +1,117 @@
1
+ import "node:module";
2
+ import { identityCacheKey } from "../identity.mjs";
3
+ function documentCacheKey(identity, requestKey) {
4
+ if ('string' != typeof requestKey || 0 === requestKey.length) throw new TypeError('A document cache request key must be nonempty.');
5
+ return JSON.stringify([
6
+ identityCacheKey(identity),
7
+ requestKey
8
+ ]);
9
+ }
10
+ function responseHeaders(headers) {
11
+ const fields = [];
12
+ headers.forEach((value, name)=>{
13
+ if ('set-cookie' !== name) fields.push([
14
+ name,
15
+ value
16
+ ]);
17
+ });
18
+ for (const cookie of headers.getSetCookie())fields.push([
19
+ 'set-cookie',
20
+ cookie
21
+ ]);
22
+ return fields;
23
+ }
24
+ function policyHeaders(headers) {
25
+ const result = new Headers();
26
+ for (const [name, value] of headers)result.append(name, value);
27
+ return result;
28
+ }
29
+ const NO_STORE = {
30
+ mode: 'no-store',
31
+ ages: []
32
+ };
33
+ const CACHE_DIRECTIVE = /\s*([!#$%&'*+.^_`|~\w-]+)\s*(?:=\s*([!#$%&'*+.^_`|~\w-]+|"(?:[^"\\]|\\.)*"))?\s*(?:,|$)/uy;
34
+ function parseCacheControl(value) {
35
+ const directives = [];
36
+ let index = 0;
37
+ while(index < value.length){
38
+ CACHE_DIRECTIVE.lastIndex = index;
39
+ const match = CACHE_DIRECTIVE.exec(value);
40
+ if (!match || CACHE_DIRECTIVE.lastIndex === index) return;
41
+ directives.push({
42
+ name: match[1].toLowerCase(),
43
+ value: match[2]
44
+ });
45
+ index = CACHE_DIRECTIVE.lastIndex;
46
+ }
47
+ return directives;
48
+ }
49
+ function cacheDirectives(value) {
50
+ let mode = 'public';
51
+ const ages = [];
52
+ const ageNames = new Set();
53
+ const parsed = parseCacheControl(value ?? '');
54
+ if (!parsed) return NO_STORE;
55
+ for (const { name, value: raw } of parsed){
56
+ if ('no-store' === name || 'no-cache' === name) mode = 'no-store';
57
+ else if ('private' === name && 'no-store' !== mode) mode = 'private';
58
+ if ('max-age' === name || 's-maxage' === name) {
59
+ const age = Number(raw);
60
+ if (ageNames.has(name) || !/^\d+$/u.test(raw ?? '') || !Number.isSafeInteger(age)) return NO_STORE;
61
+ ageNames.add(name);
62
+ ages.push(age);
63
+ }
64
+ }
65
+ return {
66
+ mode,
67
+ ages
68
+ };
69
+ }
70
+ function headerMaxAgeSeconds(headers) {
71
+ return cacheDirectives(headers.get('cache-control')).ages;
72
+ }
73
+ function headerCacheMode(headers, ages) {
74
+ const control = cacheDirectives(headers.get('cache-control'));
75
+ ages.push(...control.ages);
76
+ if (headers.has('set-cookie') || headers.get('vary')?.split(',').some((value)=>'*' === value.trim())) return 'no-store';
77
+ return control.mode;
78
+ }
79
+ function isSingleHtmlContentType(headers) {
80
+ const type = headers.get('content-type');
81
+ return !type?.includes(',') && type?.split(';')[0].trim().toLowerCase() === 'text/html';
82
+ }
83
+ function permitsDocumentCache(policy) {
84
+ if ('document' !== policy.kind || 200 !== policy.status || 'public' !== policy.cache.mode) return false;
85
+ const headers = policyHeaders(policy.headers);
86
+ return 'public' === headerCacheMode(headers, []) && isSingleHtmlContentType(headers);
87
+ }
88
+ function restrictDocumentCache(policy, sources, outgoing) {
89
+ const original = policyHeaders(policy.headers);
90
+ let mode = policy.cache.mode;
91
+ const ages = 'public' === policy.cache.mode ? [
92
+ policy.cache.maxAgeSeconds
93
+ ] : [];
94
+ for (const headers of [
95
+ original,
96
+ ...sources,
97
+ outgoing
98
+ ]){
99
+ const restricted = headerCacheMode(headers, ages);
100
+ if ('no-store' === restricted) mode = 'no-store';
101
+ else if ('private' === restricted && 'public' === mode) mode = 'private';
102
+ }
103
+ if ('document' !== policy.kind || 200 !== policy.status || !isSingleHtmlContentType(original) || !isSingleHtmlContentType(outgoing)) mode = 'no-store';
104
+ if ('public' === mode) {
105
+ const maxAgeSeconds = Math.min(...ages);
106
+ outgoing.set('cache-control', `public, max-age=${maxAgeSeconds}, s-maxage=${maxAgeSeconds}, must-revalidate`);
107
+ return {
108
+ mode,
109
+ maxAgeSeconds
110
+ };
111
+ }
112
+ outgoing.set('cache-control', 'private' === mode ? 'private, max-age=0, must-revalidate' : 'no-store');
113
+ return {
114
+ mode
115
+ };
116
+ }
117
+ export { documentCacheKey, headerMaxAgeSeconds, parseCacheControl, permitsDocumentCache, policyHeaders, responseHeaders, restrictDocumentCache };
@@ -0,0 +1,4 @@
1
+ import "node:module";
2
+ export { untilAborted } from "../abort.mjs";
3
+ export { documentCacheKey, headerMaxAgeSeconds, permitsDocumentCache, policyHeaders, responseHeaders, restrictDocumentCache } from "./cache.mjs";
4
+ export { createRequestSession } from "./request.mjs";