@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,724 @@
1
+ import "node:module";
2
+ import { untilAborted } from "../abort.mjs";
3
+ import { identityCacheKey } from "../identity.mjs";
4
+ import { parseCacheControl, responseHeaders } from "../session/cache.mjs";
5
+ import { readBoundedDataText } from "./body.mjs";
6
+ import { DataProtocolError, assertPublicData, serializePublicData } from "./codec.mjs";
7
+ import { createRequestDataPolicy } from "./private.mjs";
8
+ import { DATA_CONTENT_TYPE, DATA_PROTOCOL_VERSION, DATA_STREAM_CONTENT_TYPE, LOADER_ID_PARAM } from "./types.mjs";
9
+ const DEFERRED = Symbol('ultramodern.data.deferred');
10
+ const REDIRECT_STATUSES = new Set([
11
+ 301,
12
+ 302,
13
+ 303,
14
+ 307,
15
+ 308
16
+ ]);
17
+ const BODYLESS_STATUSES = new Set([
18
+ 204,
19
+ 205,
20
+ 304
21
+ ]);
22
+ const REPRESENTATION_HEADERS = new Set([
23
+ 'content-type',
24
+ 'content-length',
25
+ 'content-encoding',
26
+ 'content-digest',
27
+ 'repr-digest',
28
+ 'digest',
29
+ 'transfer-encoding',
30
+ 'content-range',
31
+ 'accept-ranges',
32
+ 'content-disposition',
33
+ 'content-language',
34
+ 'content-location',
35
+ 'etag',
36
+ 'last-modified',
37
+ 'location'
38
+ ]);
39
+ function mergeHeaderFields(primary, secondary) {
40
+ const primaryNames = new Set(primary.map(([name])=>name.toLowerCase()));
41
+ return [
42
+ ...primary.map(([name, value])=>[
43
+ name,
44
+ value
45
+ ]),
46
+ ...secondary.filter(([name])=>ACCUMULATED_HEADERS.has(name.toLowerCase()) || !primaryNames.has(name.toLowerCase())).map(([name, value])=>[
47
+ name,
48
+ value
49
+ ])
50
+ ];
51
+ }
52
+ const HOP_BY_HOP_HEADERS = new Set([
53
+ 'connection',
54
+ 'keep-alive',
55
+ 'proxy-connection',
56
+ 'te',
57
+ 'trailer',
58
+ 'upgrade'
59
+ ]);
60
+ function projectedHeaderFilter(headers) {
61
+ const stripped = new Set([
62
+ ...REPRESENTATION_HEADERS,
63
+ ...HOP_BY_HOP_HEADERS
64
+ ]);
65
+ for (const [name, value] of headers)if ('connection' === name.toLowerCase()) {
66
+ for (const field of value.split(','))if (field.trim()) stripped.add(field.trim().toLowerCase());
67
+ }
68
+ return (name)=>!stripped.has(name.toLowerCase());
69
+ }
70
+ const outcomePolicies = new WeakMap();
71
+ function deferData(critical, deferred, init) {
72
+ for (const record of [
73
+ critical,
74
+ deferred
75
+ ]){
76
+ if (null === record || 'object' != typeof record || Object.getPrototypeOf(record) !== Object.prototype && null !== Object.getPrototypeOf(record)) throw new DataProtocolError('Deferred data requires plain critical and deferred records');
77
+ for (const key of Reflect.ownKeys(record)){
78
+ const descriptor = Object.getOwnPropertyDescriptor(record, key);
79
+ if (descriptor.get || descriptor.set) throw new DataProtocolError('Deferred data must not contain accessors');
80
+ if ('symbol' == typeof key || !descriptor.enumerable) throw new DataProtocolError('Deferred data keys must be enumerable strings');
81
+ }
82
+ }
83
+ assertPublicData(critical);
84
+ for (const [key, promise] of Object.entries(deferred)){
85
+ if (Object.hasOwn(critical, key) || !(promise instanceof Promise)) throw new DataProtocolError('Deferred data needs unique keys and Promise values');
86
+ promise.catch(()=>void 0);
87
+ }
88
+ return {
89
+ [DEFERRED]: true,
90
+ critical,
91
+ deferred: Object.fromEntries(Object.entries(deferred)),
92
+ ...void 0 === init ? {} : {
93
+ init
94
+ }
95
+ };
96
+ }
97
+ function cacheLifetime(cacheControl) {
98
+ const directives = parseCacheControl(cacheControl);
99
+ if (!directives) return;
100
+ const names = new Set();
101
+ const ages = [];
102
+ for (const { name, value: raw } of directives){
103
+ if ('max-age' !== name && 's-maxage' !== name) continue;
104
+ if (void 0 === raw) return;
105
+ if (names.has(name) || !/^\d+$/.test(raw)) return;
106
+ const age = Number(raw);
107
+ if (!Number.isSafeInteger(age)) return;
108
+ names.add(name);
109
+ ages.push(age);
110
+ }
111
+ if (!names.has('max-age')) return;
112
+ return ages.length > 0 ? Math.min(...ages) : void 0;
113
+ }
114
+ const ACCUMULATED_HEADERS = new Set([
115
+ 'set-cookie',
116
+ 'vary',
117
+ 'link',
118
+ 'server-timing',
119
+ 'content-security-policy',
120
+ 'content-security-policy-report-only'
121
+ ]);
122
+ function mergeDataResponseMetadata(outcomes, nativeResponse) {
123
+ const headers = new Headers();
124
+ let cachePolicy = 'public';
125
+ for (const outcome of outcomes){
126
+ for (const [name, value] of outcome.response.headers)if (ACCUMULATED_HEADERS.has(name.toLowerCase())) headers.append(name, value);
127
+ else headers.set(name, value);
128
+ if ('success' !== outcome.kind || 'no-store' === outcome.response.cachePolicy) cachePolicy = 'no-store';
129
+ else if ('private' === outcome.response.cachePolicy && 'public' === cachePolicy) cachePolicy = 'private';
130
+ }
131
+ const vary = headers.get('vary');
132
+ if (null !== vary) {
133
+ const fields = new Map();
134
+ for (const field of vary.split(',')){
135
+ const name = field.trim();
136
+ const key = name.toLowerCase();
137
+ if (name && !fields.has(key)) fields.set(key, name);
138
+ }
139
+ headers.set('vary', [
140
+ ...fields.values()
141
+ ].join(', '));
142
+ }
143
+ if (0 === outcomes.length || headers.has('set-cookie') || nativeResponse.status >= 400) cachePolicy = 'no-store';
144
+ if ('no-store' !== cachePolicy) {
145
+ const ages = outcomes.map((outcome)=>cacheLifetime(new Headers(outcome.response.headers).get('cache-control') ?? ''));
146
+ if ('public' === cachePolicy && ages.some((age)=>void 0 === age)) cachePolicy = 'no-store';
147
+ else {
148
+ const maxAge = Math.min(...ages.map((age)=>age ?? 0));
149
+ headers.set('cache-control', 'private' === cachePolicy ? `private, max-age=${maxAge}, must-revalidate` : `public, max-age=${maxAge}`);
150
+ }
151
+ }
152
+ if ('no-store' === cachePolicy) headers.set('cache-control', 'no-store');
153
+ return {
154
+ status: nativeResponse.status,
155
+ statusText: nativeResponse.statusText ?? '',
156
+ headers: responseHeaders(headers),
157
+ cachePolicy
158
+ };
159
+ }
160
+ function dataMetadataToDocumentPolicy(metadata) {
161
+ const headers = new Headers();
162
+ const fields = [
163
+ ...metadata.headers
164
+ ];
165
+ const keep = projectedHeaderFilter(fields);
166
+ for (const [name, value] of fields)if (keep(name)) headers.append(name, value);
167
+ headers.set('content-type', 'text/html; charset=utf-8');
168
+ let cache = {
169
+ mode: 'no-store'
170
+ };
171
+ const cacheControl = headers.get('cache-control') ?? '';
172
+ const directives = cacheDirectiveNames(cacheControl);
173
+ if ('private' === metadata.cachePolicy) cache = {
174
+ mode: 'private'
175
+ };
176
+ if ('public' === metadata.cachePolicy && 200 === metadata.status && !headers.has('set-cookie') && !directives.has('private') && !directives.has('no-store') && !directives.has('no-cache') && !headers.get('vary')?.split(',').some((value)=>'*' === value.trim())) {
177
+ const maxAgeSeconds = cacheLifetime(cacheControl);
178
+ if (void 0 !== maxAgeSeconds) cache = {
179
+ mode: 'public',
180
+ maxAgeSeconds
181
+ };
182
+ }
183
+ if (headers.has('set-cookie') || metadata.status >= 400) cache = {
184
+ mode: 'no-store'
185
+ };
186
+ if ('no-store' === cache.mode) headers.set('cache-control', 'no-store');
187
+ else if ('private' === cache.mode && !directives.has('private')) headers.set('cache-control', 'private');
188
+ return {
189
+ kind: 'document',
190
+ status: metadata.status,
191
+ headers: responseHeaders(headers),
192
+ cache
193
+ };
194
+ }
195
+ function listFieldElements(value) {
196
+ const elements = [];
197
+ let current = '';
198
+ let quoted = false;
199
+ let bracketed = false;
200
+ for(let index = 0; index < value.length; index++){
201
+ const char = value[index];
202
+ if (quoted) {
203
+ current += char;
204
+ if ('\\' === char && index + 1 < value.length) current += value[++index];
205
+ else if ('"' === char) quoted = false;
206
+ } else if ('"' === char) {
207
+ quoted = true;
208
+ current += char;
209
+ } else if ('<' === char) {
210
+ bracketed = true;
211
+ current += char;
212
+ } else if ('>' === char) {
213
+ bracketed = false;
214
+ current += char;
215
+ } else if (',' !== char || bracketed) current += char;
216
+ else {
217
+ if (current.trim()) elements.push(current.trim());
218
+ current = '';
219
+ }
220
+ }
221
+ if (current.trim()) elements.push(current.trim());
222
+ return elements;
223
+ }
224
+ function mergeDataResponseIntoResponse(response, metadata) {
225
+ const headers = new Headers(response.headers);
226
+ const nativeCookies = headers.getSetCookie();
227
+ const metadataHeaders = dataMetadataToDocumentPolicy(metadata).headers;
228
+ const metadataCookies = [];
229
+ for (const [name, value] of metadataHeaders){
230
+ const headerName = name.toLowerCase();
231
+ if ('set-cookie' === headerName) metadataCookies.push(value);
232
+ else if (ACCUMULATED_HEADERS.has(headerName) && 'vary' !== headerName) {
233
+ const present = new Set(listFieldElements(headers.get(name) ?? ''));
234
+ const missing = listFieldElements(value).filter((item)=>!present.has(item));
235
+ if (missing.length) headers.append(name, missing.join(', '));
236
+ } else if ('vary' === headerName) {
237
+ const fields = new Map();
238
+ for (const field of `${headers.get('vary') ?? ''},${value}`.split(',')){
239
+ const item = field.trim();
240
+ if (item && !fields.has(item.toLowerCase())) fields.set(item.toLowerCase(), item);
241
+ }
242
+ headers.set('vary', [
243
+ ...fields.values()
244
+ ].join(', '));
245
+ } else if (!headers.has(name)) headers.set(name, value);
246
+ }
247
+ if (metadataCookies.length > 0) {
248
+ headers.delete('set-cookie');
249
+ const counts = new Map();
250
+ for (const cookie of metadataCookies){
251
+ headers.append('set-cookie', cookie);
252
+ counts.set(cookie, (counts.get(cookie) ?? 0) + 1);
253
+ }
254
+ for (const cookie of nativeCookies){
255
+ const remaining = counts.get(cookie) ?? 0;
256
+ if (remaining > 0) counts.set(cookie, remaining - 1);
257
+ else headers.append('set-cookie', cookie);
258
+ }
259
+ }
260
+ headers.set('cache-control', 'no-store');
261
+ return new Response(response.body, {
262
+ status: response.status,
263
+ statusText: response.statusText,
264
+ headers
265
+ });
266
+ }
267
+ function cacheDirectiveNames(cacheControl) {
268
+ const directives = parseCacheControl(cacheControl);
269
+ return directives ? new Set(directives.map(({ name })=>name)) : new Set([
270
+ 'no-store'
271
+ ]);
272
+ }
273
+ function responseMetadata(init = {}) {
274
+ const status = init.status ?? 200;
275
+ if (!Number.isInteger(status) || status < 200 || status > 599) throw new DataProtocolError('A data response requires a valid HTTP response status');
276
+ const headers = new Headers(init.headers);
277
+ const directives = cacheDirectiveNames(headers.get('cache-control') ?? '');
278
+ return {
279
+ status,
280
+ statusText: init.statusText ?? '',
281
+ headers: responseHeaders(headers),
282
+ cachePolicy: directives.has('no-store') || directives.has('no-cache') ? 'no-store' : directives.has('private') ? 'private' : directives.has('public') ? 'public' : 'no-store'
283
+ };
284
+ }
285
+ const DOM_EXCEPTION_FIELDS = {
286
+ name: Object.getOwnPropertyDescriptor(DOMException.prototype, 'name')?.get,
287
+ message: Object.getOwnPropertyDescriptor(DOMException.prototype, 'message')?.get
288
+ };
289
+ function publicDataError(error, production = true) {
290
+ const fallback = {
291
+ name: 'Error',
292
+ message: 'Unexpected Server Error'
293
+ };
294
+ if (production) return fallback;
295
+ if (error instanceof Error || error instanceof DOMException) {
296
+ const field = (key)=>{
297
+ let object = error;
298
+ for(let depth = 0; null !== object && depth < 100; depth++){
299
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
300
+ if (descriptor) {
301
+ if ('string' == typeof descriptor.value) return descriptor.value;
302
+ const intrinsic = object === DOMException.prototype && 'stack' !== key ? DOM_EXCEPTION_FIELDS[key] : void 0;
303
+ if (intrinsic && descriptor.get === intrinsic) {
304
+ const value = intrinsic.call(error);
305
+ return 'string' == typeof value ? value : void 0;
306
+ }
307
+ break;
308
+ }
309
+ object = Object.getPrototypeOf(object);
310
+ }
311
+ };
312
+ try {
313
+ const name = field('name') ?? fallback.name;
314
+ const message = field('message') ?? fallback.message;
315
+ const stack = field('stack');
316
+ return {
317
+ name,
318
+ message,
319
+ ...stack ? {
320
+ stack
321
+ } : {}
322
+ };
323
+ } catch {}
324
+ }
325
+ return fallback;
326
+ }
327
+ async function responseValue(response, signal) {
328
+ if (BODYLESS_STATUSES.has(response.status) || null === response.body) return;
329
+ const text = await readBoundedDataText(response, signal);
330
+ if (/\bapplication\/(?:[^;]+\+)?json\b/i.test(response.headers.get('content-type') ?? '')) try {
331
+ return JSON.parse(text);
332
+ } catch {
333
+ throw new DataProtocolError('A data Response contains malformed JSON');
334
+ }
335
+ return text;
336
+ }
337
+ async function normalizeDataResult(value, options = {}) {
338
+ const thrown = options.thrown ?? false;
339
+ const production = options.production ?? true;
340
+ if (value instanceof Response) {
341
+ const response = responseMetadata(value);
342
+ if (REDIRECT_STATUSES.has(value.status)) {
343
+ const location = value.headers.get('location');
344
+ if (!location) throw new DataProtocolError('A redirect data Response requires Location');
345
+ value.body?.cancel().catch(()=>void 0);
346
+ return {
347
+ kind: 'redirect',
348
+ location,
349
+ response
350
+ };
351
+ }
352
+ const data = await responseValue(value, options.signal);
353
+ assertPublicData(data);
354
+ if (404 === value.status) return {
355
+ kind: 'not-found',
356
+ value: data,
357
+ thrown,
358
+ response
359
+ };
360
+ if (thrown || value.status >= 400) return {
361
+ kind: 'error',
362
+ error: {
363
+ name: 'DataResponseError',
364
+ message: value.status >= 500 && production ? 'Unexpected Server Error' : value.statusText || `Data request failed (${value.status})`
365
+ },
366
+ ...value.status >= 500 && production ? {} : {
367
+ data
368
+ },
369
+ thrown,
370
+ response
371
+ };
372
+ return {
373
+ kind: 'success',
374
+ value: data,
375
+ response
376
+ };
377
+ }
378
+ if (thrown) return {
379
+ kind: 'error',
380
+ error: publicDataError(value, production),
381
+ thrown: true,
382
+ response: responseMetadata({
383
+ status: 500,
384
+ headers: {
385
+ 'cache-control': 'no-store'
386
+ }
387
+ })
388
+ };
389
+ if (value && 'object' == typeof value && DEFERRED in value) {
390
+ const data = value;
391
+ const response = responseMetadata(data.init);
392
+ if (REDIRECT_STATUSES.has(response.status) || response.status >= 400 || BODYLESS_STATUSES.has(response.status)) throw new DataProtocolError('Deferred data must have a successful body-bearing response status');
393
+ return {
394
+ kind: 'deferred',
395
+ critical: data.critical,
396
+ deferred: data.deferred,
397
+ response
398
+ };
399
+ }
400
+ assertPublicData(value);
401
+ return {
402
+ kind: 'success',
403
+ value,
404
+ response: responseMetadata()
405
+ };
406
+ }
407
+ async function invokeRouteData(handler, input, options = {}) {
408
+ input.request.signal.throwIfAborted();
409
+ const policy = createRequestDataPolicy([
410
+ input.context,
411
+ input.request,
412
+ ...options.privateValues ?? []
413
+ ]);
414
+ return invokeOwnedRouteData(handler, input, options, policy);
415
+ }
416
+ async function invokeOwnedRouteData(handler, input, options, policy) {
417
+ input.request.signal.throwIfAborted();
418
+ let value;
419
+ let thrown = false;
420
+ try {
421
+ value = await untilAborted(handler(input), input.request.signal);
422
+ } catch (error) {
423
+ input.request.signal.throwIfAborted();
424
+ value = error;
425
+ thrown = true;
426
+ }
427
+ if (input.request.signal.aborted) {
428
+ if (value instanceof Response) value.body?.cancel(input.request.signal.reason).catch(()=>void 0);
429
+ input.request.signal.throwIfAborted();
430
+ }
431
+ policy.assertRoot(value);
432
+ let outcome = await normalizeDataResult(value, {
433
+ ...options,
434
+ thrown,
435
+ signal: input.request.signal
436
+ });
437
+ if ('deferred' === outcome.kind) {
438
+ policy.assertValue(outcome.critical);
439
+ outcome = {
440
+ ...outcome,
441
+ deferred: Object.fromEntries(Object.entries(outcome.deferred).map(([key, promise])=>{
442
+ const checked = Promise.prototype.then.call(promise, (value)=>{
443
+ policy.assertValue(value);
444
+ return value;
445
+ }, (error)=>{
446
+ policy.assertRoot(error);
447
+ throw error;
448
+ });
449
+ checked.catch(()=>void 0);
450
+ return [
451
+ key,
452
+ checked
453
+ ];
454
+ }))
455
+ };
456
+ } else policy.assertValue(publicOutcome(outcome));
457
+ outcomePolicies.set(outcome, policy);
458
+ return outcome;
459
+ }
460
+ function publicOutcome(outcome) {
461
+ const status = outcome.response.status;
462
+ switch(outcome.kind){
463
+ case 'success':
464
+ return {
465
+ kind: 'success',
466
+ value: outcome.value,
467
+ status
468
+ };
469
+ case 'deferred':
470
+ return {
471
+ kind: 'success',
472
+ value: outcome.critical,
473
+ status
474
+ };
475
+ case 'redirect':
476
+ return {
477
+ kind: 'redirect',
478
+ location: outcome.location,
479
+ status
480
+ };
481
+ case 'not-found':
482
+ return {
483
+ kind: 'not-found',
484
+ value: outcome.value,
485
+ thrown: outcome.thrown,
486
+ status
487
+ };
488
+ case 'error':
489
+ return {
490
+ kind: 'error',
491
+ error: outcome.error,
492
+ ...Object.hasOwn(outcome, 'data') ? {
493
+ data: outcome.data
494
+ } : {},
495
+ thrown: outcome.thrown,
496
+ status
497
+ };
498
+ }
499
+ }
500
+ function wireEnvelope(outcome, identity, routeId, operation) {
501
+ identityCacheKey(identity);
502
+ return {
503
+ version: DATA_PROTOCOL_VERSION,
504
+ identity: {
505
+ renderer: identity.renderer,
506
+ appId: identity.appId,
507
+ entryName: identity.entryName,
508
+ protocolVersion: identity.protocolVersion,
509
+ buildId: identity.buildId
510
+ },
511
+ routeId,
512
+ operation,
513
+ outcome: publicOutcome(outcome),
514
+ ...'deferred' === outcome.kind ? {
515
+ deferredKeys: Object.keys(outcome.deferred)
516
+ } : {}
517
+ };
518
+ }
519
+ function createDeferredStream(outcome, envelope, signal, production, onCancel, policy) {
520
+ const encoder = new TextEncoder();
521
+ const projectError = (error)=>{
522
+ try {
523
+ policy?.assertRoot(error);
524
+ } catch (violation) {
525
+ error = violation;
526
+ }
527
+ return publicDataError(error, production);
528
+ };
529
+ policy?.assertValue({
530
+ type: 'initial',
531
+ envelope
532
+ });
533
+ const initialBytes = encoder.encode(`${serializePublicData({
534
+ type: 'initial',
535
+ envelope
536
+ })}\n`);
537
+ const transport = new TransformStream();
538
+ const writer = transport.writable.getWriter();
539
+ let active = true;
540
+ let finished = false;
541
+ let cancelled = false;
542
+ const stopped = new AbortController();
543
+ const cancelResources = (reason)=>{
544
+ if (finished || cancelled) return;
545
+ cancelled = true;
546
+ stopped.abort(reason);
547
+ try {
548
+ Promise.resolve(onCancel?.(reason)).catch(()=>void 0);
549
+ } catch {}
550
+ };
551
+ const cleanup = ()=>{
552
+ if (!active) return;
553
+ active = false;
554
+ signal?.removeEventListener('abort', abort);
555
+ };
556
+ const abort = ()=>{
557
+ if (!active) return;
558
+ const reason = signal?.reason ?? new DOMException('The data request was aborted', 'AbortError');
559
+ cancelResources(reason);
560
+ cleanup();
561
+ writer.abort(reason).catch(()=>void 0);
562
+ };
563
+ let writes = Promise.resolve();
564
+ const write = (frame, encoded)=>{
565
+ const next = writes.then(async ()=>{
566
+ if (!active) return;
567
+ if (encoded) return void await writer.write(encoded);
568
+ let text;
569
+ try {
570
+ policy?.assertValue(frame);
571
+ text = serializePublicData(frame);
572
+ } catch (error) {
573
+ if ('resolve' !== frame.type) throw error;
574
+ text = serializePublicData({
575
+ type: 'reject',
576
+ key: frame.key,
577
+ error: projectError(error)
578
+ });
579
+ }
580
+ await writer.write(encoder.encode(`${text}\n`));
581
+ });
582
+ writes = next;
583
+ return next;
584
+ };
585
+ writer.closed.catch((reason)=>{
586
+ cancelResources(reason);
587
+ cleanup();
588
+ });
589
+ if (signal?.aborted) abort();
590
+ else signal?.addEventListener('abort', abort, {
591
+ once: true
592
+ });
593
+ (async ()=>{
594
+ await write({
595
+ type: 'initial',
596
+ envelope
597
+ }, initialBytes);
598
+ await Promise.all(Object.entries(outcome.deferred).map(async ([key, promise])=>{
599
+ let frame;
600
+ try {
601
+ const value = await untilAborted(promise, stopped.signal);
602
+ assertPublicData(value);
603
+ frame = {
604
+ type: 'resolve',
605
+ key,
606
+ value
607
+ };
608
+ } catch (error) {
609
+ frame = {
610
+ type: 'reject',
611
+ key,
612
+ error: projectError(error)
613
+ };
614
+ }
615
+ await write(frame);
616
+ }));
617
+ if (!active) return;
618
+ await write({
619
+ type: 'complete'
620
+ });
621
+ await writer.close();
622
+ finished = true;
623
+ cleanup();
624
+ })().catch((error)=>{
625
+ cancelResources(error);
626
+ cleanup();
627
+ writer.abort(error).catch(()=>void 0);
628
+ });
629
+ return transport.readable;
630
+ }
631
+ function createDataResponse(outcome, identity, options) {
632
+ const policy = outcomePolicies.get(outcome);
633
+ policy?.assertValue(publicOutcome(outcome));
634
+ const envelope = wireEnvelope(outcome, identity, options.routeId, options.operation);
635
+ const headers = new Headers();
636
+ const fields = [
637
+ ...outcome.response.headers
638
+ ];
639
+ const keep = projectedHeaderFilter(fields);
640
+ for (const [name, value] of fields)if (keep(name)) headers.append(name, value);
641
+ headers.set('x-modernjs-response', 'yes');
642
+ if (!headers.has('cache-control') || headers.has('set-cookie') || 'success' !== outcome.kind || 'no-store' === outcome.response.cachePolicy) headers.set('cache-control', 'no-store');
643
+ if ('redirect' === outcome.kind) headers.set('x-modernjs-redirect', outcome.location);
644
+ headers.set('content-type', `${'deferred' === outcome.kind ? DATA_STREAM_CONTENT_TYPE : DATA_CONTENT_TYPE}; charset=utf-8`);
645
+ const status = 'redirect' === outcome.kind || 206 === outcome.response.status || BODYLESS_STATUSES.has(outcome.response.status) ? 200 : outcome.response.status;
646
+ let body;
647
+ if ('deferred' === outcome.kind) body = createDeferredStream(outcome, envelope, options.signal, options.production ?? true, options.onCancel, policy);
648
+ else {
649
+ policy?.assertValue(envelope);
650
+ body = serializePublicData(envelope);
651
+ }
652
+ return new Response(body, {
653
+ status,
654
+ headers
655
+ });
656
+ }
657
+ async function handleDataRequest(options) {
658
+ const url = new URL(options.request.url);
659
+ const routeId = url.searchParams.get(LOADER_ID_PARAM);
660
+ if (!routeId) return;
661
+ identityCacheKey(options.identity);
662
+ const context = options.context;
663
+ const cancellation = new AbortController();
664
+ const request = new Request(options.request, {
665
+ signal: AbortSignal.any([
666
+ options.request.signal,
667
+ cancellation.signal
668
+ ])
669
+ });
670
+ request.signal.throwIfAborted();
671
+ const policy = createRequestDataPolicy([
672
+ context,
673
+ options.request,
674
+ request,
675
+ ...options.privateValues ?? []
676
+ ]);
677
+ if (![
678
+ 'GET',
679
+ 'HEAD',
680
+ 'POST',
681
+ 'PUT',
682
+ 'PATCH',
683
+ 'DELETE'
684
+ ].includes(request.method)) return new Response('Unsupported data method', {
685
+ status: 405,
686
+ headers: {
687
+ allow: 'GET, HEAD, POST, PUT, PATCH, DELETE',
688
+ 'cache-control': 'no-store'
689
+ }
690
+ });
691
+ const operation = 'GET' === request.method || 'HEAD' === request.method ? 'loader' : 'action';
692
+ const selected = await untilAborted(options.selectRoute(request, routeId, operation), request.signal);
693
+ if (!selected || selected.routeId !== routeId) return new Response('Data route is not authorized for this URL', {
694
+ status: 403,
695
+ headers: {
696
+ 'cache-control': 'no-store'
697
+ }
698
+ });
699
+ const productionOptions = void 0 === options.production ? {} : {
700
+ production: options.production
701
+ };
702
+ const outcome = await invokeOwnedRouteData(selected.handler, {
703
+ request,
704
+ routeId,
705
+ params: selected.params,
706
+ context
707
+ }, productionOptions, policy);
708
+ const response = createDataResponse(outcome, options.identity, {
709
+ routeId,
710
+ operation,
711
+ signal: request.signal,
712
+ ...productionOptions,
713
+ onCancel: (reason)=>cancellation.abort(reason)
714
+ });
715
+ if ('HEAD' === request.method) {
716
+ response.body?.cancel().catch(()=>void 0);
717
+ return new Response(null, {
718
+ status: response.status,
719
+ headers: response.headers
720
+ });
721
+ }
722
+ return response;
723
+ }
724
+ export { createDataResponse, dataMetadataToDocumentPolicy, deferData, handleDataRequest, invokeRouteData, mergeDataResponseIntoResponse, mergeDataResponseMetadata, mergeHeaderFields, normalizeDataResult, publicDataError, responseHeaders as collectDataHeaders };