@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,8 +78,24 @@ 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/utils/escapeScriptContent.ts
82
+ var ESCAPE_LOOKUP, ESCAPE_REGEX, escapeScriptContent = (content) => content.replace(ESCAPE_REGEX, (char) => {
83
+ const escaped = ESCAPE_LOOKUP[char];
84
+ return escaped !== undefined ? escaped : char;
85
+ });
86
+ var init_escapeScriptContent = __esm(() => {
87
+ ESCAPE_LOOKUP = {
88
+ "\u2028": "\\u2028",
89
+ "\u2029": "\\u2029",
90
+ "&": "\\u0026",
91
+ "<": "\\u003C",
92
+ ">": "\\u003E"
93
+ };
94
+ ESCAPE_REGEX = /[&><\u2028\u2029]/g;
95
+ });
96
+
81
97
  // src/core/islandPageContext.ts
82
- var BOOTSTRAP_MANIFEST_KEY = "BootstrapClient", ISLAND_MARKER = 'data-island="true"', MANIFEST_MARKER = "__ABSOLUTE_MANIFEST__", ISLAND_STATE_MARKER = "__ABS_ISLAND_STATE__", CLOSING_HEAD_TAG = "</head>", buildIslandsHeadMarkup = (manifest) => {
98
+ 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) => {
83
99
  const manifestScript = `<script>window.__ABSOLUTE_MANIFEST__ = ${JSON.stringify(manifest)}</script>`;
84
100
  const islandStateScript = `<script>window.__ABS_ISLAND_STATE__ = ${JSON.stringify(globalThis.__ABS_ISLAND_STATE__ ?? {})}</script>`;
85
101
  const bootstrapPath = manifest[BOOTSTRAP_MANIFEST_KEY];
@@ -98,10 +114,10 @@ var BOOTSTRAP_MANIFEST_KEY = "BootstrapClient", ISLAND_MARKER = 'data-island="tr
98
114
  }
99
115
  }
100
116
  return `<!DOCTYPE html><html><head>${markup}</head><body>${html}</body></html>`;
