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