@absolutejs/absolute 0.19.0-beta.367 → 0.19.0-beta.369

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.
@@ -78,49 +78,6 @@ var __legacyMetadataTS = (k, v) => {
78
78
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
79
79
  var __require = import.meta.require;
80
80
 
81
- // src/client/streamSwap.ts
82
- var streamSwapRuntime = () => {
83
- if (window.__ABS_SLOT_RUNTIME__ === true)
84
- return;
85
- window.__ABS_SLOT_RUNTIME__ = true;
86
- window.__ABS_SLOT_PENDING__ = window.__ABS_SLOT_PENDING__ ?? {};
87
- const pending = window.__ABS_SLOT_PENDING__;
88
- const apply = (id, html) => {
89
- const node = document.getElementById(`slot-${id}`);
90
- if (!node) {
91
- pending[id] = html;
92
- return;
93
- }
94
- node.innerHTML = html;
95
- delete pending[id];
96
- };
97
- const flush = () => {
98
- for (const id in pending) {
99
- if (!Object.prototype.hasOwnProperty.call(pending, id))
100
- continue;
101
- apply(id, pending[id] ?? "");
102
- }
103
- };
104
- window.__ABS_SLOT_ENQUEUE__ = (id, html) => {
105
- apply(id, html);
106
- };
107
- if (typeof MutationObserver === "function") {
108
- const observer = new MutationObserver(flush);
109
- const root = document.documentElement ?? document.body ?? document;
110
- observer.observe(root, { childList: true, subtree: true });
111
- }
112
- if (document.readyState === "loading") {
113
- document.addEventListener("DOMContentLoaded", flush, { once: true });
114
- }
115
- flush();
116
- }, stripFunctionWrapper = (value) => {
117
- const start = value.indexOf("{");
118
- const end = value.lastIndexOf("}");
119
- if (start < 0 || end <= start)
120
- return "";
121
- return value.slice(start + 1, end);
122
- }, getStreamSwapRuntimeScript = () => `(function(){${stripFunctionWrapper(streamSwapRuntime.toString())}})();`;
123
-
124
81
  // src/utils/escapeScriptContent.ts
125
82
  var ESCAPE_LOOKUP, ESCAPE_REGEX, escapeScriptContent = (content) => content.replace(ESCAPE_REGEX, (char) => {
126
83
  const escaped = ESCAPE_LOOKUP[char];
@@ -137,258 +94,43 @@ var init_escapeScriptContent = __esm(() => {
137
94
  ESCAPE_REGEX = /[&><\u2028\u2029]/g;
138
95
  });
139
96
 
140
- // src/utils/streamingSlots.ts
141
- var SLOT_ID_PREFIX = "abs-slot-", SLOT_PLACEHOLDER_PREFIX = "slot-", CLOSING_HEAD_TAG = "</head>", CLOSING_HEAD_TAG_LENGTH, CLOSING_PAGE_TAG_REGEX, STREAMING_RUNTIME_GLOBAL = "__ABS_SLOT_ENQUEUE__", STREAMING_PENDING_GLOBAL = "__ABS_SLOT_PENDING__", STREAM_TAIL_LOOKBEHIND = 128, STREAMING_SLOT_TIMEOUT_MS = 5000, STREAMING_SLOT_MAX_PER_RESPONSE = 128, STREAMING_SLOT_MAX_HTML_BYTES = 64000, createSlotPlaceholderId = (id) => `${SLOT_PLACEHOLDER_PREFIX}${id}`, createSlotPatchStatement = (id, html) => `(window.${STREAMING_RUNTIME_GLOBAL}||function(i,h){window.${STREAMING_PENDING_GLOBAL}=window.${STREAMING_PENDING_GLOBAL}||{};window.${STREAMING_PENDING_GLOBAL}[i]=h;})(${JSON.stringify(id)},${JSON.stringify(html)});`, createNonceAttr = (nonce) => nonce ? ` nonce="${nonce}"` : "", createStreamingSlotId = () => `${SLOT_ID_PREFIX}${Math.random().toString(36).slice(2, 10)}`, getStreamingSlotsRuntimeScript = () => getStreamSwapRuntimeScript(), renderStreamingSlotsRuntimeTag = (nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(getStreamingSlotsRuntimeScript())}</script>`, renderStreamingSlotPlaceholder = (id, fallbackHtml = "") => `<div id="${createSlotPlaceholderId(id)}" data-absolute-slot="true">${fallbackHtml}</div>`, renderStreamingSlotPatchTag = (id, html, nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(createSlotPatchStatement(id, html))}</script>`, injectHtmlIntoHead = (html, injection) => {
142
- const closingHeadIndex = html.indexOf(CLOSING_HEAD_TAG);
97
+ // src/constants.ts
98
+ var ANGULAR_INIT_TIMEOUT_MS = 500, ANSI_ESCAPE_LENGTH = 3, ASCII_SPACE = 32, BASE_36_RADIX = 36, BUN_BUILD_WARNING_SUPPRESSION = "wildcard sideEffects are not supported yet", BODY_SLICE_LENGTH = 2000, BYTES_PER_KILOBYTE = 1024, CLI_ARGS_OFFSET = 3, CSS_ERROR_RESOLVE_DELAY_MS = 50, CSS_MAX_CHECK_ATTEMPTS = 10, CSS_MAX_PARSE_TIMEOUT_MS = 500, CSS_SHEET_READY_TIMEOUT_MS = 100, DEFAULT_CHUNK_SIZE = 16384, DEFAULT_DEBOUNCE_MS = 15, DEFAULT_PORT = 3000, DEV_SERVER_RESTART_DEBOUNCE_MS = 100, DOM_UPDATE_DELAY_MS = 50, FILE_PROTOCOL_PREFIX_LENGTH = 7, FOCUS_ID_PREFIX_LENGTH = 3, FOCUS_IDX_PREFIX_LENGTH = 4, FOCUS_NAME_PREFIX_LENGTH = 5, HMR_UPDATE_TIMEOUT_MS = 2000, HOOK_SIGNATURE_LENGTH = 12, EXCLUDE_LAST_OFFSET = -1, HOURS_IN_DAY = 24, HOURS_IN_HALF_DAY = 12, MAX_ERROR_LENGTH = 200, MAX_RECONNECT_ATTEMPTS = 60, MILLISECONDS_IN_A_SECOND = 1000, MINUTES_IN_AN_HOUR = 60, SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_MINUTE, MILLISECONDS_IN_A_DAY, OVERLAY_FADE_DURATION_MS = 150, PING_INTERVAL_MS = 30000, RAF_BATCH_COUNT = 3, RANDOM_ID_END_INDEX = 11, REBUILD_BATCH_DELAY_MS = 10, REBUILD_RELOAD_DELAY_MS = 200, RECONNECT_INITIAL_DELAY_MS = 500, RECONNECT_POLL_INTERVAL_MS = 300, SIGINT_EXIT_CODE = 130, SIGTERM_EXIT_CODE = 143, SVELTE_CSS_LOAD_TIMEOUT_MS = 500, TIME_PRECISION = 2, TWO_THIRDS, UNFOUND_INDEX = -1, WEBSOCKET_NORMAL_CLOSURE = 1000;
99
+ var init_constants = __esm(() => {
100
+ MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
101
+ MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
102
+ TWO_THIRDS = 2 / 3;
103
+ });
104
+
105
+ // src/core/islandPageContext.ts
106
+ var BOOTSTRAP_MANIFEST_KEY = "BootstrapClient", ISLAND_MARKER = 'data-island="true"', MANIFEST_MARKER = "__ABSOLUTE_MANIFEST__", ISLAND_STATE_MARKER = "__ABS_ISLAND_STATE__", CLOSING_HEAD_TAG2 = "</head>", buildIslandsHeadMarkup = (manifest) => {
107
+ const manifestScript = `<script>window.__ABSOLUTE_MANIFEST__ = ${JSON.stringify(manifest)}</script>`;
108
+ const islandStateScript = `<script>window.__ABS_ISLAND_STATE__ = ${JSON.stringify(globalThis.__ABS_ISLAND_STATE__ ?? {})}</script>`;
109
+ const bootstrapPath = manifest[BOOTSTRAP_MANIFEST_KEY];
110
+ const bootstrapScript = bootstrapPath ? `<script type="module" src="${bootstrapPath}"></script>` : "";
111
+ return `${manifestScript}${islandStateScript}${bootstrapScript}`;
112
+ }, injectHeadMarkup = (html, markup) => {
113
+ const closingHeadIndex = html.indexOf("</head>");
143
114
  if (closingHeadIndex >= 0) {
144
- return `${html.slice(0, closingHeadIndex)}${injection}${html.slice(closingHeadIndex)}`;
145
- }
146
- return `${html}${injection}`;
147
- }, toUint8 = (value, encoder) => encoder.encode(value), currentStreamingSlotPolicy, clonePolicy = (policy) => ({
148
- ...policy
149
- }), normalizeSlotBytes = (value, fallback) => {
150
- if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
151
- return Math.floor(value);
152
- }
153
- return fallback;
154
- }, normalizeSlotText = (value, fallback) => typeof value === "string" ? value : fallback, normalizeSlotError = (value, fallback) => typeof value === "string" ? value : fallback, hasPolicyValue = (policy, key) => Object.prototype.hasOwnProperty.call(policy, key), applyStreamingSlotPolicyOverrides = (base, overridePolicy = {}) => ({
155
- timeoutMs: hasPolicyValue(overridePolicy, "timeoutMs") ? normalizeSlotBytes(overridePolicy.timeoutMs, base.timeoutMs) : base.timeoutMs,
156
- fallbackHtml: hasPolicyValue(overridePolicy, "fallbackHtml") ? normalizeSlotText(overridePolicy.fallbackHtml, "") : base.fallbackHtml,
157
- errorHtml: hasPolicyValue(overridePolicy, "errorHtml") ? normalizeSlotError(overridePolicy.errorHtml) : base.errorHtml,
158
- maxSlotsPerResponse: hasPolicyValue(overridePolicy, "maxSlotsPerResponse") ? normalizeSlotBytes(overridePolicy.maxSlotsPerResponse, base.maxSlotsPerResponse) : base.maxSlotsPerResponse,
159
- maxSlotHtmlSizeBytes: hasPolicyValue(overridePolicy, "maxSlotHtmlSizeBytes") ? normalizeSlotBytes(overridePolicy.maxSlotHtmlSizeBytes, base.maxSlotHtmlSizeBytes) : base.maxSlotHtmlSizeBytes,
160
- onError: hasPolicyValue(overridePolicy, "onError") ? overridePolicy.onError : base.onError,
161
- onSlotMetric: hasPolicyValue(overridePolicy, "onSlotMetric") ? overridePolicy.onSlotMetric : base.onSlotMetric
162
- }), createCombinedSlotErrorHandler = (policyOnError, enhancerOnError) => {
163
- if (!policyOnError && !enhancerOnError)
164
- return;
165
- return (error, slot) => {
166
- policyOnError?.(error, slot);
167
- enhancerOnError?.(error, slot);
168
- };
169
- }, createCombinedSlotMetricHandler = (policyOnSlotMetric, callOnSlotMetric) => {
170
- if (!policyOnSlotMetric && !callOnSlotMetric)
171
- return;
172
- return (metric) => {
173
- policyOnSlotMetric?.(metric);
174
- callOnSlotMetric?.(metric);
175
- };
176
- }, resolveStreamingSlotPolicy = (overridePolicy = {}) => {
177
- const base = getStreamingSlotPolicy();
178
- return applyStreamingSlotPolicyOverrides(base, overridePolicy);
179
- }, getStreamingSlotPolicy = () => clonePolicy(currentStreamingSlotPolicy), setStreamingSlotPolicy = (policy = {}) => {
180
- const base = getStreamingSlotPolicy();
181
- currentStreamingSlotPolicy = applyStreamingSlotPolicyOverrides(base, policy);
182
- }, withStreamingSlotPolicy = async (policy, callback) => {
183
- const previous = getStreamingSlotPolicy();
184
- setStreamingSlotPolicy(policy);
185
- try {
186
- return await callback();
187
- } finally {
188
- currentStreamingSlotPolicy = previous;
189
- }
190
- }, emitSlotMetric = (metric, onSlotMetric) => {
191
- onSlotMetric?.(metric);
192
- }, createTimeoutError = (slot, timeoutMs) => {
193
- const error = new Error(`Streaming slot "${slot.id}" timed out after ${timeoutMs}ms`);
194
- error.__absTimeout = true;
195
- return error;
196
- }, toStreamingSlot = (slot, policy) => ({
197
- errorHtml: slot.errorHtml === undefined ? policy.errorHtml : slot.errorHtml,
198
- fallbackHtml: normalizeSlotText(slot.fallbackHtml, policy.fallbackHtml),
199
- id: slot.id ?? createStreamingSlotId(),
200
- timeoutMs: normalizeSlotBytes(slot.timeoutMs, policy.timeoutMs),
201
- resolve: slot.resolve
202
- }), prepareSlots = ({
203
- policy,
204
- slots,
205
- onError,
206
- onSlotMetric
207
- }) => {
208
- const preparedSlots = slots.map((slot) => toStreamingSlot(slot, policy));
209
- const maxSlotsPerResponse = policy.maxSlotsPerResponse;
210
- if (maxSlotsPerResponse === 0) {
211
- const error = new Error("Streaming slot limit is set to 0");
212
- for (const slot of preparedSlots) {
213
- onError?.(error, slot);
214
- emitSlotMetric({
215
- type: "dropped",
216
- slotId: slot.id,
217
- reason: "maxSlotsPerResponse is 0"
218
- }, onSlotMetric);
219
- }
220
- return [];
221
- }
222
- if (preparedSlots.length <= maxSlotsPerResponse) {
223
- preparedSlots.forEach((slot) => emitSlotMetric({
224
- type: "prepared",
225
- slotId: slot.id
226
- }, onSlotMetric));
227
- return preparedSlots;
115
+ return `${html.slice(0, closingHeadIndex)}${markup}${html.slice(closingHeadIndex)}`;
228
116
  }
229
- const keptSlots = preparedSlots.slice(0, maxSlotsPerResponse);
230
- const droppedSlots = preparedSlots.slice(maxSlotsPerResponse);
231
- droppedSlots.forEach((slot) => {
232
- onError?.(new Error(`Streaming slot "${slot.id}" dropped because ${maxSlotsPerResponse} slots is the configured maximum`), slot);
233
- emitSlotMetric({
234
- type: "dropped",
235
- slotId: slot.id,
236
- reason: `maxSlotsPerResponse is ${maxSlotsPerResponse}`
237
- }, onSlotMetric);
238
- });
239
- keptSlots.forEach((slot) => emitSlotMetric({
240
- type: "prepared",
241
- slotId: slot.id
242
- }, onSlotMetric));
243
- return keptSlots;
244
- }, htmlByteLength = (value, encoder) => encoder.encode(value).length, resolveSlot = async (slot, onError, policy, onSlotMetric) => {
245
- const safePolicy = policy ?? getStreamingSlotPolicy();
246
- const encoder = new TextEncoder;
247
- const start = Date.now();
248
- try {
249
- const maybeAsyncValue = Promise.resolve(slot.resolve());
250
- const resolved = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
251
- maybeAsyncValue,
252
- new Promise((_, reject) => setTimeout(() => {
253
- reject(createTimeoutError(slot, slot.timeoutMs ?? 0));
254
- }, slot.timeoutMs))
255
- ]) : await maybeAsyncValue;
256
- const html = typeof resolved === "string" ? resolved : `${resolved}`;
257
- if (safePolicy.maxSlotHtmlSizeBytes > 0 && htmlByteLength(html, encoder) > safePolicy.maxSlotHtmlSizeBytes) {
258
- const bytes2 = htmlByteLength(html, encoder);
259
- const error = new Error(`Streaming slot "${slot.id}" exceeded max payload size of ${safePolicy.maxSlotHtmlSizeBytes} bytes`);
260
- const durationMs2 = Date.now() - start;
261
- onError?.(error, slot);
262
- emitSlotMetric({
263
- type: "size_exceeded",
264
- slotId: slot.id,
265
- durationMs: durationMs2,
266
- bytes: bytes2,
267
- error
268
- }, onSlotMetric);
269
- const fallbackHtml = typeof slot.errorHtml === "string" ? slot.errorHtml : null;
270
- return {
271
- html: fallbackHtml,
272
- id: slot.id,
273
- durationMs: durationMs2,
274
- bytes: fallbackHtml === null ? 0 : htmlByteLength(fallbackHtml, encoder)
275
- };
276
- }
277
- const durationMs = Date.now() - start;
278
- const bytes = htmlByteLength(html, encoder);
279
- emitSlotMetric({
280
- type: "resolved",
281
- slotId: slot.id,
282
- durationMs,
283
- bytes
284
- }, onSlotMetric);
285
- return {
286
- html,
287
- id: slot.id,
288
- durationMs,
289
- bytes
290
- };
291
- } catch (error) {
292
- const durationMs = Date.now() - start;
293
- onError?.(error, slot);
294
- emitSlotMetric({
295
- type: error?.__absTimeout === true ? "timeout" : "error",
296
- slotId: slot.id,
297
- durationMs,
298
- error
299
- }, onSlotMetric);
300
- if (typeof slot.errorHtml === "string") {
301
- const html = slot.errorHtml;
302
- return {
303
- html,
304
- id: slot.id,
305
- durationMs,
306
- bytes: htmlByteLength(html, encoder)
307
- };
117
+ const openingBodyIndex = html.indexOf("<body");
118
+ if (openingBodyIndex >= 0) {
119
+ const bodyStart = html.indexOf(">", openingBodyIndex);
120
+ if (bodyStart >= 0) {
121
+ return `${html.slice(0, openingBodyIndex)}<head>${markup}</head>${html.slice(openingBodyIndex)}`;
308
122
  }
309
- return {
310
- html: null,
311
- id: slot.id,
312
- durationMs,
313
- bytes: 0
314
- };
315
123
  }
316
- }, nextResolvedSlot = async (pending) => {
317
- const wrapped = pending.map((promise) => promise.then((result) => ({
318
- original: promise,
319
- result
320
- })));
321
- return Promise.race(wrapped);
322
- }, streamChunkToString = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true }), streamOutOfOrderSlots = ({
323
- footerHtml = "",
324
- headerHtml = "",
325
- nonce,
326
- policy,
327
- onSlotMetric,
328
- onError,
329
- slots
330
- }) => {
331
- const resolvedPolicy = resolveStreamingSlotPolicy(policy);
332
- const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
333
- const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
334
- const effectivePolicy = {
335
- ...resolvedPolicy,
336
- onSlotMetric: combinedOnSlotMetric
337
- };
338
- const preparedSlots = prepareSlots({
339
- policy: effectivePolicy,
340
- slots,
341
- onError: combinedOnError,
342
- onSlotMetric: combinedOnSlotMetric
343
- });
124
+ return `<!DOCTYPE html><html><head>${markup}</head><body>${html}</body></html>`;
125
+ }, streamChunkToString2 = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true }), pipeStreamWithHeadInjection = (stream, markup) => {
344
126
  const encoder = new TextEncoder;
127
+ const decoder = new TextDecoder;
128
+ const lookbehind = CLOSING_HEAD_TAG2.length - 1;
345
129
  return new ReadableStream({
346
130
  async start(controller) {
347
- try {
348
- let header = headerHtml;
349
- if (preparedSlots.length > 0 && !header.includes(STREAMING_RUNTIME_GLOBAL)) {
350
- header = injectHtmlIntoHead(header, renderStreamingSlotsRuntimeTag(nonce));
351
- }
352
- controller.enqueue(toUint8(header, encoder));
353
- const pending = preparedSlots.map((slot) => {
354
- const fallback = renderStreamingSlotPlaceholder(slot.id, slot.fallbackHtml ?? "");
355
- controller.enqueue(toUint8(fallback, encoder));
356
- return resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric);
357
- });
358
- while (pending.length > 0) {
359
- const { original, result } = await nextResolvedSlot(pending);
360
- const index = pending.indexOf(original);
361
- if (index >= 0)
362
- pending.splice(index, 1);
363
- if (result.html === null)
364
- continue;
365
- emitSlotMetric({
366
- type: "patched",
367
- slotId: result.id,
368
- durationMs: result.durationMs,
369
- bytes: result.bytes
370
- }, combinedOnSlotMetric);
371
- controller.enqueue(toUint8(renderStreamingSlotPatchTag(result.id, result.html, nonce), encoder));
372
- }
373
- if (footerHtml.length > 0) {
374
- controller.enqueue(toUint8(footerHtml, encoder));
375
- }
376
- controller.close();
377
- } catch (error) {
378
- controller.error(error);
379
- }
380
- }
381
- });
382
- }, injectStreamingRuntimeIntoStream = (stream, nonce) => {
383
- const runtimeTag = renderStreamingSlotsRuntimeTag(nonce);
384
- const encoder = new TextEncoder;
385
- const decoder = new TextDecoder;
386
- const lookbehind = CLOSING_HEAD_TAG_LENGTH - 1;
387
- return new ReadableStream({
388
- async start(controller) {
389
- const reader = stream.getReader();
390
- let injected = false;
391
- let pending = "";
131
+ const reader = stream.getReader();
132
+ let injected = false;
133
+ let pending = "";
392
134
  try {
393
135
  for (;; ) {
394
136
  const { done, value } = await reader.read();
@@ -396,16 +138,16 @@ var SLOT_ID_PREFIX = "abs-slot-", SLOT_PLACEHOLDER_PREFIX = "slot-", CLOSING_HEA
396
138
  break;
397
139
  if (!value)
398
140
  continue;
399
- pending += streamChunkToString(value, decoder);
141
+ pending += streamChunkToString2(value, decoder);
400
142
  if (injected) {
401
143
  controller.enqueue(encoder.encode(pending));
402
144
  pending = "";
403
145
  continue;
404
146
  }
405
- const headIndex = pending.indexOf(CLOSING_HEAD_TAG);
147
+ const headIndex = pending.indexOf(CLOSING_HEAD_TAG2);
406
148
  if (headIndex >= 0) {
407
- const withRuntime = `${pending.slice(0, headIndex)}${runtimeTag}${pending.slice(headIndex)}`;
408
- controller.enqueue(encoder.encode(withRuntime));
149
+ const next = `${pending.slice(0, headIndex)}${markup}${pending.slice(headIndex)}`;
150
+ controller.enqueue(encoder.encode(next));
409
151
  pending = "";
410
152
  injected = true;
411
153
  continue;
@@ -418,7 +160,7 @@ var SLOT_ID_PREFIX = "abs-slot-", SLOT_PLACEHOLDER_PREFIX = "slot-", CLOSING_HEA
418
160
  }
419
161
  pending += decoder.decode();
420
162
  if (!injected) {
421
- pending = injectHtmlIntoHead(pending, runtimeTag);
163
+ pending = injectHeadMarkup(pending, markup);
422
164
  }
423
165
  if (pending.length > 0) {
424
166
  controller.enqueue(encoder.encode(pending));
@@ -429,352 +171,78 @@ var SLOT_ID_PREFIX = "abs-slot-", SLOT_PLACEHOLDER_PREFIX = "slot-", CLOSING_HEA
429
171
  }
430
172
  }
431
173
  });
432
- }, appendStreamingSlotPatchesToStream = (stream, slots = [], {
433
- injectRuntime = true,
434
- nonce,
435
- onError,
436
- onSlotMetric,
437
- policy
438
- } = {}) => {
439
- const resolvedPolicy = resolveStreamingSlotPolicy(policy);
440
- const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
441
- const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
442
- const effectivePolicy = {
443
- ...resolvedPolicy,
444
- onSlotMetric: combinedOnSlotMetric
445
- };
446
- const preparedSlots = prepareSlots({
447
- policy: effectivePolicy,
448
- slots,
449
- onError: combinedOnError,
450
- onSlotMetric: combinedOnSlotMetric
451
- });
452
- if (preparedSlots.length === 0)
453
- return stream;
454
- const source = injectRuntime ? injectStreamingRuntimeIntoStream(stream, nonce) : stream;
174
+ }, pipeStreamWithIslandMarkerDetection = (stream, markup) => {
455
175
  const encoder = new TextEncoder;
456
176
  const decoder = new TextDecoder;
457
- const reader = source.getReader();
458
- const pending = preparedSlots.map((slot) => resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric));
177
+ const lookbehind = Math.max(ISLAND_MARKER.length, 1024);
459
178
  return new ReadableStream({
460
179
  async start(controller) {
461
- let baseDone = false;
462
- let baseRead = reader.read();
463
- let tail = "";
464
- let footer = "";
180
+ const reader = stream.getReader();
181
+ let injected = false;
182
+ let pending = "";
465
183
  try {
466
- while (!baseDone || pending.length > 0) {
467
- const racers = [];
468
- if (!baseDone) {
469
- racers.push(baseRead.then(({ done, value }) => ({
470
- done,
471
- kind: "base",
472
- value
473
- })));
474
- }
475
- if (pending.length > 0) {
476
- racers.push(nextResolvedSlot(pending).then((resolved) => ({
477
- kind: "slot",
478
- ...resolved
479
- })));
480
- }
481
- if (racers.length === 0)
184
+ for (;; ) {
185
+ const { done, value } = await reader.read();
186
+ if (done)
482
187
  break;
483
- const winner = await Promise.race(racers);
484
- if (winner.kind === "base") {
485
- if (winner.done) {
486
- baseDone = true;
487
- tail += decoder.decode();
488
- const footerStart = tail.search(CLOSING_PAGE_TAG_REGEX);
489
- if (footerStart >= 0) {
490
- const content = tail.slice(0, footerStart);
491
- footer = tail.slice(footerStart);
492
- if (content.length > 0) {
493
- controller.enqueue(encoder.encode(content));
494
- }
495
- } else if (tail.length > 0) {
496
- controller.enqueue(encoder.encode(tail));
497
- }
498
- tail = "";
499
- } else if (winner.value) {
500
- tail += streamChunkToString(winner.value, decoder);
501
- if (tail.length > STREAM_TAIL_LOOKBEHIND) {
502
- const content = tail.slice(0, tail.length - STREAM_TAIL_LOOKBEHIND);
503
- controller.enqueue(encoder.encode(content));
504
- tail = tail.slice(-STREAM_TAIL_LOOKBEHIND);
505
- }
506
- baseRead = reader.read();
507
- }
188
+ if (!value)
189
+ continue;
190
+ pending += streamChunkToString2(value, decoder);
191
+ if (injected) {
192
+ controller.enqueue(encoder.encode(pending));
193
+ pending = "";
508
194
  continue;
509
195
  }
510
- const index = pending.indexOf(winner.original);
511
- if (index >= 0)
512
- pending.splice(index, 1);
513
- if (winner.result.html === null)
196
+ const markerIndex = pending.indexOf(ISLAND_MARKER);
197
+ if (markerIndex >= 0) {
198
+ const tagStart = pending.lastIndexOf("<", markerIndex);
199
+ const injectAt = tagStart >= 0 ? tagStart : markerIndex;
200
+ const next = `${pending.slice(0, injectAt)}${markup}${pending.slice(injectAt)}`;
201
+ controller.enqueue(encoder.encode(next));
202
+ pending = "";
203
+ injected = true;
514
204
  continue;
515
- emitSlotMetric({
516
- type: "patched",
517
- slotId: winner.result.id,
518
- durationMs: winner.result.durationMs,
519
- bytes: winner.result.bytes
520
- }, combinedOnSlotMetric);
521
- controller.enqueue(encoder.encode(renderStreamingSlotPatchTag(winner.result.id, winner.result.html, nonce)));
205
+ }
206
+ if (pending.length > lookbehind) {
207
+ const safeText = pending.slice(0, pending.length - lookbehind);
208
+ controller.enqueue(encoder.encode(safeText));
209
+ pending = pending.slice(-lookbehind);
210
+ }
211
+ }
212
+ pending += decoder.decode();
213
+ if (pending.length > 0) {
214
+ controller.enqueue(encoder.encode(pending));
522
215
  }
523
- if (footer.length > 0)
524
- controller.enqueue(encoder.encode(footer));
525
216
  controller.close();
526
217
  } catch (error) {
527
218
  controller.error(error);
528
219
  }
529
220
  }
530
221
  });
531
- };
532
- var init_streamingSlots = __esm(() => {
533
- init_escapeScriptContent();
534
- CLOSING_HEAD_TAG_LENGTH = CLOSING_HEAD_TAG.length;
535
- CLOSING_PAGE_TAG_REGEX = /<\/body>\s*<\/html>\s*$/i;
536
- currentStreamingSlotPolicy = {
537
- timeoutMs: STREAMING_SLOT_TIMEOUT_MS,
538
- fallbackHtml: "",
539
- errorHtml: undefined,
540
- maxSlotsPerResponse: STREAMING_SLOT_MAX_PER_RESPONSE,
541
- maxSlotHtmlSizeBytes: STREAMING_SLOT_MAX_HTML_BYTES
542
- };
543
- });
544
-
545
- // src/core/streamingSlotRegistrar.ts
546
- var registrar = null, setStreamingSlotRegistrar = (nextRegistrar) => {
547
- registrar = nextRegistrar;
548
- }, registerStreamingSlot = (slot) => {
549
- registrar?.(slot);
550
- };
551
-
552
- // src/core/streamingSlotRegistry.ts
553
- var asyncLocalStorage, isServerRuntime = () => typeof process !== "undefined" && typeof process.versions?.node === "string", ensureAsyncLocalStorage = async () => {
554
- if (typeof asyncLocalStorage !== "undefined")
555
- return asyncLocalStorage;
556
- if (!isServerRuntime()) {
557
- asyncLocalStorage = null;
558
- return asyncLocalStorage;
222
+ }, htmlContainsIslands = (html) => html.includes(ISLAND_MARKER), injectIslandPageContext = (html, options) => {
223
+ const manifest = globalThis.__absoluteManifest;
224
+ const hasIslands = options?.hasIslands ?? htmlContainsIslands(html);
225
+ if (!manifest || !hasIslands) {
226
+ return html;
559
227
  }
560
- const mod = await import("async_hooks");
561
- asyncLocalStorage = new mod.AsyncLocalStorage;
562
- return asyncLocalStorage;
563
- }, registerStreamingSlot2 = (slot) => {
564
- if (!asyncLocalStorage)
565
- return;
566
- const store = asyncLocalStorage.getStore();
567
- if (!store)
568
- return;
569
- store.set(slot.id, slot);
570
- }, runWithStreamingSlotRegistry = async (task) => {
571
- const storage = await ensureAsyncLocalStorage();
572
- if (!storage) {
573
- return {
574
- result: await task(),
575
- slots: []
576
- };
228
+ if (html.includes(MANIFEST_MARKER) || html.includes(ISLAND_STATE_MARKER)) {
229
+ return html;
577
230
  }
578
- return storage.run(new Map, async () => {
579
- const result = await task();
580
- const store = storage.getStore();
581
- return {
582
- result,
583
- slots: store ? [...store.values()] : []
584
- };
585
- });
586
- };
587
- var init_streamingSlotRegistry = __esm(() => {
588
- setStreamingSlotRegistrar(registerStreamingSlot2);
589
- });
590
-
591
- // src/core/responseEnhancers.ts
592
- var toResponse = async (responseLike) => await responseLike, cloneHeaders = (response) => {
593
- const headers = new Headers(response.headers);
594
- return headers;
595
- }, enhanceHtmlResponseWithStreamingSlots = (response, { nonce, onError, streamingSlots = [], policy } = {}) => {
596
- if (!response.body || streamingSlots.length === 0) {
597
- return response;
231
+ return injectHeadMarkup(html, buildIslandsHeadMarkup(manifest));
232
+ }, injectIslandPageContextStream = (stream, options) => {
233
+ const manifest = globalThis.__absoluteManifest;
234
+ if (!manifest)
235
+ return stream;
236
+ const markup = buildIslandsHeadMarkup(manifest);
237
+ if (options?.hasIslands === true) {
238
+ return pipeStreamWithHeadInjection(stream, markup);
598
239
  }
599
- const body = appendStreamingSlotPatchesToStream(response.body, streamingSlots, {
600
- nonce,
601
- onError,
602
- policy
603
- });
604
- return new Response(body, {
605
- headers: cloneHeaders(response),
606
- status: response.status,
607
- statusText: response.statusText
608
- });
609
- }, withStreamingSlots = async (responseLike, options = {}) => enhanceHtmlResponseWithStreamingSlots(await toResponse(responseLike), options), mergeStreamingSlots = (registered, explicit) => {
610
- const merged = new Map;
611
- for (const slot of registered)
612
- merged.set(slot.id, slot);
613
- for (const slot of explicit)
614
- merged.set(slot.id, slot);
615
- return [...merged.values()];
616
- }, withRegisteredStreamingSlots = async (renderResponse, options = {}) => {
617
- const { result, slots } = await runWithStreamingSlotRegistry(renderResponse);
618
- const explicit = options.streamingSlots ?? [];
619
- return withStreamingSlots(result, {
620
- ...options,
621
- streamingSlots: mergeStreamingSlots(slots, explicit)
622
- });
623
- };
624
- var init_responseEnhancers = __esm(() => {
625
- init_streamingSlots();
626
- init_streamingSlotRegistry();
627
- });
628
-
629
- // src/constants.ts
630
- var ANGULAR_INIT_TIMEOUT_MS = 500, ANSI_ESCAPE_LENGTH = 3, ASCII_SPACE = 32, BASE_36_RADIX = 36, BUN_BUILD_WARNING_SUPPRESSION = "wildcard sideEffects are not supported yet", BODY_SLICE_LENGTH = 2000, BYTES_PER_KILOBYTE = 1024, CLI_ARGS_OFFSET = 3, CSS_ERROR_RESOLVE_DELAY_MS = 50, CSS_MAX_CHECK_ATTEMPTS = 10, CSS_MAX_PARSE_TIMEOUT_MS = 500, CSS_SHEET_READY_TIMEOUT_MS = 100, DEFAULT_CHUNK_SIZE = 16384, DEFAULT_DEBOUNCE_MS = 15, DEFAULT_PORT = 3000, DEV_SERVER_RESTART_DEBOUNCE_MS = 100, DOM_UPDATE_DELAY_MS = 50, FILE_PROTOCOL_PREFIX_LENGTH = 7, FOCUS_ID_PREFIX_LENGTH = 3, FOCUS_IDX_PREFIX_LENGTH = 4, FOCUS_NAME_PREFIX_LENGTH = 5, HMR_UPDATE_TIMEOUT_MS = 2000, HOOK_SIGNATURE_LENGTH = 12, EXCLUDE_LAST_OFFSET = -1, HOURS_IN_DAY = 24, HOURS_IN_HALF_DAY = 12, MAX_ERROR_LENGTH = 200, MAX_RECONNECT_ATTEMPTS = 60, MILLISECONDS_IN_A_SECOND = 1000, MINUTES_IN_AN_HOUR = 60, SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_MINUTE, MILLISECONDS_IN_A_DAY, OVERLAY_FADE_DURATION_MS = 150, PING_INTERVAL_MS = 30000, RAF_BATCH_COUNT = 3, RANDOM_ID_END_INDEX = 11, REBUILD_BATCH_DELAY_MS = 10, REBUILD_RELOAD_DELAY_MS = 200, RECONNECT_INITIAL_DELAY_MS = 500, RECONNECT_POLL_INTERVAL_MS = 300, SIGINT_EXIT_CODE = 130, SIGTERM_EXIT_CODE = 143, SVELTE_CSS_LOAD_TIMEOUT_MS = 500, TIME_PRECISION = 2, TWO_THIRDS, UNFOUND_INDEX = -1, WEBSOCKET_NORMAL_CLOSURE = 1000;
631
- var init_constants = __esm(() => {
632
- MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
633
- MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
634
- TWO_THIRDS = 2 / 3;
635
- });
636
-
637
- // src/core/islandPageContext.ts
638
- var BOOTSTRAP_MANIFEST_KEY = "BootstrapClient", ISLAND_MARKER = 'data-island="true"', MANIFEST_MARKER = "__ABSOLUTE_MANIFEST__", ISLAND_STATE_MARKER = "__ABS_ISLAND_STATE__", CLOSING_HEAD_TAG2 = "</head>", buildIslandsHeadMarkup = (manifest) => {
639
- const manifestScript = `<script>window.__ABSOLUTE_MANIFEST__ = ${JSON.stringify(manifest)}</script>`;
640
- const islandStateScript = `<script>window.__ABS_ISLAND_STATE__ = ${JSON.stringify(globalThis.__ABS_ISLAND_STATE__ ?? {})}</script>`;
641
- const bootstrapPath = manifest[BOOTSTRAP_MANIFEST_KEY];
642
- const bootstrapScript = bootstrapPath ? `<script type="module" src="${bootstrapPath}"></script>` : "";
643
- return `${manifestScript}${islandStateScript}${bootstrapScript}`;
644
- }, injectHeadMarkup = (html, markup) => {
645
- const closingHeadIndex = html.indexOf("</head>");
646
- if (closingHeadIndex >= 0) {
647
- return `${html.slice(0, closingHeadIndex)}${markup}${html.slice(closingHeadIndex)}`;
648
- }
649
- const openingBodyIndex = html.indexOf("<body");
650
- if (openingBodyIndex >= 0) {
651
- const bodyStart = html.indexOf(">", openingBodyIndex);
652
- if (bodyStart >= 0) {
653
- return `${html.slice(0, openingBodyIndex)}<head>${markup}</head>${html.slice(openingBodyIndex)}`;
654
- }
655
- }
656
- return `<!DOCTYPE html><html><head>${markup}</head><body>${html}</body></html>`;
657
- }, streamChunkToString2 = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true }), pipeStreamWithHeadInjection = (stream, markup) => {
658
- const encoder = new TextEncoder;
659
- const decoder = new TextDecoder;
660
- const lookbehind = CLOSING_HEAD_TAG2.length - 1;
661
- return new ReadableStream({
662
- async start(controller) {
663
- const reader = stream.getReader();
664
- let injected = false;
665
- let pending = "";
666
- try {
667
- for (;; ) {
668
- const { done, value } = await reader.read();
669
- if (done)
670
- break;
671
- if (!value)
672
- continue;
673
- pending += streamChunkToString2(value, decoder);
674
- if (injected) {
675
- controller.enqueue(encoder.encode(pending));
676
- pending = "";
677
- continue;
678
- }
679
- const headIndex = pending.indexOf(CLOSING_HEAD_TAG2);
680
- if (headIndex >= 0) {
681
- const next = `${pending.slice(0, headIndex)}${markup}${pending.slice(headIndex)}`;
682
- controller.enqueue(encoder.encode(next));
683
- pending = "";
684
- injected = true;
685
- continue;
686
- }
687
- if (pending.length > lookbehind) {
688
- const safeText = pending.slice(0, pending.length - lookbehind);
689
- controller.enqueue(encoder.encode(safeText));
690
- pending = pending.slice(-lookbehind);
691
- }
692
- }
693
- pending += decoder.decode();
694
- if (!injected) {
695
- pending = injectHeadMarkup(pending, markup);
696
- }
697
- if (pending.length > 0) {
698
- controller.enqueue(encoder.encode(pending));
699
- }
700
- controller.close();
701
- } catch (error) {
702
- controller.error(error);
703
- }
704
- }
705
- });
706
- }, pipeStreamWithIslandMarkerDetection = (stream, markup) => {
707
- const encoder = new TextEncoder;
708
- const decoder = new TextDecoder;
709
- const lookbehind = Math.max(ISLAND_MARKER.length, 1024);
710
- return new ReadableStream({
711
- async start(controller) {
712
- const reader = stream.getReader();
713
- let injected = false;
714
- let pending = "";
715
- try {
716
- for (;; ) {
717
- const { done, value } = await reader.read();
718
- if (done)
719
- break;
720
- if (!value)
721
- continue;
722
- pending += streamChunkToString2(value, decoder);
723
- if (injected) {
724
- controller.enqueue(encoder.encode(pending));
725
- pending = "";
726
- continue;
727
- }
728
- const markerIndex = pending.indexOf(ISLAND_MARKER);
729
- if (markerIndex >= 0) {
730
- const tagStart = pending.lastIndexOf("<", markerIndex);
731
- const injectAt = tagStart >= 0 ? tagStart : markerIndex;
732
- const next = `${pending.slice(0, injectAt)}${markup}${pending.slice(injectAt)}`;
733
- controller.enqueue(encoder.encode(next));
734
- pending = "";
735
- injected = true;
736
- continue;
737
- }
738
- if (pending.length > lookbehind) {
739
- const safeText = pending.slice(0, pending.length - lookbehind);
740
- controller.enqueue(encoder.encode(safeText));
741
- pending = pending.slice(-lookbehind);
742
- }
743
- }
744
- pending += decoder.decode();
745
- if (pending.length > 0) {
746
- controller.enqueue(encoder.encode(pending));
747
- }
748
- controller.close();
749
- } catch (error) {
750
- controller.error(error);
751
- }
752
- }
753
- });
754
- }, htmlContainsIslands = (html) => html.includes(ISLAND_MARKER), injectIslandPageContext = (html, options) => {
755
- const manifest = globalThis.__absoluteManifest;
756
- const hasIslands = options?.hasIslands ?? htmlContainsIslands(html);
757
- if (!manifest || !hasIslands) {
758
- return html;
759
- }
760
- if (html.includes(MANIFEST_MARKER) || html.includes(ISLAND_STATE_MARKER)) {
761
- return html;
762
- }
763
- return injectHeadMarkup(html, buildIslandsHeadMarkup(manifest));
764
- }, injectIslandPageContextStream = (stream, options) => {
765
- const manifest = globalThis.__absoluteManifest;
766
- if (!manifest)
767
- return stream;
768
- const markup = buildIslandsHeadMarkup(manifest);
769
- if (options?.hasIslands === true) {
770
- return pipeStreamWithHeadInjection(stream, markup);
771
- }
772
- if (options?.hasIslands === false) {
773
- return stream;
774
- }
775
- return pipeStreamWithIslandMarkerDetection(stream, markup);
776
- }, setCurrentIslandManifest = (manifest) => {
777
- globalThis.__absoluteManifest = manifest;
240
+ if (options?.hasIslands === false) {
241
+ return stream;
242
+ }
243
+ return pipeStreamWithIslandMarkerDetection(stream, markup);
244
+ }, setCurrentIslandManifest = (manifest) => {
245
+ globalThis.__absoluteManifest = manifest;
778
246
  };
779
247
 
780
248
  // src/utils/ssrErrorPage.ts
@@ -11309,8 +10777,586 @@ var init_imageProcessing = __esm(() => {
11309
10777
  // src/angular/index.ts
11310
10778
  import"@angular/compiler";
11311
10779
 
10780
+ // src/client/streamSwap.ts
10781
+ var streamSwapRuntime = () => {
10782
+ if (window.__ABS_SLOT_RUNTIME__ === true)
10783
+ return;
10784
+ window.__ABS_SLOT_RUNTIME__ = true;
10785
+ window.__ABS_SLOT_PENDING__ = window.__ABS_SLOT_PENDING__ ?? {};
10786
+ const pending = window.__ABS_SLOT_PENDING__;
10787
+ const apply = (id, html) => {
10788
+ const node = document.getElementById(`slot-${id}`);
10789
+ if (!node) {
10790
+ pending[id] = html;
10791
+ return;
10792
+ }
10793
+ node.innerHTML = html;
10794
+ delete pending[id];
10795
+ };
10796
+ const flush = () => {
10797
+ for (const id in pending) {
10798
+ if (!Object.prototype.hasOwnProperty.call(pending, id))
10799
+ continue;
10800
+ apply(id, pending[id] ?? "");
10801
+ }
10802
+ };
10803
+ window.__ABS_SLOT_ENQUEUE__ = (id, html) => {
10804
+ apply(id, html);
10805
+ };
10806
+ if (typeof MutationObserver === "function") {
10807
+ const observer = new MutationObserver(flush);
10808
+ const root = document.documentElement ?? document.body ?? document;
10809
+ observer.observe(root, { childList: true, subtree: true });
10810
+ }
10811
+ if (document.readyState === "loading") {
10812
+ document.addEventListener("DOMContentLoaded", flush, { once: true });
10813
+ }
10814
+ flush();
10815
+ };
10816
+ var stripFunctionWrapper = (value) => {
10817
+ const start = value.indexOf("{");
10818
+ const end = value.lastIndexOf("}");
10819
+ if (start < 0 || end <= start)
10820
+ return "";
10821
+ return value.slice(start + 1, end);
10822
+ };
10823
+ var getStreamSwapRuntimeScript = () => `(function(){${stripFunctionWrapper(streamSwapRuntime.toString())}})();`;
10824
+
10825
+ // src/utils/streamingSlots.ts
10826
+ init_escapeScriptContent();
10827
+ var SLOT_ID_PREFIX = "abs-slot-";
10828
+ var SLOT_PLACEHOLDER_PREFIX = "slot-";
10829
+ var CLOSING_HEAD_TAG = "</head>";
10830
+ var CLOSING_HEAD_TAG_LENGTH = CLOSING_HEAD_TAG.length;
10831
+ var CLOSING_PAGE_TAG_REGEX = /<\/body>\s*<\/html>\s*$/i;
10832
+ var STREAMING_RUNTIME_GLOBAL = "__ABS_SLOT_ENQUEUE__";
10833
+ var STREAMING_PENDING_GLOBAL = "__ABS_SLOT_PENDING__";
10834
+ var STREAM_TAIL_LOOKBEHIND = 128;
10835
+ var STREAMING_SLOT_TIMEOUT_MS = 5000;
10836
+ var STREAMING_SLOT_MAX_PER_RESPONSE = 128;
10837
+ var STREAMING_SLOT_MAX_HTML_BYTES = 64000;
10838
+ var createSlotPlaceholderId = (id) => `${SLOT_PLACEHOLDER_PREFIX}${id}`;
10839
+ var createSlotPatchStatement = (id, html) => `(window.${STREAMING_RUNTIME_GLOBAL}||function(i,h){window.${STREAMING_PENDING_GLOBAL}=window.${STREAMING_PENDING_GLOBAL}||{};window.${STREAMING_PENDING_GLOBAL}[i]=h;})(${JSON.stringify(id)},${JSON.stringify(html)});`;
10840
+ var createNonceAttr = (nonce) => nonce ? ` nonce="${nonce}"` : "";
10841
+ var createStreamingSlotId = () => `${SLOT_ID_PREFIX}${Math.random().toString(36).slice(2, 10)}`;
10842
+ var getStreamingSlotsRuntimeScript = () => getStreamSwapRuntimeScript();
10843
+ var renderStreamingSlotsRuntimeTag = (nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(getStreamingSlotsRuntimeScript())}</script>`;
10844
+ var renderStreamingSlotPlaceholder = (id, fallbackHtml = "") => `<div id="${createSlotPlaceholderId(id)}" data-absolute-slot="true">${fallbackHtml}</div>`;
10845
+ var renderStreamingSlotPatchTag = (id, html, nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(createSlotPatchStatement(id, html))}</script>`;
10846
+ var injectHtmlIntoHead = (html, injection) => {
10847
+ const closingHeadIndex = html.indexOf(CLOSING_HEAD_TAG);
10848
+ if (closingHeadIndex >= 0) {
10849
+ return `${html.slice(0, closingHeadIndex)}${injection}${html.slice(closingHeadIndex)}`;
10850
+ }
10851
+ return `${html}${injection}`;
10852
+ };
10853
+ var toUint8 = (value, encoder) => encoder.encode(value);
10854
+ var currentStreamingSlotPolicy = {
10855
+ timeoutMs: STREAMING_SLOT_TIMEOUT_MS,
10856
+ fallbackHtml: "",
10857
+ errorHtml: undefined,
10858
+ maxSlotsPerResponse: STREAMING_SLOT_MAX_PER_RESPONSE,
10859
+ maxSlotHtmlSizeBytes: STREAMING_SLOT_MAX_HTML_BYTES
10860
+ };
10861
+ var clonePolicy = (policy) => ({
10862
+ ...policy
10863
+ });
10864
+ var normalizeSlotBytes = (value, fallback) => {
10865
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
10866
+ return Math.floor(value);
10867
+ }
10868
+ return fallback;
10869
+ };
10870
+ var normalizeSlotText = (value, fallback) => typeof value === "string" ? value : fallback;
10871
+ var normalizeSlotError = (value, fallback) => typeof value === "string" ? value : fallback;
10872
+ var hasPolicyValue = (policy, key) => Object.prototype.hasOwnProperty.call(policy, key);
10873
+ var applyStreamingSlotPolicyOverrides = (base, overridePolicy = {}) => ({
10874
+ timeoutMs: hasPolicyValue(overridePolicy, "timeoutMs") ? normalizeSlotBytes(overridePolicy.timeoutMs, base.timeoutMs) : base.timeoutMs,
10875
+ fallbackHtml: hasPolicyValue(overridePolicy, "fallbackHtml") ? normalizeSlotText(overridePolicy.fallbackHtml, "") : base.fallbackHtml,
10876
+ errorHtml: hasPolicyValue(overridePolicy, "errorHtml") ? normalizeSlotError(overridePolicy.errorHtml) : base.errorHtml,
10877
+ maxSlotsPerResponse: hasPolicyValue(overridePolicy, "maxSlotsPerResponse") ? normalizeSlotBytes(overridePolicy.maxSlotsPerResponse, base.maxSlotsPerResponse) : base.maxSlotsPerResponse,
10878
+ maxSlotHtmlSizeBytes: hasPolicyValue(overridePolicy, "maxSlotHtmlSizeBytes") ? normalizeSlotBytes(overridePolicy.maxSlotHtmlSizeBytes, base.maxSlotHtmlSizeBytes) : base.maxSlotHtmlSizeBytes,
10879
+ onError: hasPolicyValue(overridePolicy, "onError") ? overridePolicy.onError : base.onError,
10880
+ onSlotMetric: hasPolicyValue(overridePolicy, "onSlotMetric") ? overridePolicy.onSlotMetric : base.onSlotMetric
10881
+ });
10882
+ var createCombinedSlotErrorHandler = (policyOnError, enhancerOnError) => {
10883
+ if (!policyOnError && !enhancerOnError)
10884
+ return;
10885
+ return (error, slot) => {
10886
+ policyOnError?.(error, slot);
10887
+ enhancerOnError?.(error, slot);
10888
+ };
10889
+ };
10890
+ var createCombinedSlotMetricHandler = (policyOnSlotMetric, callOnSlotMetric) => {
10891
+ if (!policyOnSlotMetric && !callOnSlotMetric)
10892
+ return;
10893
+ return (metric) => {
10894
+ policyOnSlotMetric?.(metric);
10895
+ callOnSlotMetric?.(metric);
10896
+ };
10897
+ };
10898
+ var resolveStreamingSlotPolicy = (overridePolicy = {}) => {
10899
+ const base = getStreamingSlotPolicy();
10900
+ return applyStreamingSlotPolicyOverrides(base, overridePolicy);
10901
+ };
10902
+ var getStreamingSlotPolicy = () => clonePolicy(currentStreamingSlotPolicy);
10903
+ var setStreamingSlotPolicy = (policy = {}) => {
10904
+ const base = getStreamingSlotPolicy();
10905
+ currentStreamingSlotPolicy = applyStreamingSlotPolicyOverrides(base, policy);
10906
+ };
10907
+ var withStreamingSlotPolicy = async (policy, callback) => {
10908
+ const previous = getStreamingSlotPolicy();
10909
+ setStreamingSlotPolicy(policy);
10910
+ try {
10911
+ return await callback();
10912
+ } finally {
10913
+ currentStreamingSlotPolicy = previous;
10914
+ }
10915
+ };
10916
+ var emitSlotMetric = (metric, onSlotMetric) => {
10917
+ onSlotMetric?.(metric);
10918
+ };
10919
+ var createTimeoutError = (slot, timeoutMs) => {
10920
+ const error = new Error(`Streaming slot "${slot.id}" timed out after ${timeoutMs}ms`);
10921
+ error.__absTimeout = true;
10922
+ return error;
10923
+ };
10924
+ var toStreamingSlot = (slot, policy) => ({
10925
+ errorHtml: slot.errorHtml === undefined ? policy.errorHtml : slot.errorHtml,
10926
+ fallbackHtml: normalizeSlotText(slot.fallbackHtml, policy.fallbackHtml),
10927
+ id: slot.id ?? createStreamingSlotId(),
10928
+ timeoutMs: normalizeSlotBytes(slot.timeoutMs, policy.timeoutMs),
10929
+ resolve: slot.resolve
10930
+ });
10931
+ var prepareSlots = ({
10932
+ policy,
10933
+ slots,
10934
+ onError,
10935
+ onSlotMetric
10936
+ }) => {
10937
+ const preparedSlots = slots.map((slot) => toStreamingSlot(slot, policy));
10938
+ const maxSlotsPerResponse = policy.maxSlotsPerResponse;
10939
+ if (maxSlotsPerResponse === 0) {
10940
+ const error = new Error("Streaming slot limit is set to 0");
10941
+ for (const slot of preparedSlots) {
10942
+ onError?.(error, slot);
10943
+ emitSlotMetric({
10944
+ type: "dropped",
10945
+ slotId: slot.id,
10946
+ reason: "maxSlotsPerResponse is 0"
10947
+ }, onSlotMetric);
10948
+ }
10949
+ return [];
10950
+ }
10951
+ if (preparedSlots.length <= maxSlotsPerResponse) {
10952
+ preparedSlots.forEach((slot) => emitSlotMetric({
10953
+ type: "prepared",
10954
+ slotId: slot.id
10955
+ }, onSlotMetric));
10956
+ return preparedSlots;
10957
+ }
10958
+ const keptSlots = preparedSlots.slice(0, maxSlotsPerResponse);
10959
+ const droppedSlots = preparedSlots.slice(maxSlotsPerResponse);
10960
+ droppedSlots.forEach((slot) => {
10961
+ onError?.(new Error(`Streaming slot "${slot.id}" dropped because ${maxSlotsPerResponse} slots is the configured maximum`), slot);
10962
+ emitSlotMetric({
10963
+ type: "dropped",
10964
+ slotId: slot.id,
10965
+ reason: `maxSlotsPerResponse is ${maxSlotsPerResponse}`
10966
+ }, onSlotMetric);
10967
+ });
10968
+ keptSlots.forEach((slot) => emitSlotMetric({
10969
+ type: "prepared",
10970
+ slotId: slot.id
10971
+ }, onSlotMetric));
10972
+ return keptSlots;
10973
+ };
10974
+ var htmlByteLength = (value, encoder) => encoder.encode(value).length;
10975
+ var resolveSlot = async (slot, onError, policy, onSlotMetric) => {
10976
+ const safePolicy = policy ?? getStreamingSlotPolicy();
10977
+ const encoder = new TextEncoder;
10978
+ const start = Date.now();
10979
+ try {
10980
+ const maybeAsyncValue = Promise.resolve(slot.resolve());
10981
+ const resolved = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
10982
+ maybeAsyncValue,
10983
+ new Promise((_, reject) => setTimeout(() => {
10984
+ reject(createTimeoutError(slot, slot.timeoutMs ?? 0));
10985
+ }, slot.timeoutMs))
10986
+ ]) : await maybeAsyncValue;
10987
+ const html = typeof resolved === "string" ? resolved : `${resolved}`;
10988
+ if (safePolicy.maxSlotHtmlSizeBytes > 0 && htmlByteLength(html, encoder) > safePolicy.maxSlotHtmlSizeBytes) {
10989
+ const bytes2 = htmlByteLength(html, encoder);
10990
+ const error = new Error(`Streaming slot "${slot.id}" exceeded max payload size of ${safePolicy.maxSlotHtmlSizeBytes} bytes`);
10991
+ const durationMs2 = Date.now() - start;
10992
+ onError?.(error, slot);
10993
+ emitSlotMetric({
10994
+ type: "size_exceeded",
10995
+ slotId: slot.id,
10996
+ durationMs: durationMs2,
10997
+ bytes: bytes2,
10998
+ error
10999
+ }, onSlotMetric);
11000
+ const fallbackHtml = typeof slot.errorHtml === "string" ? slot.errorHtml : null;
11001
+ return {
11002
+ html: fallbackHtml,
11003
+ id: slot.id,
11004
+ durationMs: durationMs2,
11005
+ bytes: fallbackHtml === null ? 0 : htmlByteLength(fallbackHtml, encoder)
11006
+ };
11007
+ }
11008
+ const durationMs = Date.now() - start;
11009
+ const bytes = htmlByteLength(html, encoder);
11010
+ emitSlotMetric({
11011
+ type: "resolved",
11012
+ slotId: slot.id,
11013
+ durationMs,
11014
+ bytes
11015
+ }, onSlotMetric);
11016
+ return {
11017
+ html,
11018
+ id: slot.id,
11019
+ durationMs,
11020
+ bytes
11021
+ };
11022
+ } catch (error) {
11023
+ const durationMs = Date.now() - start;
11024
+ onError?.(error, slot);
11025
+ emitSlotMetric({
11026
+ type: error?.__absTimeout === true ? "timeout" : "error",
11027
+ slotId: slot.id,
11028
+ durationMs,
11029
+ error
11030
+ }, onSlotMetric);
11031
+ if (typeof slot.errorHtml === "string") {
11032
+ const html = slot.errorHtml;
11033
+ return {
11034
+ html,
11035
+ id: slot.id,
11036
+ durationMs,
11037
+ bytes: htmlByteLength(html, encoder)
11038
+ };
11039
+ }
11040
+ return {
11041
+ html: null,
11042
+ id: slot.id,
11043
+ durationMs,
11044
+ bytes: 0
11045
+ };
11046
+ }
11047
+ };
11048
+ var nextResolvedSlot = async (pending) => {
11049
+ const wrapped = pending.map((promise) => promise.then((result) => ({
11050
+ original: promise,
11051
+ result
11052
+ })));
11053
+ return Promise.race(wrapped);
11054
+ };
11055
+ var streamChunkToString = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true });
11056
+ var streamOutOfOrderSlots = ({
11057
+ footerHtml = "",
11058
+ headerHtml = "",
11059
+ nonce,
11060
+ policy,
11061
+ onSlotMetric,
11062
+ onError,
11063
+ slots
11064
+ }) => {
11065
+ const resolvedPolicy = resolveStreamingSlotPolicy(policy);
11066
+ const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
11067
+ const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
11068
+ const effectivePolicy = {
11069
+ ...resolvedPolicy,
11070
+ onSlotMetric: combinedOnSlotMetric
11071
+ };
11072
+ const preparedSlots = prepareSlots({
11073
+ policy: effectivePolicy,
11074
+ slots,
11075
+ onError: combinedOnError,
11076
+ onSlotMetric: combinedOnSlotMetric
11077
+ });
11078
+ const encoder = new TextEncoder;
11079
+ return new ReadableStream({
11080
+ async start(controller) {
11081
+ try {
11082
+ let header = headerHtml;
11083
+ if (preparedSlots.length > 0 && !header.includes(STREAMING_RUNTIME_GLOBAL)) {
11084
+ header = injectHtmlIntoHead(header, renderStreamingSlotsRuntimeTag(nonce));
11085
+ }
11086
+ controller.enqueue(toUint8(header, encoder));
11087
+ const pending = preparedSlots.map((slot) => {
11088
+ const fallback = renderStreamingSlotPlaceholder(slot.id, slot.fallbackHtml ?? "");
11089
+ controller.enqueue(toUint8(fallback, encoder));
11090
+ return resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric);
11091
+ });
11092
+ while (pending.length > 0) {
11093
+ const { original, result } = await nextResolvedSlot(pending);
11094
+ const index = pending.indexOf(original);
11095
+ if (index >= 0)
11096
+ pending.splice(index, 1);
11097
+ if (result.html === null)
11098
+ continue;
11099
+ emitSlotMetric({
11100
+ type: "patched",
11101
+ slotId: result.id,
11102
+ durationMs: result.durationMs,
11103
+ bytes: result.bytes
11104
+ }, combinedOnSlotMetric);
11105
+ controller.enqueue(toUint8(renderStreamingSlotPatchTag(result.id, result.html, nonce), encoder));
11106
+ }
11107
+ if (footerHtml.length > 0) {
11108
+ controller.enqueue(toUint8(footerHtml, encoder));
11109
+ }
11110
+ controller.close();
11111
+ } catch (error) {
11112
+ controller.error(error);
11113
+ }
11114
+ }
11115
+ });
11116
+ };
11117
+ var injectStreamingRuntimeIntoStream = (stream, nonce) => {
11118
+ const runtimeTag = renderStreamingSlotsRuntimeTag(nonce);
11119
+ const encoder = new TextEncoder;
11120
+ const decoder = new TextDecoder;
11121
+ const lookbehind = CLOSING_HEAD_TAG_LENGTH - 1;
11122
+ return new ReadableStream({
11123
+ async start(controller) {
11124
+ const reader = stream.getReader();
11125
+ let injected = false;
11126
+ let pending = "";
11127
+ try {
11128
+ for (;; ) {
11129
+ const { done, value } = await reader.read();
11130
+ if (done)
11131
+ break;
11132
+ if (!value)
11133
+ continue;
11134
+ pending += streamChunkToString(value, decoder);
11135
+ if (injected) {
11136
+ controller.enqueue(encoder.encode(pending));
11137
+ pending = "";
11138
+ continue;
11139
+ }
11140
+ const headIndex = pending.indexOf(CLOSING_HEAD_TAG);
11141
+ if (headIndex >= 0) {
11142
+ const withRuntime = `${pending.slice(0, headIndex)}${runtimeTag}${pending.slice(headIndex)}`;
11143
+ controller.enqueue(encoder.encode(withRuntime));
11144
+ pending = "";
11145
+ injected = true;
11146
+ continue;
11147
+ }
11148
+ if (pending.length > lookbehind) {
11149
+ const safeText = pending.slice(0, pending.length - lookbehind);
11150
+ controller.enqueue(encoder.encode(safeText));
11151
+ pending = pending.slice(-lookbehind);
11152
+ }
11153
+ }
11154
+ pending += decoder.decode();
11155
+ if (!injected) {
11156
+ pending = injectHtmlIntoHead(pending, runtimeTag);
11157
+ }
11158
+ if (pending.length > 0) {
11159
+ controller.enqueue(encoder.encode(pending));
11160
+ }
11161
+ controller.close();
11162
+ } catch (error) {
11163
+ controller.error(error);
11164
+ }
11165
+ }
11166
+ });
11167
+ };
11168
+ var appendStreamingSlotPatchesToStream = (stream, slots = [], {
11169
+ injectRuntime = true,
11170
+ nonce,
11171
+ onError,
11172
+ onSlotMetric,
11173
+ policy
11174
+ } = {}) => {
11175
+ const resolvedPolicy = resolveStreamingSlotPolicy(policy);
11176
+ const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
11177
+ const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
11178
+ const effectivePolicy = {
11179
+ ...resolvedPolicy,
11180
+ onSlotMetric: combinedOnSlotMetric
11181
+ };
11182
+ const preparedSlots = prepareSlots({
11183
+ policy: effectivePolicy,
11184
+ slots,
11185
+ onError: combinedOnError,
11186
+ onSlotMetric: combinedOnSlotMetric
11187
+ });
11188
+ if (preparedSlots.length === 0)
11189
+ return stream;
11190
+ const source = injectRuntime ? injectStreamingRuntimeIntoStream(stream, nonce) : stream;
11191
+ const encoder = new TextEncoder;
11192
+ const decoder = new TextDecoder;
11193
+ const reader = source.getReader();
11194
+ const pending = preparedSlots.map((slot) => resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric));
11195
+ return new ReadableStream({
11196
+ async start(controller) {
11197
+ let baseDone = false;
11198
+ let baseRead = reader.read();
11199
+ let tail = "";
11200
+ let footer = "";
11201
+ try {
11202
+ while (!baseDone || pending.length > 0) {
11203
+ const racers = [];
11204
+ if (!baseDone) {
11205
+ racers.push(baseRead.then(({ done, value }) => ({
11206
+ done,
11207
+ kind: "base",
11208
+ value
11209
+ })));
11210
+ }
11211
+ if (pending.length > 0) {
11212
+ racers.push(nextResolvedSlot(pending).then((resolved) => ({
11213
+ kind: "slot",
11214
+ ...resolved
11215
+ })));
11216
+ }
11217
+ if (racers.length === 0)
11218
+ break;
11219
+ const winner = await Promise.race(racers);
11220
+ if (winner.kind === "base") {
11221
+ if (winner.done) {
11222
+ baseDone = true;
11223
+ tail += decoder.decode();
11224
+ const footerStart = tail.search(CLOSING_PAGE_TAG_REGEX);
11225
+ if (footerStart >= 0) {
11226
+ const content = tail.slice(0, footerStart);
11227
+ footer = tail.slice(footerStart);
11228
+ if (content.length > 0) {
11229
+ controller.enqueue(encoder.encode(content));
11230
+ }
11231
+ } else if (tail.length > 0) {
11232
+ controller.enqueue(encoder.encode(tail));
11233
+ }
11234
+ tail = "";
11235
+ } else if (winner.value) {
11236
+ tail += streamChunkToString(winner.value, decoder);
11237
+ if (tail.length > STREAM_TAIL_LOOKBEHIND) {
11238
+ const content = tail.slice(0, tail.length - STREAM_TAIL_LOOKBEHIND);
11239
+ controller.enqueue(encoder.encode(content));
11240
+ tail = tail.slice(-STREAM_TAIL_LOOKBEHIND);
11241
+ }
11242
+ baseRead = reader.read();
11243
+ }
11244
+ continue;
11245
+ }
11246
+ const index = pending.indexOf(winner.original);
11247
+ if (index >= 0)
11248
+ pending.splice(index, 1);
11249
+ if (winner.result.html === null)
11250
+ continue;
11251
+ emitSlotMetric({
11252
+ type: "patched",
11253
+ slotId: winner.result.id,
11254
+ durationMs: winner.result.durationMs,
11255
+ bytes: winner.result.bytes
11256
+ }, combinedOnSlotMetric);
11257
+ controller.enqueue(encoder.encode(renderStreamingSlotPatchTag(winner.result.id, winner.result.html, nonce)));
11258
+ }
11259
+ if (footer.length > 0)
11260
+ controller.enqueue(encoder.encode(footer));
11261
+ controller.close();
11262
+ } catch (error) {
11263
+ controller.error(error);
11264
+ }
11265
+ }
11266
+ });
11267
+ };
11268
+
11269
+ // src/core/streamingSlotRegistrar.ts
11270
+ var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
11271
+ var getRegistrarGlobal = () => globalThis;
11272
+ var setStreamingSlotRegistrar = (nextRegistrar) => {
11273
+ getRegistrarGlobal()[STREAMING_SLOT_REGISTRAR_KEY] = nextRegistrar;
11274
+ };
11275
+ var registerStreamingSlot = (slot) => {
11276
+ getRegistrarGlobal()[STREAMING_SLOT_REGISTRAR_KEY]?.(slot);
11277
+ };
11278
+
11279
+ // src/core/streamingSlotRegistry.ts
11280
+ var asyncLocalStorage;
11281
+ var isServerRuntime = () => typeof process !== "undefined" && typeof process.versions?.node === "string";
11282
+ var ensureAsyncLocalStorage = async () => {
11283
+ if (typeof asyncLocalStorage !== "undefined")
11284
+ return asyncLocalStorage;
11285
+ if (!isServerRuntime()) {
11286
+ asyncLocalStorage = null;
11287
+ return asyncLocalStorage;
11288
+ }
11289
+ const mod = await import("async_hooks");
11290
+ asyncLocalStorage = new mod.AsyncLocalStorage;
11291
+ return asyncLocalStorage;
11292
+ };
11293
+ var registerStreamingSlot2 = (slot) => {
11294
+ if (!asyncLocalStorage)
11295
+ return;
11296
+ const store = asyncLocalStorage.getStore();
11297
+ if (!store)
11298
+ return;
11299
+ store.set(slot.id, slot);
11300
+ };
11301
+ setStreamingSlotRegistrar(registerStreamingSlot2);
11302
+ var runWithStreamingSlotRegistry = async (task) => {
11303
+ const storage = await ensureAsyncLocalStorage();
11304
+ if (!storage) {
11305
+ return {
11306
+ result: await task(),
11307
+ slots: []
11308
+ };
11309
+ }
11310
+ return storage.run(new Map, async () => {
11311
+ const result = await task();
11312
+ const store = storage.getStore();
11313
+ return {
11314
+ result,
11315
+ slots: store ? [...store.values()] : []
11316
+ };
11317
+ });
11318
+ };
11319
+
11320
+ // src/core/responseEnhancers.ts
11321
+ var toResponse = async (responseLike) => await responseLike;
11322
+ var cloneHeaders = (response) => {
11323
+ const headers = new Headers(response.headers);
11324
+ return headers;
11325
+ };
11326
+ var enhanceHtmlResponseWithStreamingSlots = (response, { nonce, onError, streamingSlots = [], policy } = {}) => {
11327
+ if (!response.body || streamingSlots.length === 0) {
11328
+ return response;
11329
+ }
11330
+ const body = appendStreamingSlotPatchesToStream(response.body, streamingSlots, {
11331
+ nonce,
11332
+ onError,
11333
+ policy
11334
+ });
11335
+ return new Response(body, {
11336
+ headers: cloneHeaders(response),
11337
+ status: response.status,
11338
+ statusText: response.statusText
11339
+ });
11340
+ };
11341
+ var withStreamingSlots = async (responseLike, options = {}) => enhanceHtmlResponseWithStreamingSlots(await toResponse(responseLike), options);
11342
+ var mergeStreamingSlots = (registered, explicit) => {
11343
+ const merged = new Map;
11344
+ for (const slot of registered)
11345
+ merged.set(slot.id, slot);
11346
+ for (const slot of explicit)
11347
+ merged.set(slot.id, slot);
11348
+ return [...merged.values()];
11349
+ };
11350
+ var withRegisteredStreamingSlots = async (renderResponse, options = {}) => {
11351
+ const { result, slots } = await runWithStreamingSlotRegistry(renderResponse);
11352
+ const explicit = options.streamingSlots ?? [];
11353
+ return withStreamingSlots(result, {
11354
+ ...options,
11355
+ streamingSlots: mergeStreamingSlots(slots, explicit)
11356
+ });
11357
+ };
11358
+
11312
11359
  // src/core/wrapPageHandlerWithStreamingSlots.ts
11313
- init_responseEnhancers();
11314
11360
  var wrapPageHandlerWithStreamingSlots = (handler) => (...args) => withRegisteredStreamingSlots(() => handler(...args));
11315
11361
 
11316
11362
  // src/angular/index.ts
@@ -11737,5 +11783,5 @@ export {
11737
11783
  DeferSlotComponent
11738
11784
  };
11739
11785
 
11740
- //# debugId=569922FFC27F43D464756E2164756E21
11786
+ //# debugId=ADFC4B4BF6E33FDD64756E2164756E21
11741
11787
  //# sourceMappingURL=index.js.map