101
- }, streamChunkToString = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true }), pipeStreamWithHeadInjection = (stream, markup) => {
117
+ }, streamChunkToString2 = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true }), pipeStreamWithHeadInjection = (stream, markup) => {
102
118
  const encoder = new TextEncoder;
103
119
  const decoder = new TextDecoder;
104
- const lookbehind = CLOSING_HEAD_TAG.length - 1;
120
+ const lookbehind = CLOSING_HEAD_TAG2.length - 1;
105
121
  return new ReadableStream({
106
122
  async start(controller) {
107
123
  const reader = stream.getReader();
@@ -114,13 +130,13 @@ var BOOTSTRAP_MANIFEST_KEY = "BootstrapClient", ISLAND_MARKER = 'data-island="tr
114
130
  break;
115
131
  if (!value)
116
132
  continue;
117
- pending += streamChunkToString(value, decoder);
133
+ pending += streamChunkToString2(value, decoder);
118
134
  if (injected) {
119
135
  controller.enqueue(encoder.encode(pending));
120
136
  pending = "";
121
137
  continue;
122
138
  }
123
- const headIndex = pending.indexOf(CLOSING_HEAD_TAG);
139
+ const headIndex = pending.indexOf(CLOSING_HEAD_TAG2);
124
140
  if (headIndex >= 0) {
125
141
  const next = `${pending.slice(0, headIndex)}${markup}${pending.slice(headIndex)}`;
126
142
  controller.enqueue(encoder.encode(next));
@@ -163,7 +179,7 @@ var BOOTSTRAP_MANIFEST_KEY = "BootstrapClient", ISLAND_MARKER = 'data-island="tr
163
179
  break;
164
180
  if (!value)
165
181
  continue;
166
- pending += streamChunkToString(value, decoder);
182
+ pending += streamChunkToString2(value, decoder);
167
183
  if (injected) {
168
184
  controller.enqueue(encoder.encode(pending));
169
185
  pending = "";
@@ -221,877 +237,309 @@ var BOOTSTRAP_MANIFEST_KEY = "BootstrapClient", ISLAND_MARKER = 'data-island="tr
221
237
  globalThis.__absoluteManifest = manifest;
222
238
  };
223
239
 
224
- // src/client/streamSwap.ts
225
- var streamSwapRuntime = () => {
226
- if (window.__ABS_SLOT_RUNTIME__ === true)
227
- return;
228
- window.__ABS_SLOT_RUNTIME__ = true;
229
- window.__ABS_SLOT_PENDING__ = window.__ABS_SLOT_PENDING__ ?? {};
230
- const pending = window.__ABS_SLOT_PENDING__;
231
- const apply = (id, html) => {
232
- const node = document.getElementById(`slot-${id}`);
233
- if (!node) {
234
- pending[id] = html;
235
- return;
236
- }
237
- node.innerHTML = html;
238
- delete pending[id];
239
- };
240
- const flush = () => {
241
- for (const id in pending) {
242
- if (!Object.prototype.hasOwnProperty.call(pending, id))
243
- continue;
244
- apply(id, pending[id] ?? "");
245
- }
246
- };
247
- window.__ABS_SLOT_ENQUEUE__ = (id, html) => {
248
- apply(id, html);
249
- };
250
- if (typeof MutationObserver === "function") {
251
- const observer = new MutationObserver(flush);
252
- const root = document.documentElement ?? document.body ?? document;
253
- observer.observe(root, { childList: true, subtree: true });
254
- }
255
- if (document.readyState === "loading") {
256
- document.addEventListener("DOMContentLoaded", flush, { once: true });
257
- }
258
- flush();
259
- }, stripFunctionWrapper = (value) => {
260
- const start = value.indexOf("{");
261
- const end = value.lastIndexOf("}");
262
- if (start < 0 || end <= start)
263
- return "";
264
- return value.slice(start + 1, end);
265
- }, getStreamSwapRuntimeScript = () => `(function(){${stripFunctionWrapper(streamSwapRuntime.toString())}})();`;
266
-
267
- // src/utils/escapeScriptContent.ts
268
- var ESCAPE_LOOKUP, ESCAPE_REGEX, escapeScriptContent = (content) => content.replace(ESCAPE_REGEX, (char) => {
269
- const escaped = ESCAPE_LOOKUP[char];
270
- return escaped !== undefined ? escaped : char;
271
- });
272
- var init_escapeScriptContent = __esm(() => {
273
- ESCAPE_LOOKUP = {
274
- "\u2028": "\\u2028",
275
- "\u2029": "\\u2029",
276
- "&": "\\u0026",
277
- "<": "\\u003C",
278
- ">": "\\u003E"
240
+ // src/utils/ssrErrorPage.ts
241
+ var ssrErrorPage = (framework, error) => {
242
+ const frameworkColors = {
243
+ angular: "#dd0031",
244
+ html: "#e34c26",
245
+ htmx: "#1a365d",
246
+ react: "#61dafb",
247
+ svelte: "#ff3e00",
248
+ vue: "#42b883"
279
249
  };
280
- ESCAPE_REGEX = /[&><\u2028\u2029]/g;
281
- });
250
+ const accent = frameworkColors[framework] ?? "#94a3b8";
251
+ const label = framework.charAt(0).toUpperCase() + framework.slice(1);
252
+ const message = error instanceof Error ? error.message : String(error);
253
+ return `<!DOCTYPE html>
254
+ <html>
255
+ <head>
256
+ <meta charset="utf-8">
257
+ <meta name="viewport" content="width=device-width, initial-scale=1">
258
+ <title>SSR Error - AbsoluteJS</title>
259
+ <style>
260
+ *{margin:0;padding:0;box-sizing:border-box}
261
+ body{min-height:100vh;background:linear-gradient(135deg,rgba(15,23,42,0.98) 0%,rgba(30,41,59,0.98) 100%);color:#e2e8f0;font-family:"JetBrains Mono","Fira Code",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:14px;line-height:1.6;display:flex;align-items:flex-start;justify-content:center;padding:32px}
262
+ .card{max-width:720px;width:100%;background:rgba(30,41,59,0.6);border:1px solid rgba(71,85,105,0.5);border-radius:16px;box-shadow:0 25px 50px -12px rgba(0,0,0,0.5),0 0 0 1px rgba(255,255,255,0.05);overflow:hidden}
263
+ .header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 24px;background:rgba(15,23,42,0.5);border-bottom:1px solid rgba(71,85,105,0.4)}
264
+ .brand{font-weight:700;font-size:20px;color:#fff;letter-spacing:-0.02em}
265
+ .badge{padding:5px 10px;border-radius:8px;font-size:12px;font-weight:600;background:${accent};color:#fff;opacity:0.95;box-shadow:0 2px 4px rgba(0,0,0,0.2)}
266
+ .kind{color:#94a3b8;font-size:13px;font-weight:500}
267
+ .content{padding:24px}
268
+ .label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.08em;color:#94a3b8;margin-bottom:8px}
269
+ .message{margin:0;padding:16px 20px;background:rgba(239,68,68,0.12);border:1px solid rgba(239,68,68,0.25);border-radius:10px;overflow-x:auto;white-space:pre-wrap;word-break:break-word;color:#fca5a5;font-size:13px;line-height:1.5}
270
+ .hint{margin-top:20px;padding:12px 20px;background:rgba(71,85,105,0.3);border-radius:10px;border:1px solid rgba(71,85,105,0.4);color:#cbd5e1;font-size:13px}
271
+ </style>
272
+ </head>
273
+ <body>
274
+ <div class="card">
275
+ <div class="header">
276
+ <div style="display:flex;align-items:center;gap:12px">
277
+ <span class="brand">AbsoluteJS</span>
278
+ <span class="badge">${label}</span>
279
+ </div>
280
+ <span class="kind">Server Render Error</span>
281
+ </div>
282
+ <div class="content">
283
+ <div class="label">What went wrong</div>
284
+ <pre class="message">${message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}</pre>
285
+ <div class="hint">A component threw during server-side rendering. Check the terminal for the full stack trace.</div>
286
+ </div>
287
+ </div>
288
+ </body>
289
+ </html>`;
290
+ };
282
291
 
283
- // src/utils/streamingSlots.ts
284
- var SLOT_ID_PREFIX = "abs-slot-", SLOT_PLACEHOLDER_PREFIX = "slot-", CLOSING_HEAD_TAG2 = "</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) => {
285
- const closingHeadIndex = html.indexOf(CLOSING_HEAD_TAG2);
286
- if (closingHeadIndex >= 0) {
287
- return `${html.slice(0, closingHeadIndex)}${injection}${html.slice(closingHeadIndex)}`;
288
- }
289
- return `${html}${injection}`;
290
- }, toUint8 = (value, encoder) => encoder.encode(value), currentStreamingSlotPolicy, clonePolicy = (policy) => ({
291
- ...policy
292
- }), normalizeSlotBytes = (value, fallback) => {
293
- if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
294
- return Math.floor(value);
292
+ // src/utils/stringModifiers.ts
293
+ var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9\-_]+/g, "").replace(/[-_]{2,}/g, "-"), toKebab = (str) => normalizeSlug(str).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), toPascal = (str) => {
294
+ if (!str.includes("-") && !str.includes("_")) {
295
+ return str.charAt(0).toUpperCase() + str.slice(1);
295
296
  }
296
- return fallback;
297
- }, 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 = {}) => ({
298
- timeoutMs: hasPolicyValue(overridePolicy, "timeoutMs") ? normalizeSlotBytes(overridePolicy.timeoutMs, base.timeoutMs) : base.timeoutMs,
299
- fallbackHtml: hasPolicyValue(overridePolicy, "fallbackHtml") ? normalizeSlotText(overridePolicy.fallbackHtml, "") : base.fallbackHtml,
300
- errorHtml: hasPolicyValue(overridePolicy, "errorHtml") ? normalizeSlotError(overridePolicy.errorHtml) : base.errorHtml,
301
- maxSlotsPerResponse: hasPolicyValue(overridePolicy, "maxSlotsPerResponse") ? normalizeSlotBytes(overridePolicy.maxSlotsPerResponse, base.maxSlotsPerResponse) : base.maxSlotsPerResponse,
302
- maxSlotHtmlSizeBytes: hasPolicyValue(overridePolicy, "maxSlotHtmlSizeBytes") ? normalizeSlotBytes(overridePolicy.maxSlotHtmlSizeBytes, base.maxSlotHtmlSizeBytes) : base.maxSlotHtmlSizeBytes,
303
- onError: hasPolicyValue(overridePolicy, "onError") ? overridePolicy.onError : base.onError,
304
- onSlotMetric: hasPolicyValue(overridePolicy, "onSlotMetric") ? overridePolicy.onSlotMetric : base.onSlotMetric
305
- }), createCombinedSlotErrorHandler = (policyOnError, enhancerOnError) => {
306
- if (!policyOnError && !enhancerOnError)
297
+ return normalizeSlug(str).split(/[-_]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase()).join("");
298
+ }, toScreamingSnake = (str) => str.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
299
+
300
+ // src/utils/resolveConvention.ts
301
+ import { basename } from "path";
302
+ var CONVENTIONS_KEY = "__absoluteConventions", isConventionsMap = (value) => Boolean(value) && typeof value === "object", getMap = () => {
303
+ const value = Reflect.get(globalThis, CONVENTIONS_KEY);
304
+ if (isConventionsMap(value))
305
+ return value;
306
+ const empty = {};
307
+ return empty;
308
+ }, derivePageName = (pagePath) => {
309
+ const base = basename(pagePath);
310
+ const dotIndex = base.indexOf(".");
311
+ const name = dotIndex > 0 ? base.slice(0, dotIndex) : base;
312
+ return toPascal(name);
313
+ }, resolveErrorConventionPath = (framework, pageName) => {
314
+ const conventions = getMap()[framework];
315
+ if (!conventions)
307
316
  return;
308
- return (error, slot) => {
309
- policyOnError?.(error, slot);
310
- enhancerOnError?.(error, slot);
311
- };
312
- }, createCombinedSlotMetricHandler = (policyOnSlotMetric, callOnSlotMetric) => {
313
- if (!policyOnSlotMetric && !callOnSlotMetric)
317
+ return conventions.pages?.[pageName]?.error ?? conventions.defaults?.error;
318
+ }, resolveNotFoundConventionPath = (framework) => getMap()[framework]?.defaults?.notFound, setConventions = (map) => {
319
+ Reflect.set(globalThis, CONVENTIONS_KEY, map);
320
+ }, isDev = () => true, buildErrorProps = (error) => {
321
+ const message = error instanceof Error ? error.message : String(error);
322
+ const stack = isDev() && error instanceof Error ? error.stack : undefined;
323
+ return { error: { message, stack } };
324
+ }, renderReactError = async (conventionPath, errorProps) => {
325
+ const { createElement } = await import("react");
326
+ const { renderToReadableStream } = await import("react-dom/server");
327
+ const mod = await import(conventionPath);
328
+ const [firstKey] = Object.keys(mod);
329
+ const ErrorComponent = mod.default ?? (firstKey ? mod[firstKey] : undefined);
330
+ const element = createElement(ErrorComponent, errorProps);
331
+ const stream = await renderToReadableStream(element);
332
+ return new Response(stream, {
333
+ headers: { "Content-Type": "text/html" },
334
+ status: 500
335
+ });
336
+ }, renderSvelteError = async (conventionPath, errorProps) => {
337
+ const { render } = await import("svelte/server");
338
+ const mod = await import(conventionPath);
339
+ const ErrorComponent = mod.default;
340
+ const { head, body } = render(ErrorComponent, {
341
+ props: errorProps
342
+ });
343
+ const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
344
+ return new Response(html, {
345
+ headers: { "Content-Type": "text/html" },
346
+ status: 500
347
+ });
348
+ }, unescapeVueStyles = (ssrBody) => {
349
+ let styles = "";
350
+ const body = ssrBody.replace(/<style>([\s\S]*?)<\/style>/g, (_, css) => {
351
+ styles += `<style>${css.replace(/&quot;/g, '"').replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">")}</style>`;
352
+ return "";
353
+ });
354
+ return { body, styles };
355
+ }, renderVueError = async (conventionPath, errorProps) => {
356
+ const { createSSRApp, h } = await import("vue");
357
+ const { renderToString } = await import("vue/server-renderer");
358
+ const mod = await import(conventionPath);
359
+ const ErrorComponent = mod.default;
360
+ const app = createSSRApp({
361
+ render: () => h(ErrorComponent, errorProps)
362
+ });
363
+ const rawBody = await renderToString(app);
364
+ const { styles, body } = unescapeVueStyles(rawBody);
365
+ const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
366
+ return new Response(html, {
367
+ headers: { "Content-Type": "text/html" },
368
+ status: 500
369
+ });
370
+ }, renderAngularError = async (conventionPath, errorProps) => {
371
+ const mod = await import(conventionPath);
372
+ const renderError = mod.default ?? mod.renderError;
373
+ if (typeof renderError !== "function")
374
+ return null;
375
+ const html = renderError(errorProps);
376
+ return new Response(html, {
377
+ headers: { "Content-Type": "text/html" },
378
+ status: 500
379
+ });
380
+ }, logConventionRenderError = (framework, label, renderError) => {
381
+ const message = renderError instanceof Error ? renderError.message : "";
382
+ if (message.includes("Cannot find module") || message.includes("Cannot find package") || message.includes("not found in module")) {
383
+ console.error(`[SSR] Convention ${label} page for ${framework} failed: missing framework package. Ensure the ${framework} runtime is installed (e.g. bun add ${framework === "react" ? "react react-dom" : framework}).`);
314
384
  return;
315
- return (metric) => {
316
- policyOnSlotMetric?.(metric);
317
- callOnSlotMetric?.(metric);
318
- };
319
- }, resolveStreamingSlotPolicy = (overridePolicy = {}) => {
320
- const base = getStreamingSlotPolicy();
321
- return applyStreamingSlotPolicyOverrides(base, overridePolicy);
322
- }, getStreamingSlotPolicy = () => clonePolicy(currentStreamingSlotPolicy), setStreamingSlotPolicy = (policy = {}) => {
323
- const base = getStreamingSlotPolicy();
324
- currentStreamingSlotPolicy = applyStreamingSlotPolicyOverrides(base, policy);
325
- }, withStreamingSlotPolicy = async (policy, callback) => {
326
- const previous = getStreamingSlotPolicy();
327
- setStreamingSlotPolicy(policy);
328
- try {
329
- return await callback();
330
- } finally {
331
- currentStreamingSlotPolicy = previous;
332
385
  }
333
- }, emitSlotMetric = (metric, onSlotMetric) => {
334
- onSlotMetric?.(metric);
335
- }, createTimeoutError = (slot, timeoutMs) => {
336
- const error = new Error(`Streaming slot "${slot.id}" timed out after ${timeoutMs}ms`);
337
- error.__absTimeout = true;
338
- return error;
339
- }, toStreamingSlot = (slot, policy) => ({
340
- errorHtml: slot.errorHtml === undefined ? policy.errorHtml : slot.errorHtml,
341
- fallbackHtml: normalizeSlotText(slot.fallbackHtml, policy.fallbackHtml),
342
- id: slot.id ?? createStreamingSlotId(),
343
- timeoutMs: normalizeSlotBytes(slot.timeoutMs, policy.timeoutMs),
344
- resolve: slot.resolve
345
- }), prepareSlots = ({
346
- policy,
347
- slots,
348
- onError,
349
- onSlotMetric
350
- }) => {
351
- const preparedSlots = slots.map((slot) => toStreamingSlot(slot, policy));
352
- const maxSlotsPerResponse = policy.maxSlotsPerResponse;
353
- if (maxSlotsPerResponse === 0) {
354
- const error = new Error("Streaming slot limit is set to 0");
355
- for (const slot of preparedSlots) {
356
- onError?.(error, slot);
357
- emitSlotMetric({
358
- type: "dropped",
359
- slotId: slot.id,
360
- reason: "maxSlotsPerResponse is 0"
361
- }, onSlotMetric);
362
- }
363
- return [];
386
+ console.error(`[SSR] Failed to render ${framework} convention ${label} page:`, renderError);
387
+ }, ERROR_RENDERERS, renderConventionError = async (framework, pageName, error) => {
388
+ const conventionPath = resolveErrorConventionPath(framework, pageName);
389
+ if (!conventionPath)
390
+ return null;
391
+ const errorProps = buildErrorProps(error);
392
+ const renderer = ERROR_RENDERERS[framework];
393
+ if (!renderer)
394
+ return null;
395
+ try {
396
+ return await renderer(conventionPath, errorProps);
397
+ } catch (renderError) {
398
+ logConventionRenderError(framework, "error", renderError);
364
399
  }
365
- if (preparedSlots.length <= maxSlotsPerResponse) {
366
- preparedSlots.forEach((slot) => emitSlotMetric({
367
- type: "prepared",
368
- slotId: slot.id
369
- }, onSlotMetric));
370
- return preparedSlots;
400
+ return null;
401
+ }, renderReactNotFound = async (conventionPath) => {
402
+ const { createElement } = await import("react");
403
+ const { renderToReadableStream } = await import("react-dom/server");
404
+ const mod = await import(conventionPath);
405
+ const [nfKey] = Object.keys(mod);
406
+ const NotFoundComponent = mod.default ?? (nfKey ? mod[nfKey] : undefined);
407
+ const element = createElement(NotFoundComponent);
408
+ const stream = await renderToReadableStream(element);
409
+ return new Response(stream, {
410
+ headers: { "Content-Type": "text/html" },
411
+ status: 404
412
+ });
413
+ }, renderSvelteNotFound = async (conventionPath) => {
414
+ const { render } = await import("svelte/server");
415
+ const mod = await import(conventionPath);
416
+ const NotFoundComponent = mod.default;
417
+ const { head, body } = render(NotFoundComponent);
418
+ const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
419
+ return new Response(html, {
420
+ headers: { "Content-Type": "text/html" },
421
+ status: 404
422
+ });
423
+ }, renderVueNotFound = async (conventionPath) => {
424
+ const { createSSRApp, h } = await import("vue");
425
+ const { renderToString } = await import("vue/server-renderer");
426
+ const mod = await import(conventionPath);
427
+ const NotFoundComponent = mod.default;
428
+ const app = createSSRApp({
429
+ render: () => h(NotFoundComponent)
430
+ });
431
+ const rawBody = await renderToString(app);
432
+ const { styles, body } = unescapeVueStyles(rawBody);
433
+ const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
434
+ return new Response(html, {
435
+ headers: { "Content-Type": "text/html" },
436
+ status: 404
437
+ });
438
+ }, renderAngularNotFound = async (conventionPath) => {
439
+ const mod = await import(conventionPath);
440
+ const renderNotFound = mod.default ?? mod.renderNotFound;
441
+ if (typeof renderNotFound !== "function")
442
+ return null;
443
+ const html = renderNotFound();
444
+ return new Response(html, {
445
+ headers: { "Content-Type": "text/html" },
446
+ status: 404
447
+ });
448
+ }, NOT_FOUND_RENDERERS, renderConventionNotFound = async (framework) => {
449
+ const conventionPath = resolveNotFoundConventionPath(framework);
450
+ if (!conventionPath)
451
+ return null;
452
+ const renderer = NOT_FOUND_RENDERERS[framework];
453
+ if (!renderer)
454
+ return null;
455
+ try {
456
+ return await renderer(conventionPath);
457
+ } catch (renderError) {
458
+ logConventionRenderError(framework, "not-found", renderError);
371
459
  }
372
- const keptSlots = preparedSlots.slice(0, maxSlotsPerResponse);
373
- const droppedSlots = preparedSlots.slice(maxSlotsPerResponse);
374
- droppedSlots.forEach((slot) => {
375
- onError?.(new Error(`Streaming slot "${slot.id}" dropped because ${maxSlotsPerResponse} slots is the configured maximum`), slot);
376
- emitSlotMetric({
377
- type: "dropped",
378
- slotId: slot.id,
379
- reason: `maxSlotsPerResponse is ${maxSlotsPerResponse}`
380
- }, onSlotMetric);
460
+ return null;
461
+ }, NOT_FOUND_PRIORITY, renderFirstNotFound = async () => {
462
+ for (const framework of NOT_FOUND_PRIORITY) {
463
+ if (!getMap()[framework]?.defaults?.notFound)
464
+ continue;
465
+ const response = await renderConventionNotFound(framework);
466
+ if (response)
467
+ return response;
468
+ }
469
+ return null;
470
+ };
471
+ var init_resolveConvention = __esm(() => {
472
+ ERROR_RENDERERS = {
473
+ angular: renderAngularError,
474
+ react: renderReactError,
475
+ svelte: renderSvelteError,
476
+ vue: renderVueError
477
+ };
478
+ NOT_FOUND_RENDERERS = {
479
+ angular: renderAngularNotFound,
480
+ react: renderReactNotFound,
481
+ svelte: renderSvelteNotFound,
482
+ vue: renderVueNotFound
483
+ };
484
+ NOT_FOUND_PRIORITY = [
485
+ "react",
486
+ "svelte",
487
+ "vue",
488
+ "angular"
489
+ ];
490
+ });
491
+
492
+ // src/react/pageHandler.ts
493
+ var ssrDirty = false, buildDirtyResponse = (index, maybeProps) => {
494
+ const propsScript = maybeProps ? `window.__INITIAL_PROPS__=${JSON.stringify(maybeProps)};` : "";
495
+ const dirtyFlag = "window.__SSR_DIRTY__=true;";
496
+ const refreshSetup = "window.__REFRESH_BUFFER__=[];" + "window.$RefreshReg$=function(t,i){window.__REFRESH_BUFFER__.push([t,i])};" + "window.$RefreshSig$=function(){return function(t){return t}};";
497
+ const inlineScript = `${propsScript}${dirtyFlag}${refreshSetup}`;
498
+ const html = `<!DOCTYPE html><html><head></head><body>` + `<script>${inlineScript}</script>` + `<script type="module" src="${index}"></script>` + `</body></html>`;
499
+ return new Response(html, {
500
+ headers: { "Content-Type": "text/html" }
381
501
  });
382
- keptSlots.forEach((slot) => emitSlotMetric({
383
- type: "prepared",
384
- slotId: slot.id
385
- }, onSlotMetric));
386
- return keptSlots;
387
- }, htmlByteLength = (value, encoder) => encoder.encode(value).length, resolveSlot = async (slot, onError, policy, onSlotMetric) => {
388
- const safePolicy = policy ?? getStreamingSlotPolicy();
389
- const encoder = new TextEncoder;
390
- const start = Date.now();
502
+ }, handleReactPageRequest = async (PageComponent, index, ...props) => {
503
+ const [maybeProps] = props;
504
+ if (ssrDirty) {
505
+ return buildDirtyResponse(index, maybeProps);
506
+ }
391
507
  try {
392
- const maybeAsyncValue = Promise.resolve(slot.resolve());
393
- const resolved = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
394
- maybeAsyncValue,
395
- new Promise((_, reject) => setTimeout(() => {
396
- reject(createTimeoutError(slot, slot.timeoutMs ?? 0));
397
- }, slot.timeoutMs))
398
- ]) : await maybeAsyncValue;
399
- const html = typeof resolved === "string" ? resolved : `${resolved}`;
400
- if (safePolicy.maxSlotHtmlSizeBytes > 0 && htmlByteLength(html, encoder) > safePolicy.maxSlotHtmlSizeBytes) {
401
- const bytes2 = htmlByteLength(html, encoder);
402
- const error = new Error(`Streaming slot "${slot.id}" exceeded max payload size of ${safePolicy.maxSlotHtmlSizeBytes} bytes`);
403
- const durationMs2 = Date.now() - start;
404
- onError?.(error, slot);
405
- emitSlotMetric({
406
- type: "size_exceeded",
407
- slotId: slot.id,
408
- durationMs: durationMs2,
409
- bytes: bytes2,
410
- error
411
- }, onSlotMetric);
412
- const fallbackHtml = typeof slot.errorHtml === "string" ? slot.errorHtml : null;
413
- return {
414
- html: fallbackHtml,
415
- id: slot.id,
416
- durationMs: durationMs2,
417
- bytes: fallbackHtml === null ? 0 : htmlByteLength(fallbackHtml, encoder)
418
- };
508
+ const { createElement } = await import("react");
509
+ const { renderToReadableStream } = await import("react-dom/server");
510
+ const element = maybeProps !== undefined ? createElement(PageComponent, maybeProps) : createElement(PageComponent);
511
+ const propsScript = maybeProps ? `window.__INITIAL_PROPS__=${JSON.stringify(maybeProps)};` : "";
512
+ const refreshSetup = "window.__REFRESH_BUFFER__=[];window.$RefreshReg$=function(t,i){window.__REFRESH_BUFFER__.push([t,i])};window.$RefreshSig$=function(){return function(t){return t}};";
513
+ const stream = await renderToReadableStream(element, {
514
+ bootstrapModules: [index],
515
+ bootstrapScriptContent: propsScript + refreshSetup || undefined,
516
+ onError(error) {
517
+ console.error("[SSR] React streaming error:", error);
518
+ }
519
+ });
520
+ if ("allReady" in stream && stream.allReady instanceof Promise) {
521
+ await stream.allReady;
419
522
  }
420
- const durationMs = Date.now() - start;
421
- const bytes = htmlByteLength(html, encoder);
422
- emitSlotMetric({
423
- type: "resolved",
424
- slotId: slot.id,
425
- durationMs,
426
- bytes
427
- }, onSlotMetric);
428
- return {
429
- html,
430
- id: slot.id,
431
- durationMs,
432
- bytes
433
- };
523
+ const htmlStream = injectIslandPageContextStream(stream);
524
+ return new Response(htmlStream, {
525
+ headers: { "Content-Type": "text/html" }
526
+ });
434
527
  } catch (error) {
435
- const durationMs = Date.now() - start;
436
- onError?.(error, slot);
437
- emitSlotMetric({
438
- type: error?.__absTimeout === true ? "timeout" : "error",
439
- slotId: slot.id,
440
- durationMs,
441
- error
442
- }, onSlotMetric);
443
- if (typeof slot.errorHtml === "string") {
444
- const html = slot.errorHtml;
445
- return {
446
- html,
447
- id: slot.id,
448
- durationMs,
449
- bytes: htmlByteLength(html, encoder)
450
- };
451
- }
452
- return {
453
- html: null,
454
- id: slot.id,
455
- durationMs,
456
- bytes: 0
457
- };
458
- }
459
- }, nextResolvedSlot = async (pending) => {
460
- const wrapped = pending.map((promise) => promise.then((result) => ({
461
- original: promise,
462
- result
463
- })));
464
- return Promise.race(wrapped);
465
- }, streamChunkToString2 = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true }), streamOutOfOrderSlots = ({
466
- footerHtml = "",
467
- headerHtml = "",
468
- nonce,
469
- policy,
470
- onSlotMetric,
471
- onError,
472
- slots
473
- }) => {
474
- const resolvedPolicy = resolveStreamingSlotPolicy(policy);
475
- const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
476
- const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
477
- const effectivePolicy = {
478
- ...resolvedPolicy,
479
- onSlotMetric: combinedOnSlotMetric
480
- };
481
- const preparedSlots = prepareSlots({
482
- policy: effectivePolicy,
483
- slots,
484
- onError: combinedOnError,
485
- onSlotMetric: combinedOnSlotMetric
486
- });
487
- const encoder = new TextEncoder;
488
- return new ReadableStream({
489
- async start(controller) {
490
- try {
491
- let header = headerHtml;
492
- if (preparedSlots.length > 0 && !header.includes(STREAMING_RUNTIME_GLOBAL)) {
493
- header = injectHtmlIntoHead(header, renderStreamingSlotsRuntimeTag(nonce));
494
- }
495
- controller.enqueue(toUint8(header, encoder));
496
- const pending = preparedSlots.map((slot) => {
497
- const fallback = renderStreamingSlotPlaceholder(slot.id, slot.fallbackHtml ?? "");
498
- controller.enqueue(toUint8(fallback, encoder));
499
- return resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric);
500
- });
501
- while (pending.length > 0) {
502
- const { original, result } = await nextResolvedSlot(pending);
503
- const index = pending.indexOf(original);
504
- if (index >= 0)
505
- pending.splice(index, 1);
506
- if (result.html === null)
507
- continue;
508
- emitSlotMetric({
509
- type: "patched",
510
- slotId: result.id,
511
- durationMs: result.durationMs,
512
- bytes: result.bytes
513
- }, combinedOnSlotMetric);
514
- controller.enqueue(toUint8(renderStreamingSlotPatchTag(result.id, result.html, nonce), encoder));
515
- }
516
- if (footerHtml.length > 0) {
517
- controller.enqueue(toUint8(footerHtml, encoder));
518
- }
519
- controller.close();
520
- } catch (error) {
521
- controller.error(error);
522
- }
523
- }
524
- });
525
- }, injectStreamingRuntimeIntoStream = (stream, nonce) => {
526
- const runtimeTag = renderStreamingSlotsRuntimeTag(nonce);
527
- const encoder = new TextEncoder;
528
- const decoder = new TextDecoder;
529
- const lookbehind = CLOSING_HEAD_TAG_LENGTH - 1;
530
- return new ReadableStream({
531
- async start(controller) {
532
- const reader = stream.getReader();
533
- let injected = false;
534
- let pending = "";
535
- try {
536
- for (;; ) {
537
- const { done, value } = await reader.read();
538
- if (done)
539
- break;
540
- if (!value)
541
- continue;
542
- pending += streamChunkToString2(value, decoder);
543
- if (injected) {
544
- controller.enqueue(encoder.encode(pending));
545
- pending = "";
546
- continue;
547
- }
548
- const headIndex = pending.indexOf(CLOSING_HEAD_TAG2);
549
- if (headIndex >= 0) {
550
- const withRuntime = `${pending.slice(0, headIndex)}${runtimeTag}${pending.slice(headIndex)}`;
551
- controller.enqueue(encoder.encode(withRuntime));
552
- pending = "";
553
- injected = true;
554
- continue;
555
- }
556
- if (pending.length > lookbehind) {
557
- const safeText = pending.slice(0, pending.length - lookbehind);
558
- controller.enqueue(encoder.encode(safeText));
559
- pending = pending.slice(-lookbehind);
560
- }
561
- }
562
- pending += decoder.decode();
563
- if (!injected) {
564
- pending = injectHtmlIntoHead(pending, runtimeTag);
565
- }
566
- if (pending.length > 0) {
567
- controller.enqueue(encoder.encode(pending));
568
- }
569
- controller.close();
570
- } catch (error) {
571
- controller.error(error);
572
- }
573
- }
574
- });
575
- }, appendStreamingSlotPatchesToStream = (stream, slots = [], {
576
- injectRuntime = true,
577
- nonce,
578
- onError,
579
- onSlotMetric,
580
- policy
581
- } = {}) => {
582
- const resolvedPolicy = resolveStreamingSlotPolicy(policy);
583
- const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
584
- const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
585
- const effectivePolicy = {
586
- ...resolvedPolicy,
587
- onSlotMetric: combinedOnSlotMetric
588
- };
589
- const preparedSlots = prepareSlots({
590
- policy: effectivePolicy,
591
- slots,
592
- onError: combinedOnError,
593
- onSlotMetric: combinedOnSlotMetric
594
- });
595
- if (preparedSlots.length === 0)
596
- return stream;
597
- const source = injectRuntime ? injectStreamingRuntimeIntoStream(stream, nonce) : stream;
598
- const encoder = new TextEncoder;
599
- const decoder = new TextDecoder;
600
- const reader = source.getReader();
601
- const pending = preparedSlots.map((slot) => resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric));
602
- return new ReadableStream({
603
- async start(controller) {
604
- let baseDone = false;
605
- let baseRead = reader.read();
606
- let tail = "";
607
- let footer = "";
608
- try {
609
- while (!baseDone || pending.length > 0) {
610
- const racers = [];
611
- if (!baseDone) {
612
- racers.push(baseRead.then(({ done, value }) => ({
613
- done,
614
- kind: "base",
615
- value
616
- })));
617
- }
618
- if (pending.length > 0) {
619
- racers.push(nextResolvedSlot(pending).then((resolved) => ({
620
- kind: "slot",
621
- ...resolved
622
- })));
623
- }
624
- if (racers.length === 0)
625
- break;
626
- const winner = await Promise.race(racers);
627
- if (winner.kind === "base") {
628
- if (winner.done) {
629
- baseDone = true;
630
- tail += decoder.decode();
631
- const footerStart = tail.search(CLOSING_PAGE_TAG_REGEX);
632
- if (footerStart >= 0) {
633
- const content = tail.slice(0, footerStart);
634
- footer = tail.slice(footerStart);
635
- if (content.length > 0) {
636
- controller.enqueue(encoder.encode(content));
637
- }
638
- } else if (tail.length > 0) {
639
- controller.enqueue(encoder.encode(tail));
640
- }
641
- tail = "";
642
- } else if (winner.value) {
643
- tail += streamChunkToString2(winner.value, decoder);
644
- if (tail.length > STREAM_TAIL_LOOKBEHIND) {
645
- const content = tail.slice(0, tail.length - STREAM_TAIL_LOOKBEHIND);
646
- controller.enqueue(encoder.encode(content));
647
- tail = tail.slice(-STREAM_TAIL_LOOKBEHIND);
648
- }
649
- baseRead = reader.read();
650
- }
651
- continue;
652
- }
653
- const index = pending.indexOf(winner.original);
654
- if (index >= 0)
655
- pending.splice(index, 1);
656
- if (winner.result.html === null)
657
- continue;
658
- emitSlotMetric({
659
- type: "patched",
660
- slotId: winner.result.id,
661
- durationMs: winner.result.durationMs,
662
- bytes: winner.result.bytes
663
- }, combinedOnSlotMetric);
664
- controller.enqueue(encoder.encode(renderStreamingSlotPatchTag(winner.result.id, winner.result.html, nonce)));
665
- }
666
- if (footer.length > 0)
667
- controller.enqueue(encoder.encode(footer));
668
- controller.close();
669
- } catch (error) {
670
- controller.error(error);
671
- }
672
- }
673
- });
674
- };
675
- var init_streamingSlots = __esm(() => {
676
- init_escapeScriptContent();
677
- CLOSING_HEAD_TAG_LENGTH = CLOSING_HEAD_TAG2.length;
678
- CLOSING_PAGE_TAG_REGEX = /<\/body>\s*<\/html>\s*$/i;
679
- currentStreamingSlotPolicy = {
680
- timeoutMs: STREAMING_SLOT_TIMEOUT_MS,
681
- fallbackHtml: "",
682
- errorHtml: undefined,
683
- maxSlotsPerResponse: STREAMING_SLOT_MAX_PER_RESPONSE,
684
- maxSlotHtmlSizeBytes: STREAMING_SLOT_MAX_HTML_BYTES
685
- };
686
- });
687
-
688
- // src/core/streamingSlotRegistrar.ts
689
- var registrar = null, setStreamingSlotRegistrar = (nextRegistrar) => {
690
- registrar = nextRegistrar;
691
- }, registerStreamingSlot = (slot) => {
692
- registrar?.(slot);
693
- };
694
-
695
- // src/core/streamingSlotRegistry.ts
696
- var asyncLocalStorage, isServerRuntime = () => typeof process !== "undefined" && typeof process.versions?.node === "string", ensureAsyncLocalStorage = async () => {
697
- if (typeof asyncLocalStorage !== "undefined")
698
- return asyncLocalStorage;
699
- if (!isServerRuntime()) {
700
- asyncLocalStorage = null;
701
- return asyncLocalStorage;
702
- }
703
- const mod = await import("async_hooks");
704
- asyncLocalStorage = new mod.AsyncLocalStorage;
705
- return asyncLocalStorage;
706
- }, registerStreamingSlot2 = (slot) => {
707
- if (!asyncLocalStorage)
708
- return;
709
- const store = asyncLocalStorage.getStore();
710
- if (!store)
711
- return;
712
- store.set(slot.id, slot);
713
- }, runWithStreamingSlotRegistry = async (task) => {
714
- const storage = await ensureAsyncLocalStorage();
715
- if (!storage) {
716
- return {
717
- result: await task(),
718
- slots: []
719
- };
720
- }
721
- return storage.run(new Map, async () => {
722
- const result = await task();
723
- const store = storage.getStore();
724
- return {
725
- result,
726
- slots: store ? [...store.values()] : []
727
- };
728
- });
729
- };
730
- var init_streamingSlotRegistry = __esm(() => {
731
- setStreamingSlotRegistrar(registerStreamingSlot2);
732
- });
733
-
734
- // src/core/responseEnhancers.ts
735
- var toResponse = async (responseLike) => await responseLike, cloneHeaders = (response) => {
736
- const headers = new Headers(response.headers);
737
- return headers;
738
- }, enhanceHtmlResponseWithStreamingSlots = (response, { nonce, onError, streamingSlots = [], policy } = {}) => {
739
- if (!response.body || streamingSlots.length === 0) {
740
- return response;
741
- }
742
- const body = appendStreamingSlotPatchesToStream(response.body, streamingSlots, {
743
- nonce,
744
- onError,
745
- policy
746
- });
747
- return new Response(body, {
748
- headers: cloneHeaders(response),
749
- status: response.status,
750
- statusText: response.statusText
751
- });
752
- }, withStreamingSlots = async (responseLike, options = {}) => enhanceHtmlResponseWithStreamingSlots(await toResponse(responseLike), options), mergeStreamingSlots = (registered, explicit) => {
753
- const merged = new Map;
754
- for (const slot of registered)
755
- merged.set(slot.id, slot);
756
- for (const slot of explicit)
757
- merged.set(slot.id, slot);
758
- return [...merged.values()];
759
- }, withRegisteredStreamingSlots = async (renderResponse, options = {}) => {
760
- const { result, slots } = await runWithStreamingSlotRegistry(renderResponse);
761
- const explicit = options.streamingSlots ?? [];
762
- return withStreamingSlots(result, {
763
- ...options,
764
- streamingSlots: mergeStreamingSlots(slots, explicit)
765
- });
766
- };
767
- var init_responseEnhancers = __esm(() => {
768
- init_streamingSlots();
769
- init_streamingSlotRegistry();
770
- });
771
-
772
- // src/utils/ssrErrorPage.ts
773
- var ssrErrorPage = (framework, error) => {
774
- const frameworkColors = {
775
- angular: "#dd0031",
776
- html: "#e34c26",
777
- htmx: "#1a365d",
778
- react: "#61dafb",
779
- svelte: "#ff3e00",
780
- vue: "#42b883"
781
- };
782
- const accent = frameworkColors[framework] ?? "#94a3b8";
783
- const label = framework.charAt(0).toUpperCase() + framework.slice(1);
784
- const message = error instanceof Error ? error.message : String(error);
785
- return `<!DOCTYPE html>
786
- <html>
787
- <head>
788
- <meta charset="utf-8">
789
- <meta name="viewport" content="width=device-width, initial-scale=1">
790
- <title>SSR Error - AbsoluteJS</title>
791
- <style>
792
- *{margin:0;padding:0;box-sizing:border-box}
793
- body{min-height:100vh;background:linear-gradient(135deg,rgba(15,23,42,0.98) 0%,rgba(30,41,59,0.98) 100%);color:#e2e8f0;font-family:"JetBrains Mono","Fira Code",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:14px;line-height:1.6;display:flex;align-items:flex-start;justify-content:center;padding:32px}
794
- .card{max-width:720px;width:100%;background:rgba(30,41,59,0.6);border:1px solid rgba(71,85,105,0.5);border-radius:16px;box-shadow:0 25px 50px -12px rgba(0,0,0,0.5),0 0 0 1px rgba(255,255,255,0.05);overflow:hidden}
795
- .header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 24px;background:rgba(15,23,42,0.5);border-bottom:1px solid rgba(71,85,105,0.4)}
796
- .brand{font-weight:700;font-size:20px;color:#fff;letter-spacing:-0.02em}
797
- .badge{padding:5px 10px;border-radius:8px;font-size:12px;font-weight:600;background:${accent};color:#fff;opacity:0.95;box-shadow:0 2px 4px rgba(0,0,0,0.2)}
798
- .kind{color:#94a3b8;font-size:13px;font-weight:500}
799
- .content{padding:24px}
800
- .label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.08em;color:#94a3b8;margin-bottom:8px}
801
- .message{margin:0;padding:16px 20px;background:rgba(239,68,68,0.12);border:1px solid rgba(239,68,68,0.25);border-radius:10px;overflow-x:auto;white-space:pre-wrap;word-break:break-word;color:#fca5a5;font-size:13px;line-height:1.5}
802
- .hint{margin-top:20px;padding:12px 20px;background:rgba(71,85,105,0.3);border-radius:10px;border:1px solid rgba(71,85,105,0.4);color:#cbd5e1;font-size:13px}
803
- </style>
804
- </head>
805
- <body>
806
- <div class="card">
807
- <div class="header">
808
- <div style="display:flex;align-items:center;gap:12px">
809
- <span class="brand">AbsoluteJS</span>
810
- <span class="badge">${label}</span>
811
- </div>
812
- <span class="kind">Server Render Error</span>
813
- </div>
814
- <div class="content">
815
- <div class="label">What went wrong</div>
816
- <pre class="message">${message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}</pre>
817
- <div class="hint">A component threw during server-side rendering. Check the terminal for the full stack trace.</div>
818
- </div>
819
- </div>
820
- </body>
821
- </html>`;
822
- };
823
-
824
- // src/utils/stringModifiers.ts
825
- var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9\-_]+/g, "").replace(/[-_]{2,}/g, "-"), toKebab = (str) => normalizeSlug(str).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), toPascal = (str) => {
826
- if (!str.includes("-") && !str.includes("_")) {
827
- return str.charAt(0).toUpperCase() + str.slice(1);
828
- }
829
- return normalizeSlug(str).split(/[-_]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase()).join("");
830
- }, toScreamingSnake = (str) => str.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
831
-
832
- // src/utils/resolveConvention.ts
833
- import { basename } from "path";
834
- var CONVENTIONS_KEY = "__absoluteConventions", isConventionsMap = (value) => Boolean(value) && typeof value === "object", getMap = () => {
835
- const value = Reflect.get(globalThis, CONVENTIONS_KEY);
836
- if (isConventionsMap(value))
837
- return value;
838
- const empty = {};
839
- return empty;
840
- }, derivePageName = (pagePath) => {
841
- const base = basename(pagePath);
842
- const dotIndex = base.indexOf(".");
843
- const name = dotIndex > 0 ? base.slice(0, dotIndex) : base;
844
- return toPascal(name);
845
- }, resolveErrorConventionPath = (framework, pageName) => {
846
- const conventions = getMap()[framework];
847
- if (!conventions)
848
- return;
849
- return conventions.pages?.[pageName]?.error ?? conventions.defaults?.error;
850
- }, resolveNotFoundConventionPath = (framework) => getMap()[framework]?.defaults?.notFound, setConventions = (map) => {
851
- Reflect.set(globalThis, CONVENTIONS_KEY, map);
852
- }, isDev = () => true, buildErrorProps = (error) => {
853
- const message = error instanceof Error ? error.message : String(error);
854
- const stack = isDev() && error instanceof Error ? error.stack : undefined;
855
- return { error: { message, stack } };
856
- }, renderReactError = async (conventionPath, errorProps) => {
857
- const { createElement } = await import("react");
858
- const { renderToReadableStream } = await import("react-dom/server");
859
- const mod = await import(conventionPath);
860
- const [firstKey] = Object.keys(mod);
861
- const ErrorComponent = mod.default ?? (firstKey ? mod[firstKey] : undefined);
862
- const element = createElement(ErrorComponent, errorProps);
863
- const stream = await renderToReadableStream(element);
864
- return new Response(stream, {
865
- headers: { "Content-Type": "text/html" },
866
- status: 500
867
- });
868
- }, renderSvelteError = async (conventionPath, errorProps) => {
869
- const { render } = await import("svelte/server");
870
- const mod = await import(conventionPath);
871
- const ErrorComponent = mod.default;
872
- const { head, body } = render(ErrorComponent, {
873
- props: errorProps
874
- });
875
- const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
876
- return new Response(html, {
877
- headers: { "Content-Type": "text/html" },
878
- status: 500
879
- });
880
- }, unescapeVueStyles = (ssrBody) => {
881
- let styles = "";
882
- const body = ssrBody.replace(/<style>([\s\S]*?)<\/style>/g, (_, css) => {
883
- styles += `<style>${css.replace(/&quot;/g, '"').replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">")}</style>`;
884
- return "";
885
- });
886
- return { body, styles };
887
- }, renderVueError = async (conventionPath, errorProps) => {
888
- const { createSSRApp, h } = await import("vue");
889
- const { renderToString } = await import("vue/server-renderer");
890
- const mod = await import(conventionPath);
891
- const ErrorComponent = mod.default;
892
- const app = createSSRApp({
893
- render: () => h(ErrorComponent, errorProps)
894
- });
895
- const rawBody = await renderToString(app);
896
- const { styles, body } = unescapeVueStyles(rawBody);
897
- const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
898
- return new Response(html, {
899
- headers: { "Content-Type": "text/html" },
900
- status: 500
901
- });
902
- }, renderAngularError = async (conventionPath, errorProps) => {
903
- const mod = await import(conventionPath);
904
- const renderError = mod.default ?? mod.renderError;
905
- if (typeof renderError !== "function")
906
- return null;
907
- const html = renderError(errorProps);
908
- return new Response(html, {
909
- headers: { "Content-Type": "text/html" },
910
- status: 500
911
- });
912
- }, logConventionRenderError = (framework, label, renderError) => {
913
- const message = renderError instanceof Error ? renderError.message : "";
914
- if (message.includes("Cannot find module") || message.includes("Cannot find package") || message.includes("not found in module")) {
915
- console.error(`[SSR] Convention ${label} page for ${framework} failed: missing framework package. Ensure the ${framework} runtime is installed (e.g. bun add ${framework === "react" ? "react react-dom" : framework}).`);
916
- return;
917
- }
918
- console.error(`[SSR] Failed to render ${framework} convention ${label} page:`, renderError);
919
- }, ERROR_RENDERERS, renderConventionError = async (framework, pageName, error) => {
920
- const conventionPath = resolveErrorConventionPath(framework, pageName);
921
- if (!conventionPath)
922
- return null;
923
- const errorProps = buildErrorProps(error);
924
- const renderer = ERROR_RENDERERS[framework];
925
- if (!renderer)
926
- return null;
927
- try {
928
- return await renderer(conventionPath, errorProps);
929
- } catch (renderError) {
930
- logConventionRenderError(framework, "error", renderError);
931
- }
932
- return null;
933
- }, renderReactNotFound = async (conventionPath) => {
934
- const { createElement } = await import("react");
935
- const { renderToReadableStream } = await import("react-dom/server");
936
- const mod = await import(conventionPath);
937
- const [nfKey] = Object.keys(mod);
938
- const NotFoundComponent = mod.default ?? (nfKey ? mod[nfKey] : undefined);
939
- const element = createElement(NotFoundComponent);
940
- const stream = await renderToReadableStream(element);
941
- return new Response(stream, {
942
- headers: { "Content-Type": "text/html" },
943
- status: 404
944
- });
945
- }, renderSvelteNotFound = async (conventionPath) => {
946
- const { render } = await import("svelte/server");
947
- const mod = await import(conventionPath);
948
- const NotFoundComponent = mod.default;
949
- const { head, body } = render(NotFoundComponent);
950
- const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
951
- return new Response(html, {
952
- headers: { "Content-Type": "text/html" },
953
- status: 404
954
- });
955
- }, renderVueNotFound = async (conventionPath) => {
956
- const { createSSRApp, h } = await import("vue");
957
- const { renderToString } = await import("vue/server-renderer");
958
- const mod = await import(conventionPath);
959
- const NotFoundComponent = mod.default;
960
- const app = createSSRApp({
961
- render: () => h(NotFoundComponent)
962
- });
963
- const rawBody = await renderToString(app);
964
- const { styles, body } = unescapeVueStyles(rawBody);
965
- const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
966
- return new Response(html, {
967
- headers: { "Content-Type": "text/html" },
968
- status: 404
969
- });
970
- }, renderAngularNotFound = async (conventionPath) => {
971
- const mod = await import(conventionPath);
972
- const renderNotFound = mod.default ?? mod.renderNotFound;
973
- if (typeof renderNotFound !== "function")
974
- return null;
975
- const html = renderNotFound();
976
- return new Response(html, {
977
- headers: { "Content-Type": "text/html" },
978
- status: 404
979
- });
980
- }, NOT_FOUND_RENDERERS, renderConventionNotFound = async (framework) => {
981
- const conventionPath = resolveNotFoundConventionPath(framework);
982
- if (!conventionPath)
983
- return null;
984
- const renderer = NOT_FOUND_RENDERERS[framework];
985
- if (!renderer)
986
- return null;
987
- try {
988
- return await renderer(conventionPath);
989
- } catch (renderError) {
990
- logConventionRenderError(framework, "not-found", renderError);
991
- }
992
- return null;
993
- }, NOT_FOUND_PRIORITY, renderFirstNotFound = async () => {
994
- for (const framework of NOT_FOUND_PRIORITY) {
995
- if (!getMap()[framework]?.defaults?.notFound)
996
- continue;
997
- const response = await renderConventionNotFound(framework);
998
- if (response)
999
- return response;
1000
- }
1001
- return null;
1002
- };
1003
- var init_resolveConvention = __esm(() => {
1004
- ERROR_RENDERERS = {
1005
- angular: renderAngularError,
1006
- react: renderReactError,
1007
- svelte: renderSvelteError,
1008
- vue: renderVueError
1009
- };
1010
- NOT_FOUND_RENDERERS = {
1011
- angular: renderAngularNotFound,
1012
- react: renderReactNotFound,
1013
- svelte: renderSvelteNotFound,
1014
- vue: renderVueNotFound
1015
- };
1016
- NOT_FOUND_PRIORITY = [
1017
- "react",
1018
- "svelte",
1019
- "vue",
1020
- "angular"
1021
- ];
1022
- });
1023
-
1024
- // src/react/streamingSlotCollection.tsx
1025
- import { createContext, useContext } from "react";
1026
- import { jsxDEV } from "react/jsx-dev-runtime";
1027
- var StreamingSlotCollectorContext, StreamingSlotCollectorProvider = ({
1028
- children,
1029
- collector
1030
- }) => /* @__PURE__ */ jsxDEV(StreamingSlotCollectorContext.Provider, {
1031
- value: collector,
1032
- children
1033
- }, undefined, false, undefined, this), useStreamingSlotCollector = () => useContext(StreamingSlotCollectorContext);
1034
- var init_streamingSlotCollection = __esm(() => {
1035
- StreamingSlotCollectorContext = createContext(null);
1036
- });
1037
-
1038
- // src/react/pageHandler.ts
1039
- var ssrDirty = false, buildDirtyResponse = (index, maybeProps) => {
1040
- const propsScript = maybeProps ? `window.__INITIAL_PROPS__=${JSON.stringify(maybeProps)};` : "";
1041
- const dirtyFlag = "window.__SSR_DIRTY__=true;";
1042
- const refreshSetup = "window.__REFRESH_BUFFER__=[];" + "window.$RefreshReg$=function(t,i){window.__REFRESH_BUFFER__.push([t,i])};" + "window.$RefreshSig$=function(){return function(t){return t}};";
1043
- const inlineScript = `${propsScript}${dirtyFlag}${refreshSetup}`;
1044
- const html = `<!DOCTYPE html><html><head></head><body>` + `<script>${inlineScript}</script>` + `<script type="module" src="${index}"></script>` + `</body></html>`;
1045
- return new Response(html, {
1046
- headers: { "Content-Type": "text/html" }
1047
- });
1048
- }, handleReactPageRequest = async (PageComponent, index, ...props) => {
1049
- const [maybeProps] = props;
1050
- if (ssrDirty) {
1051
- return buildDirtyResponse(index, maybeProps);
1052
- }
1053
- try {
1054
- const { createElement } = await import("react");
1055
- const { renderToReadableStream } = await import("react-dom/server");
1056
- const slotCollector = new Map;
1057
- const pageElement = maybeProps !== undefined ? createElement(PageComponent, maybeProps) : createElement(PageComponent);
1058
- const element = createElement(StreamingSlotCollectorProvider, { collector: slotCollector }, pageElement);
1059
- const propsScript = maybeProps ? `window.__INITIAL_PROPS__=${JSON.stringify(maybeProps)};` : "";
1060
- const refreshSetup = "window.__REFRESH_BUFFER__=[];window.$RefreshReg$=function(t,i){window.__REFRESH_BUFFER__.push([t,i])};window.$RefreshSig$=function(){return function(t){return t}};";
1061
- const stream = await renderToReadableStream(element, {
1062
- bootstrapModules: [index],
1063
- bootstrapScriptContent: propsScript + refreshSetup || undefined,
1064
- onError(error) {
1065
- console.error("[SSR] React streaming error:", error);
1066
- }
1067
- });
1068
- if ("allReady" in stream && stream.allReady instanceof Promise) {
1069
- await stream.allReady;
1070
- }
1071
- const htmlStream = injectIslandPageContextStream(stream);
1072
- return withStreamingSlots(new Response(htmlStream, {
1073
- headers: { "Content-Type": "text/html" }
1074
- }), {
1075
- streamingSlots: [...slotCollector.values()]
1076
- });
1077
- } catch (error) {
1078
- console.error("[SSR] React render error:", error);
1079
- const pageName = PageComponent.name || PageComponent.displayName || "";
1080
- const conventionResponse = await renderConventionError("react", pageName, error);
1081
- if (conventionResponse)
1082
- return conventionResponse;
1083
- return new Response(ssrErrorPage("react", error), {
1084
- headers: { "Content-Type": "text/html" },
1085
- status: 500
1086
- });
528
+ console.error("[SSR] React render error:", error);
529
+ const pageName = PageComponent.name || PageComponent.displayName || "";
530
+ const conventionResponse = await renderConventionError("react", pageName, error);
531
+ if (conventionResponse)
532
+ return conventionResponse;
533
+ return new Response(ssrErrorPage("react", error), {
534
+ headers: { "Content-Type": "text/html" },
535
+ status: 500
536
+ });
1087
537
  }
1088
538
  }, invalidateReactSsrCache = () => {
1089
539
  ssrDirty = true;
1090
540
  };
