@absolutejs/absolute 0.19.0-beta.366 → 0.19.0-beta.368
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/angular/browser.js +13 -8
- package/dist/angular/browser.js.map +4 -4
- package/dist/angular/components/core/streamingSlotRegistrar.js +4 -3
- package/dist/angular/components/core/streamingSlotRegistry.js +4 -3
- package/dist/angular/index.js +668 -622
- package/dist/angular/index.js.map +8 -8
- package/dist/build.js +24 -579
- package/dist/build.js.map +4 -10
- package/dist/client/index.js +45 -44
- package/dist/client/index.js.map +4 -4
- package/dist/index.js +1322 -1138
- package/dist/index.js.map +28 -22
- package/dist/react/components/browser/index.js +9 -20
- package/dist/react/components/index.js +17 -35
- package/dist/react/components/index.js.map +5 -6
- package/dist/react/index.js +1125 -1095
- package/dist/react/index.js.map +12 -12
- package/dist/react/server.js +3 -574
- package/dist/react/server.js.map +4 -11
- package/dist/src/core/pageHandlers.d.ts +1 -0
- package/dist/src/react/index.d.ts +1 -1
- package/dist/svelte/index.js +1506 -1460
- package/dist/svelte/index.js.map +8 -8
- package/dist/vue/components/index.js +13 -8
- package/dist/vue/components/index.js.map +5 -5
- package/dist/vue/index.js +667 -619
- package/dist/vue/index.js.map +8 -8
- package/package.json +1 -1
- package/dist/src/react/streamingSlotCollection.d.ts +0 -9
package/dist/vue/index.js
CHANGED
|
@@ -78,56 +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/core/streamingSlotRegistrar.ts
|
|
82
|
-
var registrar = null, setStreamingSlotRegistrar = (nextRegistrar) => {
|
|
83
|
-
registrar = nextRegistrar;
|
|
84
|
-
}, registerStreamingSlot = (slot) => {
|
|
85
|
-
registrar?.(slot);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// src/client/streamSwap.ts
|
|
89
|
-
var streamSwapRuntime = () => {
|
|
90
|
-
if (window.__ABS_SLOT_RUNTIME__ === true)
|
|
91
|
-
return;
|
|
92
|
-
window.__ABS_SLOT_RUNTIME__ = true;
|
|
93
|
-
window.__ABS_SLOT_PENDING__ = window.__ABS_SLOT_PENDING__ ?? {};
|
|
94
|
-
const pending = window.__ABS_SLOT_PENDING__;
|
|
95
|
-
const apply = (id, html) => {
|
|
96
|
-
const node = document.getElementById(`slot-${id}`);
|
|
97
|
-
if (!node) {
|
|
98
|
-
pending[id] = html;
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
node.innerHTML = html;
|
|
102
|
-
delete pending[id];
|
|
103
|
-
};
|
|
104
|
-
const flush = () => {
|
|
105
|
-
for (const id in pending) {
|
|
106
|
-
if (!Object.prototype.hasOwnProperty.call(pending, id))
|
|
107
|
-
continue;
|
|
108
|
-
apply(id, pending[id] ?? "");
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
window.__ABS_SLOT_ENQUEUE__ = (id, html) => {
|
|
112
|
-
apply(id, html);
|
|
113
|
-
};
|
|
114
|
-
if (typeof MutationObserver === "function") {
|
|
115
|
-
const observer = new MutationObserver(flush);
|
|
116
|
-
const root = document.documentElement ?? document.body ?? document;
|
|
117
|
-
observer.observe(root, { childList: true, subtree: true });
|
|
118
|
-
}
|
|
119
|
-
if (document.readyState === "loading") {
|
|
120
|
-
document.addEventListener("DOMContentLoaded", flush, { once: true });
|
|
121
|
-
}
|
|
122
|
-
flush();
|
|
123
|
-
}, stripFunctionWrapper = (value) => {
|
|
124
|
-
const start = value.indexOf("{");
|
|
125
|
-
const end = value.lastIndexOf("}");
|
|
126
|
-
if (start < 0 || end <= start)
|
|
127
|
-
return "";
|
|
128
|
-
return value.slice(start + 1, end);
|
|
129
|
-
}, getStreamSwapRuntimeScript = () => `(function(){${stripFunctionWrapper(streamSwapRuntime.toString())}})();`;
|
|
130
|
-
|
|
131
81
|
// src/utils/escapeScriptContent.ts
|
|
132
82
|
var ESCAPE_LOOKUP, ESCAPE_REGEX, escapeScriptContent = (content) => content.replace(ESCAPE_REGEX, (char) => {
|
|
133
83
|
const escaped = ESCAPE_LOOKUP[char];
|
|
@@ -144,253 +94,38 @@ var init_escapeScriptContent = __esm(() => {
|
|
|
144
94
|
ESCAPE_REGEX = /[&><\u2028\u2029]/g;
|
|
145
95
|
});
|
|
146
96
|
|
|
147
|
-
// src/
|
|
148
|
-
var
|
|
149
|
-
|
|
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>");
|
|
150
114
|
if (closingHeadIndex >= 0) {
|
|
151
|
-
return `${html.slice(0, closingHeadIndex)}${
|
|
152
|
-
}
|
|
153
|
-
return `${html}${injection}`;
|
|
154
|
-
}, toUint8 = (value, encoder) => encoder.encode(value), currentStreamingSlotPolicy, clonePolicy = (policy) => ({
|
|
155
|
-
...policy
|
|
156
|
-
}), normalizeSlotBytes = (value, fallback) => {
|
|
157
|
-
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
158
|
-
return Math.floor(value);
|
|
159
|
-
}
|
|
160
|
-
return fallback;
|
|
161
|
-
}, 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 = {}) => ({
|
|
162
|
-
timeoutMs: hasPolicyValue(overridePolicy, "timeoutMs") ? normalizeSlotBytes(overridePolicy.timeoutMs, base.timeoutMs) : base.timeoutMs,
|
|
163
|
-
fallbackHtml: hasPolicyValue(overridePolicy, "fallbackHtml") ? normalizeSlotText(overridePolicy.fallbackHtml, "") : base.fallbackHtml,
|
|
164
|
-
errorHtml: hasPolicyValue(overridePolicy, "errorHtml") ? normalizeSlotError(overridePolicy.errorHtml) : base.errorHtml,
|
|
165
|
-
maxSlotsPerResponse: hasPolicyValue(overridePolicy, "maxSlotsPerResponse") ? normalizeSlotBytes(overridePolicy.maxSlotsPerResponse, base.maxSlotsPerResponse) : base.maxSlotsPerResponse,
|
|
166
|
-
maxSlotHtmlSizeBytes: hasPolicyValue(overridePolicy, "maxSlotHtmlSizeBytes") ? normalizeSlotBytes(overridePolicy.maxSlotHtmlSizeBytes, base.maxSlotHtmlSizeBytes) : base.maxSlotHtmlSizeBytes,
|
|
167
|
-
onError: hasPolicyValue(overridePolicy, "onError") ? overridePolicy.onError : base.onError,
|
|
168
|
-
onSlotMetric: hasPolicyValue(overridePolicy, "onSlotMetric") ? overridePolicy.onSlotMetric : base.onSlotMetric
|
|
169
|
-
}), createCombinedSlotErrorHandler = (policyOnError, enhancerOnError) => {
|
|
170
|
-
if (!policyOnError && !enhancerOnError)
|
|
171
|
-
return;
|
|
172
|
-
return (error, slot) => {
|
|
173
|
-
policyOnError?.(error, slot);
|
|
174
|
-
enhancerOnError?.(error, slot);
|
|
175
|
-
};
|
|
176
|
-
}, createCombinedSlotMetricHandler = (policyOnSlotMetric, callOnSlotMetric) => {
|
|
177
|
-
if (!policyOnSlotMetric && !callOnSlotMetric)
|
|
178
|
-
return;
|
|
179
|
-
return (metric) => {
|
|
180
|
-
policyOnSlotMetric?.(metric);
|
|
181
|
-
callOnSlotMetric?.(metric);
|
|
182
|
-
};
|
|
183
|
-
}, resolveStreamingSlotPolicy = (overridePolicy = {}) => {
|
|
184
|
-
const base = getStreamingSlotPolicy();
|
|
185
|
-
return applyStreamingSlotPolicyOverrides(base, overridePolicy);
|
|
186
|
-
}, getStreamingSlotPolicy = () => clonePolicy(currentStreamingSlotPolicy), setStreamingSlotPolicy = (policy = {}) => {
|
|
187
|
-
const base = getStreamingSlotPolicy();
|
|
188
|
-
currentStreamingSlotPolicy = applyStreamingSlotPolicyOverrides(base, policy);
|
|
189
|
-
}, withStreamingSlotPolicy = async (policy, callback) => {
|
|
190
|
-
const previous = getStreamingSlotPolicy();
|
|
191
|
-
setStreamingSlotPolicy(policy);
|
|
192
|
-
try {
|
|
193
|
-
return await callback();
|
|
194
|
-
} finally {
|
|
195
|
-
currentStreamingSlotPolicy = previous;
|
|
196
|
-
}
|
|
197
|
-
}, emitSlotMetric = (metric, onSlotMetric) => {
|
|
198
|
-
onSlotMetric?.(metric);
|
|
199
|
-
}, createTimeoutError = (slot, timeoutMs) => {
|
|
200
|
-
const error = new Error(`Streaming slot "${slot.id}" timed out after ${timeoutMs}ms`);
|
|
201
|
-
error.__absTimeout = true;
|
|
202
|
-
return error;
|
|
203
|
-
}, toStreamingSlot = (slot, policy) => ({
|
|
204
|
-
errorHtml: slot.errorHtml === undefined ? policy.errorHtml : slot.errorHtml,
|
|
205
|
-
fallbackHtml: normalizeSlotText(slot.fallbackHtml, policy.fallbackHtml),
|
|
206
|
-
id: slot.id ?? createStreamingSlotId(),
|
|
207
|
-
timeoutMs: normalizeSlotBytes(slot.timeoutMs, policy.timeoutMs),
|
|
208
|
-
resolve: slot.resolve
|
|
209
|
-
}), prepareSlots = ({
|
|
210
|
-
policy,
|
|
211
|
-
slots,
|
|
212
|
-
onError,
|
|
213
|
-
onSlotMetric
|
|
214
|
-
}) => {
|
|
215
|
-
const preparedSlots = slots.map((slot) => toStreamingSlot(slot, policy));
|
|
216
|
-
const maxSlotsPerResponse = policy.maxSlotsPerResponse;
|
|
217
|
-
if (maxSlotsPerResponse === 0) {
|
|
218
|
-
const error = new Error("Streaming slot limit is set to 0");
|
|
219
|
-
for (const slot of preparedSlots) {
|
|
220
|
-
onError?.(error, slot);
|
|
221
|
-
emitSlotMetric({
|
|
222
|
-
type: "dropped",
|
|
223
|
-
slotId: slot.id,
|
|
224
|
-
reason: "maxSlotsPerResponse is 0"
|
|
225
|
-
}, onSlotMetric);
|
|
226
|
-
}
|
|
227
|
-
return [];
|
|
228
|
-
}
|
|
229
|
-
if (preparedSlots.length <= maxSlotsPerResponse) {
|
|
230
|
-
preparedSlots.forEach((slot) => emitSlotMetric({
|
|
231
|
-
type: "prepared",
|
|
232
|
-
slotId: slot.id
|
|
233
|
-
}, onSlotMetric));
|
|
234
|
-
return preparedSlots;
|
|
115
|
+
return `${html.slice(0, closingHeadIndex)}${markup}${html.slice(closingHeadIndex)}`;
|
|
235
116
|
}
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
type: "dropped",
|
|
242
|
-
slotId: slot.id,
|
|
243
|
-
reason: `maxSlotsPerResponse is ${maxSlotsPerResponse}`
|
|
244
|
-
}, onSlotMetric);
|
|
245
|
-
});
|
|
246
|
-
keptSlots.forEach((slot) => emitSlotMetric({
|
|
247
|
-
type: "prepared",
|
|
248
|
-
slotId: slot.id
|
|
249
|
-
}, onSlotMetric));
|
|
250
|
-
return keptSlots;
|
|
251
|
-
}, htmlByteLength = (value, encoder) => encoder.encode(value).length, resolveSlot = async (slot, onError, policy, onSlotMetric) => {
|
|
252
|
-
const safePolicy = policy ?? getStreamingSlotPolicy();
|
|
253
|
-
const encoder = new TextEncoder;
|
|
254
|
-
const start = Date.now();
|
|
255
|
-
try {
|
|
256
|
-
const maybeAsyncValue = Promise.resolve(slot.resolve());
|
|
257
|
-
const resolved = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
|
|
258
|
-
maybeAsyncValue,
|
|
259
|
-
new Promise((_, reject) => setTimeout(() => {
|
|
260
|
-
reject(createTimeoutError(slot, slot.timeoutMs ?? 0));
|
|
261
|
-
}, slot.timeoutMs))
|
|
262
|
-
]) : await maybeAsyncValue;
|
|
263
|
-
const html = typeof resolved === "string" ? resolved : `${resolved}`;
|
|
264
|
-
if (safePolicy.maxSlotHtmlSizeBytes > 0 && htmlByteLength(html, encoder) > safePolicy.maxSlotHtmlSizeBytes) {
|
|
265
|
-
const bytes2 = htmlByteLength(html, encoder);
|
|
266
|
-
const error = new Error(`Streaming slot "${slot.id}" exceeded max payload size of ${safePolicy.maxSlotHtmlSizeBytes} bytes`);
|
|
267
|
-
const durationMs2 = Date.now() - start;
|
|
268
|
-
onError?.(error, slot);
|
|
269
|
-
emitSlotMetric({
|
|
270
|
-
type: "size_exceeded",
|
|
271
|
-
slotId: slot.id,
|
|
272
|
-
durationMs: durationMs2,
|
|
273
|
-
bytes: bytes2,
|
|
274
|
-
error
|
|
275
|
-
}, onSlotMetric);
|
|
276
|
-
const fallbackHtml = typeof slot.errorHtml === "string" ? slot.errorHtml : null;
|
|
277
|
-
return {
|
|
278
|
-
html: fallbackHtml,
|
|
279
|
-
id: slot.id,
|
|
280
|
-
durationMs: durationMs2,
|
|
281
|
-
bytes: fallbackHtml === null ? 0 : htmlByteLength(fallbackHtml, encoder)
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
const durationMs = Date.now() - start;
|
|
285
|
-
const bytes = htmlByteLength(html, encoder);
|
|
286
|
-
emitSlotMetric({
|
|
287
|
-
type: "resolved",
|
|
288
|
-
slotId: slot.id,
|
|
289
|
-
durationMs,
|
|
290
|
-
bytes
|
|
291
|
-
}, onSlotMetric);
|
|
292
|
-
return {
|
|
293
|
-
html,
|
|
294
|
-
id: slot.id,
|
|
295
|
-
durationMs,
|
|
296
|
-
bytes
|
|
297
|
-
};
|
|
298
|
-
} catch (error) {
|
|
299
|
-
const durationMs = Date.now() - start;
|
|
300
|
-
onError?.(error, slot);
|
|
301
|
-
emitSlotMetric({
|
|
302
|
-
type: error?.__absTimeout === true ? "timeout" : "error",
|
|
303
|
-
slotId: slot.id,
|
|
304
|
-
durationMs,
|
|
305
|
-
error
|
|
306
|
-
}, onSlotMetric);
|
|
307
|
-
if (typeof slot.errorHtml === "string") {
|
|
308
|
-
const html = slot.errorHtml;
|
|
309
|
-
return {
|
|
310
|
-
html,
|
|
311
|
-
id: slot.id,
|
|
312
|
-
durationMs,
|
|
313
|
-
bytes: htmlByteLength(html, encoder)
|
|
314
|
-
};
|
|
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)}`;
|
|
315
122
|
}
|
|
316
|
-
return {
|
|
317
|
-
html: null,
|
|
318
|
-
id: slot.id,
|
|
319
|
-
durationMs,
|
|
320
|
-
bytes: 0
|
|
321
|
-
};
|
|
322
123
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
original: promise,
|
|
326
|
-
result
|
|
327
|
-
})));
|
|
328
|
-
return Promise.race(wrapped);
|
|
329
|
-
}, streamChunkToString = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true }), streamOutOfOrderSlots = ({
|
|
330
|
-
footerHtml = "",
|
|
331
|
-
headerHtml = "",
|
|
332
|
-
nonce,
|
|
333
|
-
policy,
|
|
334
|
-
onSlotMetric,
|
|
335
|
-
onError,
|
|
336
|
-
slots
|
|
337
|
-
}) => {
|
|
338
|
-
const resolvedPolicy = resolveStreamingSlotPolicy(policy);
|
|
339
|
-
const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
|
|
340
|
-
const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
|
|
341
|
-
const effectivePolicy = {
|
|
342
|
-
...resolvedPolicy,
|
|
343
|
-
onSlotMetric: combinedOnSlotMetric
|
|
344
|
-
};
|
|
345
|
-
const preparedSlots = prepareSlots({
|
|
346
|
-
policy: effectivePolicy,
|
|
347
|
-
slots,
|
|
348
|
-
onError: combinedOnError,
|
|
349
|
-
onSlotMetric: combinedOnSlotMetric
|
|
350
|
-
});
|
|
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) => {
|
|
351
126
|
const encoder = new TextEncoder;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
try {
|
|
355
|
-
let header = headerHtml;
|
|
356
|
-
if (preparedSlots.length > 0 && !header.includes(STREAMING_RUNTIME_GLOBAL)) {
|
|
357
|
-
header = injectHtmlIntoHead(header, renderStreamingSlotsRuntimeTag(nonce));
|
|
358
|
-
}
|
|
359
|
-
controller.enqueue(toUint8(header, encoder));
|
|
360
|
-
const pending = preparedSlots.map((slot) => {
|
|
361
|
-
const fallback = renderStreamingSlotPlaceholder(slot.id, slot.fallbackHtml ?? "");
|
|
362
|
-
controller.enqueue(toUint8(fallback, encoder));
|
|
363
|
-
return resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric);
|
|
364
|
-
});
|
|
365
|
-
while (pending.length > 0) {
|
|
366
|
-
const { original, result } = await nextResolvedSlot(pending);
|
|
367
|
-
const index = pending.indexOf(original);
|
|
368
|
-
if (index >= 0)
|
|
369
|
-
pending.splice(index, 1);
|
|
370
|
-
if (result.html === null)
|
|
371
|
-
continue;
|
|
372
|
-
emitSlotMetric({
|
|
373
|
-
type: "patched",
|
|
374
|
-
slotId: result.id,
|
|
375
|
-
durationMs: result.durationMs,
|
|
376
|
-
bytes: result.bytes
|
|
377
|
-
}, combinedOnSlotMetric);
|
|
378
|
-
controller.enqueue(toUint8(renderStreamingSlotPatchTag(result.id, result.html, nonce), encoder));
|
|
379
|
-
}
|
|
380
|
-
if (footerHtml.length > 0) {
|
|
381
|
-
controller.enqueue(toUint8(footerHtml, encoder));
|
|
382
|
-
}
|
|
383
|
-
controller.close();
|
|
384
|
-
} catch (error) {
|
|
385
|
-
controller.error(error);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
}, injectStreamingRuntimeIntoStream = (stream, nonce) => {
|
|
390
|
-
const runtimeTag = renderStreamingSlotsRuntimeTag(nonce);
|
|
391
|
-
const encoder = new TextEncoder;
|
|
392
|
-
const decoder = new TextDecoder;
|
|
393
|
-
const lookbehind = CLOSING_HEAD_TAG_LENGTH - 1;
|
|
127
|
+
const decoder = new TextDecoder;
|
|
128
|
+
const lookbehind = CLOSING_HEAD_TAG2.length - 1;
|
|
394
129
|
return new ReadableStream({
|
|
395
130
|
async start(controller) {
|
|
396
131
|
const reader = stream.getReader();
|
|
@@ -403,16 +138,16 @@ var SLOT_ID_PREFIX = "abs-slot-", SLOT_PLACEHOLDER_PREFIX = "slot-", CLOSING_HEA
|
|
|
403
138
|
break;
|
|
404
139
|
if (!value)
|
|
405
140
|
continue;
|
|
406
|
-
pending +=
|
|
141
|
+
pending += streamChunkToString2(value, decoder);
|
|
407
142
|
if (injected) {
|
|
408
143
|
controller.enqueue(encoder.encode(pending));
|
|
409
144
|
pending = "";
|
|
410
145
|
continue;
|
|
411
146
|
}
|
|
412
|
-
const headIndex = pending.indexOf(
|
|
147
|
+
const headIndex = pending.indexOf(CLOSING_HEAD_TAG2);
|
|
413
148
|
if (headIndex >= 0) {
|
|
414
|
-
const
|
|
415
|
-
controller.enqueue(encoder.encode(
|
|
149
|
+
const next = `${pending.slice(0, headIndex)}${markup}${pending.slice(headIndex)}`;
|
|
150
|
+
controller.enqueue(encoder.encode(next));
|
|
416
151
|
pending = "";
|
|
417
152
|
injected = true;
|
|
418
153
|
continue;
|
|
@@ -425,7 +160,7 @@ var SLOT_ID_PREFIX = "abs-slot-", SLOT_PLACEHOLDER_PREFIX = "slot-", CLOSING_HEA
|
|
|
425
160
|
}
|
|
426
161
|
pending += decoder.decode();
|
|
427
162
|
if (!injected) {
|
|
428
|
-
pending =
|
|
163
|
+
pending = injectHeadMarkup(pending, markup);
|
|
429
164
|
}
|
|
430
165
|
if (pending.length > 0) {
|
|
431
166
|
controller.enqueue(encoder.encode(pending));
|
|
@@ -436,345 +171,78 @@ var SLOT_ID_PREFIX = "abs-slot-", SLOT_PLACEHOLDER_PREFIX = "slot-", CLOSING_HEA
|
|
|
436
171
|
}
|
|
437
172
|
}
|
|
438
173
|
});
|
|
439
|
-
},
|
|
440
|
-
injectRuntime = true,
|
|
441
|
-
nonce,
|
|
442
|
-
onError,
|
|
443
|
-
onSlotMetric,
|
|
444
|
-
policy
|
|
445
|
-
} = {}) => {
|
|
446
|
-
const resolvedPolicy = resolveStreamingSlotPolicy(policy);
|
|
447
|
-
const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
|
|
448
|
-
const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
|
|
449
|
-
const effectivePolicy = {
|
|
450
|
-
...resolvedPolicy,
|
|
451
|
-
onSlotMetric: combinedOnSlotMetric
|
|
452
|
-
};
|
|
453
|
-
const preparedSlots = prepareSlots({
|
|
454
|
-
policy: effectivePolicy,
|
|
455
|
-
slots,
|
|
456
|
-
onError: combinedOnError,
|
|
457
|
-
onSlotMetric: combinedOnSlotMetric
|
|
458
|
-
});
|
|
459
|
-
if (preparedSlots.length === 0)
|
|
460
|
-
return stream;
|
|
461
|
-
const source = injectRuntime ? injectStreamingRuntimeIntoStream(stream, nonce) : stream;
|
|
174
|
+
}, pipeStreamWithIslandMarkerDetection = (stream, markup) => {
|
|
462
175
|
const encoder = new TextEncoder;
|
|
463
176
|
const decoder = new TextDecoder;
|
|
464
|
-
const
|
|
465
|
-
const pending = preparedSlots.map((slot) => resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric));
|
|
177
|
+
const lookbehind = Math.max(ISLAND_MARKER.length, 1024);
|
|
466
178
|
return new ReadableStream({
|
|
467
179
|
async start(controller) {
|
|
468
|
-
|
|
469
|
-
let
|
|
470
|
-
let
|
|
471
|
-
let footer = "";
|
|
180
|
+
const reader = stream.getReader();
|
|
181
|
+
let injected = false;
|
|
182
|
+
let pending = "";
|
|
472
183
|
try {
|
|
473
|
-
|
|
474
|
-
const
|
|
475
|
-
if (
|
|
476
|
-
racers.push(baseRead.then(({ done, value }) => ({
|
|
477
|
-
done,
|
|
478
|
-
kind: "base",
|
|
479
|
-
value
|
|
480
|
-
})));
|
|
481
|
-
}
|
|
482
|
-
if (pending.length > 0) {
|
|
483
|
-
racers.push(nextResolvedSlot(pending).then((resolved) => ({
|
|
484
|
-
kind: "slot",
|
|
485
|
-
...resolved
|
|
486
|
-
})));
|
|
487
|
-
}
|
|
488
|
-
if (racers.length === 0)
|
|
184
|
+
for (;; ) {
|
|
185
|
+
const { done, value } = await reader.read();
|
|
186
|
+
if (done)
|
|
489
187
|
break;
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
if (footerStart >= 0) {
|
|
497
|
-
const content = tail.slice(0, footerStart);
|
|
498
|
-
footer = tail.slice(footerStart);
|
|
499
|
-
if (content.length > 0) {
|
|
500
|
-
controller.enqueue(encoder.encode(content));
|
|
501
|
-
}
|
|
502
|
-
} else if (tail.length > 0) {
|
|
503
|
-
controller.enqueue(encoder.encode(tail));
|
|
504
|
-
}
|
|
505
|
-
tail = "";
|
|
506
|
-
} else if (winner.value) {
|
|
507
|
-
tail += streamChunkToString(winner.value, decoder);
|
|
508
|
-
if (tail.length > STREAM_TAIL_LOOKBEHIND) {
|
|
509
|
-
const content = tail.slice(0, tail.length - STREAM_TAIL_LOOKBEHIND);
|
|
510
|
-
controller.enqueue(encoder.encode(content));
|
|
511
|
-
tail = tail.slice(-STREAM_TAIL_LOOKBEHIND);
|
|
512
|
-
}
|
|
513
|
-
baseRead = reader.read();
|
|
514
|
-
}
|
|
188
|
+
if (!value)
|
|
189
|
+
continue;
|
|
190
|
+
pending += streamChunkToString2(value, decoder);
|
|
191
|
+
if (injected) {
|
|
192
|
+
controller.enqueue(encoder.encode(pending));
|
|
193
|
+
pending = "";
|
|
515
194
|
continue;
|
|
516
195
|
}
|
|
517
|
-
const
|
|
518
|
-
if (
|
|
519
|
-
pending.
|
|
520
|
-
|
|
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;
|
|
521
204
|
continue;
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
|
|
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));
|
|
529
215
|
}
|
|
530
|
-
if (footer.length > 0)
|
|
531
|
-
controller.enqueue(encoder.encode(footer));
|
|
532
216
|
controller.close();
|
|
533
217
|
} catch (error) {
|
|
534
218
|
controller.error(error);
|
|
535
219
|
}
|
|
536
220
|
}
|
|
537
221
|
});
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
currentStreamingSlotPolicy = {
|
|
544
|
-
timeoutMs: STREAMING_SLOT_TIMEOUT_MS,
|
|
545
|
-
fallbackHtml: "",
|
|
546
|
-
errorHtml: undefined,
|
|
547
|
-
maxSlotsPerResponse: STREAMING_SLOT_MAX_PER_RESPONSE,
|
|
548
|
-
maxSlotHtmlSizeBytes: STREAMING_SLOT_MAX_HTML_BYTES
|
|
549
|
-
};
|
|
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
|
-
|
|
561
|
-
|
|
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
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
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
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
|
@@ -2106,6 +1574,18 @@ import { defineComponent as defineComponent2, h as h2 } from "vue";
|
|
|
2106
1574
|
|
|
2107
1575
|
// src/vue/components/StreamSlot.ts
|
|
2108
1576
|
import { defineComponent, h } from "vue";
|
|
1577
|
+
|
|
1578
|
+
// src/core/streamingSlotRegistrar.ts
|
|
1579
|
+
var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
|
|
1580
|
+
var getRegistrarGlobal = () => globalThis;
|
|
1581
|
+
var setStreamingSlotRegistrar = (nextRegistrar) => {
|
|
1582
|
+
getRegistrarGlobal()[STREAMING_SLOT_REGISTRAR_KEY] = nextRegistrar;
|
|
1583
|
+
};
|
|
1584
|
+
var registerStreamingSlot = (slot) => {
|
|
1585
|
+
getRegistrarGlobal()[STREAMING_SLOT_REGISTRAR_KEY]?.(slot);
|
|
1586
|
+
};
|
|
1587
|
+
|
|
1588
|
+
// src/vue/components/StreamSlot.ts
|
|
2109
1589
|
var StreamSlot = defineComponent({
|
|
2110
1590
|
name: "AbsoluteStreamSlot",
|
|
2111
1591
|
props: {
|
|
@@ -2156,8 +1636,576 @@ var SuspenseSlot = defineComponent2({
|
|
|
2156
1636
|
return () => h2(StreamSlot, props);
|
|
2157
1637
|
}
|
|
2158
1638
|
});
|
|
1639
|
+
// src/client/streamSwap.ts
|
|
1640
|
+
var streamSwapRuntime = () => {
|
|
1641
|
+
if (window.__ABS_SLOT_RUNTIME__ === true)
|
|
1642
|
+
return;
|
|
1643
|
+
window.__ABS_SLOT_RUNTIME__ = true;
|
|
1644
|
+
window.__ABS_SLOT_PENDING__ = window.__ABS_SLOT_PENDING__ ?? {};
|
|
1645
|
+
const pending = window.__ABS_SLOT_PENDING__;
|
|
1646
|
+
const apply = (id, html) => {
|
|
1647
|
+
const node = document.getElementById(`slot-${id}`);
|
|
1648
|
+
if (!node) {
|
|
1649
|
+
pending[id] = html;
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
node.innerHTML = html;
|
|
1653
|
+
delete pending[id];
|
|
1654
|
+
};
|
|
1655
|
+
const flush = () => {
|
|
1656
|
+
for (const id in pending) {
|
|
1657
|
+
if (!Object.prototype.hasOwnProperty.call(pending, id))
|
|
1658
|
+
continue;
|
|
1659
|
+
apply(id, pending[id] ?? "");
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
window.__ABS_SLOT_ENQUEUE__ = (id, html) => {
|
|
1663
|
+
apply(id, html);
|
|
1664
|
+
};
|
|
1665
|
+
if (typeof MutationObserver === "function") {
|
|
1666
|
+
const observer = new MutationObserver(flush);
|
|
1667
|
+
const root = document.documentElement ?? document.body ?? document;
|
|
1668
|
+
observer.observe(root, { childList: true, subtree: true });
|
|
1669
|
+
}
|
|
1670
|
+
if (document.readyState === "loading") {
|
|
1671
|
+
document.addEventListener("DOMContentLoaded", flush, { once: true });
|
|
1672
|
+
}
|
|
1673
|
+
flush();
|
|
1674
|
+
};
|
|
1675
|
+
var stripFunctionWrapper = (value) => {
|
|
1676
|
+
const start = value.indexOf("{");
|
|
1677
|
+
const end = value.lastIndexOf("}");
|
|
1678
|
+
if (start < 0 || end <= start)
|
|
1679
|
+
return "";
|
|
1680
|
+
return value.slice(start + 1, end);
|
|
1681
|
+
};
|
|
1682
|
+
var getStreamSwapRuntimeScript = () => `(function(){${stripFunctionWrapper(streamSwapRuntime.toString())}})();`;
|
|
1683
|
+
|
|
1684
|
+
// src/utils/streamingSlots.ts
|
|
1685
|
+
init_escapeScriptContent();
|
|
1686
|
+
var SLOT_ID_PREFIX = "abs-slot-";
|
|
1687
|
+
var SLOT_PLACEHOLDER_PREFIX = "slot-";
|
|
1688
|
+
var CLOSING_HEAD_TAG = "</head>";
|
|
1689
|
+
var CLOSING_HEAD_TAG_LENGTH = CLOSING_HEAD_TAG.length;
|
|
1690
|
+
var CLOSING_PAGE_TAG_REGEX = /<\/body>\s*<\/html>\s*$/i;
|
|
1691
|
+
var STREAMING_RUNTIME_GLOBAL = "__ABS_SLOT_ENQUEUE__";
|
|
1692
|
+
var STREAMING_PENDING_GLOBAL = "__ABS_SLOT_PENDING__";
|
|
1693
|
+
var STREAM_TAIL_LOOKBEHIND = 128;
|
|
1694
|
+
var STREAMING_SLOT_TIMEOUT_MS = 5000;
|
|
1695
|
+
var STREAMING_SLOT_MAX_PER_RESPONSE = 128;
|
|
1696
|
+
var STREAMING_SLOT_MAX_HTML_BYTES = 64000;
|
|
1697
|
+
var createSlotPlaceholderId = (id) => `${SLOT_PLACEHOLDER_PREFIX}${id}`;
|
|
1698
|
+
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)});`;
|
|
1699
|
+
var createNonceAttr = (nonce) => nonce ? ` nonce="${nonce}"` : "";
|
|
1700
|
+
var createStreamingSlotId = () => `${SLOT_ID_PREFIX}${Math.random().toString(36).slice(2, 10)}`;
|
|
1701
|
+
var getStreamingSlotsRuntimeScript = () => getStreamSwapRuntimeScript();
|
|
1702
|
+
var renderStreamingSlotsRuntimeTag = (nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(getStreamingSlotsRuntimeScript())}</script>`;
|
|
1703
|
+
var renderStreamingSlotPlaceholder = (id, fallbackHtml = "") => `<div id="${createSlotPlaceholderId(id)}" data-absolute-slot="true">${fallbackHtml}</div>`;
|
|
1704
|
+
var renderStreamingSlotPatchTag = (id, html, nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(createSlotPatchStatement(id, html))}</script>`;
|
|
1705
|
+
var injectHtmlIntoHead = (html, injection) => {
|
|
1706
|
+
const closingHeadIndex = html.indexOf(CLOSING_HEAD_TAG);
|
|
1707
|
+
if (closingHeadIndex >= 0) {
|
|
1708
|
+
return `${html.slice(0, closingHeadIndex)}${injection}${html.slice(closingHeadIndex)}`;
|
|
1709
|
+
}
|
|
1710
|
+
return `${html}${injection}`;
|
|
1711
|
+
};
|
|
1712
|
+
var toUint8 = (value, encoder) => encoder.encode(value);
|
|
1713
|
+
var currentStreamingSlotPolicy = {
|
|
1714
|
+
timeoutMs: STREAMING_SLOT_TIMEOUT_MS,
|
|
1715
|
+
fallbackHtml: "",
|
|
1716
|
+
errorHtml: undefined,
|
|
1717
|
+
maxSlotsPerResponse: STREAMING_SLOT_MAX_PER_RESPONSE,
|
|
1718
|
+
maxSlotHtmlSizeBytes: STREAMING_SLOT_MAX_HTML_BYTES
|
|
1719
|
+
};
|
|
1720
|
+
var clonePolicy = (policy) => ({
|
|
1721
|
+
...policy
|
|
1722
|
+
});
|
|
1723
|
+
var normalizeSlotBytes = (value, fallback) => {
|
|
1724
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
1725
|
+
return Math.floor(value);
|
|
1726
|
+
}
|
|
1727
|
+
return fallback;
|
|
1728
|
+
};
|
|
1729
|
+
var normalizeSlotText = (value, fallback) => typeof value === "string" ? value : fallback;
|
|
1730
|
+
var normalizeSlotError = (value, fallback) => typeof value === "string" ? value : fallback;
|
|
1731
|
+
var hasPolicyValue = (policy, key) => Object.prototype.hasOwnProperty.call(policy, key);
|
|
1732
|
+
var applyStreamingSlotPolicyOverrides = (base, overridePolicy = {}) => ({
|
|
1733
|
+
timeoutMs: hasPolicyValue(overridePolicy, "timeoutMs") ? normalizeSlotBytes(overridePolicy.timeoutMs, base.timeoutMs) : base.timeoutMs,
|
|
1734
|
+
fallbackHtml: hasPolicyValue(overridePolicy, "fallbackHtml") ? normalizeSlotText(overridePolicy.fallbackHtml, "") : base.fallbackHtml,
|
|
1735
|
+
errorHtml: hasPolicyValue(overridePolicy, "errorHtml") ? normalizeSlotError(overridePolicy.errorHtml) : base.errorHtml,
|
|
1736
|
+
maxSlotsPerResponse: hasPolicyValue(overridePolicy, "maxSlotsPerResponse") ? normalizeSlotBytes(overridePolicy.maxSlotsPerResponse, base.maxSlotsPerResponse) : base.maxSlotsPerResponse,
|
|
1737
|
+
maxSlotHtmlSizeBytes: hasPolicyValue(overridePolicy, "maxSlotHtmlSizeBytes") ? normalizeSlotBytes(overridePolicy.maxSlotHtmlSizeBytes, base.maxSlotHtmlSizeBytes) : base.maxSlotHtmlSizeBytes,
|
|
1738
|
+
onError: hasPolicyValue(overridePolicy, "onError") ? overridePolicy.onError : base.onError,
|
|
1739
|
+
onSlotMetric: hasPolicyValue(overridePolicy, "onSlotMetric") ? overridePolicy.onSlotMetric : base.onSlotMetric
|
|
1740
|
+
});
|
|
1741
|
+
var createCombinedSlotErrorHandler = (policyOnError, enhancerOnError) => {
|
|
1742
|
+
if (!policyOnError && !enhancerOnError)
|
|
1743
|
+
return;
|
|
1744
|
+
return (error, slot) => {
|
|
1745
|
+
policyOnError?.(error, slot);
|
|
1746
|
+
enhancerOnError?.(error, slot);
|
|
1747
|
+
};
|
|
1748
|
+
};
|
|
1749
|
+
var createCombinedSlotMetricHandler = (policyOnSlotMetric, callOnSlotMetric) => {
|
|
1750
|
+
if (!policyOnSlotMetric && !callOnSlotMetric)
|
|
1751
|
+
return;
|
|
1752
|
+
return (metric) => {
|
|
1753
|
+
policyOnSlotMetric?.(metric);
|
|
1754
|
+
callOnSlotMetric?.(metric);
|
|
1755
|
+
};
|
|
1756
|
+
};
|
|
1757
|
+
var resolveStreamingSlotPolicy = (overridePolicy = {}) => {
|
|
1758
|
+
const base = getStreamingSlotPolicy();
|
|
1759
|
+
return applyStreamingSlotPolicyOverrides(base, overridePolicy);
|
|
1760
|
+
};
|
|
1761
|
+
var getStreamingSlotPolicy = () => clonePolicy(currentStreamingSlotPolicy);
|
|
1762
|
+
var setStreamingSlotPolicy = (policy = {}) => {
|
|
1763
|
+
const base = getStreamingSlotPolicy();
|
|
1764
|
+
currentStreamingSlotPolicy = applyStreamingSlotPolicyOverrides(base, policy);
|
|
1765
|
+
};
|
|
1766
|
+
var withStreamingSlotPolicy = async (policy, callback) => {
|
|
1767
|
+
const previous = getStreamingSlotPolicy();
|
|
1768
|
+
setStreamingSlotPolicy(policy);
|
|
1769
|
+
try {
|
|
1770
|
+
return await callback();
|
|
1771
|
+
} finally {
|
|
1772
|
+
currentStreamingSlotPolicy = previous;
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
var emitSlotMetric = (metric, onSlotMetric) => {
|
|
1776
|
+
onSlotMetric?.(metric);
|
|
1777
|
+
};
|
|
1778
|
+
var createTimeoutError = (slot, timeoutMs) => {
|
|
1779
|
+
const error = new Error(`Streaming slot "${slot.id}" timed out after ${timeoutMs}ms`);
|
|
1780
|
+
error.__absTimeout = true;
|
|
1781
|
+
return error;
|
|
1782
|
+
};
|
|
1783
|
+
var toStreamingSlot = (slot, policy) => ({
|
|
1784
|
+
errorHtml: slot.errorHtml === undefined ? policy.errorHtml : slot.errorHtml,
|
|
1785
|
+
fallbackHtml: normalizeSlotText(slot.fallbackHtml, policy.fallbackHtml),
|
|
1786
|
+
id: slot.id ?? createStreamingSlotId(),
|
|
1787
|
+
timeoutMs: normalizeSlotBytes(slot.timeoutMs, policy.timeoutMs),
|
|
1788
|
+
resolve: slot.resolve
|
|
1789
|
+
});
|
|
1790
|
+
var prepareSlots = ({
|
|
1791
|
+
policy,
|
|
1792
|
+
slots,
|
|
1793
|
+
onError,
|
|
1794
|
+
onSlotMetric
|
|
1795
|
+
}) => {
|
|
1796
|
+
const preparedSlots = slots.map((slot) => toStreamingSlot(slot, policy));
|
|
1797
|
+
const maxSlotsPerResponse = policy.maxSlotsPerResponse;
|
|
1798
|
+
if (maxSlotsPerResponse === 0) {
|
|
1799
|
+
const error = new Error("Streaming slot limit is set to 0");
|
|
1800
|
+
for (const slot of preparedSlots) {
|
|
1801
|
+
onError?.(error, slot);
|
|
1802
|
+
emitSlotMetric({
|
|
1803
|
+
type: "dropped",
|
|
1804
|
+
slotId: slot.id,
|
|
1805
|
+
reason: "maxSlotsPerResponse is 0"
|
|
1806
|
+
}, onSlotMetric);
|
|
1807
|
+
}
|
|
1808
|
+
return [];
|
|
1809
|
+
}
|
|
1810
|
+
if (preparedSlots.length <= maxSlotsPerResponse) {
|
|
1811
|
+
preparedSlots.forEach((slot) => emitSlotMetric({
|
|
1812
|
+
type: "prepared",
|
|
1813
|
+
slotId: slot.id
|
|
1814
|
+
}, onSlotMetric));
|
|
1815
|
+
return preparedSlots;
|
|
1816
|
+
}
|
|
1817
|
+
const keptSlots = preparedSlots.slice(0, maxSlotsPerResponse);
|
|
1818
|
+
const droppedSlots = preparedSlots.slice(maxSlotsPerResponse);
|
|
1819
|
+
droppedSlots.forEach((slot) => {
|
|
1820
|
+
onError?.(new Error(`Streaming slot "${slot.id}" dropped because ${maxSlotsPerResponse} slots is the configured maximum`), slot);
|
|
1821
|
+
emitSlotMetric({
|
|
1822
|
+
type: "dropped",
|
|
1823
|
+
slotId: slot.id,
|
|
1824
|
+
reason: `maxSlotsPerResponse is ${maxSlotsPerResponse}`
|
|
1825
|
+
}, onSlotMetric);
|
|
1826
|
+
});
|
|
1827
|
+
keptSlots.forEach((slot) => emitSlotMetric({
|
|
1828
|
+
type: "prepared",
|
|
1829
|
+
slotId: slot.id
|
|
1830
|
+
}, onSlotMetric));
|
|
1831
|
+
return keptSlots;
|
|
1832
|
+
};
|
|
1833
|
+
var htmlByteLength = (value, encoder) => encoder.encode(value).length;
|
|
1834
|
+
var resolveSlot = async (slot, onError, policy, onSlotMetric) => {
|
|
1835
|
+
const safePolicy = policy ?? getStreamingSlotPolicy();
|
|
1836
|
+
const encoder = new TextEncoder;
|
|
1837
|
+
const start = Date.now();
|
|
1838
|
+
try {
|
|
1839
|
+
const maybeAsyncValue = Promise.resolve(slot.resolve());
|
|
1840
|
+
const resolved = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
|
|
1841
|
+
maybeAsyncValue,
|
|
1842
|
+
new Promise((_, reject) => setTimeout(() => {
|
|
1843
|
+
reject(createTimeoutError(slot, slot.timeoutMs ?? 0));
|
|
1844
|
+
}, slot.timeoutMs))
|
|
1845
|
+
]) : await maybeAsyncValue;
|
|
1846
|
+
const html = typeof resolved === "string" ? resolved : `${resolved}`;
|
|
1847
|
+
if (safePolicy.maxSlotHtmlSizeBytes > 0 && htmlByteLength(html, encoder) > safePolicy.maxSlotHtmlSizeBytes) {
|
|
1848
|
+
const bytes2 = htmlByteLength(html, encoder);
|
|
1849
|
+
const error = new Error(`Streaming slot "${slot.id}" exceeded max payload size of ${safePolicy.maxSlotHtmlSizeBytes} bytes`);
|
|
1850
|
+
const durationMs2 = Date.now() - start;
|
|
1851
|
+
onError?.(error, slot);
|
|
1852
|
+
emitSlotMetric({
|
|
1853
|
+
type: "size_exceeded",
|
|
1854
|
+
slotId: slot.id,
|
|
1855
|
+
durationMs: durationMs2,
|
|
1856
|
+
bytes: bytes2,
|
|
1857
|
+
error
|
|
1858
|
+
}, onSlotMetric);
|
|
1859
|
+
const fallbackHtml = typeof slot.errorHtml === "string" ? slot.errorHtml : null;
|
|
1860
|
+
return {
|
|
1861
|
+
html: fallbackHtml,
|
|
1862
|
+
id: slot.id,
|
|
1863
|
+
durationMs: durationMs2,
|
|
1864
|
+
bytes: fallbackHtml === null ? 0 : htmlByteLength(fallbackHtml, encoder)
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
const durationMs = Date.now() - start;
|
|
1868
|
+
const bytes = htmlByteLength(html, encoder);
|
|
1869
|
+
emitSlotMetric({
|
|
1870
|
+
type: "resolved",
|
|
1871
|
+
slotId: slot.id,
|
|
1872
|
+
durationMs,
|
|
1873
|
+
bytes
|
|
1874
|
+
}, onSlotMetric);
|
|
1875
|
+
return {
|
|
1876
|
+
html,
|
|
1877
|
+
id: slot.id,
|
|
1878
|
+
durationMs,
|
|
1879
|
+
bytes
|
|
1880
|
+
};
|
|
1881
|
+
} catch (error) {
|
|
1882
|
+
const durationMs = Date.now() - start;
|
|
1883
|
+
onError?.(error, slot);
|
|
1884
|
+
emitSlotMetric({
|
|
1885
|
+
type: error?.__absTimeout === true ? "timeout" : "error",
|
|
1886
|
+
slotId: slot.id,
|
|
1887
|
+
durationMs,
|
|
1888
|
+
error
|
|
1889
|
+
}, onSlotMetric);
|
|
1890
|
+
if (typeof slot.errorHtml === "string") {
|
|
1891
|
+
const html = slot.errorHtml;
|
|
1892
|
+
return {
|
|
1893
|
+
html,
|
|
1894
|
+
id: slot.id,
|
|
1895
|
+
durationMs,
|
|
1896
|
+
bytes: htmlByteLength(html, encoder)
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
return {
|
|
1900
|
+
html: null,
|
|
1901
|
+
id: slot.id,
|
|
1902
|
+
durationMs,
|
|
1903
|
+
bytes: 0
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
};
|
|
1907
|
+
var nextResolvedSlot = async (pending) => {
|
|
1908
|
+
const wrapped = pending.map((promise) => promise.then((result) => ({
|
|
1909
|
+
original: promise,
|
|
1910
|
+
result
|
|
1911
|
+
})));
|
|
1912
|
+
return Promise.race(wrapped);
|
|
1913
|
+
};
|
|
1914
|
+
var streamChunkToString = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true });
|
|
1915
|
+
var streamOutOfOrderSlots = ({
|
|
1916
|
+
footerHtml = "",
|
|
1917
|
+
headerHtml = "",
|
|
1918
|
+
nonce,
|
|
1919
|
+
policy,
|
|
1920
|
+
onSlotMetric,
|
|
1921
|
+
onError,
|
|
1922
|
+
slots
|
|
1923
|
+
}) => {
|
|
1924
|
+
const resolvedPolicy = resolveStreamingSlotPolicy(policy);
|
|
1925
|
+
const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
|
|
1926
|
+
const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
|
|
1927
|
+
const effectivePolicy = {
|
|
1928
|
+
...resolvedPolicy,
|
|
1929
|
+
onSlotMetric: combinedOnSlotMetric
|
|
1930
|
+
};
|
|
1931
|
+
const preparedSlots = prepareSlots({
|
|
1932
|
+
policy: effectivePolicy,
|
|
1933
|
+
slots,
|
|
1934
|
+
onError: combinedOnError,
|
|
1935
|
+
onSlotMetric: combinedOnSlotMetric
|
|
1936
|
+
});
|
|
1937
|
+
const encoder = new TextEncoder;
|
|
1938
|
+
return new ReadableStream({
|
|
1939
|
+
async start(controller) {
|
|
1940
|
+
try {
|
|
1941
|
+
let header = headerHtml;
|
|
1942
|
+
if (preparedSlots.length > 0 && !header.includes(STREAMING_RUNTIME_GLOBAL)) {
|
|
1943
|
+
header = injectHtmlIntoHead(header, renderStreamingSlotsRuntimeTag(nonce));
|
|
1944
|
+
}
|
|
1945
|
+
controller.enqueue(toUint8(header, encoder));
|
|
1946
|
+
const pending = preparedSlots.map((slot) => {
|
|
1947
|
+
const fallback = renderStreamingSlotPlaceholder(slot.id, slot.fallbackHtml ?? "");
|
|
1948
|
+
controller.enqueue(toUint8(fallback, encoder));
|
|
1949
|
+
return resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric);
|
|
1950
|
+
});
|
|
1951
|
+
while (pending.length > 0) {
|
|
1952
|
+
const { original, result } = await nextResolvedSlot(pending);
|
|
1953
|
+
const index = pending.indexOf(original);
|
|
1954
|
+
if (index >= 0)
|
|
1955
|
+
pending.splice(index, 1);
|
|
1956
|
+
if (result.html === null)
|
|
1957
|
+
continue;
|
|
1958
|
+
emitSlotMetric({
|
|
1959
|
+
type: "patched",
|
|
1960
|
+
slotId: result.id,
|
|
1961
|
+
durationMs: result.durationMs,
|
|
1962
|
+
bytes: result.bytes
|
|
1963
|
+
}, combinedOnSlotMetric);
|
|
1964
|
+
controller.enqueue(toUint8(renderStreamingSlotPatchTag(result.id, result.html, nonce), encoder));
|
|
1965
|
+
}
|
|
1966
|
+
if (footerHtml.length > 0) {
|
|
1967
|
+
controller.enqueue(toUint8(footerHtml, encoder));
|
|
1968
|
+
}
|
|
1969
|
+
controller.close();
|
|
1970
|
+
} catch (error) {
|
|
1971
|
+
controller.error(error);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
};
|
|
1976
|
+
var injectStreamingRuntimeIntoStream = (stream, nonce) => {
|
|
1977
|
+
const runtimeTag = renderStreamingSlotsRuntimeTag(nonce);
|
|
1978
|
+
const encoder = new TextEncoder;
|
|
1979
|
+
const decoder = new TextDecoder;
|
|
1980
|
+
const lookbehind = CLOSING_HEAD_TAG_LENGTH - 1;
|
|
1981
|
+
return new ReadableStream({
|
|
1982
|
+
async start(controller) {
|
|
1983
|
+
const reader = stream.getReader();
|
|
1984
|
+
let injected = false;
|
|
1985
|
+
let pending = "";
|
|
1986
|
+
try {
|
|
1987
|
+
for (;; ) {
|
|
1988
|
+
const { done, value } = await reader.read();
|
|
1989
|
+
if (done)
|
|
1990
|
+
break;
|
|
1991
|
+
if (!value)
|
|
1992
|
+
continue;
|
|
1993
|
+
pending += streamChunkToString(value, decoder);
|
|
1994
|
+
if (injected) {
|
|
1995
|
+
controller.enqueue(encoder.encode(pending));
|
|
1996
|
+
pending = "";
|
|
1997
|
+
continue;
|
|
1998
|
+
}
|
|
1999
|
+
const headIndex = pending.indexOf(CLOSING_HEAD_TAG);
|
|
2000
|
+
if (headIndex >= 0) {
|
|
2001
|
+
const withRuntime = `${pending.slice(0, headIndex)}${runtimeTag}${pending.slice(headIndex)}`;
|
|
2002
|
+
controller.enqueue(encoder.encode(withRuntime));
|
|
2003
|
+
pending = "";
|
|
2004
|
+
injected = true;
|
|
2005
|
+
continue;
|
|
2006
|
+
}
|
|
2007
|
+
if (pending.length > lookbehind) {
|
|
2008
|
+
const safeText = pending.slice(0, pending.length - lookbehind);
|
|
2009
|
+
controller.enqueue(encoder.encode(safeText));
|
|
2010
|
+
pending = pending.slice(-lookbehind);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
pending += decoder.decode();
|
|
2014
|
+
if (!injected) {
|
|
2015
|
+
pending = injectHtmlIntoHead(pending, runtimeTag);
|
|
2016
|
+
}
|
|
2017
|
+
if (pending.length > 0) {
|
|
2018
|
+
controller.enqueue(encoder.encode(pending));
|
|
2019
|
+
}
|
|
2020
|
+
controller.close();
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
controller.error(error);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
};
|
|
2027
|
+
var appendStreamingSlotPatchesToStream = (stream, slots = [], {
|
|
2028
|
+
injectRuntime = true,
|
|
2029
|
+
nonce,
|
|
2030
|
+
onError,
|
|
2031
|
+
onSlotMetric,
|
|
2032
|
+
policy
|
|
2033
|
+
} = {}) => {
|
|
2034
|
+
const resolvedPolicy = resolveStreamingSlotPolicy(policy);
|
|
2035
|
+
const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
|
|
2036
|
+
const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
|
|
2037
|
+
const effectivePolicy = {
|
|
2038
|
+
...resolvedPolicy,
|
|
2039
|
+
onSlotMetric: combinedOnSlotMetric
|
|
2040
|
+
};
|
|
2041
|
+
const preparedSlots = prepareSlots({
|
|
2042
|
+
policy: effectivePolicy,
|
|
2043
|
+
slots,
|
|
2044
|
+
onError: combinedOnError,
|
|
2045
|
+
onSlotMetric: combinedOnSlotMetric
|
|
2046
|
+
});
|
|
2047
|
+
if (preparedSlots.length === 0)
|
|
2048
|
+
return stream;
|
|
2049
|
+
const source = injectRuntime ? injectStreamingRuntimeIntoStream(stream, nonce) : stream;
|
|
2050
|
+
const encoder = new TextEncoder;
|
|
2051
|
+
const decoder = new TextDecoder;
|
|
2052
|
+
const reader = source.getReader();
|
|
2053
|
+
const pending = preparedSlots.map((slot) => resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric));
|
|
2054
|
+
return new ReadableStream({
|
|
2055
|
+
async start(controller) {
|
|
2056
|
+
let baseDone = false;
|
|
2057
|
+
let baseRead = reader.read();
|
|
2058
|
+
let tail = "";
|
|
2059
|
+
let footer = "";
|
|
2060
|
+
try {
|
|
2061
|
+
while (!baseDone || pending.length > 0) {
|
|
2062
|
+
const racers = [];
|
|
2063
|
+
if (!baseDone) {
|
|
2064
|
+
racers.push(baseRead.then(({ done, value }) => ({
|
|
2065
|
+
done,
|
|
2066
|
+
kind: "base",
|
|
2067
|
+
value
|
|
2068
|
+
})));
|
|
2069
|
+
}
|
|
2070
|
+
if (pending.length > 0) {
|
|
2071
|
+
racers.push(nextResolvedSlot(pending).then((resolved) => ({
|
|
2072
|
+
kind: "slot",
|
|
2073
|
+
...resolved
|
|
2074
|
+
})));
|
|
2075
|
+
}
|
|
2076
|
+
if (racers.length === 0)
|
|
2077
|
+
break;
|
|
2078
|
+
const winner = await Promise.race(racers);
|
|
2079
|
+
if (winner.kind === "base") {
|
|
2080
|
+
if (winner.done) {
|
|
2081
|
+
baseDone = true;
|
|
2082
|
+
tail += decoder.decode();
|
|
2083
|
+
const footerStart = tail.search(CLOSING_PAGE_TAG_REGEX);
|
|
2084
|
+
if (footerStart >= 0) {
|
|
2085
|
+
const content = tail.slice(0, footerStart);
|
|
2086
|
+
footer = tail.slice(footerStart);
|
|
2087
|
+
if (content.length > 0) {
|
|
2088
|
+
controller.enqueue(encoder.encode(content));
|
|
2089
|
+
}
|
|
2090
|
+
} else if (tail.length > 0) {
|
|
2091
|
+
controller.enqueue(encoder.encode(tail));
|
|
2092
|
+
}
|
|
2093
|
+
tail = "";
|
|
2094
|
+
} else if (winner.value) {
|
|
2095
|
+
tail += streamChunkToString(winner.value, decoder);
|
|
2096
|
+
if (tail.length > STREAM_TAIL_LOOKBEHIND) {
|
|
2097
|
+
const content = tail.slice(0, tail.length - STREAM_TAIL_LOOKBEHIND);
|
|
2098
|
+
controller.enqueue(encoder.encode(content));
|
|
2099
|
+
tail = tail.slice(-STREAM_TAIL_LOOKBEHIND);
|
|
2100
|
+
}
|
|
2101
|
+
baseRead = reader.read();
|
|
2102
|
+
}
|
|
2103
|
+
continue;
|
|
2104
|
+
}
|
|
2105
|
+
const index = pending.indexOf(winner.original);
|
|
2106
|
+
if (index >= 0)
|
|
2107
|
+
pending.splice(index, 1);
|
|
2108
|
+
if (winner.result.html === null)
|
|
2109
|
+
continue;
|
|
2110
|
+
emitSlotMetric({
|
|
2111
|
+
type: "patched",
|
|
2112
|
+
slotId: winner.result.id,
|
|
2113
|
+
durationMs: winner.result.durationMs,
|
|
2114
|
+
bytes: winner.result.bytes
|
|
2115
|
+
}, combinedOnSlotMetric);
|
|
2116
|
+
controller.enqueue(encoder.encode(renderStreamingSlotPatchTag(winner.result.id, winner.result.html, nonce)));
|
|
2117
|
+
}
|
|
2118
|
+
if (footer.length > 0)
|
|
2119
|
+
controller.enqueue(encoder.encode(footer));
|
|
2120
|
+
controller.close();
|
|
2121
|
+
} catch (error) {
|
|
2122
|
+
controller.error(error);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
});
|
|
2126
|
+
};
|
|
2127
|
+
|
|
2128
|
+
// src/core/streamingSlotRegistry.ts
|
|
2129
|
+
var asyncLocalStorage;
|
|
2130
|
+
var isServerRuntime = () => typeof process !== "undefined" && typeof process.versions?.node === "string";
|
|
2131
|
+
var ensureAsyncLocalStorage = async () => {
|
|
2132
|
+
if (typeof asyncLocalStorage !== "undefined")
|
|
2133
|
+
return asyncLocalStorage;
|
|
2134
|
+
if (!isServerRuntime()) {
|
|
2135
|
+
asyncLocalStorage = null;
|
|
2136
|
+
return asyncLocalStorage;
|
|
2137
|
+
}
|
|
2138
|
+
const mod = await import("async_hooks");
|
|
2139
|
+
asyncLocalStorage = new mod.AsyncLocalStorage;
|
|
2140
|
+
return asyncLocalStorage;
|
|
2141
|
+
};
|
|
2142
|
+
var registerStreamingSlot2 = (slot) => {
|
|
2143
|
+
if (!asyncLocalStorage)
|
|
2144
|
+
return;
|
|
2145
|
+
const store = asyncLocalStorage.getStore();
|
|
2146
|
+
if (!store)
|
|
2147
|
+
return;
|
|
2148
|
+
store.set(slot.id, slot);
|
|
2149
|
+
};
|
|
2150
|
+
setStreamingSlotRegistrar(registerStreamingSlot2);
|
|
2151
|
+
var runWithStreamingSlotRegistry = async (task) => {
|
|
2152
|
+
const storage = await ensureAsyncLocalStorage();
|
|
2153
|
+
if (!storage) {
|
|
2154
|
+
return {
|
|
2155
|
+
result: await task(),
|
|
2156
|
+
slots: []
|
|
2157
|
+
};
|
|
2158
|
+
}
|
|
2159
|
+
return storage.run(new Map, async () => {
|
|
2160
|
+
const result = await task();
|
|
2161
|
+
const store = storage.getStore();
|
|
2162
|
+
return {
|
|
2163
|
+
result,
|
|
2164
|
+
slots: store ? [...store.values()] : []
|
|
2165
|
+
};
|
|
2166
|
+
});
|
|
2167
|
+
};
|
|
2168
|
+
|
|
2169
|
+
// src/core/responseEnhancers.ts
|
|
2170
|
+
var toResponse = async (responseLike) => await responseLike;
|
|
2171
|
+
var cloneHeaders = (response) => {
|
|
2172
|
+
const headers = new Headers(response.headers);
|
|
2173
|
+
return headers;
|
|
2174
|
+
};
|
|
2175
|
+
var enhanceHtmlResponseWithStreamingSlots = (response, { nonce, onError, streamingSlots = [], policy } = {}) => {
|
|
2176
|
+
if (!response.body || streamingSlots.length === 0) {
|
|
2177
|
+
return response;
|
|
2178
|
+
}
|
|
2179
|
+
const body = appendStreamingSlotPatchesToStream(response.body, streamingSlots, {
|
|
2180
|
+
nonce,
|
|
2181
|
+
onError,
|
|
2182
|
+
policy
|
|
2183
|
+
});
|
|
2184
|
+
return new Response(body, {
|
|
2185
|
+
headers: cloneHeaders(response),
|
|
2186
|
+
status: response.status,
|
|
2187
|
+
statusText: response.statusText
|
|
2188
|
+
});
|
|
2189
|
+
};
|
|
2190
|
+
var withStreamingSlots = async (responseLike, options = {}) => enhanceHtmlResponseWithStreamingSlots(await toResponse(responseLike), options);
|
|
2191
|
+
var mergeStreamingSlots = (registered, explicit) => {
|
|
2192
|
+
const merged = new Map;
|
|
2193
|
+
for (const slot of registered)
|
|
2194
|
+
merged.set(slot.id, slot);
|
|
2195
|
+
for (const slot of explicit)
|
|
2196
|
+
merged.set(slot.id, slot);
|
|
2197
|
+
return [...merged.values()];
|
|
2198
|
+
};
|
|
2199
|
+
var withRegisteredStreamingSlots = async (renderResponse, options = {}) => {
|
|
2200
|
+
const { result, slots } = await runWithStreamingSlotRegistry(renderResponse);
|
|
2201
|
+
const explicit = options.streamingSlots ?? [];
|
|
2202
|
+
return withStreamingSlots(result, {
|
|
2203
|
+
...options,
|
|
2204
|
+
streamingSlots: mergeStreamingSlots(slots, explicit)
|
|
2205
|
+
});
|
|
2206
|
+
};
|
|
2207
|
+
|
|
2159
2208
|
// src/core/wrapPageHandlerWithStreamingSlots.ts
|
|
2160
|
-
init_responseEnhancers();
|
|
2161
2209
|
var wrapPageHandlerWithStreamingSlots = (handler) => (...args) => withRegisteredStreamingSlots(() => handler(...args));
|
|
2162
2210
|
|
|
2163
2211
|
// src/vue/index.ts
|
|
@@ -2482,5 +2530,5 @@ export {
|
|
|
2482
2530
|
Image_default as Image
|
|
2483
2531
|
};
|
|
2484
2532
|
|
|
2485
|
-
//# debugId=
|
|
2533
|
+
//# debugId=B29486E07F4255F064756E2164756E21
|
|
2486
2534
|
//# sourceMappingURL=index.js.map
|