1091
541
  var init_pageHandler = __esm(() => {
1092
- init_responseEnhancers();
1093
542
  init_resolveConvention();
1094
- init_streamingSlotCollection();
1095
543
  });
1096
544
 
1097
545
  // src/core/islandManifest.ts
@@ -1665,413 +1113,995 @@ var angularIslandSelector = "abs-angular-island", getAngularIslandSelector = (_i
1665
1113
  platformProviders: [],
1666
1114
  url: "/"
1667
1115
  }));
1668
- const islandHtml = extractAngularIslandRoot(html, selector);
1669
- requestCache?.set(renderCacheKey, islandHtml);
1670
- return islandHtml;
1116
+ const islandHtml = extractAngularIslandRoot(html, selector);
1117
+ requestCache?.set(renderCacheKey, islandHtml);
1118
+ return islandHtml;
1119
+ };
1120
+ var init_islands2 = __esm(() => {
1121
+ init_angularDeps();
1122
+ init_ssrRender();
1123
+ init_registerClientScript();
1124
+ wrapperMetadataCache = new Map;
1125
+ requestRenderCache = new Map;
1126
+ });
1127
+
1128
+ // src/core/islandSsr.ts
1129
+ import { createElement } from "react";
1130
+ import { renderToStaticMarkup } from "react-dom/server";
1131
+ import { render as renderSvelte } from "svelte/server";
1132
+ import { createSSRApp, h } from "vue";
1133
+ import { renderToString as renderVueToString } from "vue/server-renderer";
1134
+ var renderReactIslandToHtml = (component, props) => renderToStaticMarkup(createElement(component, props)), renderSvelteIslandToHtml = (component, props) => {
1135
+ const { body } = renderSvelte(component, { props });
1136
+ return body;
1137
+ }, renderVueIslandToHtml = (component, props) => {
1138
+ const app = createSSRApp({
1139
+ render: () => h(component, props)
1140
+ });
1141
+ return renderVueToString(app);
1142
+ };
1143
+ var init_islandSsr = __esm(() => {
1144
+ init_islands2();
1145
+ });
1146
+
1147
+ // src/constants.ts
1148
+ 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;
1149
+ var init_constants = __esm(() => {
1150
+ MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
1151
+ MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
1152
+ TWO_THIRDS = 2 / 3;
1153
+ });
1154
+
1155
+ // src/svelte/lowerIslandSyntax.ts
1156
+ var ISLAND_TAG_RE, extractBracedExpression = (text, braceStart) => {
1157
+ let depth = 0;
1158
+ for (let index = braceStart;index < text.length; index += 1) {
1159
+ const char = text[index];
1160
+ if (char === "{")
1161
+ depth += 1;
1162
+ if (char === "}")
1163
+ depth -= 1;
1164
+ if (depth === 0) {
1165
+ return text.slice(braceStart + 1, index).trim();
1166
+ }
1167
+ }
1168
+ return null;
1169
+ }, extractIslandAttribute = (attributeString, name) => {
1170
+ const quotedMatch = attributeString.match(new RegExp(`\\b${name}\\s*=\\s*["']([^"']+)["']`));
1171
+ if (quotedMatch?.[1]) {
1172
+ return { expression: JSON.stringify(quotedMatch[1]), found: true };
1173
+ }
1174
+ const attributeIndex = attributeString.search(new RegExp(`\\b${name}\\s*=\\s*\\{`));
1175
+ if (attributeIndex < 0) {
1176
+ return { expression: "", found: false };
1177
+ }
1178
+ const braceStart = attributeString.indexOf("{", attributeIndex);
1179
+ if (braceStart < 0) {
1180
+ return { expression: "", found: false };
1181
+ }
1182
+ const expression = extractBracedExpression(attributeString, braceStart);
1183
+ if (expression === null) {
1184
+ return { expression: "", found: false };
1185
+ }
1186
+ return { expression, found: true };
1187
+ }, lowerSvelteIslandSyntax = (source, _mode = "server") => {
1188
+ if (!source.includes("<Island")) {
1189
+ return { code: source, transformed: false };
1190
+ }
1191
+ let islandIndex = 0;
1192
+ const transformedMarkup = source.replace(ISLAND_TAG_RE, (fullMatch, attributeString) => {
1193
+ const framework = extractIslandAttribute(attributeString, "framework");
1194
+ const component = extractIslandAttribute(attributeString, "component");
1195
+ if (!framework.found || !component.found) {
1196
+ return fullMatch;
1197
+ }
1198
+ const hydrate = extractIslandAttribute(attributeString, "hydrate");
1199
+ const props = extractIslandAttribute(attributeString, "props");
1200
+ const slotId = `absolute-svelte-island-${islandIndex.toString(BASE_36_RADIX)}`;
1201
+ islandIndex += 1;
1202
+ const resolveExpression = `await __absoluteResolveIslandHtml(${JSON.stringify(slotId)}, { component: ${component.expression}, framework: ${framework.expression}, hydrate: ${hydrate.found ? hydrate.expression : JSON.stringify("load")}, props: ${props.found ? props.expression : "{}"} })`;
1203
+ return `<div data-absolute-island-slot="${slotId}" style="display: contents">{@html ${resolveExpression}}</div>`;
1204
+ });
1205
+ const importLine = 'import { resolveIslandHtml as __absoluteResolveIslandHtml } from "@absolutejs/absolute/svelte";';
1206
+ if (transformedMarkup.includes("<script")) {
1207
+ return {
1208
+ code: transformedMarkup.replace(/<script(\s[^>]*)?>/, (match) => `${match}
1209
+ ${importLine}
1210
+ `),
1211
+ transformed: true
1212
+ };
1213
+ }
1214
+ return {
1215
+ code: `<script lang="ts">
1216
+ ${importLine}
1217
+ </script>
1218
+ ${transformedMarkup}`,
1219
+ transformed: true
1220
+ };
1221
+ };
1222
+ var init_lowerIslandSyntax = __esm(() => {
1223
+ init_constants();
1224
+ ISLAND_TAG_RE = /<Island\b([\s\S]*?)\/>/g;
1225
+ });
1226
+
1227
+ // src/core/svelteServerModule.ts
1228
+ import { mkdir, readdir } from "fs/promises";
1229
+ import { basename as basename2, dirname as dirname2, extname, join as join2, relative, resolve as resolve3 } from "path";
1230
+ var serverCacheRoot, compiledModuleCache, originalSourcePathCache, transpiler, ensureRelativeImportPath = (from, target) => {
1231
+ const importPath = relative(dirname2(from), target).replace(/\\/g, "/");
1232
+ return importPath.startsWith(".") ? importPath : `./${importPath}`;
1233
+ }, processDirectoryEntries = (entries, dir, targetFileName, stack) => {
1234
+ for (const entry of entries) {
1235
+ const entryPath = join2(dir, entry.name);
1236
+ if (entry.isDirectory())
1237
+ stack.push(entryPath);
1238
+ if (entry.isFile() && entry.name === targetFileName) {
1239
+ return entryPath;
1240
+ }
1241
+ }
1242
+ return null;
1243
+ }, searchDirectoryLevel = async (dirs, targetFileName) => {
1244
+ if (dirs.length === 0)
1245
+ return null;
1246
+ const nextStack = [];
1247
+ const dirEntries = await Promise.all(dirs.map(async (dir) => ({
1248
+ dir,
1249
+ entries: await readdir(dir, {
1250
+ encoding: "utf-8",
1251
+ withFileTypes: true
1252
+ })
1253
+ })));
1254
+ for (const { dir, entries } of dirEntries) {
1255
+ const found = processDirectoryEntries(entries, dir, targetFileName, nextStack);
1256
+ if (found)
1257
+ return found;
1258
+ }
1259
+ return searchDirectoryLevel(nextStack, targetFileName);
1260
+ }, findSourceFileByBasename = async (searchRoot, targetFileName) => searchDirectoryLevel([searchRoot], targetFileName), normalizeBuiltSvelteFileName = (sourcePath) => basename2(sourcePath).replace(/-[a-z0-9]{6,}(?=\.svelte$)/i, ""), resolveOriginalSourcePath = async (sourcePath) => {
1261
+ const cachedPath = originalSourcePathCache.get(sourcePath);
1262
+ if (cachedPath !== undefined) {
1263
+ return cachedPath;
1264
+ }
1265
+ if (!sourcePath.includes(`${join2(process.cwd(), "build")}${process.platform === "win32" ? "" : "/"}`) && !sourcePath.includes("/build/")) {
1266
+ originalSourcePathCache.set(sourcePath, sourcePath);
1267
+ return sourcePath;
1268
+ }
1269
+ const resolvedSourcePath = await findSourceFileByBasename(join2(process.cwd(), "src"), normalizeBuiltSvelteFileName(sourcePath));
1270
+ const nextPath = resolvedSourcePath ?? sourcePath;
1271
+ originalSourcePathCache.set(sourcePath, nextPath);
1272
+ return nextPath;
1273
+ }, resolveRelativeModule = async (spec, from) => {
1274
+ if (!spec.startsWith(".")) {
1275
+ return null;
1276
+ }
1277
+ const basePath = resolve3(dirname2(from), spec);
1278
+ const candidates = [
1279
+ basePath,
1280
+ `${basePath}.ts`,
1281
+ `${basePath}.js`,
1282
+ `${basePath}.mjs`,
1283
+ `${basePath}.cjs`,
1284
+ `${basePath}.json`,
1285
+ join2(basePath, "index.ts"),
1286
+ join2(basePath, "index.js"),
1287
+ join2(basePath, "index.mjs"),
1288
+ join2(basePath, "index.cjs"),
1289
+ join2(basePath, "index.json")
1290
+ ];
1291
+ const existResults = await Promise.all(candidates.map((candidate) => Bun.file(candidate).exists()));
1292
+ const foundIndex = existResults.indexOf(true);
1293
+ return foundIndex >= 0 ? candidates[foundIndex] ?? null : null;
1294
+ }, getCachedModulePath = (sourcePath) => {
1295
+ const relativeSourcePath = relative(process.cwd(), sourcePath).replace(/\\/g, "/");
1296
+ const normalizedSourcePath = relativeSourcePath.startsWith("..") ? sourcePath.replace(/[:\\/]/g, "_") : relativeSourcePath;
1297
+ return join2(serverCacheRoot, `${normalizedSourcePath}.server.js`);
1298
+ }, resolveSvelteImport = async (spec, from) => {
1299
+ if (spec.startsWith("/")) {
1300
+ return spec;
1301
+ }
1302
+ if (!spec.startsWith(".")) {
1303
+ return null;
1304
+ }
1305
+ const explicitPath = resolve3(dirname2(from), spec);
1306
+ if (extname(explicitPath) === ".svelte") {
1307
+ return explicitPath;
1308
+ }
1309
+ const candidate = `${explicitPath}.svelte`;
1310
+ if (await Bun.file(candidate).exists() === true) {
1311
+ return candidate;
1312
+ }
1313
+ return null;
1314
+ }, writeIfChanged = async (path, content) => {
1315
+ const targetFile = Bun.file(path);
1316
+ const exists = await targetFile.exists();
1317
+ if (exists) {
1318
+ const currentContent = await targetFile.text();
1319
+ if (currentContent === content) {
1320
+ return;
1321
+ }
1322
+ }
1323
+ await Bun.write(path, content);
1324
+ }, compileSvelteServerModule = async (sourcePath) => {
1325
+ const cachedModulePath = compiledModuleCache.get(sourcePath);
1326
+ if (cachedModulePath) {
1327
+ return cachedModulePath;
1328
+ }
1329
+ const resolutionSourcePath = await resolveOriginalSourcePath(sourcePath);
1330
+ const source = await Bun.file(sourcePath).text();
1331
+ const { compile, preprocess } = await import("svelte/compiler");
1332
+ const loweredSource = lowerSvelteIslandSyntax(source, "server");
1333
+ const preprocessed = await preprocess(loweredSource.code, {});
1334
+ let transpiled = sourcePath.endsWith(".ts") || sourcePath.endsWith(".svelte.ts") ? transpiler.transformSync(preprocessed.code) : preprocessed.code;
1335
+ const childImportSpecs = Array.from(transpiled.matchAll(/from\s+['"]([^'"]+)['"]/g)).map((match) => match[1]).filter((value) => value !== undefined);
1336
+ const resolvedChildModules = await Promise.all(childImportSpecs.map((spec) => resolveSvelteImport(spec, resolutionSourcePath)));
1337
+ const resolvedModuleImports = await Promise.all(childImportSpecs.map((spec) => resolveRelativeModule(spec, resolutionSourcePath)));
1338
+ const childModulePaths = new Map;
1339
+ const rewrittenModulePaths = new Map;
1340
+ const compiledChildren = await Promise.all(childImportSpecs.map(async (spec, index) => {
1341
+ const resolvedChild = resolvedChildModules[index];
1342
+ if (!spec || !resolvedChild)
1343
+ return null;
1344
+ return {
1345
+ compiledPath: await compileSvelteServerModule(resolvedChild),
1346
+ spec
1347
+ };
1348
+ }));
1349
+ for (const result of compiledChildren) {
1350
+ if (result)
1351
+ childModulePaths.set(result.spec, result.compiledPath);
1352
+ }
1353
+ for (let index = 0;index < childImportSpecs.length; index += 1) {
1354
+ const spec = childImportSpecs[index];
1355
+ const resolvedModuleImport = resolvedModuleImports[index];
1356
+ if (!spec || !resolvedModuleImport)
1357
+ continue;
1358
+ if (resolvedChildModules[index])
1359
+ continue;
1360
+ rewrittenModulePaths.set(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), resolvedModuleImport));
1361
+ }
1362
+ for (const [spec, resolvedModuleImport] of rewrittenModulePaths) {
1363
+ transpiled = transpiled.replaceAll(spec, resolvedModuleImport);
1364
+ }
1365
+ let compiledCode = compile(transpiled, {
1366
+ css: "injected",
1367
+ experimental: {
1368
+ async: loweredSource.transformed
1369
+ },
1370
+ filename: resolutionSourcePath,
1371
+ generate: "server"
1372
+ }).js.code;
1373
+ for (const [spec, compiledChildPath] of childModulePaths) {
1374
+ compiledCode = compiledCode.replaceAll(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), compiledChildPath));
1375
+ }
1376
+ for (const [spec, resolvedModuleImport] of rewrittenModulePaths) {
1377
+ compiledCode = compiledCode.replaceAll(spec, resolvedModuleImport);
1378
+ }
1379
+ const compiledModulePath = getCachedModulePath(sourcePath);
1380
+ await mkdir(dirname2(compiledModulePath), { recursive: true });
1381
+ await writeIfChanged(compiledModulePath, compiledCode);
1382
+ compiledModuleCache.set(sourcePath, compiledModulePath);
1383
+ return compiledModulePath;
1671
1384
  };
1672
- var init_islands2 = __esm(() => {
1673
- init_angularDeps();
1674
- init_ssrRender();
1675
- init_registerClientScript();
1676
- wrapperMetadataCache = new Map;
1677
- requestRenderCache = new Map;
1678
- });
1679
-
1680
- // src/core/islandSsr.ts
1681
- import { createElement } from "react";
1682
- import { renderToStaticMarkup } from "react-dom/server";
1683
- import { render as renderSvelte } from "svelte/server";
1684
- import { createSSRApp, h } from "vue";
1685
- import { renderToString as renderVueToString } from "vue/server-renderer";
1686
- var renderReactIslandToHtml = (component, props) => renderToStaticMarkup(createElement(component, props)), renderSvelteIslandToHtml = (component, props) => {
1687
- const { body } = renderSvelte(component, { props });
1688
- return body;
1689
- }, renderVueIslandToHtml = (component, props) => {
1690
- const app = createSSRApp({
1691
- render: () => h(component, props)
1385
+ var init_svelteServerModule = __esm(() => {
1386
+ init_lowerIslandSyntax();
1387
+ serverCacheRoot = join2(process.cwd(), ".absolutejs", "islands", "svelte");
1388
+ compiledModuleCache = new Map;
1389
+ originalSourcePathCache = new Map;
1390
+ transpiler = new Bun.Transpiler({
1391
+ loader: "ts",
1392
+ target: "browser"
1692
1393
  });
1693
- return renderVueToString(app);
1694
- };
1695
- var init_islandSsr = __esm(() => {
1696
- init_islands2();
1697
- });
1698
-
1699
- // src/constants.ts
1700
- 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;
1701
- var init_constants = __esm(() => {
1702
- MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
1703
- MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
1704
- TWO_THIRDS = 2 / 3;
1705
1394
  });
1706
1395
 
1707
- // src/svelte/lowerIslandSyntax.ts
1708
- var ISLAND_TAG_RE, extractBracedExpression = (text, braceStart) => {
1709
- let depth = 0;
1710
- for (let index = braceStart;index < text.length; index += 1) {
1711
- const char = text[index];
1712
- if (char === "{")
1713
- depth += 1;
1714
- if (char === "}")
1715
- depth -= 1;
1716
- if (depth === 0) {
1717
- return text.slice(braceStart + 1, index).trim();
1718
- }
1396
+ // src/core/renderIslandMarkup.ts
1397
+ var islandSequence = 0, resolvedServerComponentCache, resolvedServerBuildComponentCache, nextIslandId = () => {
1398
+ islandSequence += 1;
1399
+ return `island-${islandSequence}`;
1400
+ }, isRecord2 = (value) => typeof value === "object" && value !== null, isReactServerIslandComponent = (value) => typeof value === "function", isSvelteServerIslandComponent = (value) => typeof value === "function", isVueServerIslandComponent = (value) => typeof value === "function" || isRecord2(value), isAngularServerIslandComponent = (value) => typeof value === "function", resolveBuildReferencePath = (source, registryPath) => {
1401
+ if (source.startsWith("file://"))
1402
+ return new URL(source).pathname;
1403
+ if (source.startsWith("."))
1404
+ return new URL(source, registryPath).pathname;
1405
+ return source;
1406
+ }, loadAndCompileServerBuildComponent = async (buildReferencePath) => {
1407
+ const compiledModulePath = await compileSvelteServerModule(buildReferencePath);
1408
+ const loadedModule = await import(compiledModulePath);
1409
+ return "default" in loadedModule ? loadedModule.default : loadedModule;
1410
+ }, loadServerBuildComponent = async (buildReferencePath) => {
1411
+ const cachedBuildComponent = resolvedServerBuildComponentCache.get(buildReferencePath);
1412
+ if (cachedBuildComponent) {
1413
+ return cachedBuildComponent;
1719
1414
  }
1720
- return null;
1721
- }, extractIslandAttribute = (attributeString, name) => {
1722
- const quotedMatch = attributeString.match(new RegExp(`\\b${name}\\s*=\\s*["']([^"']+)["']`));
1723
- if (quotedMatch?.[1]) {
1724
- return { expression: JSON.stringify(quotedMatch[1]), found: true };
1415
+ const loadPromise = loadAndCompileServerBuildComponent(buildReferencePath);
1416
+ resolvedServerBuildComponentCache.set(buildReferencePath, loadPromise);
1417
+ return loadPromise;
1418
+ }, loadServerImportComponent = async (resolvedComponent) => {
1419
+ const resolvedModulePath = resolvedComponent.startsWith(".") ? new URL(resolvedComponent, import.meta.url).pathname : resolvedComponent;
1420
+ const importTarget = resolvedModulePath.endsWith(".svelte") ? await compileSvelteServerModule(resolvedModulePath) : resolvedModulePath;
1421
+ const loadedModule = await import(importTarget);
1422
+ return "default" in loadedModule ? loadedModule.default : loadedModule;
1423
+ }, resolveIslandComponent = async (component) => {
1424
+ const buildReference = getIslandBuildReference(component);
1425
+ const buildReferencePath = buildReference?.source ? resolveBuildReferencePath(buildReference.source, import.meta.url) : null;
1426
+ if (buildReferencePath?.endsWith(".svelte")) {
1427
+ return loadServerBuildComponent(buildReferencePath);
1725
1428
  }
1726
- const attributeIndex = attributeString.search(new RegExp(`\\b${name}\\s*=\\s*\\{`));
1727
- if (attributeIndex < 0) {
1728
- return { expression: "", found: false };
1429
+ const resolvedComponent = getIslandComponent(component);
1430
+ if (typeof resolvedComponent !== "string") {
1431
+ return resolvedComponent;
1729
1432
  }
1730
- const braceStart = attributeString.indexOf("{", attributeIndex);
1731
- if (braceStart < 0) {
1732
- return { expression: "", found: false };
1433
+ return loadServerImportComponent(resolvedComponent);
1434
+ }, resolveServerIslandComponent = async (component) => {
1435
+ const cachedResolvedComponent = resolvedServerComponentCache.get(component);
1436
+ if (cachedResolvedComponent) {
1437
+ return cachedResolvedComponent;
1733
1438
  }
1734
- const expression = extractBracedExpression(attributeString, braceStart);
1735
- if (expression === null) {
1736
- return { expression: "", found: false };
1439
+ const resolutionPromise = resolveIslandComponent(component);
1440
+ resolvedServerComponentCache.set(component, resolutionPromise);
1441
+ return resolutionPromise;
1442
+ }, resolveReactServerIslandComponent = async (component) => {
1443
+ const resolvedComponent = await resolveServerIslandComponent(component);
1444
+ if (!isReactServerIslandComponent(resolvedComponent)) {
1445
+ throw new Error("Resolved React island is not a valid React component.");
1737
1446
  }
1738
- return { expression, found: true };
1739
- }, lowerSvelteIslandSyntax = (source, _mode = "server") => {
1740
- if (!source.includes("<Island")) {
1741
- return { code: source, transformed: false };
1447
+ return resolvedComponent;
1448
+ }, resolveSvelteServerIslandComponent = async (component) => {
1449
+ const resolvedComponent = await resolveServerIslandComponent(component);
1450
+ if (!isSvelteServerIslandComponent(resolvedComponent)) {
1451
+ throw new Error("Resolved Svelte island is not a valid Svelte component.");
1742
1452
  }
1743
- let islandIndex = 0;
1744
- const transformedMarkup = source.replace(ISLAND_TAG_RE, (fullMatch, attributeString) => {
1745
- const framework = extractIslandAttribute(attributeString, "framework");
1746
- const component = extractIslandAttribute(attributeString, "component");
1747
- if (!framework.found || !component.found) {
1748
- return fullMatch;
1453
+ return resolvedComponent;
1454
+ }, resolveVueServerIslandComponent = async (component) => {
1455
+ const resolvedComponent = await resolveServerIslandComponent(component);
1456
+ if (!isVueServerIslandComponent(resolvedComponent)) {
1457
+ throw new Error("Resolved Vue island is not a valid Vue component.");
1458
+ }
1459
+ return resolvedComponent;
1460
+ }, resolveAngularServerIslandComponent = async (component) => {
1461
+ const resolvedComponent = await resolveServerIslandComponent(component);
1462
+ if (!isAngularServerIslandComponent(resolvedComponent)) {
1463
+ throw new Error("Resolved Angular island is not a valid Angular component.");
1464
+ }
1465
+ return resolvedComponent;
1466
+ }, renderIslandMarkup = async (registry, props) => {
1467
+ const result = await renderIslandResult(registry, props);
1468
+ return `<div ${serializeIslandAttributes(result.attributes)}>${result.html}</div>`;
1469
+ }, renderIslandResult = async (registry, props) => {
1470
+ const islandId = nextIslandId();
1471
+ const attributes = getIslandMarkerAttributes(props);
1472
+ if (props.framework === "react") {
1473
+ const entry = registry.react?.[props.component];
1474
+ if (!entry) {
1475
+ throw new Error(`Island component "${props.component}" is not registered for framework "react".`);
1749
1476
  }
1750
- const hydrate = extractIslandAttribute(attributeString, "hydrate");
1751
- const props = extractIslandAttribute(attributeString, "props");
1752
- const slotId = `absolute-svelte-island-${islandIndex.toString(BASE_36_RADIX)}`;
1753
- islandIndex += 1;
1754
- const resolveExpression = `await __absoluteResolveIslandHtml(${JSON.stringify(slotId)}, { component: ${component.expression}, framework: ${framework.expression}, hydrate: ${hydrate.found ? hydrate.expression : JSON.stringify("load")}, props: ${props.found ? props.expression : "{}"} })`;
1755
- return `<div data-absolute-island-slot="${slotId}" style="display: contents">{@html ${resolveExpression}}</div>`;
1756
- });
1757
- const importLine = 'import { resolveIslandHtml as __absoluteResolveIslandHtml } from "@absolutejs/absolute/svelte";';
1758
- if (transformedMarkup.includes("<script")) {
1477
+ const component = await resolveReactServerIslandComponent(entry);
1478
+ const html = renderReactIslandToHtml(component, props.props);
1479
+ return { attributes, html };
1480
+ }
1481
+ if (props.framework === "svelte") {
1482
+ const entry = registry.svelte?.[props.component];
1483
+ if (!entry) {
1484
+ throw new Error(`Island component "${props.component}" is not registered for framework "svelte".`);
1485
+ }
1486
+ const component = await resolveSvelteServerIslandComponent(entry);
1487
+ const html = renderSvelteIslandToHtml(component, props.props);
1488
+ return { attributes, html };
1489
+ }
1490
+ if (props.framework === "vue") {
1491
+ const entry = registry.vue?.[props.component];
1492
+ if (!entry) {
1493
+ throw new Error(`Island component "${props.component}" is not registered for framework "vue".`);
1494
+ }
1495
+ const component = await resolveVueServerIslandComponent(entry);
1496
+ const html = await renderVueIslandToHtml(component, props.props);
1497
+ return { attributes, html };
1498
+ }
1499
+ if (props.framework === "angular") {
1500
+ const entry = registry.angular?.[props.component];
1501
+ if (!entry) {
1502
+ throw new Error(`Island component "${props.component}" is not registered for framework "angular".`);
1503
+ }
1504
+ const component = await resolveAngularServerIslandComponent(entry);
1505
+ const html = await renderAngularIslandToHtml(component, props.props, islandId);
1759
1506
  return {
1760
- code: transformedMarkup.replace(/<script(\s[^>]*)?>/, (match) => `${match}
1761
- ${importLine}
1762
- `),
1763
- transformed: true
1507
+ attributes: {
1508
+ ...getIslandMarkerAttributes(props, islandId)
1509
+ },
1510
+ html
1764
1511
  };
1765
1512
  }
1766
- return {
1767
- code: `<script lang="ts">
1768
- ${importLine}
1769
- </script>
1770
- ${transformedMarkup}`,
1771
- transformed: true
1772
- };
1513
+ throw new Error(`Framework "${props.framework}" is not implemented in this prototype.`);
1773
1514
  };
1774
- var init_lowerIslandSyntax = __esm(() => {
1775
- init_constants();
1776
- ISLAND_TAG_RE = /<Island\b([\s\S]*?)\/>/g;
1515
+ var init_renderIslandMarkup = __esm(() => {
1516
+ init_islandSsr();
1517
+ init_svelteServerModule();
1518
+ init_islandMarkupAttributes();
1519
+ init_islands();
1520
+ resolvedServerComponentCache = new Map;
1521
+ resolvedServerBuildComponentCache = new Map;
1777
1522
  });
1778
1523
 
1779
- // src/core/svelteServerModule.ts
1780
- import { mkdir, readdir } from "fs/promises";
1781
- import { basename as basename2, dirname as dirname2, extname, join as join2, relative, resolve as resolve3 } from "path";
1782
- var serverCacheRoot, compiledModuleCache, originalSourcePathCache, transpiler, ensureRelativeImportPath = (from, target) => {
1783
- const importPath = relative(dirname2(from), target).replace(/\\/g, "/");
1784
- return importPath.startsWith(".") ? importPath : `./${importPath}`;
1785
- }, processDirectoryEntries = (entries, dir, targetFileName, stack) => {
1786
- for (const entry of entries) {
1787
- const entryPath = join2(dir, entry.name);
1788
- if (entry.isDirectory())
1789
- stack.push(entryPath);
1790
- if (entry.isFile() && entry.name === targetFileName) {
1791
- return entryPath;
1524
+ // src/client/streamSwap.ts
1525
+ var streamSwapRuntime = () => {
1526
+ if (window.__ABS_SLOT_RUNTIME__ === true)
1527
+ return;
1528
+ window.__ABS_SLOT_RUNTIME__ = true;
1529
+ window.__ABS_SLOT_PENDING__ = window.__ABS_SLOT_PENDING__ ?? {};
1530
+ const pending = window.__ABS_SLOT_PENDING__;
1531
+ const apply = (id, html) => {
1532
+ const node = document.getElementById(`slot-${id}`);
1533
+ if (!node) {
1534
+ pending[id] = html;
1535
+ return;
1792
1536
  }
1537
+ node.innerHTML = html;
1538
+ delete pending[id];
1539
+ };
1540
+ const flush = () => {
1541
+ for (const id in pending) {
1542
+ if (!Object.prototype.hasOwnProperty.call(pending, id))
1543
+ continue;
1544
+ apply(id, pending[id] ?? "");
1545
+ }
1546
+ };
1547
+ window.__ABS_SLOT_ENQUEUE__ = (id, html) => {
1548
+ apply(id, html);
1549
+ };
1550
+ if (typeof MutationObserver === "function") {
1551
+ const observer = new MutationObserver(flush);
1552
+ const root = document.documentElement ?? document.body ?? document;
1553
+ observer.observe(root, { childList: true, subtree: true });
1793
1554
  }
1794
- return null;
1795
- }, searchDirectoryLevel = async (dirs, targetFileName) => {
1796
- if (dirs.length === 0)
1797
- return null;
1798
- const nextStack = [];
1799
- const dirEntries = await Promise.all(dirs.map(async (dir) => ({
1800
- dir,
1801
- entries: await readdir(dir, {
1802
- encoding: "utf-8",
1803
- withFileTypes: true
1804
- })
1805
- })));
1806
- for (const { dir, entries } of dirEntries) {
1807
- const found = processDirectoryEntries(entries, dir, targetFileName, nextStack);
1808
- if (found)
1809
- return found;
1810
- }
1811
- return searchDirectoryLevel(nextStack, targetFileName);
1812
- }, findSourceFileByBasename = async (searchRoot, targetFileName) => searchDirectoryLevel([searchRoot], targetFileName), normalizeBuiltSvelteFileName = (sourcePath) => basename2(sourcePath).replace(/-[a-z0-9]{6,}(?=\.svelte$)/i, ""), resolveOriginalSourcePath = async (sourcePath) => {
1813
- const cachedPath = originalSourcePathCache.get(sourcePath);
1814
- if (cachedPath !== undefined) {
1815
- return cachedPath;
1816
- }
1817
- if (!sourcePath.includes(`${join2(process.cwd(), "build")}${process.platform === "win32" ? "" : "/"}`) && !sourcePath.includes("/build/")) {
1818
- originalSourcePathCache.set(sourcePath, sourcePath);
1819
- return sourcePath;
1555
+ if (document.readyState === "loading") {
1556
+ document.addEventListener("DOMContentLoaded", flush, { once: true });
1820
1557
  }
1821
- const resolvedSourcePath = await findSourceFileByBasename(join2(process.cwd(), "src"), normalizeBuiltSvelteFileName(sourcePath));
1822
- const nextPath = resolvedSourcePath ?? sourcePath;
1823
- originalSourcePathCache.set(sourcePath, nextPath);
1824
- return nextPath;
1825
- }, resolveRelativeModule = async (spec, from) => {
1826
- if (!spec.startsWith(".")) {
1827
- return null;
1558
+ flush();
1559
+ };
1560
+ var stripFunctionWrapper = (value) => {
1561
+ const start = value.indexOf("{");
1562
+ const end = value.lastIndexOf("}");
1563
+ if (start < 0 || end <= start)
1564
+ return "";
1565
+ return value.slice(start + 1, end);
1566
+ };
1567
+ var getStreamSwapRuntimeScript = () => `(function(){${stripFunctionWrapper(streamSwapRuntime.toString())}})();`;
1568
+
1569
+ // src/utils/streamingSlots.ts
1570
+ init_escapeScriptContent();
1571
+ var SLOT_ID_PREFIX = "abs-slot-";
1572
+ var SLOT_PLACEHOLDER_PREFIX = "slot-";
1573
+ var CLOSING_HEAD_TAG = "</head>";
1574
+ var CLOSING_HEAD_TAG_LENGTH = CLOSING_HEAD_TAG.length;
1575
+ var CLOSING_PAGE_TAG_REGEX = /<\/body>\s*<\/html>\s*$/i;
1576
+ var STREAMING_RUNTIME_GLOBAL = "__ABS_SLOT_ENQUEUE__";
1577
+ var STREAMING_PENDING_GLOBAL = "__ABS_SLOT_PENDING__";
1578
+ var STREAM_TAIL_LOOKBEHIND = 128;
1579
+ var STREAMING_SLOT_TIMEOUT_MS = 5000;
1580
+ var STREAMING_SLOT_MAX_PER_RESPONSE = 128;
1581
+ var STREAMING_SLOT_MAX_HTML_BYTES = 64000;
1582
+ var createSlotPlaceholderId = (id) => `${SLOT_PLACEHOLDER_PREFIX}${id}`;
1583
+ 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)});`;
1584
+ var createNonceAttr = (nonce) => nonce ? ` nonce="${nonce}"` : "";
1585
+ var createStreamingSlotId = () => `${SLOT_ID_PREFIX}${Math.random().toString(36).slice(2, 10)}`;
1586
+ var getStreamingSlotsRuntimeScript = () => getStreamSwapRuntimeScript();
1587
+ var renderStreamingSlotsRuntimeTag = (nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(getStreamingSlotsRuntimeScript())}</script>`;
1588
+ var renderStreamingSlotPlaceholder = (id, fallbackHtml = "") => `<div id="${createSlotPlaceholderId(id)}" data-absolute-slot="true">${fallbackHtml}</div>`;
1589
+ var renderStreamingSlotPatchTag = (id, html, nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(createSlotPatchStatement(id, html))}</script>`;
1590
+ var injectHtmlIntoHead = (html, injection) => {
1591
+ const closingHeadIndex = html.indexOf(CLOSING_HEAD_TAG);
1592
+ if (closingHeadIndex >= 0) {
1593
+ return `${html.slice(0, closingHeadIndex)}${injection}${html.slice(closingHeadIndex)}`;
1828
1594
  }
1829
- const basePath = resolve3(dirname2(from), spec);
1830
- const candidates = [
1831
- basePath,
1832
- `${basePath}.ts`,
1833
- `${basePath}.js`,
1834
- `${basePath}.mjs`,
1835
- `${basePath}.cjs`,
1836
- `${basePath}.json`,
1837
- join2(basePath, "index.ts"),
1838
- join2(basePath, "index.js"),
1839
- join2(basePath, "index.mjs"),
1840
- join2(basePath, "index.cjs"),
1841
- join2(basePath, "index.json")
1842
- ];
1843
- const existResults = await Promise.all(candidates.map((candidate) => Bun.file(candidate).exists()));
1844
- const foundIndex = existResults.indexOf(true);
1845
- return foundIndex >= 0 ? candidates[foundIndex] ?? null : null;
1846
- }, getCachedModulePath = (sourcePath) => {
1847
- const relativeSourcePath = relative(process.cwd(), sourcePath).replace(/\\/g, "/");
1848
- const normalizedSourcePath = relativeSourcePath.startsWith("..") ? sourcePath.replace(/[:\\/]/g, "_") : relativeSourcePath;
1849
- return join2(serverCacheRoot, `${normalizedSourcePath}.server.js`);
1850
- }, resolveSvelteImport = async (spec, from) => {
1851
- if (spec.startsWith("/")) {
1852
- return spec;
1595
+ return `${html}${injection}`;
1596
+ };
1597
+ var toUint8 = (value, encoder) => encoder.encode(value);
1598
+ var currentStreamingSlotPolicy = {
1599
+ timeoutMs: STREAMING_SLOT_TIMEOUT_MS,
1600
+ fallbackHtml: "",
1601
+ errorHtml: undefined,
1602
+ maxSlotsPerResponse: STREAMING_SLOT_MAX_PER_RESPONSE,
1603
+ maxSlotHtmlSizeBytes: STREAMING_SLOT_MAX_HTML_BYTES
1604
+ };
1605
+ var clonePolicy = (policy) => ({
1606
+ ...policy
1607
+ });
1608
+ var normalizeSlotBytes = (value, fallback) => {
1609
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
1610
+ return Math.floor(value);
1853
1611
  }
1854
- if (!spec.startsWith(".")) {
1855
- return null;
1612
+ return fallback;
1613
+ };
1614
+ var normalizeSlotText = (value, fallback) => typeof value === "string" ? value : fallback;
1615
+ var normalizeSlotError = (value, fallback) => typeof value === "string" ? value : fallback;
1616
+ var hasPolicyValue = (policy, key) => Object.prototype.hasOwnProperty.call(policy, key);
1617
+ var applyStreamingSlotPolicyOverrides = (base, overridePolicy = {}) => ({
1618
+ timeoutMs: hasPolicyValue(overridePolicy, "timeoutMs") ? normalizeSlotBytes(overridePolicy.timeoutMs, base.timeoutMs) : base.timeoutMs,
1619
+ fallbackHtml: hasPolicyValue(overridePolicy, "fallbackHtml") ? normalizeSlotText(overridePolicy.fallbackHtml, "") : base.fallbackHtml,
1620
+ errorHtml: hasPolicyValue(overridePolicy, "errorHtml") ? normalizeSlotError(overridePolicy.errorHtml) : base.errorHtml,
1621
+ maxSlotsPerResponse: hasPolicyValue(overridePolicy, "maxSlotsPerResponse") ? normalizeSlotBytes(overridePolicy.maxSlotsPerResponse, base.maxSlotsPerResponse) : base.maxSlotsPerResponse,
1622
+ maxSlotHtmlSizeBytes: hasPolicyValue(overridePolicy, "maxSlotHtmlSizeBytes") ? normalizeSlotBytes(overridePolicy.maxSlotHtmlSizeBytes, base.maxSlotHtmlSizeBytes) : base.maxSlotHtmlSizeBytes,
1623
+ onError: hasPolicyValue(overridePolicy, "onError") ? overridePolicy.onError : base.onError,
1624
+ onSlotMetric: hasPolicyValue(overridePolicy, "onSlotMetric") ? overridePolicy.onSlotMetric : base.onSlotMetric
1625
+ });
1626
+ var createCombinedSlotErrorHandler = (policyOnError, enhancerOnError) => {
1627
+ if (!policyOnError && !enhancerOnError)
1628
+ return;
1629
+ return (error, slot) => {
1630
+ policyOnError?.(error, slot);
1631
+ enhancerOnError?.(error, slot);
1632
+ };
1633
+ };
1634
+ var createCombinedSlotMetricHandler = (policyOnSlotMetric, callOnSlotMetric) => {
1635
+ if (!policyOnSlotMetric && !callOnSlotMetric)
1636
+ return;
1637
+ return (metric) => {
1638
+ policyOnSlotMetric?.(metric);
1639
+ callOnSlotMetric?.(metric);
1640
+ };
1641
+ };
1642
+ var resolveStreamingSlotPolicy = (overridePolicy = {}) => {
1643
+ const base = getStreamingSlotPolicy();
1644
+ return applyStreamingSlotPolicyOverrides(base, overridePolicy);
1645
+ };
1646
+ var getStreamingSlotPolicy = () => clonePolicy(currentStreamingSlotPolicy);
1647
+ var setStreamingSlotPolicy = (policy = {}) => {
1648
+ const base = getStreamingSlotPolicy();
1649
+ currentStreamingSlotPolicy = applyStreamingSlotPolicyOverrides(base, policy);
1650
+ };
1651
+ var withStreamingSlotPolicy = async (policy, callback) => {
1652
+ const previous = getStreamingSlotPolicy();
1653
+ setStreamingSlotPolicy(policy);
1654
+ try {
1655
+ return await callback();
1656
+ } finally {
1657
+ currentStreamingSlotPolicy = previous;
1856
1658
  }
1857
- const explicitPath = resolve3(dirname2(from), spec);
1858
- if (extname(explicitPath) === ".svelte") {
1859
- return explicitPath;
1659
+ };
1660
+ var emitSlotMetric = (metric, onSlotMetric) => {
1661
+ onSlotMetric?.(metric);
1662
+ };
1663
+ var createTimeoutError = (slot, timeoutMs) => {
1664
+ const error = new Error(`Streaming slot "${slot.id}" timed out after ${timeoutMs}ms`);
1665
+ error.__absTimeout = true;
1666
+ return error;
1667
+ };
1668
+ var toStreamingSlot = (slot, policy) => ({
1669
+ errorHtml: slot.errorHtml === undefined ? policy.errorHtml : slot.errorHtml,
1670
+ fallbackHtml: normalizeSlotText(slot.fallbackHtml, policy.fallbackHtml),
1671
+ id: slot.id ?? createStreamingSlotId(),
1672
+ timeoutMs: normalizeSlotBytes(slot.timeoutMs, policy.timeoutMs),
1673
+ resolve: slot.resolve
1674
+ });
1675
+ var prepareSlots = ({
1676
+ policy,
1677
+ slots,
1678
+ onError,
1679
+ onSlotMetric
1680
+ }) => {
1681
+ const preparedSlots = slots.map((slot) => toStreamingSlot(slot, policy));
1682
+ const maxSlotsPerResponse = policy.maxSlotsPerResponse;
1683
+ if (maxSlotsPerResponse === 0) {
1684
+ const error = new Error("Streaming slot limit is set to 0");
1685
+ for (const slot of preparedSlots) {
1686
+ onError?.(error, slot);
1687
+ emitSlotMetric({
1688
+ type: "dropped",
1689
+ slotId: slot.id,
1690
+ reason: "maxSlotsPerResponse is 0"
1691
+ }, onSlotMetric);
1692
+ }
1693
+ return [];
1860
1694
  }
1861
- const candidate = `${explicitPath}.svelte`;
1862
- if (await Bun.file(candidate).exists() === true) {
1863
- return candidate;
1695
+ if (preparedSlots.length <= maxSlotsPerResponse) {
1696
+ preparedSlots.forEach((slot) => emitSlotMetric({
1697
+ type: "prepared",
1698
+ slotId: slot.id
1699
+ }, onSlotMetric));
1700
+ return preparedSlots;
1864
1701
  }
1865
- return null;
1866
- }, writeIfChanged = async (path, content) => {
1867
- const targetFile = Bun.file(path);
1868
- const exists = await targetFile.exists();
1869
- if (exists) {
1870
- const currentContent = await targetFile.text();
1871
- if (currentContent === content) {
1872
- return;
1702
+ const keptSlots = preparedSlots.slice(0, maxSlotsPerResponse);
1703
+ const droppedSlots = preparedSlots.slice(maxSlotsPerResponse);
1704
+ droppedSlots.forEach((slot) => {
1705
+ onError?.(new Error(`Streaming slot "${slot.id}" dropped because ${maxSlotsPerResponse} slots is the configured maximum`), slot);
1706
+ emitSlotMetric({
1707
+ type: "dropped",
1708
+ slotId: slot.id,
1709
+ reason: `maxSlotsPerResponse is ${maxSlotsPerResponse}`
1710
+ }, onSlotMetric);
1711
+ });
1712
+ keptSlots.forEach((slot) => emitSlotMetric({
1713
+ type: "prepared",
1714
+ slotId: slot.id
1715
+ }, onSlotMetric));
1716
+ return keptSlots;
1717
+ };
1718
+ var htmlByteLength = (value, encoder) => encoder.encode(value).length;
1719
+ var resolveSlot = async (slot, onError, policy, onSlotMetric) => {
1720
+ const safePolicy = policy ?? getStreamingSlotPolicy();
1721
+ const encoder = new TextEncoder;
1722
+ const start = Date.now();
1723
+ try {
1724
+ const maybeAsyncValue = Promise.resolve(slot.resolve());
1725
+ const resolved = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
1726
+ maybeAsyncValue,
1727
+ new Promise((_, reject) => setTimeout(() => {
1728
+ reject(createTimeoutError(slot, slot.timeoutMs ?? 0));
1729
+ }, slot.timeoutMs))
1730
+ ]) : await maybeAsyncValue;
1731
+ const html = typeof resolved === "string" ? resolved : `${resolved}`;
1732
+ if (safePolicy.maxSlotHtmlSizeBytes > 0 && htmlByteLength(html, encoder) > safePolicy.maxSlotHtmlSizeBytes) {
1733
+ const bytes2 = htmlByteLength(html, encoder);
1734
+ const error = new Error(`Streaming slot "${slot.id}" exceeded max payload size of ${safePolicy.maxSlotHtmlSizeBytes} bytes`);
1735
+ const durationMs2 = Date.now() - start;
1736
+ onError?.(error, slot);
1737
+ emitSlotMetric({
1738
+ type: "size_exceeded",
1739
+ slotId: slot.id,
1740
+ durationMs: durationMs2,
1741
+ bytes: bytes2,
1742
+ error
1743
+ }, onSlotMetric);
1744
+ const fallbackHtml = typeof slot.errorHtml === "string" ? slot.errorHtml : null;
1745
+ return {
1746
+ html: fallbackHtml,
1747
+ id: slot.id,
1748
+ durationMs: durationMs2,
1749
+ bytes: fallbackHtml === null ? 0 : htmlByteLength(fallbackHtml, encoder)
1750
+ };
1873
1751
  }
1874
- }
1875
- await Bun.write(path, content);
1876
- }, compileSvelteServerModule = async (sourcePath) => {
1877
- const cachedModulePath = compiledModuleCache.get(sourcePath);
1878
- if (cachedModulePath) {
1879
- return cachedModulePath;
1880
- }
1881
- const resolutionSourcePath = await resolveOriginalSourcePath(sourcePath);
1882
- const source = await Bun.file(sourcePath).text();
1883
- const { compile, preprocess } = await import("svelte/compiler");
1884
- const loweredSource = lowerSvelteIslandSyntax(source, "server");
1885
- const preprocessed = await preprocess(loweredSource.code, {});
1886
- let transpiled = sourcePath.endsWith(".ts") || sourcePath.endsWith(".svelte.ts") ? transpiler.transformSync(preprocessed.code) : preprocessed.code;
1887
- const childImportSpecs = Array.from(transpiled.matchAll(/from\s+['"]([^'"]+)['"]/g)).map((match) => match[1]).filter((value) => value !== undefined);
1888
- const resolvedChildModules = await Promise.all(childImportSpecs.map((spec) => resolveSvelteImport(spec, resolutionSourcePath)));
1889
- const resolvedModuleImports = await Promise.all(childImportSpecs.map((spec) => resolveRelativeModule(spec, resolutionSourcePath)));
1890
- const childModulePaths = new Map;
1891
- const rewrittenModulePaths = new Map;
1892
- const compiledChildren = await Promise.all(childImportSpecs.map(async (spec, index) => {
1893
- const resolvedChild = resolvedChildModules[index];
1894
- if (!spec || !resolvedChild)
1895
- return null;
1752
+ const durationMs = Date.now() - start;
1753
+ const bytes = htmlByteLength(html, encoder);
1754
+ emitSlotMetric({
1755
+ type: "resolved",
1756
+ slotId: slot.id,
1757
+ durationMs,
1758
+ bytes
1759
+ }, onSlotMetric);
1896
1760
  return {
1897
- compiledPath: await compileSvelteServerModule(resolvedChild),
1898
- spec
1761
+ html,
1762
+ id: slot.id,
1763
+ durationMs,
1764
+ bytes
1765
+ };
1766
+ } catch (error) {
1767
+ const durationMs = Date.now() - start;
1768
+ onError?.(error, slot);
1769
+ emitSlotMetric({
1770
+ type: error?.__absTimeout === true ? "timeout" : "error",
1771
+ slotId: slot.id,
1772
+ durationMs,
1773
+ error
1774
+ }, onSlotMetric);
1775
+ if (typeof slot.errorHtml === "string") {
1776
+ const html = slot.errorHtml;
1777
+ return {
1778
+ html,
1779
+ id: slot.id,
1780
+ durationMs,
1781
+ bytes: htmlByteLength(html, encoder)
1782
+ };
1783
+ }
1784
+ return {
1785
+ html: null,
1786
+ id: slot.id,
1787
+ durationMs,
1788
+ bytes: 0
1899
1789
  };
1900
- }));
1901
- for (const result of compiledChildren) {
1902
- if (result)
1903
- childModulePaths.set(result.spec, result.compiledPath);
1904
- }
1905
- for (let index = 0;index < childImportSpecs.length; index += 1) {
1906
- const spec = childImportSpecs[index];
1907
- const resolvedModuleImport = resolvedModuleImports[index];
1908
- if (!spec || !resolvedModuleImport)
1909
- continue;
1910
- if (resolvedChildModules[index])
1911
- continue;
1912
- rewrittenModulePaths.set(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), resolvedModuleImport));
1913
- }
1914
- for (const [spec, resolvedModuleImport] of rewrittenModulePaths) {
1915
- transpiled = transpiled.replaceAll(spec, resolvedModuleImport);
1916
- }
1917
- let compiledCode = compile(transpiled, {
1918
- css: "injected",
1919
- experimental: {
1920
- async: loweredSource.transformed
1921
- },
1922
- filename: resolutionSourcePath,
1923
- generate: "server"
1924
- }).js.code;
1925
- for (const [spec, compiledChildPath] of childModulePaths) {
1926
- compiledCode = compiledCode.replaceAll(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), compiledChildPath));
1927
- }
1928
- for (const [spec, resolvedModuleImport] of rewrittenModulePaths) {
1929
- compiledCode = compiledCode.replaceAll(spec, resolvedModuleImport);
1930
1790
  }
1931
- const compiledModulePath = getCachedModulePath(sourcePath);
1932
- await mkdir(dirname2(compiledModulePath), { recursive: true });
1933
- await writeIfChanged(compiledModulePath, compiledCode);
1934
- compiledModuleCache.set(sourcePath, compiledModulePath);
1935
- return compiledModulePath;
1936
1791
  };
1937
- var init_svelteServerModule = __esm(() => {
1938
- init_lowerIslandSyntax();
1939
- serverCacheRoot = join2(process.cwd(), ".absolutejs", "islands", "svelte");
1940
- compiledModuleCache = new Map;
1941
- originalSourcePathCache = new Map;
1942
- transpiler = new Bun.Transpiler({
1943
- loader: "ts",
1944
- target: "browser"
1792
+ var nextResolvedSlot = async (pending) => {
1793
+ const wrapped = pending.map((promise) => promise.then((result) => ({
1794
+ original: promise,
1795
+ result
1796
+ })));
1797
+ return Promise.race(wrapped);
1798
+ };
1799
+ var streamChunkToString = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true });
1800
+ var streamOutOfOrderSlots = ({
1801
+ footerHtml = "",
1802
+ headerHtml = "",
1803
+ nonce,
1804
+ policy,
1805
+ onSlotMetric,
1806
+ onError,
1807
+ slots
1808
+ }) => {
1809
+ const resolvedPolicy = resolveStreamingSlotPolicy(policy);
1810
+ const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
1811
+ const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
1812
+ const effectivePolicy = {
1813
+ ...resolvedPolicy,
1814
+ onSlotMetric: combinedOnSlotMetric
1815
+ };
1816
+ const preparedSlots = prepareSlots({
1817
+ policy: effectivePolicy,
1818
+ slots,
1819
+ onError: combinedOnError,
1820
+ onSlotMetric: combinedOnSlotMetric
1945
1821
  });
1946
- });
1947
-
1948
- // src/core/renderIslandMarkup.ts
1949
- var islandSequence = 0, resolvedServerComponentCache, resolvedServerBuildComponentCache, nextIslandId = () => {
1950
- islandSequence += 1;
1951
- return `island-${islandSequence}`;
1952
- }, isRecord2 = (value) => typeof value === "object" && value !== null, isReactServerIslandComponent = (value) => typeof value === "function", isSvelteServerIslandComponent = (value) => typeof value === "function", isVueServerIslandComponent = (value) => typeof value === "function" || isRecord2(value), isAngularServerIslandComponent = (value) => typeof value === "function", resolveBuildReferencePath = (source, registryPath) => {
1953
- if (source.startsWith("file://"))
1954
- return new URL(source).pathname;
1955
- if (source.startsWith("."))
1956
- return new URL(source, registryPath).pathname;
1957
- return source;
1958
- }, loadAndCompileServerBuildComponent = async (buildReferencePath) => {
1959
- const compiledModulePath = await compileSvelteServerModule(buildReferencePath);
1960
- const loadedModule = await import(compiledModulePath);
1961
- return "default" in loadedModule ? loadedModule.default : loadedModule;
1962
- }, loadServerBuildComponent = async (buildReferencePath) => {
1963
- const cachedBuildComponent = resolvedServerBuildComponentCache.get(buildReferencePath);
1964
- if (cachedBuildComponent) {
1965
- return cachedBuildComponent;
1966
- }
1967
- const loadPromise = loadAndCompileServerBuildComponent(buildReferencePath);
1968
- resolvedServerBuildComponentCache.set(buildReferencePath, loadPromise);
1969
- return loadPromise;
1970
- }, loadServerImportComponent = async (resolvedComponent) => {
1971
- const resolvedModulePath = resolvedComponent.startsWith(".") ? new URL(resolvedComponent, import.meta.url).pathname : resolvedComponent;
1972
- const importTarget = resolvedModulePath.endsWith(".svelte") ? await compileSvelteServerModule(resolvedModulePath) : resolvedModulePath;
1973
- const loadedModule = await import(importTarget);
1974
- return "default" in loadedModule ? loadedModule.default : loadedModule;
1975
- }, resolveIslandComponent = async (component) => {
1976
- const buildReference = getIslandBuildReference(component);
1977
- const buildReferencePath = buildReference?.source ? resolveBuildReferencePath(buildReference.source, import.meta.url) : null;
1978
- if (buildReferencePath?.endsWith(".svelte")) {
1979
- return loadServerBuildComponent(buildReferencePath);
1980
- }
1981
- const resolvedComponent = getIslandComponent(component);
1982
- if (typeof resolvedComponent !== "string") {
1983
- return resolvedComponent;
1984
- }
1985
- return loadServerImportComponent(resolvedComponent);
1986
- }, resolveServerIslandComponent = async (component) => {
1987
- const cachedResolvedComponent = resolvedServerComponentCache.get(component);
1988
- if (cachedResolvedComponent) {
1989
- return cachedResolvedComponent;
1990
- }
1991
- const resolutionPromise = resolveIslandComponent(component);
1992
- resolvedServerComponentCache.set(component, resolutionPromise);
1993
- return resolutionPromise;
1994
- }, resolveReactServerIslandComponent = async (component) => {
1995
- const resolvedComponent = await resolveServerIslandComponent(component);
1996
- if (!isReactServerIslandComponent(resolvedComponent)) {
1997
- throw new Error("Resolved React island is not a valid React component.");
1998
- }
1999
- return resolvedComponent;
2000
- }, resolveSvelteServerIslandComponent = async (component) => {
2001
- const resolvedComponent = await resolveServerIslandComponent(component);
2002
- if (!isSvelteServerIslandComponent(resolvedComponent)) {
2003
- throw new Error("Resolved Svelte island is not a valid Svelte component.");
2004
- }
2005
- return resolvedComponent;
2006
- }, resolveVueServerIslandComponent = async (component) => {
2007
- const resolvedComponent = await resolveServerIslandComponent(component);
2008
- if (!isVueServerIslandComponent(resolvedComponent)) {
2009
- throw new Error("Resolved Vue island is not a valid Vue component.");
2010
- }
2011
- return resolvedComponent;
2012
- }, resolveAngularServerIslandComponent = async (component) => {
2013
- const resolvedComponent = await resolveServerIslandComponent(component);
2014
- if (!isAngularServerIslandComponent(resolvedComponent)) {
2015
- throw new Error("Resolved Angular island is not a valid Angular component.");
2016
- }
2017
- return resolvedComponent;
2018
- }, renderIslandMarkup = async (registry, props) => {
2019
- const result = await renderIslandResult(registry, props);
2020
- return `<div ${serializeIslandAttributes(result.attributes)}>${result.html}</div>`;
2021
- }, renderIslandResult = async (registry, props) => {
2022
- const islandId = nextIslandId();
2023
- const attributes = getIslandMarkerAttributes(props);
2024
- if (props.framework === "react") {
2025
- const entry = registry.react?.[props.component];
2026
- if (!entry) {
2027
- throw new Error(`Island component "${props.component}" is not registered for framework "react".`);
1822
+ const encoder = new TextEncoder;
1823
+ return new ReadableStream({
1824
+ async start(controller) {
1825
+ try {
1826
+ let header = headerHtml;
1827
+ if (preparedSlots.length > 0 && !header.includes(STREAMING_RUNTIME_GLOBAL)) {
1828
+ header = injectHtmlIntoHead(header, renderStreamingSlotsRuntimeTag(nonce));
1829
+ }
1830
+ controller.enqueue(toUint8(header, encoder));
1831
+ const pending = preparedSlots.map((slot) => {
1832
+ const fallback = renderStreamingSlotPlaceholder(slot.id, slot.fallbackHtml ?? "");
1833
+ controller.enqueue(toUint8(fallback, encoder));
1834
+ return resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric);
1835
+ });
1836
+ while (pending.length > 0) {
1837
+ const { original, result } = await nextResolvedSlot(pending);
1838
+ const index = pending.indexOf(original);
1839
+ if (index >= 0)
1840
+ pending.splice(index, 1);
1841
+ if (result.html === null)
1842
+ continue;
1843
+ emitSlotMetric({
1844
+ type: "patched",
1845
+ slotId: result.id,
1846
+ durationMs: result.durationMs,
1847
+ bytes: result.bytes
1848
+ }, combinedOnSlotMetric);
1849
+ controller.enqueue(toUint8(renderStreamingSlotPatchTag(result.id, result.html, nonce), encoder));
1850
+ }
1851
+ if (footerHtml.length > 0) {
1852
+ controller.enqueue(toUint8(footerHtml, encoder));
1853
+ }
1854
+ controller.close();
1855
+ } catch (error) {
1856
+ controller.error(error);
1857
+ }
1858
+ }
1859
+ });
1860
+ };
1861
+ var injectStreamingRuntimeIntoStream = (stream, nonce) => {
1862
+ const runtimeTag = renderStreamingSlotsRuntimeTag(nonce);
1863
+ const encoder = new TextEncoder;
1864
+ const decoder = new TextDecoder;
1865
+ const lookbehind = CLOSING_HEAD_TAG_LENGTH - 1;
1866
+ return new ReadableStream({
1867
+ async start(controller) {
1868
+ const reader = stream.getReader();
1869
+ let injected = false;
1870
+ let pending = "";
1871
+ try {
1872
+ for (;; ) {
1873
+ const { done, value } = await reader.read();
1874
+ if (done)
1875
+ break;
1876
+ if (!value)
1877
+ continue;
1878
+ pending += streamChunkToString(value, decoder);
1879
+ if (injected) {
1880
+ controller.enqueue(encoder.encode(pending));
1881
+ pending = "";
1882
+ continue;
1883
+ }
1884
+ const headIndex = pending.indexOf(CLOSING_HEAD_TAG);
1885
+ if (headIndex >= 0) {
1886
+ const withRuntime = `${pending.slice(0, headIndex)}${runtimeTag}${pending.slice(headIndex)}`;
1887
+ controller.enqueue(encoder.encode(withRuntime));
1888
+ pending = "";
1889
+ injected = true;
1890
+ continue;
1891
+ }
1892
+ if (pending.length > lookbehind) {
1893
+ const safeText = pending.slice(0, pending.length - lookbehind);
1894
+ controller.enqueue(encoder.encode(safeText));
1895
+ pending = pending.slice(-lookbehind);
1896
+ }
1897
+ }
1898
+ pending += decoder.decode();
1899
+ if (!injected) {
1900
+ pending = injectHtmlIntoHead(pending, runtimeTag);
1901
+ }
1902
+ if (pending.length > 0) {
1903
+ controller.enqueue(encoder.encode(pending));
1904
+ }
1905
+ controller.close();
1906
+ } catch (error) {
1907
+ controller.error(error);
1908
+ }
2028
1909
  }
2029
- const component = await resolveReactServerIslandComponent(entry);
2030
- const html = renderReactIslandToHtml(component, props.props);
2031
- return { attributes, html };
2032
- }
2033
- if (props.framework === "svelte") {
2034
- const entry = registry.svelte?.[props.component];
2035
- if (!entry) {
2036
- throw new Error(`Island component "${props.component}" is not registered for framework "svelte".`);
1910
+ });
1911
+ };
1912
+ var appendStreamingSlotPatchesToStream = (stream, slots = [], {
1913
+ injectRuntime = true,
1914
+ nonce,
1915
+ onError,
1916
+ onSlotMetric,
1917
+ policy
1918
+ } = {}) => {
1919
+ const resolvedPolicy = resolveStreamingSlotPolicy(policy);
1920
+ const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
1921
+ const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
1922
+ const effectivePolicy = {
1923
+ ...resolvedPolicy,
1924
+ onSlotMetric: combinedOnSlotMetric
1925
+ };
1926
+ const preparedSlots = prepareSlots({
1927
+ policy: effectivePolicy,
1928
+ slots,
1929
+ onError: combinedOnError,
1930
+ onSlotMetric: combinedOnSlotMetric
1931
+ });
1932
+ if (preparedSlots.length === 0)
1933
+ return stream;
1934
+ const source = injectRuntime ? injectStreamingRuntimeIntoStream(stream, nonce) : stream;
1935
+ const encoder = new TextEncoder;
1936
+ const decoder = new TextDecoder;
1937
+ const reader = source.getReader();
1938
+ const pending = preparedSlots.map((slot) => resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric));
1939
+ return new ReadableStream({
1940
+ async start(controller) {
1941
+ let baseDone = false;
1942
+ let baseRead = reader.read();
1943
+ let tail = "";
1944
+ let footer = "";
1945
+ try {
1946
+ while (!baseDone || pending.length > 0) {
1947
+ const racers = [];
1948
+ if (!baseDone) {
1949
+ racers.push(baseRead.then(({ done, value }) => ({
1950
+ done,
1951
+ kind: "base",
1952
+ value
1953
+ })));
1954
+ }
1955
+ if (pending.length > 0) {
1956
+ racers.push(nextResolvedSlot(pending).then((resolved) => ({
1957
+ kind: "slot",
1958
+ ...resolved
1959
+ })));
1960
+ }
1961
+ if (racers.length === 0)
1962
+ break;
1963
+ const winner = await Promise.race(racers);
1964
+ if (winner.kind === "base") {
1965
+ if (winner.done) {
1966
+ baseDone = true;
1967
+ tail += decoder.decode();
1968
+ const footerStart = tail.search(CLOSING_PAGE_TAG_REGEX);
1969
+ if (footerStart >= 0) {
1970
+ const content = tail.slice(0, footerStart);
1971
+ footer = tail.slice(footerStart);
1972
+ if (content.length > 0) {
1973
+ controller.enqueue(encoder.encode(content));
1974
+ }
1975
+ } else if (tail.length > 0) {
1976
+ controller.enqueue(encoder.encode(tail));
1977
+ }
1978
+ tail = "";
1979
+ } else if (winner.value) {
1980
+ tail += streamChunkToString(winner.value, decoder);
1981
+ if (tail.length > STREAM_TAIL_LOOKBEHIND) {
1982
+ const content = tail.slice(0, tail.length - STREAM_TAIL_LOOKBEHIND);
1983
+ controller.enqueue(encoder.encode(content));
1984
+ tail = tail.slice(-STREAM_TAIL_LOOKBEHIND);
1985
+ }
1986
+ baseRead = reader.read();
1987
+ }
1988
+ continue;
1989
+ }
1990
+ const index = pending.indexOf(winner.original);
1991
+ if (index >= 0)
1992
+ pending.splice(index, 1);
1993
+ if (winner.result.html === null)
1994
+ continue;
1995
+ emitSlotMetric({
1996
+ type: "patched",
1997
+ slotId: winner.result.id,
1998
+ durationMs: winner.result.durationMs,
1999
+ bytes: winner.result.bytes
2000
+ }, combinedOnSlotMetric);
2001
+ controller.enqueue(encoder.encode(renderStreamingSlotPatchTag(winner.result.id, winner.result.html, nonce)));
2002
+ }
2003
+ if (footer.length > 0)
2004
+ controller.enqueue(encoder.encode(footer));
2005
+ controller.close();
2006
+ } catch (error) {
2007
+ controller.error(error);
2008
+ }
2037
2009
  }
2038
- const component = await resolveSvelteServerIslandComponent(entry);
2039
- const html = renderSvelteIslandToHtml(component, props.props);
2040
- return { attributes, html };
2010
+ });
2011
+ };
2012
+
2013
+ // src/core/streamingSlotRegistrar.ts
2014
+ var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
2015
+ var getRegistrarGlobal = () => globalThis;
2016
+ var setStreamingSlotRegistrar = (nextRegistrar) => {
2017
+ getRegistrarGlobal()[STREAMING_SLOT_REGISTRAR_KEY] = nextRegistrar;
2018
+ };
2019
+ var registerStreamingSlot = (slot) => {
2020
+ getRegistrarGlobal()[STREAMING_SLOT_REGISTRAR_KEY]?.(slot);
2021
+ };
2022
+
2023
+ // src/core/streamingSlotRegistry.ts
2024
+ var asyncLocalStorage;
2025
+ var isServerRuntime = () => typeof process !== "undefined" && typeof process.versions?.node === "string";
2026
+ var ensureAsyncLocalStorage = async () => {
2027
+ if (typeof asyncLocalStorage !== "undefined")
2028
+ return asyncLocalStorage;
2029
+ if (!isServerRuntime()) {
2030
+ asyncLocalStorage = null;
2031
+ return asyncLocalStorage;
2041
2032
  }
2042
- if (props.framework === "vue") {
2043
- const entry = registry.vue?.[props.component];
2044
- if (!entry) {
2045
- throw new Error(`Island component "${props.component}" is not registered for framework "vue".`);
2046
- }
2047
- const component = await resolveVueServerIslandComponent(entry);
2048
- const html = await renderVueIslandToHtml(component, props.props);
2049
- return { attributes, html };
2033
+ const mod = await import("async_hooks");
2034
+ asyncLocalStorage = new mod.AsyncLocalStorage;
2035
+ return asyncLocalStorage;
2036
+ };
2037
+ var registerStreamingSlot2 = (slot) => {
2038
+ if (!asyncLocalStorage)
2039
+ return;
2040
+ const store = asyncLocalStorage.getStore();
2041
+ if (!store)
2042
+ return;
2043
+ store.set(slot.id, slot);
2044
+ };
2045
+ setStreamingSlotRegistrar(registerStreamingSlot2);
2046
+ var runWithStreamingSlotRegistry = async (task) => {
2047
+ const storage = await ensureAsyncLocalStorage();
2048
+ if (!storage) {
2049
+ return {
2050
+ result: await task(),
2051
+ slots: []
2052
+ };
2050
2053
  }
2051
- if (props.framework === "angular") {
2052
- const entry = registry.angular?.[props.component];
2053
- if (!entry) {
2054
- throw new Error(`Island component "${props.component}" is not registered for framework "angular".`);
2055
- }
2056
- const component = await resolveAngularServerIslandComponent(entry);
2057
- const html = await renderAngularIslandToHtml(component, props.props, islandId);
2054
+ return storage.run(new Map, async () => {
2055
+ const result = await task();
2056
+ const store = storage.getStore();
2058
2057
  return {
2059
- attributes: {
2060
- ...getIslandMarkerAttributes(props, islandId)
2061
- },
2062
- html
2058
+ result,
2059
+ slots: store ? [...store.values()] : []
2063
2060
  };
2061
+ });
2062
+ };
2063
+
2064
+ // src/core/responseEnhancers.ts
2065
+ var toResponse = async (responseLike) => await responseLike;
2066
+ var cloneHeaders = (response) => {
2067
+ const headers = new Headers(response.headers);
2068
+ return headers;
2069
+ };
2070
+ var enhanceHtmlResponseWithStreamingSlots = (response, { nonce, onError, streamingSlots = [], policy } = {}) => {
2071
+ if (!response.body || streamingSlots.length === 0) {
2072
+ return response;
2064
2073
  }
2065
- throw new Error(`Framework "${props.framework}" is not implemented in this prototype.`);
2074
+ const body = appendStreamingSlotPatchesToStream(response.body, streamingSlots, {
2075
+ nonce,
2076
+ onError,
2077
+ policy
2078
+ });
2079
+ return new Response(body, {
2080
+ headers: cloneHeaders(response),
2081
+ status: response.status,
2082
+ statusText: response.statusText
2083
+ });
2084
+ };
2085
+ var withStreamingSlots = async (responseLike, options = {}) => enhanceHtmlResponseWithStreamingSlots(await toResponse(responseLike), options);
2086
+ var mergeStreamingSlots = (registered, explicit) => {
2087
+ const merged = new Map;
2088
+ for (const slot of registered)
2089
+ merged.set(slot.id, slot);
2090
+ for (const slot of explicit)
2091
+ merged.set(slot.id, slot);
2092
+ return [...merged.values()];
2093
+ };
2094
+ var withRegisteredStreamingSlots = async (renderResponse, options = {}) => {
2095
+ const { result, slots } = await runWithStreamingSlotRegistry(renderResponse);
2096
+ const explicit = options.streamingSlots ?? [];
2097
+ return withStreamingSlots(result, {
2098
+ ...options,
2099
+ streamingSlots: mergeStreamingSlots(slots, explicit)
2100
+ });
2066
2101
  };
2067
- var init_renderIslandMarkup = __esm(() => {
2068
- init_islandSsr();
2069
- init_svelteServerModule();
2070
- init_islandMarkupAttributes();
2071
- init_islands();
2072
- resolvedServerComponentCache = new Map;
2073
- resolvedServerBuildComponentCache = new Map;
2074
- });
2102
+
2103
+ // src/core/wrapPageHandlerWithStreamingSlots.ts
2104
+ var wrapPageHandlerWithStreamingSlots = (handler) => (...args) => withRegisteredStreamingSlots(() => handler(...args));
2075
2105
 
2076
2106
  // src/react/index.ts
2077
2107
  init_pageHandler();
@@ -2079,16 +2109,16 @@ init_pageHandler();
2079
2109
  // src/react/Island.tsx
2080
2110
  init_islandMarkupAttributes();
2081
2111
  init_renderIslandMarkup();
2082
- import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
2112
+ import { jsxDEV } from "react/jsx-dev-runtime";
2083
2113
  var Island = async (props) => {
2084
2114
  if (typeof window !== "undefined") {
2085
- return /* @__PURE__ */ jsxDEV2("div", {
2115
+ return /* @__PURE__ */ jsxDEV("div", {
2086
2116
  ...getIslandMarkerAttributes(props),
2087
2117
  suppressHydrationWarning: true
2088
2118
  }, undefined, false, undefined, this);
2089
2119
  }
2090
2120
  const result = await renderIslandResult(requireCurrentIslandRegistry(), props);
2091
- return /* @__PURE__ */ jsxDEV2("div", {
2121
+ return /* @__PURE__ */ jsxDEV("div", {
2092
2122
  ...result.attributes,
2093
2123
  dangerouslySetInnerHTML: { __html: result.html }
2094
2124
  }, undefined, false, undefined, this);
@@ -2096,17 +2126,17 @@ var Island = async (props) => {
2096
2126
  // src/react/createIsland.tsx
2097
2127
  init_islandMarkupAttributes();
2098
2128
  init_renderIslandMarkup();
2099
- import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
2129
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
2100
2130
  var createTypedIsland = (registry) => {
2101
2131
  const Island2 = async (props) => {
2102
2132
  if (typeof window !== "undefined") {
2103
- return /* @__PURE__ */ jsxDEV3("div", {
2133
+ return /* @__PURE__ */ jsxDEV2("div", {
2104
2134
  ...getIslandMarkerAttributes(props),
2105
2135
  suppressHydrationWarning: true
2106
2136
  }, undefined, false, undefined, this);
2107
2137
  }
2108
2138
  const result = await renderIslandResult(registry, props);
2109
- return /* @__PURE__ */ jsxDEV3("div", {
2139
+ return /* @__PURE__ */ jsxDEV2("div", {
2110
2140
  ...result.attributes,
2111
2141
  dangerouslySetInnerHTML: { __html: result.html }
2112
2142
  }, undefined, false, undefined, this);
@@ -2233,12 +2263,15 @@ var subscribeIslandStore = (store, selector, listener) => {
2233
2263
  var useIslandStore = (store, selector) => useSyncExternalStore((listener) => subscribeIslandStore(store, selector, () => {
2234
2264
  listener();
2235
2265
  }), () => readIslandStore(store, selector), () => getIslandStoreServerSnapshot(store, selector));
2266
+
2267
+ // src/react/index.ts
2268
+ var handleReactPageRequest2 = wrapPageHandlerWithStreamingSlots(handleReactPageRequest);
2236
2269
  export {
2237
2270
  useIslandStore,
2238
- handleReactPageRequest,
2271
+ handleReactPageRequest2 as handleReactPageRequest,
2239
2272
  createTypedIsland,
2240
2273
  Island
2241
2274
  };
2242
2275
 
2243
- //# debugId=15AFCB77217FB62264756E2164756E21
2276
+ //# debugId=EF9A8CE9E45AD99A64756E2164756E21
2244
2277
  //# sourceMappingURL=index.js.map