@absolutejs/absolute 0.19.0-beta.364 → 0.19.0-beta.366

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/ai/index.js +108 -2
  2. package/dist/ai/index.js.map +7 -6
  3. package/dist/ai-client/angular/ai/index.js +102 -0
  4. package/dist/ai-client/react/ai/index.js +176 -0
  5. package/dist/ai-client/vue/ai/index.js +172 -0
  6. package/dist/angular/ai/index.js +103 -1
  7. package/dist/angular/ai/index.js.map +5 -3
  8. package/dist/angular/browser.js +8 -12
  9. package/dist/angular/browser.js.map +4 -4
  10. package/dist/angular/index.js +4420 -4465
  11. package/dist/angular/index.js.map +8 -8
  12. package/dist/build.js +578 -23
  13. package/dist/build.js.map +10 -4
  14. package/dist/client/index.js +44 -45
  15. package/dist/client/index.js.map +4 -4
  16. package/dist/index.js +4340 -4363
  17. package/dist/index.js.map +15 -14
  18. package/dist/react/ai/index.js +177 -1
  19. package/dist/react/ai/index.js.map +7 -3
  20. package/dist/react/components/browser/index.js +18 -6
  21. package/dist/react/components/index.js +35 -16
  22. package/dist/react/components/index.js.map +6 -5
  23. package/dist/react/index.js +1774 -1803
  24. package/dist/react/index.js.map +12 -12
  25. package/dist/react/server.js +574 -3
  26. package/dist/react/server.js.map +11 -4
  27. package/dist/src/ai/client/ragClient.d.ts +16 -0
  28. package/dist/src/ai/index.d.ts +2 -1
  29. package/dist/src/ai/rag/chat.d.ts +41 -5
  30. package/dist/src/ai/rag/index.d.ts +1 -1
  31. package/dist/src/ai/rag/types.d.ts +1 -1
  32. package/dist/src/angular/ai/index.d.ts +1 -0
  33. package/dist/src/angular/ai/rag-client.service.d.ts +11 -0
  34. package/dist/src/core/pageHandlers.d.ts +0 -1
  35. package/dist/src/react/ai/index.d.ts +3 -0
  36. package/dist/src/react/ai/useRAGIngest.d.ts +8 -0
  37. package/dist/src/react/ai/useRAGSearch.d.ts +9 -0
  38. package/dist/src/react/ai/useRAGStatus.d.ts +9 -0
  39. package/dist/src/react/index.d.ts +1 -1
  40. package/dist/src/react/streamingSlotCollection.d.ts +9 -0
  41. package/dist/src/svelte/ai/createRAGIngest.d.ts +7 -0
  42. package/dist/src/svelte/ai/createRAGSearch.d.ts +7 -0
  43. package/dist/src/svelte/ai/createRAGStatus.d.ts +8 -0
  44. package/dist/src/svelte/ai/index.d.ts +3 -0
  45. package/dist/src/vue/ai/index.d.ts +3 -0
  46. package/dist/src/vue/ai/useRAGIngest.d.ts +7 -0
  47. package/dist/src/vue/ai/useRAGSearch.d.ts +21 -0
  48. package/dist/src/vue/ai/useRAGStatus.d.ts +8 -0
  49. package/dist/svelte/ai/index.js +171 -1
  50. package/dist/svelte/ai/index.js.map +8 -4
  51. package/dist/svelte/index.js +1881 -1926
  52. package/dist/svelte/index.js.map +8 -8
  53. package/dist/types/ai.d.ts +11 -0
  54. package/dist/vue/ai/index.js +173 -1
  55. package/dist/vue/ai/index.js.map +7 -3
  56. package/dist/vue/components/index.js +8 -12
  57. package/dist/vue/components/index.js.map +5 -5
  58. package/dist/vue/index.js +1929 -1976
  59. package/dist/vue/index.js.map +8 -8
  60. package/package.json +1 -1
package/dist/vue/index.js CHANGED
@@ -78,6 +78,56 @@ var __legacyMetadataTS = (k, v) => {
78
78
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
79
79
  var __require = import.meta.require;
80
80
 
81
+ // src/core/streamingSlotRegistrar.ts
82
+ var registrar = null, setStreamingSlotRegistrar = (nextRegistrar) => {
83
+ registrar = nextRegistrar;
84
+ }, registerStreamingSlot = (slot) => {
85
+ registrar?.(slot);
86
+ };
87
+
88
+ // src/client/streamSwap.ts
89
+ var streamSwapRuntime = () => {
90
+ if (window.__ABS_SLOT_RUNTIME__ === true)
91
+ return;
92
+ window.__ABS_SLOT_RUNTIME__ = true;
93
+ window.__ABS_SLOT_PENDING__ = window.__ABS_SLOT_PENDING__ ?? {};
94
+ const pending = window.__ABS_SLOT_PENDING__;
95
+ const apply = (id, html) => {
96
+ const node = document.getElementById(`slot-${id}`);
97
+ if (!node) {
98
+ pending[id] = html;
99
+ return;
100
+ }
101
+ node.innerHTML = html;
102
+ delete pending[id];
103
+ };
104
+ const flush = () => {
105
+ for (const id in pending) {
106
+ if (!Object.prototype.hasOwnProperty.call(pending, id))
107
+ continue;
108
+ apply(id, pending[id] ?? "");
109
+ }
110
+ };
111
+ window.__ABS_SLOT_ENQUEUE__ = (id, html) => {
112
+ apply(id, html);
113
+ };
114
+ if (typeof MutationObserver === "function") {
115
+ const observer = new MutationObserver(flush);
116
+ const root = document.documentElement ?? document.body ?? document;
117
+ observer.observe(root, { childList: true, subtree: true });
118
+ }
119
+ if (document.readyState === "loading") {
120
+ document.addEventListener("DOMContentLoaded", flush, { once: true });
121
+ }
122
+ flush();
123
+ }, stripFunctionWrapper = (value) => {
124
+ const start = value.indexOf("{");
125
+ const end = value.lastIndexOf("}");
126
+ if (start < 0 || end <= start)
127
+ return "";
128
+ return value.slice(start + 1, end);
129
+ }, getStreamSwapRuntimeScript = () => `(function(){${stripFunctionWrapper(streamSwapRuntime.toString())}})();`;
130
+
81
131
  // src/utils/escapeScriptContent.ts
82
132
  var ESCAPE_LOOKUP, ESCAPE_REGEX, escapeScriptContent = (content) => content.replace(ESCAPE_REGEX, (char) => {
83
133
  const escaped = ESCAPE_LOOKUP[char];
@@ -94,2117 +144,2020 @@ var init_escapeScriptContent = __esm(() => {
94
144
  ESCAPE_REGEX = /[&><\u2028\u2029]/g;
95
145
  });
96
146
 
97
- // src/constants.ts
98
- var ANGULAR_INIT_TIMEOUT_MS = 500, ANSI_ESCAPE_LENGTH = 3, ASCII_SPACE = 32, BASE_36_RADIX = 36, BUN_BUILD_WARNING_SUPPRESSION = "wildcard sideEffects are not supported yet", BODY_SLICE_LENGTH = 2000, BYTES_PER_KILOBYTE = 1024, CLI_ARGS_OFFSET = 3, CSS_ERROR_RESOLVE_DELAY_MS = 50, CSS_MAX_CHECK_ATTEMPTS = 10, CSS_MAX_PARSE_TIMEOUT_MS = 500, CSS_SHEET_READY_TIMEOUT_MS = 100, DEFAULT_CHUNK_SIZE = 16384, DEFAULT_DEBOUNCE_MS = 15, DEFAULT_PORT = 3000, DEV_SERVER_RESTART_DEBOUNCE_MS = 100, DOM_UPDATE_DELAY_MS = 50, FILE_PROTOCOL_PREFIX_LENGTH = 7, FOCUS_ID_PREFIX_LENGTH = 3, FOCUS_IDX_PREFIX_LENGTH = 4, FOCUS_NAME_PREFIX_LENGTH = 5, HMR_UPDATE_TIMEOUT_MS = 2000, HOOK_SIGNATURE_LENGTH = 12, EXCLUDE_LAST_OFFSET = -1, HOURS_IN_DAY = 24, HOURS_IN_HALF_DAY = 12, MAX_ERROR_LENGTH = 200, MAX_RECONNECT_ATTEMPTS = 60, MILLISECONDS_IN_A_SECOND = 1000, MINUTES_IN_AN_HOUR = 60, SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_MINUTE, MILLISECONDS_IN_A_DAY, OVERLAY_FADE_DURATION_MS = 150, PING_INTERVAL_MS = 30000, RAF_BATCH_COUNT = 3, RANDOM_ID_END_INDEX = 11, REBUILD_BATCH_DELAY_MS = 10, REBUILD_RELOAD_DELAY_MS = 200, RECONNECT_INITIAL_DELAY_MS = 500, RECONNECT_POLL_INTERVAL_MS = 300, SIGINT_EXIT_CODE = 130, SIGTERM_EXIT_CODE = 143, SVELTE_CSS_LOAD_TIMEOUT_MS = 500, TIME_PRECISION = 2, TWO_THIRDS, UNFOUND_INDEX = -1, WEBSOCKET_NORMAL_CLOSURE = 1000;
99
- var init_constants = __esm(() => {
100
- MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
101
- MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
102
- TWO_THIRDS = 2 / 3;
103
- });
104
-
105
- // src/core/islandPageContext.ts
106
- var BOOTSTRAP_MANIFEST_KEY = "BootstrapClient", ISLAND_MARKER = 'data-island="true"', MANIFEST_MARKER = "__ABSOLUTE_MANIFEST__", ISLAND_STATE_MARKER = "__ABS_ISLAND_STATE__", CLOSING_HEAD_TAG2 = "</head>", buildIslandsHeadMarkup = (manifest) => {
107
- const manifestScript = `<script>window.__ABSOLUTE_MANIFEST__ = ${JSON.stringify(manifest)}</script>`;
108
- const islandStateScript = `<script>window.__ABS_ISLAND_STATE__ = ${JSON.stringify(globalThis.__ABS_ISLAND_STATE__ ?? {})}</script>`;
109
- const bootstrapPath = manifest[BOOTSTRAP_MANIFEST_KEY];
110
- const bootstrapScript = bootstrapPath ? `<script type="module" src="${bootstrapPath}"></script>` : "";
111
- return `${manifestScript}${islandStateScript}${bootstrapScript}`;
112
- }, injectHeadMarkup = (html, markup) => {
113
- const closingHeadIndex = html.indexOf("</head>");
147
+ // src/utils/streamingSlots.ts
148
+ var SLOT_ID_PREFIX = "abs-slot-", SLOT_PLACEHOLDER_PREFIX = "slot-", CLOSING_HEAD_TAG = "</head>", CLOSING_HEAD_TAG_LENGTH, CLOSING_PAGE_TAG_REGEX, STREAMING_RUNTIME_GLOBAL = "__ABS_SLOT_ENQUEUE__", STREAMING_PENDING_GLOBAL = "__ABS_SLOT_PENDING__", STREAM_TAIL_LOOKBEHIND = 128, STREAMING_SLOT_TIMEOUT_MS = 5000, STREAMING_SLOT_MAX_PER_RESPONSE = 128, STREAMING_SLOT_MAX_HTML_BYTES = 64000, createSlotPlaceholderId = (id) => `${SLOT_PLACEHOLDER_PREFIX}${id}`, createSlotPatchStatement = (id, html) => `(window.${STREAMING_RUNTIME_GLOBAL}||function(i,h){window.${STREAMING_PENDING_GLOBAL}=window.${STREAMING_PENDING_GLOBAL}||{};window.${STREAMING_PENDING_GLOBAL}[i]=h;})(${JSON.stringify(id)},${JSON.stringify(html)});`, createNonceAttr = (nonce) => nonce ? ` nonce="${nonce}"` : "", createStreamingSlotId = () => `${SLOT_ID_PREFIX}${Math.random().toString(36).slice(2, 10)}`, getStreamingSlotsRuntimeScript = () => getStreamSwapRuntimeScript(), renderStreamingSlotsRuntimeTag = (nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(getStreamingSlotsRuntimeScript())}</script>`, renderStreamingSlotPlaceholder = (id, fallbackHtml = "") => `<div id="${createSlotPlaceholderId(id)}" data-absolute-slot="true">${fallbackHtml}</div>`, renderStreamingSlotPatchTag = (id, html, nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(createSlotPatchStatement(id, html))}</script>`, injectHtmlIntoHead = (html, injection) => {
149
+ const closingHeadIndex = html.indexOf(CLOSING_HEAD_TAG);
114
150
  if (closingHeadIndex >= 0) {
115
- return `${html.slice(0, closingHeadIndex)}${markup}${html.slice(closingHeadIndex)}`;
116
- }
117
- const openingBodyIndex = html.indexOf("<body");
118
- if (openingBodyIndex >= 0) {
119
- const bodyStart = html.indexOf(">", openingBodyIndex);
120
- if (bodyStart >= 0) {
121
- return `${html.slice(0, openingBodyIndex)}<head>${markup}</head>${html.slice(openingBodyIndex)}`;
122
- }
151
+ return `${html.slice(0, closingHeadIndex)}${injection}${html.slice(closingHeadIndex)}`;
123
152
  }
124
- return `<!DOCTYPE html><html><head>${markup}</head><body>${html}</body></html>`;
125
- }, streamChunkToString2 = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true }), pipeStreamWithHeadInjection = (stream, markup) => {
126
- const encoder = new TextEncoder;
127
- const decoder = new TextDecoder;
128
- const lookbehind = CLOSING_HEAD_TAG2.length - 1;
129
- return new ReadableStream({
130
- async start(controller) {
131
- const reader = stream.getReader();
132
- let injected = false;
133
- let pending = "";
134
- try {
135
- for (;; ) {
136
- const { done, value } = await reader.read();
137
- if (done)
138
- break;
139
- if (!value)
140
- continue;
141
- pending += streamChunkToString2(value, decoder);
142
- if (injected) {
143
- controller.enqueue(encoder.encode(pending));
144
- pending = "";
145
- continue;
146
- }
147
- const headIndex = pending.indexOf(CLOSING_HEAD_TAG2);
148
- if (headIndex >= 0) {
149
- const next = `${pending.slice(0, headIndex)}${markup}${pending.slice(headIndex)}`;
150
- controller.enqueue(encoder.encode(next));
151
- pending = "";
152
- injected = true;
153
- continue;
154
- }
155
- if (pending.length > lookbehind) {
156
- const safeText = pending.slice(0, pending.length - lookbehind);
157
- controller.enqueue(encoder.encode(safeText));
158
- pending = pending.slice(-lookbehind);
159
- }
160
- }
161
- pending += decoder.decode();
162
- if (!injected) {
163
- pending = injectHeadMarkup(pending, markup);
164
- }
165
- if (pending.length > 0) {
166
- controller.enqueue(encoder.encode(pending));
167
- }
168
- controller.close();
169
- } catch (error) {
170
- controller.error(error);
171
- }
172
- }
173
- });
174
- }, pipeStreamWithIslandMarkerDetection = (stream, markup) => {
175
- const encoder = new TextEncoder;
176
- const decoder = new TextDecoder;
177
- const lookbehind = Math.max(ISLAND_MARKER.length, 1024);
178
- return new ReadableStream({
179
- async start(controller) {
180
- const reader = stream.getReader();
181
- let injected = false;
182
- let pending = "";
183
- try {
184
- for (;; ) {
185
- const { done, value } = await reader.read();
186
- if (done)
187
- break;
188
- if (!value)
189
- continue;
190
- pending += streamChunkToString2(value, decoder);
191
- if (injected) {
192
- controller.enqueue(encoder.encode(pending));
193
- pending = "";
194
- continue;
195
- }
196
- const markerIndex = pending.indexOf(ISLAND_MARKER);
197
- if (markerIndex >= 0) {
198
- const tagStart = pending.lastIndexOf("<", markerIndex);
199
- const injectAt = tagStart >= 0 ? tagStart : markerIndex;
200
- const next = `${pending.slice(0, injectAt)}${markup}${pending.slice(injectAt)}`;
201
- controller.enqueue(encoder.encode(next));
202
- pending = "";
203
- injected = true;
204
- continue;
205
- }
206
- if (pending.length > lookbehind) {
207
- const safeText = pending.slice(0, pending.length - lookbehind);
208
- controller.enqueue(encoder.encode(safeText));
209
- pending = pending.slice(-lookbehind);
210
- }
211
- }
212
- pending += decoder.decode();
213
- if (pending.length > 0) {
214
- controller.enqueue(encoder.encode(pending));
215
- }
216
- controller.close();
217
- } catch (error) {
218
- controller.error(error);
219
- }
220
- }
221
- });
222
- }, htmlContainsIslands = (html) => html.includes(ISLAND_MARKER), injectIslandPageContext = (html, options) => {
223
- const manifest = globalThis.__absoluteManifest;
224
- const hasIslands = options?.hasIslands ?? htmlContainsIslands(html);
225
- if (!manifest || !hasIslands) {
226
- return html;
153
+ return `${html}${injection}`;
154
+ }, toUint8 = (value, encoder) => encoder.encode(value), currentStreamingSlotPolicy, clonePolicy = (policy) => ({
155
+ ...policy
156
+ }), normalizeSlotBytes = (value, fallback) => {
157
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
158
+ return Math.floor(value);
227
159
  }
228
- if (html.includes(MANIFEST_MARKER) || html.includes(ISLAND_STATE_MARKER)) {
229
- return html;
160
+ return fallback;
161
+ }, normalizeSlotText = (value, fallback) => typeof value === "string" ? value : fallback, normalizeSlotError = (value, fallback) => typeof value === "string" ? value : fallback, hasPolicyValue = (policy, key) => Object.prototype.hasOwnProperty.call(policy, key), applyStreamingSlotPolicyOverrides = (base, overridePolicy = {}) => ({
162
+ timeoutMs: hasPolicyValue(overridePolicy, "timeoutMs") ? normalizeSlotBytes(overridePolicy.timeoutMs, base.timeoutMs) : base.timeoutMs,
163
+ fallbackHtml: hasPolicyValue(overridePolicy, "fallbackHtml") ? normalizeSlotText(overridePolicy.fallbackHtml, "") : base.fallbackHtml,
164
+ errorHtml: hasPolicyValue(overridePolicy, "errorHtml") ? normalizeSlotError(overridePolicy.errorHtml) : base.errorHtml,
165
+ maxSlotsPerResponse: hasPolicyValue(overridePolicy, "maxSlotsPerResponse") ? normalizeSlotBytes(overridePolicy.maxSlotsPerResponse, base.maxSlotsPerResponse) : base.maxSlotsPerResponse,
166
+ maxSlotHtmlSizeBytes: hasPolicyValue(overridePolicy, "maxSlotHtmlSizeBytes") ? normalizeSlotBytes(overridePolicy.maxSlotHtmlSizeBytes, base.maxSlotHtmlSizeBytes) : base.maxSlotHtmlSizeBytes,
167
+ onError: hasPolicyValue(overridePolicy, "onError") ? overridePolicy.onError : base.onError,
168
+ onSlotMetric: hasPolicyValue(overridePolicy, "onSlotMetric") ? overridePolicy.onSlotMetric : base.onSlotMetric
169
+ }), createCombinedSlotErrorHandler = (policyOnError, enhancerOnError) => {
170
+ if (!policyOnError && !enhancerOnError)
171
+ return;
172
+ return (error, slot) => {
173
+ policyOnError?.(error, slot);
174
+ enhancerOnError?.(error, slot);
175
+ };
176
+ }, createCombinedSlotMetricHandler = (policyOnSlotMetric, callOnSlotMetric) => {
177
+ if (!policyOnSlotMetric && !callOnSlotMetric)
178
+ return;
179
+ return (metric) => {
180
+ policyOnSlotMetric?.(metric);
181
+ callOnSlotMetric?.(metric);
182
+ };
183
+ }, resolveStreamingSlotPolicy = (overridePolicy = {}) => {
184
+ const base = getStreamingSlotPolicy();
185
+ return applyStreamingSlotPolicyOverrides(base, overridePolicy);
186
+ }, getStreamingSlotPolicy = () => clonePolicy(currentStreamingSlotPolicy), setStreamingSlotPolicy = (policy = {}) => {
187
+ const base = getStreamingSlotPolicy();
188
+ currentStreamingSlotPolicy = applyStreamingSlotPolicyOverrides(base, policy);
189
+ }, withStreamingSlotPolicy = async (policy, callback) => {
190
+ const previous = getStreamingSlotPolicy();
191
+ setStreamingSlotPolicy(policy);
192
+ try {
193
+ return await callback();
194
+ } finally {
195
+ currentStreamingSlotPolicy = previous;
230
196
  }
231
- return injectHeadMarkup(html, buildIslandsHeadMarkup(manifest));
232
- }, injectIslandPageContextStream = (stream, options) => {
233
- const manifest = globalThis.__absoluteManifest;
234
- if (!manifest)
235
- return stream;
236
- const markup = buildIslandsHeadMarkup(manifest);
237
- if (options?.hasIslands === true) {
238
- return pipeStreamWithHeadInjection(stream, markup);
197
+ }, emitSlotMetric = (metric, onSlotMetric) => {
198
+ onSlotMetric?.(metric);
199
+ }, createTimeoutError = (slot, timeoutMs) => {
200
+ const error = new Error(`Streaming slot "${slot.id}" timed out after ${timeoutMs}ms`);
201
+ error.__absTimeout = true;
202
+ return error;
203
+ }, toStreamingSlot = (slot, policy) => ({
204
+ errorHtml: slot.errorHtml === undefined ? policy.errorHtml : slot.errorHtml,
205
+ fallbackHtml: normalizeSlotText(slot.fallbackHtml, policy.fallbackHtml),
206
+ id: slot.id ?? createStreamingSlotId(),
207
+ timeoutMs: normalizeSlotBytes(slot.timeoutMs, policy.timeoutMs),
208
+ resolve: slot.resolve
209
+ }), prepareSlots = ({
210
+ policy,
211
+ slots,
212
+ onError,
213
+ onSlotMetric
214
+ }) => {
215
+ const preparedSlots = slots.map((slot) => toStreamingSlot(slot, policy));
216
+ const maxSlotsPerResponse = policy.maxSlotsPerResponse;
217
+ if (maxSlotsPerResponse === 0) {
218
+ const error = new Error("Streaming slot limit is set to 0");
219
+ for (const slot of preparedSlots) {
220
+ onError?.(error, slot);
221
+ emitSlotMetric({
222
+ type: "dropped",
223
+ slotId: slot.id,
224
+ reason: "maxSlotsPerResponse is 0"
225
+ }, onSlotMetric);
226
+ }
227
+ return [];
239
228
  }
240
- if (options?.hasIslands === false) {
241
- return stream;
229
+ if (preparedSlots.length <= maxSlotsPerResponse) {
230
+ preparedSlots.forEach((slot) => emitSlotMetric({
231
+ type: "prepared",
232
+ slotId: slot.id
233
+ }, onSlotMetric));
234
+ return preparedSlots;
242
235
  }
243
- return pipeStreamWithIslandMarkerDetection(stream, markup);
244
- }, setCurrentIslandManifest = (manifest) => {
245
- globalThis.__absoluteManifest = manifest;
246
- };
247
-
248
- // src/utils/ssrErrorPage.ts
249
- var ssrErrorPage = (framework, error) => {
250
- const frameworkColors = {
251
- angular: "#dd0031",
252
- html: "#e34c26",
253
- htmx: "#1a365d",
254
- react: "#61dafb",
255
- svelte: "#ff3e00",
256
- vue: "#42b883"
257
- };
258
- const accent = frameworkColors[framework] ?? "#94a3b8";
259
- const label = framework.charAt(0).toUpperCase() + framework.slice(1);
260
- const message = error instanceof Error ? error.message : String(error);
261
- return `<!DOCTYPE html>
262
- <html>
263
- <head>
264
- <meta charset="utf-8">
265
- <meta name="viewport" content="width=device-width, initial-scale=1">
266
- <title>SSR Error - AbsoluteJS</title>
267
- <style>
268
- *{margin:0;padding:0;box-sizing:border-box}
269
- 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}
270
- .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}
271
- .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)}
272
- .brand{font-weight:700;font-size:20px;color:#fff;letter-spacing:-0.02em}
273
- .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)}
274
- .kind{color:#94a3b8;font-size:13px;font-weight:500}
275
- .content{padding:24px}
276
- .label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.08em;color:#94a3b8;margin-bottom:8px}
277
- .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}
278
- .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}
279
- </style>
280
- </head>
281
- <body>
282
- <div class="card">
283
- <div class="header">
284
- <div style="display:flex;align-items:center;gap:12px">
285
- <span class="brand">AbsoluteJS</span>
286
- <span class="badge">${label}</span>
287
- </div>
288
- <span class="kind">Server Render Error</span>
289
- </div>
290
- <div class="content">
291
- <div class="label">What went wrong</div>
292
- <pre class="message">${message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}</pre>
293
- <div class="hint">A component threw during server-side rendering. Check the terminal for the full stack trace.</div>
294
- </div>
295
- </div>
296
- </body>
297
- </html>`;
298
- };
299
-
300
- // src/utils/stringModifiers.ts
301
- 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) => {
302
- if (!str.includes("-") && !str.includes("_")) {
303
- return str.charAt(0).toUpperCase() + str.slice(1);
304
- }
305
- return normalizeSlug(str).split(/[-_]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase()).join("");
306
- }, toScreamingSnake = (str) => str.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
307
-
308
- // src/utils/resolveConvention.ts
309
- import { basename } from "path";
310
- var CONVENTIONS_KEY = "__absoluteConventions", isConventionsMap = (value) => Boolean(value) && typeof value === "object", getMap = () => {
311
- const value = Reflect.get(globalThis, CONVENTIONS_KEY);
312
- if (isConventionsMap(value))
313
- return value;
314
- const empty = {};
315
- return empty;
316
- }, derivePageName = (pagePath) => {
317
- const base = basename(pagePath);
318
- const dotIndex = base.indexOf(".");
319
- const name = dotIndex > 0 ? base.slice(0, dotIndex) : base;
320
- return toPascal(name);
321
- }, resolveErrorConventionPath = (framework, pageName) => {
322
- const conventions = getMap()[framework];
323
- if (!conventions)
324
- return;
325
- return conventions.pages?.[pageName]?.error ?? conventions.defaults?.error;
326
- }, resolveNotFoundConventionPath = (framework) => getMap()[framework]?.defaults?.notFound, setConventions = (map) => {
327
- Reflect.set(globalThis, CONVENTIONS_KEY, map);
328
- }, isDev = () => true, buildErrorProps = (error) => {
329
- const message = error instanceof Error ? error.message : String(error);
330
- const stack = isDev() && error instanceof Error ? error.stack : undefined;
331
- return { error: { message, stack } };
332
- }, renderReactError = async (conventionPath, errorProps) => {
333
- const { createElement } = await import("react");
334
- const { renderToReadableStream } = await import("react-dom/server");
335
- const mod = await import(conventionPath);
336
- const [firstKey] = Object.keys(mod);
337
- const ErrorComponent = mod.default ?? (firstKey ? mod[firstKey] : undefined);
338
- const element = createElement(ErrorComponent, errorProps);
339
- const stream = await renderToReadableStream(element);
340
- return new Response(stream, {
341
- headers: { "Content-Type": "text/html" },
342
- status: 500
343
- });
344
- }, renderSvelteError = async (conventionPath, errorProps) => {
345
- const { render } = await import("svelte/server");
346
- const mod = await import(conventionPath);
347
- const ErrorComponent = mod.default;
348
- const { head, body } = render(ErrorComponent, {
349
- props: errorProps
350
- });
351
- const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
352
- return new Response(html, {
353
- headers: { "Content-Type": "text/html" },
354
- status: 500
355
- });
356
- }, unescapeVueStyles = (ssrBody) => {
357
- let styles = "";
358
- const body = ssrBody.replace(/<style>([\s\S]*?)<\/style>/g, (_, css) => {
359
- styles += `<style>${css.replace(/&quot;/g, '"').replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">")}</style>`;
360
- return "";
361
- });
362
- return { body, styles };
363
- }, renderVueError = async (conventionPath, errorProps) => {
364
- const { createSSRApp, h: h3 } = await import("vue");
365
- const { renderToString } = await import("vue/server-renderer");
366
- const mod = await import(conventionPath);
367
- const ErrorComponent = mod.default;
368
- const app = createSSRApp({
369
- render: () => h3(ErrorComponent, errorProps)
370
- });
371
- const rawBody = await renderToString(app);
372
- const { styles, body } = unescapeVueStyles(rawBody);
373
- const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
374
- return new Response(html, {
375
- headers: { "Content-Type": "text/html" },
376
- status: 500
377
- });
378
- }, renderAngularError = async (conventionPath, errorProps) => {
379
- const mod = await import(conventionPath);
380
- const renderError = mod.default ?? mod.renderError;
381
- if (typeof renderError !== "function")
382
- return null;
383
- const html = renderError(errorProps);
384
- return new Response(html, {
385
- headers: { "Content-Type": "text/html" },
386
- status: 500
236
+ const keptSlots = preparedSlots.slice(0, maxSlotsPerResponse);
237
+ const droppedSlots = preparedSlots.slice(maxSlotsPerResponse);
238
+ droppedSlots.forEach((slot) => {
239
+ onError?.(new Error(`Streaming slot "${slot.id}" dropped because ${maxSlotsPerResponse} slots is the configured maximum`), slot);
240
+ emitSlotMetric({
241
+ type: "dropped",
242
+ slotId: slot.id,
243
+ reason: `maxSlotsPerResponse is ${maxSlotsPerResponse}`
244
+ }, onSlotMetric);
387
245
  });
388
- }, logConventionRenderError = (framework, label, renderError) => {
389
- const message = renderError instanceof Error ? renderError.message : "";
390
- if (message.includes("Cannot find module") || message.includes("Cannot find package") || message.includes("not found in module")) {
391
- 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}).`);
392
- return;
393
- }
394
- console.error(`[SSR] Failed to render ${framework} convention ${label} page:`, renderError);
395
- }, ERROR_RENDERERS, renderConventionError = async (framework, pageName, error) => {
396
- const conventionPath = resolveErrorConventionPath(framework, pageName);
397
- if (!conventionPath)
398
- return null;
399
- const errorProps = buildErrorProps(error);
400
- const renderer = ERROR_RENDERERS[framework];
401
- if (!renderer)
402
- return null;
246
+ keptSlots.forEach((slot) => emitSlotMetric({
247
+ type: "prepared",
248
+ slotId: slot.id
249
+ }, onSlotMetric));
250
+ return keptSlots;
251
+ }, htmlByteLength = (value, encoder) => encoder.encode(value).length, resolveSlot = async (slot, onError, policy, onSlotMetric) => {
252
+ const safePolicy = policy ?? getStreamingSlotPolicy();
253
+ const encoder = new TextEncoder;
254
+ const start = Date.now();
403
255
  try {
404
- return await renderer(conventionPath, errorProps);
405
- } catch (renderError) {
406
- logConventionRenderError(framework, "error", renderError);
256
+ const maybeAsyncValue = Promise.resolve(slot.resolve());
257
+ const resolved = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
258
+ maybeAsyncValue,
259
+ new Promise((_, reject) => setTimeout(() => {
260
+ reject(createTimeoutError(slot, slot.timeoutMs ?? 0));
261
+ }, slot.timeoutMs))
262
+ ]) : await maybeAsyncValue;
263
+ const html = typeof resolved === "string" ? resolved : `${resolved}`;
264
+ if (safePolicy.maxSlotHtmlSizeBytes > 0 && htmlByteLength(html, encoder) > safePolicy.maxSlotHtmlSizeBytes) {
265
+ const bytes2 = htmlByteLength(html, encoder);
266
+ const error = new Error(`Streaming slot "${slot.id}" exceeded max payload size of ${safePolicy.maxSlotHtmlSizeBytes} bytes`);
267
+ const durationMs2 = Date.now() - start;
268
+ onError?.(error, slot);
269
+ emitSlotMetric({
270
+ type: "size_exceeded",
271
+ slotId: slot.id,
272
+ durationMs: durationMs2,
273
+ bytes: bytes2,
274
+ error
275
+ }, onSlotMetric);
276
+ const fallbackHtml = typeof slot.errorHtml === "string" ? slot.errorHtml : null;
277
+ return {
278
+ html: fallbackHtml,
279
+ id: slot.id,
280
+ durationMs: durationMs2,
281
+ bytes: fallbackHtml === null ? 0 : htmlByteLength(fallbackHtml, encoder)
282
+ };
283
+ }
284
+ const durationMs = Date.now() - start;
285
+ const bytes = htmlByteLength(html, encoder);
286
+ emitSlotMetric({
287
+ type: "resolved",
288
+ slotId: slot.id,
289
+ durationMs,
290
+ bytes
291
+ }, onSlotMetric);
292
+ return {
293
+ html,
294
+ id: slot.id,
295
+ durationMs,
296
+ bytes
297
+ };
298
+ } catch (error) {
299
+ const durationMs = Date.now() - start;
300
+ onError?.(error, slot);
301
+ emitSlotMetric({
302
+ type: error?.__absTimeout === true ? "timeout" : "error",
303
+ slotId: slot.id,
304
+ durationMs,
305
+ error
306
+ }, onSlotMetric);
307
+ if (typeof slot.errorHtml === "string") {
308
+ const html = slot.errorHtml;
309
+ return {
310
+ html,
311
+ id: slot.id,
312
+ durationMs,
313
+ bytes: htmlByteLength(html, encoder)
314
+ };
315
+ }
316
+ return {
317
+ html: null,
318
+ id: slot.id,
319
+ durationMs,
320
+ bytes: 0
321
+ };
407
322
  }
408
- return null;
409
- }, renderReactNotFound = async (conventionPath) => {
410
- const { createElement } = await import("react");
411
- const { renderToReadableStream } = await import("react-dom/server");
412
- const mod = await import(conventionPath);
413
- const [nfKey] = Object.keys(mod);
414
- const NotFoundComponent = mod.default ?? (nfKey ? mod[nfKey] : undefined);
415
- const element = createElement(NotFoundComponent);
416
- const stream = await renderToReadableStream(element);
417
- return new Response(stream, {
418
- headers: { "Content-Type": "text/html" },
419
- status: 404
420
- });
421
- }, renderSvelteNotFound = async (conventionPath) => {
422
- const { render } = await import("svelte/server");
423
- const mod = await import(conventionPath);
424
- const NotFoundComponent = mod.default;
425
- const { head, body } = render(NotFoundComponent);
426
- const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
427
- return new Response(html, {
428
- headers: { "Content-Type": "text/html" },
429
- status: 404
430
- });
431
- }, renderVueNotFound = async (conventionPath) => {
432
- const { createSSRApp, h: h3 } = await import("vue");
433
- const { renderToString } = await import("vue/server-renderer");
434
- const mod = await import(conventionPath);
435
- const NotFoundComponent = mod.default;
436
- const app = createSSRApp({
437
- render: () => h3(NotFoundComponent)
438
- });
439
- const rawBody = await renderToString(app);
440
- const { styles, body } = unescapeVueStyles(rawBody);
441
- const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
442
- return new Response(html, {
443
- headers: { "Content-Type": "text/html" },
444
- status: 404
323
+ }, nextResolvedSlot = async (pending) => {
324
+ const wrapped = pending.map((promise) => promise.then((result) => ({
325
+ original: promise,
326
+ result
327
+ })));
328
+ return Promise.race(wrapped);
329
+ }, streamChunkToString = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true }), streamOutOfOrderSlots = ({
330
+ footerHtml = "",
331
+ headerHtml = "",
332
+ nonce,
333
+ policy,
334
+ onSlotMetric,
335
+ onError,
336
+ slots
337
+ }) => {
338
+ const resolvedPolicy = resolveStreamingSlotPolicy(policy);
339
+ const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
340
+ const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
341
+ const effectivePolicy = {
342
+ ...resolvedPolicy,
343
+ onSlotMetric: combinedOnSlotMetric
344
+ };
345
+ const preparedSlots = prepareSlots({
346
+ policy: effectivePolicy,
347
+ slots,
348
+ onError: combinedOnError,
349
+ onSlotMetric: combinedOnSlotMetric
445
350
  });
446
- }, renderAngularNotFound = async (conventionPath) => {
447
- const mod = await import(conventionPath);
448
- const renderNotFound = mod.default ?? mod.renderNotFound;
449
- if (typeof renderNotFound !== "function")
450
- return null;
451
- const html = renderNotFound();
452
- return new Response(html, {
453
- headers: { "Content-Type": "text/html" },
454
- status: 404
351
+ const encoder = new TextEncoder;
352
+ return new ReadableStream({
353
+ async start(controller) {
354
+ try {
355
+ let header = headerHtml;
356
+ if (preparedSlots.length > 0 && !header.includes(STREAMING_RUNTIME_GLOBAL)) {
357
+ header = injectHtmlIntoHead(header, renderStreamingSlotsRuntimeTag(nonce));
358
+ }
359
+ controller.enqueue(toUint8(header, encoder));
360
+ const pending = preparedSlots.map((slot) => {
361
+ const fallback = renderStreamingSlotPlaceholder(slot.id, slot.fallbackHtml ?? "");
362
+ controller.enqueue(toUint8(fallback, encoder));
363
+ return resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric);
364
+ });
365
+ while (pending.length > 0) {
366
+ const { original, result } = await nextResolvedSlot(pending);
367
+ const index = pending.indexOf(original);
368
+ if (index >= 0)
369
+ pending.splice(index, 1);
370
+ if (result.html === null)
371
+ continue;
372
+ emitSlotMetric({
373
+ type: "patched",
374
+ slotId: result.id,
375
+ durationMs: result.durationMs,
376
+ bytes: result.bytes
377
+ }, combinedOnSlotMetric);
378
+ controller.enqueue(toUint8(renderStreamingSlotPatchTag(result.id, result.html, nonce), encoder));
379
+ }
380
+ if (footerHtml.length > 0) {
381
+ controller.enqueue(toUint8(footerHtml, encoder));
382
+ }
383
+ controller.close();
384
+ } catch (error) {
385
+ controller.error(error);
386
+ }
387
+ }
455
388
  });
456
- }, NOT_FOUND_RENDERERS, renderConventionNotFound = async (framework) => {
457
- const conventionPath = resolveNotFoundConventionPath(framework);
458
- if (!conventionPath)
459
- return null;
460
- const renderer = NOT_FOUND_RENDERERS[framework];
461
- if (!renderer)
462
- return null;
463
- try {
464
- return await renderer(conventionPath);
465
- } catch (renderError) {
466
- logConventionRenderError(framework, "not-found", renderError);
467
- }
468
- return null;
469
- }, NOT_FOUND_PRIORITY, renderFirstNotFound = async () => {
470
- for (const framework of NOT_FOUND_PRIORITY) {
471
- if (!getMap()[framework]?.defaults?.notFound)
472
- continue;
473
- const response = await renderConventionNotFound(framework);
474
- if (response)
475
- return response;
476
- }
477
- return null;
478
- };
479
- var init_resolveConvention = __esm(() => {
480
- ERROR_RENDERERS = {
481
- angular: renderAngularError,
482
- react: renderReactError,
483
- svelte: renderSvelteError,
484
- vue: renderVueError
485
- };
486
- NOT_FOUND_RENDERERS = {
487
- angular: renderAngularNotFound,
488
- react: renderReactNotFound,
489
- svelte: renderSvelteNotFound,
490
- vue: renderVueNotFound
491
- };
492
- NOT_FOUND_PRIORITY = [
493
- "react",
494
- "svelte",
495
- "vue",
496
- "angular"
497
- ];
498
- });
499
-
500
- // src/vue/pageHandler.ts
501
- import { readdir } from "fs/promises";
502
- import { basename as basename2, dirname } from "path";
503
- var ssrDirty = false, isRecord = (value) => typeof value === "object" && value !== null, isGenericVueComponent = (value) => typeof value === "function" || isRecord(value), readHasIslands = (value) => {
504
- if (!isRecord(value))
505
- return false;
506
- const hasIslands = value["__ABSOLUTE_PAGE_HAS_ISLANDS__"];
507
- return typeof hasIslands === "boolean" ? hasIslands : false;
508
- }, readDefaultExport = (value) => isRecord(value) ? value.default : undefined, resolveCurrentGeneratedVueModulePath = async (pagePath) => {
509
- const pageDirectory = dirname(pagePath);
510
- const expectedPrefix = `${basename2(pagePath, ".js").split(".")[0]}.`;
511
- try {
512
- const pageEntries = await readdir(pageDirectory, {
513
- withFileTypes: true
514
- });
515
- const matchingEntry = pageEntries.find((entry) => entry.isFile() && entry.name.endsWith(".js") && (entry.name === `${expectedPrefix.slice(0, EXCLUDE_LAST_OFFSET)}.js` || entry.name.startsWith(expectedPrefix)));
516
- if (!matchingEntry) {
517
- return pagePath;
389
+ }, injectStreamingRuntimeIntoStream = (stream, nonce) => {
390
+ const runtimeTag = renderStreamingSlotsRuntimeTag(nonce);
391
+ const encoder = new TextEncoder;
392
+ const decoder = new TextDecoder;
393
+ const lookbehind = CLOSING_HEAD_TAG_LENGTH - 1;
394
+ return new ReadableStream({
395
+ async start(controller) {
396
+ const reader = stream.getReader();
397
+ let injected = false;
398
+ let pending = "";
399
+ try {
400
+ for (;; ) {
401
+ const { done, value } = await reader.read();
402
+ if (done)
403
+ break;
404
+ if (!value)
405
+ continue;
406
+ pending += streamChunkToString(value, decoder);
407
+ if (injected) {
408
+ controller.enqueue(encoder.encode(pending));
409
+ pending = "";
410
+ continue;
411
+ }
412
+ const headIndex = pending.indexOf(CLOSING_HEAD_TAG);
413
+ if (headIndex >= 0) {
414
+ const withRuntime = `${pending.slice(0, headIndex)}${runtimeTag}${pending.slice(headIndex)}`;
415
+ controller.enqueue(encoder.encode(withRuntime));
416
+ pending = "";
417
+ injected = true;
418
+ continue;
419
+ }
420
+ if (pending.length > lookbehind) {
421
+ const safeText = pending.slice(0, pending.length - lookbehind);
422
+ controller.enqueue(encoder.encode(safeText));
423
+ pending = pending.slice(-lookbehind);
424
+ }
425
+ }
426
+ pending += decoder.decode();
427
+ if (!injected) {
428
+ pending = injectHtmlIntoHead(pending, runtimeTag);
429
+ }
430
+ if (pending.length > 0) {
431
+ controller.enqueue(encoder.encode(pending));
432
+ }
433
+ controller.close();
434
+ } catch (error) {
435
+ controller.error(error);
436
+ }
518
437
  }
519
- return `${pageDirectory}/${matchingEntry.name}`;
520
- } catch {
521
- return pagePath;
522
- }
523
- }, buildDirtyResponse = (headTag, indexPath, maybeProps) => {
524
- const propsScript = `window.__INITIAL_PROPS__=${JSON.stringify(maybeProps ?? {})};`;
525
- const dirtyFlag = "window.__SSR_DIRTY__=true;";
526
- const html = `<!DOCTYPE html><html>${headTag}<body><div id="root"></div>` + `<script>${propsScript}${dirtyFlag}</script>` + `<script type="module" src="${indexPath}"></script>` + `</body></html>`;
527
- return new Response(html, {
528
- headers: { "Content-Type": "text/html" }
529
438
  });
530
- }, handleVuePageRequest = async (_PageComponent, pagePath, indexPath, headTag = "<head></head>", ...props) => {
531
- const [maybeProps] = props;
532
- if (ssrDirty) {
533
- return buildDirtyResponse(headTag, indexPath, maybeProps);
534
- }
535
- try {
536
- const resolvePageComponent = async () => {
537
- const passedPageComponent = _PageComponent;
538
- if (isGenericVueComponent(passedPageComponent)) {
539
- return {
540
- component: passedPageComponent,
541
- hasIslands: readHasIslands(passedPageComponent)
542
- };
543
- }
544
- const generatedPagePath = await resolveCurrentGeneratedVueModulePath(pagePath);
545
- const importedPageModule = await import(generatedPagePath);
546
- const importedPageComponent = readDefaultExport(importedPageModule) ?? importedPageModule;
547
- if (!isGenericVueComponent(importedPageComponent)) {
548
- throw new Error(`Invalid Vue page module: ${generatedPagePath}`);
549
- }
550
- return {
551
- component: importedPageComponent,
552
- hasIslands: readHasIslands(importedPageModule)
553
- };
554
- };
555
- const resolvedPage = await resolvePageComponent();
556
- const { createSSRApp, h: h3 } = await import("vue");
557
- const { renderToWebStream } = await import("vue/server-renderer");
558
- const app = createSSRApp({
559
- render: () => h3(resolvedPage.component, maybeProps ?? null)
560
- });
561
- const bodyStream = renderToWebStream(app);
562
- const head = `<!DOCTYPE html><html>${headTag}<body><div id="root">`;
563
- const tail = `</div><script>window.__INITIAL_PROPS__=${JSON.stringify(maybeProps ?? {})}</script><script type="module" src="${indexPath}"></script></body></html>`;
564
- const stream = new ReadableStream({
565
- start(controller) {
566
- controller.enqueue(head);
567
- const reader = bodyStream.getReader();
568
- const pumpLoop = () => {
569
- reader.read().then(({ done, value }) => done ? (controller.enqueue(tail), controller.close()) : (controller.enqueue(value), pumpLoop())).catch((err) => controller.error(err));
570
- };
571
- pumpLoop();
439
+ }, appendStreamingSlotPatchesToStream = (stream, slots = [], {
440
+ injectRuntime = true,
441
+ nonce,
442
+ onError,
443
+ onSlotMetric,
444
+ policy
445
+ } = {}) => {
446
+ const resolvedPolicy = resolveStreamingSlotPolicy(policy);
447
+ const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
448
+ const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
449
+ const effectivePolicy = {
450
+ ...resolvedPolicy,
451
+ onSlotMetric: combinedOnSlotMetric
452
+ };
453
+ const preparedSlots = prepareSlots({
454
+ policy: effectivePolicy,
455
+ slots,
456
+ onError: combinedOnError,
457
+ onSlotMetric: combinedOnSlotMetric
458
+ });
459
+ if (preparedSlots.length === 0)
460
+ return stream;
461
+ const source = injectRuntime ? injectStreamingRuntimeIntoStream(stream, nonce) : stream;
462
+ const encoder = new TextEncoder;
463
+ const decoder = new TextDecoder;
464
+ const reader = source.getReader();
465
+ const pending = preparedSlots.map((slot) => resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric));
466
+ return new ReadableStream({
467
+ async start(controller) {
468
+ let baseDone = false;
469
+ let baseRead = reader.read();
470
+ let tail = "";
471
+ let footer = "";
472
+ try {
473
+ while (!baseDone || pending.length > 0) {
474
+ const racers = [];
475
+ if (!baseDone) {
476
+ racers.push(baseRead.then(({ done, value }) => ({
477
+ done,
478
+ kind: "base",
479
+ value
480
+ })));
481
+ }
482
+ if (pending.length > 0) {
483
+ racers.push(nextResolvedSlot(pending).then((resolved) => ({
484
+ kind: "slot",
485
+ ...resolved
486
+ })));
487
+ }
488
+ if (racers.length === 0)
489
+ break;
490
+ const winner = await Promise.race(racers);
491
+ if (winner.kind === "base") {
492
+ if (winner.done) {
493
+ baseDone = true;
494
+ tail += decoder.decode();
495
+ const footerStart = tail.search(CLOSING_PAGE_TAG_REGEX);
496
+ if (footerStart >= 0) {
497
+ const content = tail.slice(0, footerStart);
498
+ footer = tail.slice(footerStart);
499
+ if (content.length > 0) {
500
+ controller.enqueue(encoder.encode(content));
501
+ }
502
+ } else if (tail.length > 0) {
503
+ controller.enqueue(encoder.encode(tail));
504
+ }
505
+ tail = "";
506
+ } else if (winner.value) {
507
+ tail += streamChunkToString(winner.value, decoder);
508
+ if (tail.length > STREAM_TAIL_LOOKBEHIND) {
509
+ const content = tail.slice(0, tail.length - STREAM_TAIL_LOOKBEHIND);
510
+ controller.enqueue(encoder.encode(content));
511
+ tail = tail.slice(-STREAM_TAIL_LOOKBEHIND);
512
+ }
513
+ baseRead = reader.read();
514
+ }
515
+ continue;
516
+ }
517
+ const index = pending.indexOf(winner.original);
518
+ if (index >= 0)
519
+ pending.splice(index, 1);
520
+ if (winner.result.html === null)
521
+ continue;
522
+ emitSlotMetric({
523
+ type: "patched",
524
+ slotId: winner.result.id,
525
+ durationMs: winner.result.durationMs,
526
+ bytes: winner.result.bytes
527
+ }, combinedOnSlotMetric);
528
+ controller.enqueue(encoder.encode(renderStreamingSlotPatchTag(winner.result.id, winner.result.html, nonce)));
529
+ }
530
+ if (footer.length > 0)
531
+ controller.enqueue(encoder.encode(footer));
532
+ controller.close();
533
+ } catch (error) {
534
+ controller.error(error);
572
535
  }
573
- });
574
- const htmlStream = injectIslandPageContextStream(stream, {
575
- hasIslands: resolvedPage.hasIslands
576
- });
577
- return new Response(htmlStream, {
578
- headers: { "Content-Type": "text/html" }
579
- });
580
- } catch (error) {
581
- console.error("[SSR] Vue render error:", error);
582
- const pageName = derivePageName(pagePath);
583
- const conventionResponse = await renderConventionError("vue", pageName, error);
584
- if (conventionResponse)
585
- return conventionResponse;
586
- return new Response(ssrErrorPage("vue", error), {
587
- headers: { "Content-Type": "text/html" },
588
- status: 500
589
- });
590
- }
591
- }, invalidateVueSsrCache = () => {
592
- ssrDirty = true;
536
+ }
537
+ });
593
538
  };
594
- var init_pageHandler = __esm(() => {
595
- init_constants();
596
- init_resolveConvention();
597
- });
598
-
599
- // src/core/islandManifest.ts
600
- var toIslandFrameworkSegment = (framework) => framework[0]?.toUpperCase() + framework.slice(1), collectFrameworkIslands = (manifest, prefix) => {
601
- const entries = {};
602
- let found = false;
603
- for (const [key, value] of Object.entries(manifest)) {
604
- if (!key.startsWith(prefix))
605
- continue;
606
- const component = key.slice(prefix.length);
607
- if (!component)
608
- continue;
609
- entries[component] = value;
610
- found = true;
611
- }
612
- return found ? entries : undefined;
613
- }, getIslandManifestEntries = (manifest) => {
614
- const islands = {};
615
- const frameworks = ["react", "svelte", "vue", "angular"];
616
- for (const framework of frameworks) {
617
- const prefix = `Island${toIslandFrameworkSegment(framework)}`;
618
- const entries = collectFrameworkIslands(manifest, prefix);
619
- if (entries)
620
- islands[framework] = entries;
621
- }
622
- return islands;
623
- }, getIslandManifestKey = (framework, component) => `Island${toIslandFrameworkSegment(framework)}${component}`;
624
-
625
- // src/core/islands.ts
626
- function getIslandComponent(component) {
627
- if (isIslandComponentDefinition(component)) {
628
- return component.component;
629
- }
630
- return component;
631
- }
632
- var defineIslandComponent = (component, options) => ({
633
- component,
634
- export: options.export,
635
- source: options.source
636
- }), defineIslandRegistry = (registry) => registry, isRecord2 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component) => {
637
- if (!isIslandComponentDefinition(component))
638
- return null;
639
- return {
640
- export: component.export,
641
- source: component.source
539
+ var init_streamingSlots = __esm(() => {
540
+ init_escapeScriptContent();
541
+ CLOSING_HEAD_TAG_LENGTH = CLOSING_HEAD_TAG.length;
542
+ CLOSING_PAGE_TAG_REGEX = /<\/body>\s*<\/html>\s*$/i;
543
+ currentStreamingSlotPolicy = {
544
+ timeoutMs: STREAMING_SLOT_TIMEOUT_MS,
545
+ fallbackHtml: "",
546
+ errorHtml: undefined,
547
+ maxSlotsPerResponse: STREAMING_SLOT_MAX_PER_RESPONSE,
548
+ maxSlotHtmlSizeBytes: STREAMING_SLOT_MAX_HTML_BYTES
642
549
  };
643
- }, isIslandComponentDefinition = (value) => isRecord2(value) && ("component" in value) && ("source" in value) && typeof value.source === "string", parseIslandProps = (rawProps) => {
644
- if (!rawProps)
645
- return {};
646
- return JSON.parse(rawProps);
647
- }, serializeIslandProps = (props) => JSON.stringify(props ?? {});
648
- var init_islands = () => {};
649
-
650
- // src/core/islandMarkupAttributes.ts
651
- var getIslandMarkerAttributes = (props, islandId) => ({
652
- "data-component": props.component,
653
- "data-framework": props.framework,
654
- "data-hydrate": props.hydrate ?? "load",
655
- "data-island": "true",
656
- ...islandId ? { "data-island-id": islandId } : {},
657
- "data-props": serializeIslandProps(props.props)
658
- }), escapeHtmlAttribute = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;"), serializeIslandAttributes = (attributes) => Object.entries(attributes).map(([key, value]) => `${key}="${escapeHtmlAttribute(value)}"`).join(" ");
659
- var init_islandMarkupAttributes = __esm(() => {
660
- init_islands();
661
550
  });
662
551
 
663
- // src/core/currentIslandRegistry.ts
664
- var requireCurrentIslandRegistry = () => {
665
- const registry = globalThis.__absoluteIslandRegistry;
666
- if (!registry) {
667
- throw new Error("No island registry is active. Configure `islands.registry` in absolute.config.ts before rendering <Island />.");
668
- }
669
- return registry;
670
- }, setCurrentIslandRegistry = (registry) => {
671
- globalThis.__absoluteIslandRegistry = registry;
672
- };
673
-
674
- // src/angular/injectorPatch.ts
675
- import { existsSync, readFileSync, writeFileSync } from "fs";
676
- import { dirname as dirname2, join, resolve } from "path";
677
- var applyInjectorPatch = (chunkPath, content) => {
678
- if (content.includes('Symbol.for("angular.currentInjector")')) {
679
- return;
552
+ // src/core/streamingSlotRegistry.ts
553
+ var asyncLocalStorage, isServerRuntime = () => typeof process !== "undefined" && typeof process.versions?.node === "string", ensureAsyncLocalStorage = async () => {
554
+ if (typeof asyncLocalStorage !== "undefined")
555
+ return asyncLocalStorage;
556
+ if (!isServerRuntime()) {
557
+ asyncLocalStorage = null;
558
+ return asyncLocalStorage;
680
559
  }
681
- const original = [
682
- "let _currentInjector = undefined;",
683
- "function getCurrentInjector() {",
684
- " return _currentInjector;",
685
- "}",
686
- "function setCurrentInjector(injector) {",
687
- " const former = _currentInjector;",
688
- " _currentInjector = injector;",
689
- " return former;",
690
- "}"
691
- ].join(`
692
- `);
693
- const replacement = [
694
- 'const _injSym = Symbol.for("angular.currentInjector");',
695
- "if (!globalThis[_injSym]) globalThis[_injSym] = { v: undefined };",
696
- "function getCurrentInjector() {",
697
- " return globalThis[_injSym].v;",
698
- "}",
699
- "function setCurrentInjector(injector) {",
700
- " const former = globalThis[_injSym].v;",
701
- " globalThis[_injSym].v = injector;",
702
- " return former;",
703
- "}"
704
- ].join(`
705
- `);
706
- const patched = content.replace(original, replacement);
707
- if (patched === content) {
560
+ const mod = await import("async_hooks");
561
+ asyncLocalStorage = new mod.AsyncLocalStorage;
562
+ return asyncLocalStorage;
563
+ }, registerStreamingSlot2 = (slot) => {
564
+ if (!asyncLocalStorage)
708
565
  return;
566
+ const store = asyncLocalStorage.getStore();
567
+ if (!store)
568
+ return;
569
+ store.set(slot.id, slot);
570
+ }, runWithStreamingSlotRegistry = async (task) => {
571
+ const storage = await ensureAsyncLocalStorage();
572
+ if (!storage) {
573
+ return {
574
+ result: await task(),
575
+ slots: []
576
+ };
709
577
  }
710
- writeFileSync(chunkPath, patched, "utf-8");
711
- }, resolveAngularCoreDir = () => {
712
- const fromProject = resolve(process.cwd(), "node_modules/@angular/core");
713
- if (existsSync(join(fromProject, "package.json"))) {
714
- return fromProject;
715
- }
716
- return dirname2(__require.resolve("@angular/core/package.json"));
717
- }, patchAngularInjectorSingleton = () => {
718
- try {
719
- const coreDir = resolveAngularCoreDir();
720
- const chunkPath = join(coreDir, "fesm2022", "_not_found-chunk.mjs");
721
- const content = readFileSync(chunkPath, "utf-8");
722
- applyInjectorPatch(chunkPath, content);
723
- } catch {}
578
+ return storage.run(new Map, async () => {
579
+ const result = await task();
580
+ const store = storage.getStore();
581
+ return {
582
+ result,
583
+ slots: store ? [...store.values()] : []
584
+ };
585
+ });
724
586
  };
725
- var init_injectorPatch = __esm(() => {
726
- patchAngularInjectorSingleton();
587
+ var init_streamingSlotRegistry = __esm(() => {
588
+ setStreamingSlotRegistrar(registerStreamingSlot2);
727
589
  });
728
590
 
729
- // src/angular/resolveAngularPackage.ts
730
- import { existsSync as existsSync2 } from "fs";
731
- import { resolve as resolve2 } from "path";
732
- var resolveAngularPackage = (specifier) => {
733
- const fromProject = resolve2(process.cwd(), "node_modules", specifier);
734
- if (existsSync2(fromProject)) {
735
- return fromProject;
591
+ // src/core/responseEnhancers.ts
592
+ var toResponse = async (responseLike) => await responseLike, cloneHeaders = (response) => {
593
+ const headers = new Headers(response.headers);
594
+ return headers;
595
+ }, enhanceHtmlResponseWithStreamingSlots = (response, { nonce, onError, streamingSlots = [], policy } = {}) => {
596
+ if (!response.body || streamingSlots.length === 0) {
597
+ return response;
736
598
  }
737
- return specifier;
599
+ const body = appendStreamingSlotPatchesToStream(response.body, streamingSlots, {
600
+ nonce,
601
+ onError,
602
+ policy
603
+ });
604
+ return new Response(body, {
605
+ headers: cloneHeaders(response),
606
+ status: response.status,
607
+ statusText: response.statusText
608
+ });
609
+ }, withStreamingSlots = async (responseLike, options = {}) => enhanceHtmlResponseWithStreamingSlots(await toResponse(responseLike), options), mergeStreamingSlots = (registered, explicit) => {
610
+ const merged = new Map;
611
+ for (const slot of registered)
612
+ merged.set(slot.id, slot);
613
+ for (const slot of explicit)
614
+ merged.set(slot.id, slot);
615
+ return [...merged.values()];
616
+ }, withRegisteredStreamingSlots = async (renderResponse, options = {}) => {
617
+ const { result, slots } = await runWithStreamingSlotRegistry(renderResponse);
618
+ const explicit = options.streamingSlots ?? [];
619
+ return withStreamingSlots(result, {
620
+ ...options,
621
+ streamingSlots: mergeStreamingSlots(slots, explicit)
622
+ });
738
623
  };
739
- var init_resolveAngularPackage = () => {};
624
+ var init_responseEnhancers = __esm(() => {
625
+ init_streamingSlots();
626
+ init_streamingSlotRegistry();
627
+ });
740
628
 
741
- // src/angular/angularPatch.ts
742
- var exports_angularPatch = {};
743
- __export(exports_angularPatch, {
744
- applyPatches: () => applyPatches
629
+ // src/constants.ts
630
+ var ANGULAR_INIT_TIMEOUT_MS = 500, ANSI_ESCAPE_LENGTH = 3, ASCII_SPACE = 32, BASE_36_RADIX = 36, BUN_BUILD_WARNING_SUPPRESSION = "wildcard sideEffects are not supported yet", BODY_SLICE_LENGTH = 2000, BYTES_PER_KILOBYTE = 1024, CLI_ARGS_OFFSET = 3, CSS_ERROR_RESOLVE_DELAY_MS = 50, CSS_MAX_CHECK_ATTEMPTS = 10, CSS_MAX_PARSE_TIMEOUT_MS = 500, CSS_SHEET_READY_TIMEOUT_MS = 100, DEFAULT_CHUNK_SIZE = 16384, DEFAULT_DEBOUNCE_MS = 15, DEFAULT_PORT = 3000, DEV_SERVER_RESTART_DEBOUNCE_MS = 100, DOM_UPDATE_DELAY_MS = 50, FILE_PROTOCOL_PREFIX_LENGTH = 7, FOCUS_ID_PREFIX_LENGTH = 3, FOCUS_IDX_PREFIX_LENGTH = 4, FOCUS_NAME_PREFIX_LENGTH = 5, HMR_UPDATE_TIMEOUT_MS = 2000, HOOK_SIGNATURE_LENGTH = 12, EXCLUDE_LAST_OFFSET = -1, HOURS_IN_DAY = 24, HOURS_IN_HALF_DAY = 12, MAX_ERROR_LENGTH = 200, MAX_RECONNECT_ATTEMPTS = 60, MILLISECONDS_IN_A_SECOND = 1000, MINUTES_IN_AN_HOUR = 60, SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_MINUTE, MILLISECONDS_IN_A_DAY, OVERLAY_FADE_DURATION_MS = 150, PING_INTERVAL_MS = 30000, RAF_BATCH_COUNT = 3, RANDOM_ID_END_INDEX = 11, REBUILD_BATCH_DELAY_MS = 10, REBUILD_RELOAD_DELAY_MS = 200, RECONNECT_INITIAL_DELAY_MS = 500, RECONNECT_POLL_INTERVAL_MS = 300, SIGINT_EXIT_CODE = 130, SIGTERM_EXIT_CODE = 143, SVELTE_CSS_LOAD_TIMEOUT_MS = 500, TIME_PRECISION = 2, TWO_THIRDS, UNFOUND_INDEX = -1, WEBSOCKET_NORMAL_CLOSURE = 1000;
631
+ var init_constants = __esm(() => {
632
+ MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
633
+ MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
634
+ TWO_THIRDS = 2 / 3;
745
635
  });
746
- var ensureHead = (doc) => {
747
- if (!doc || doc.head || !doc.documentElement) {
748
- return;
636
+
637
+ // src/core/islandPageContext.ts
638
+ var BOOTSTRAP_MANIFEST_KEY = "BootstrapClient", ISLAND_MARKER = 'data-island="true"', MANIFEST_MARKER = "__ABSOLUTE_MANIFEST__", ISLAND_STATE_MARKER = "__ABS_ISLAND_STATE__", CLOSING_HEAD_TAG2 = "</head>", buildIslandsHeadMarkup = (manifest) => {
639
+ const manifestScript = `<script>window.__ABSOLUTE_MANIFEST__ = ${JSON.stringify(manifest)}</script>`;
640
+ const islandStateScript = `<script>window.__ABS_ISLAND_STATE__ = ${JSON.stringify(globalThis.__ABS_ISLAND_STATE__ ?? {})}</script>`;
641
+ const bootstrapPath = manifest[BOOTSTRAP_MANIFEST_KEY];
642
+ const bootstrapScript = bootstrapPath ? `<script type="module" src="${bootstrapPath}"></script>` : "";
643
+ return `${manifestScript}${islandStateScript}${bootstrapScript}`;
644
+ }, injectHeadMarkup = (html, markup) => {
645
+ const closingHeadIndex = html.indexOf("</head>");
646
+ if (closingHeadIndex >= 0) {
647
+ return `${html.slice(0, closingHeadIndex)}${markup}${html.slice(closingHeadIndex)}`;
749
648
  }
750
- const head = doc.createElement("head");
751
- doc.documentElement.insertBefore(head, doc.documentElement.firstChild);
752
- }, applyPatches = async () => {
753
- const { \u{275}DominoAdapter } = await import(resolveAngularPackage("@angular/platform-server"));
754
- if (!\u{275}DominoAdapter?.prototype) {
755
- console.warn("[Angular Patch] \u0275DominoAdapter not found, skipping patches");
756
- return false;
649
+ const openingBodyIndex = html.indexOf("<body");
650
+ if (openingBodyIndex >= 0) {
651
+ const bodyStart = html.indexOf(">", openingBodyIndex);
652
+ if (bodyStart >= 0) {
653
+ return `${html.slice(0, openingBodyIndex)}<head>${markup}</head>${html.slice(openingBodyIndex)}`;
654
+ }
655
+ }
656
+ return `<!DOCTYPE html><html><head>${markup}</head><body>${html}</body></html>`;
657
+ }, streamChunkToString2 = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true }), pipeStreamWithHeadInjection = (stream, markup) => {
658
+ const encoder = new TextEncoder;
659
+ const decoder = new TextDecoder;
660
+ const lookbehind = CLOSING_HEAD_TAG2.length - 1;
661
+ return new ReadableStream({
662
+ async start(controller) {
663
+ const reader = stream.getReader();
664
+ let injected = false;
665
+ let pending = "";
666
+ try {
667
+ for (;; ) {
668
+ const { done, value } = await reader.read();
669
+ if (done)
670
+ break;
671
+ if (!value)
672
+ continue;
673
+ pending += streamChunkToString2(value, decoder);
674
+ if (injected) {
675
+ controller.enqueue(encoder.encode(pending));
676
+ pending = "";
677
+ continue;
678
+ }
679
+ const headIndex = pending.indexOf(CLOSING_HEAD_TAG2);
680
+ if (headIndex >= 0) {
681
+ const next = `${pending.slice(0, headIndex)}${markup}${pending.slice(headIndex)}`;
682
+ controller.enqueue(encoder.encode(next));
683
+ pending = "";
684
+ injected = true;
685
+ continue;
686
+ }
687
+ if (pending.length > lookbehind) {
688
+ const safeText = pending.slice(0, pending.length - lookbehind);
689
+ controller.enqueue(encoder.encode(safeText));
690
+ pending = pending.slice(-lookbehind);
691
+ }
692
+ }
693
+ pending += decoder.decode();
694
+ if (!injected) {
695
+ pending = injectHeadMarkup(pending, markup);
696
+ }
697
+ if (pending.length > 0) {
698
+ controller.enqueue(encoder.encode(pending));
699
+ }
700
+ controller.close();
701
+ } catch (error) {
702
+ controller.error(error);
703
+ }
704
+ }
705
+ });
706
+ }, pipeStreamWithIslandMarkerDetection = (stream, markup) => {
707
+ const encoder = new TextEncoder;
708
+ const decoder = new TextDecoder;
709
+ const lookbehind = Math.max(ISLAND_MARKER.length, 1024);
710
+ return new ReadableStream({
711
+ async start(controller) {
712
+ const reader = stream.getReader();
713
+ let injected = false;
714
+ let pending = "";
715
+ try {
716
+ for (;; ) {
717
+ const { done, value } = await reader.read();
718
+ if (done)
719
+ break;
720
+ if (!value)
721
+ continue;
722
+ pending += streamChunkToString2(value, decoder);
723
+ if (injected) {
724
+ controller.enqueue(encoder.encode(pending));
725
+ pending = "";
726
+ continue;
727
+ }
728
+ const markerIndex = pending.indexOf(ISLAND_MARKER);
729
+ if (markerIndex >= 0) {
730
+ const tagStart = pending.lastIndexOf("<", markerIndex);
731
+ const injectAt = tagStart >= 0 ? tagStart : markerIndex;
732
+ const next = `${pending.slice(0, injectAt)}${markup}${pending.slice(injectAt)}`;
733
+ controller.enqueue(encoder.encode(next));
734
+ pending = "";
735
+ injected = true;
736
+ continue;
737
+ }
738
+ if (pending.length > lookbehind) {
739
+ const safeText = pending.slice(0, pending.length - lookbehind);
740
+ controller.enqueue(encoder.encode(safeText));
741
+ pending = pending.slice(-lookbehind);
742
+ }
743
+ }
744
+ pending += decoder.decode();
745
+ if (pending.length > 0) {
746
+ controller.enqueue(encoder.encode(pending));
747
+ }
748
+ controller.close();
749
+ } catch (error) {
750
+ controller.error(error);
751
+ }
752
+ }
753
+ });
754
+ }, htmlContainsIslands = (html) => html.includes(ISLAND_MARKER), injectIslandPageContext = (html, options) => {
755
+ const manifest = globalThis.__absoluteManifest;
756
+ const hasIslands = options?.hasIslands ?? htmlContainsIslands(html);
757
+ if (!manifest || !hasIslands) {
758
+ return html;
759
+ }
760
+ if (html.includes(MANIFEST_MARKER) || html.includes(ISLAND_STATE_MARKER)) {
761
+ return html;
762
+ }
763
+ return injectHeadMarkup(html, buildIslandsHeadMarkup(manifest));
764
+ }, injectIslandPageContextStream = (stream, options) => {
765
+ const manifest = globalThis.__absoluteManifest;
766
+ if (!manifest)
767
+ return stream;
768
+ const markup = buildIslandsHeadMarkup(manifest);
769
+ if (options?.hasIslands === true) {
770
+ return pipeStreamWithHeadInjection(stream, markup);
757
771
  }
758
- const proto = \u{275}DominoAdapter.prototype;
759
- const origGetBaseHref = proto.getBaseHref;
760
- proto.getBaseHref = function(doc) {
761
- if (!doc || !doc.head || typeof doc.head.children === "undefined") {
762
- return "";
763
- }
764
- return origGetBaseHref.call(this, doc);
765
- };
766
- const origCreateHtmlDocument = proto.createHtmlDocument;
767
- proto.createHtmlDocument = function() {
768
- const doc = origCreateHtmlDocument.call(this);
769
- ensureHead(doc);
770
- return doc;
771
- };
772
- const origGetDefaultDocument = proto.getDefaultDocument;
773
- proto.getDefaultDocument = function() {
774
- const doc = origGetDefaultDocument.call(this);
775
- ensureHead(doc);
776
- return doc;
777
- };
778
- return true;
772
+ if (options?.hasIslands === false) {
773
+ return stream;
774
+ }
775
+ return pipeStreamWithIslandMarkerDetection(stream, markup);
776
+ }, setCurrentIslandManifest = (manifest) => {
777
+ globalThis.__absoluteManifest = manifest;
779
778
  };
780
- var init_angularPatch = __esm(() => {
781
- init_resolveAngularPackage();
782
- });
783
779
 
784
- // src/angular/angularDeps.ts
785
- var initDominoAdapter = (platformServer) => {
786
- try {
787
- const DominoAdapter = platformServer.\u{275}DominoAdapter;
788
- DominoAdapter?.makeCurrent?.();
789
- } catch (err) {
790
- console.error("Failed to initialize DominoAdapter:", err);
791
- }
792
- }, loadAngularDeps = async () => {
793
- patchAngularInjectorSingleton();
794
- await import(resolveAngularPackage("@angular/compiler"));
795
- const { applyPatches: applyPatches2 } = await Promise.resolve().then(() => (init_angularPatch(), exports_angularPatch));
796
- await applyPatches2();
797
- const [platformBrowser, platformServer, common, core] = await Promise.all([
798
- import(resolveAngularPackage("@angular/platform-browser")),
799
- import(resolveAngularPackage("@angular/platform-server")),
800
- import(resolveAngularPackage("@angular/common")),
801
- import(resolveAngularPackage("@angular/core"))
802
- ]);
803
- if (false) {}
804
- initDominoAdapter(platformServer);
805
- return {
806
- APP_BASE_HREF: common.APP_BASE_HREF,
807
- bootstrapApplication: platformBrowser.bootstrapApplication,
808
- DomSanitizer: platformBrowser.DomSanitizer,
809
- provideClientHydration: platformBrowser.provideClientHydration,
810
- provideServerRendering: platformServer.provideServerRendering,
811
- provideZonelessChangeDetection: core.provideZonelessChangeDetection,
812
- reflectComponentType: core.reflectComponentType,
813
- renderApplication: platformServer.renderApplication,
814
- Sanitizer: core.Sanitizer,
815
- SecurityContext: core.SecurityContext
780
+ // src/utils/ssrErrorPage.ts
781
+ var ssrErrorPage = (framework, error) => {
782
+ const frameworkColors = {
783
+ angular: "#dd0031",
784
+ html: "#e34c26",
785
+ htmx: "#1a365d",
786
+ react: "#61dafb",
787
+ svelte: "#ff3e00",
788
+ vue: "#42b883"
816
789
  };
817
- }, angularDeps = null, getAngularDeps = () => {
818
- if (!angularDeps) {
819
- angularDeps = loadAngularDeps();
820
- }
821
- return angularDeps;
790
+ const accent = frameworkColors[framework] ?? "#94a3b8";
791
+ const label = framework.charAt(0).toUpperCase() + framework.slice(1);
792
+ const message = error instanceof Error ? error.message : String(error);
793
+ return `<!DOCTYPE html>
794
+ <html>
795
+ <head>
796
+ <meta charset="utf-8">
797
+ <meta name="viewport" content="width=device-width, initial-scale=1">
798
+ <title>SSR Error - AbsoluteJS</title>
799
+ <style>
800
+ *{margin:0;padding:0;box-sizing:border-box}
801
+ 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}
802
+ .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}
803
+ .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)}
804
+ .brand{font-weight:700;font-size:20px;color:#fff;letter-spacing:-0.02em}
805
+ .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)}
806
+ .kind{color:#94a3b8;font-size:13px;font-weight:500}
807
+ .content{padding:24px}
808
+ .label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.08em;color:#94a3b8;margin-bottom:8px}
809
+ .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}
810
+ .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}
811
+ </style>
812
+ </head>
813
+ <body>
814
+ <div class="card">
815
+ <div class="header">
816
+ <div style="display:flex;align-items:center;gap:12px">
817
+ <span class="brand">AbsoluteJS</span>
818
+ <span class="badge">${label}</span>
819
+ </div>
820
+ <span class="kind">Server Render Error</span>
821
+ </div>
822
+ <div class="content">
823
+ <div class="label">What went wrong</div>
824
+ <pre class="message">${message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}</pre>
825
+ <div class="hint">A component threw during server-side rendering. Check the terminal for the full stack trace.</div>
826
+ </div>
827
+ </div>
828
+ </body>
829
+ </html>`;
822
830
  };
823
- var init_angularDeps = __esm(() => {
824
- init_injectorPatch();
825
- init_resolveAngularPackage();
826
- });
827
831
 
828
- // src/utils/registerClientScript.ts
829
- var scriptRegistry, requestCounter = 0, getRequestId = () => `req_${Date.now()}_${++requestCounter}`, ssrContextGetter = null, getSsrContextId = () => ssrContextGetter?.() || Object.getOwnPropertyDescriptor(globalThis, "__absolutejs_requestId")?.value, registerClientScript = (script, requestId) => {
830
- const id = requestId || getSsrContextId() || getRequestId();
831
- if (!scriptRegistry.has(id)) {
832
- scriptRegistry.set(id, new Set);
833
- }
834
- scriptRegistry.get(id)?.add(script);
835
- return id;
836
- }, setSsrContextGetter = (getter) => {
837
- ssrContextGetter = getter;
838
- }, clearAllClientScripts = () => {
839
- scriptRegistry.clear();
840
- }, generateClientScriptCode = (scripts) => {
841
- if (scripts.length === 0) {
842
- return "";
832
+ // src/utils/stringModifiers.ts
833
+ 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) => {
834
+ if (!str.includes("-") && !str.includes("_")) {
835
+ return str.charAt(0).toUpperCase() + str.slice(1);
843
836
  }
844
- const scriptCode = scripts.map((script, index) => {
845
- const funcString = script.toString();
846
- const bodyMatch = funcString.match(/\{([\s\S]*)\}/);
847
- if (!bodyMatch || !bodyMatch[1]) {
848
- return "";
849
- }
850
- const body = bodyMatch[1].trim();
851
- return `
852
- (function() {
853
- var executed = false;
854
- function executeScript_${index}() {
855
- if (executed) return;
856
- executed = true;
857
- ${body}
858
- }
859
-
860
- if (document.readyState === 'complete' || document.readyState === 'interactive') {
861
- executeScript_${index}();
862
- } else {
863
- document.addEventListener('DOMContentLoaded', executeScript_${index});
864
- }
865
-
866
- // Watch for hydration-added elements
867
- var observer = new MutationObserver(function() {
868
- executeScript_${index}();
869
- if (executed) observer.disconnect();
870
- });
871
- if (!executed) {
872
- observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
873
- }
837
+ return normalizeSlug(str).split(/[-_]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase()).join("");
838
+ }, toScreamingSnake = (str) => str.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
874
839
 
875
- // Single fallback timeout
876
- setTimeout(function() {
877
- executeScript_${index}();
878
- observer.disconnect();
879
- }, 1000);
880
- })();`;
881
- }).join(`
882
- `);
883
- return `<script>
884
- (function() {
885
- ${scriptCode}
886
- })();
887
- </script>`;
888
- }, getAndClearClientScripts = (requestId) => {
889
- const id = requestId || ssrContextGetter?.();
890
- if (!id)
891
- return [];
892
- const scripts = scriptRegistry.get(id);
893
- if (!scripts) {
894
- return [];
840
+ // src/utils/resolveConvention.ts
841
+ import { basename } from "path";
842
+ var CONVENTIONS_KEY = "__absoluteConventions", isConventionsMap = (value) => Boolean(value) && typeof value === "object", getMap = () => {
843
+ const value = Reflect.get(globalThis, CONVENTIONS_KEY);
844
+ if (isConventionsMap(value))
845
+ return value;
846
+ const empty = {};
847
+ return empty;
848
+ }, derivePageName = (pagePath) => {
849
+ const base = basename(pagePath);
850
+ const dotIndex = base.indexOf(".");
851
+ const name = dotIndex > 0 ? base.slice(0, dotIndex) : base;
852
+ return toPascal(name);
853
+ }, resolveErrorConventionPath = (framework, pageName) => {
854
+ const conventions = getMap()[framework];
855
+ if (!conventions)
856
+ return;
857
+ return conventions.pages?.[pageName]?.error ?? conventions.defaults?.error;
858
+ }, resolveNotFoundConventionPath = (framework) => getMap()[framework]?.defaults?.notFound, setConventions = (map) => {
859
+ Reflect.set(globalThis, CONVENTIONS_KEY, map);
860
+ }, isDev = () => true, buildErrorProps = (error) => {
861
+ const message = error instanceof Error ? error.message : String(error);
862
+ const stack = isDev() && error instanceof Error ? error.stack : undefined;
863
+ return { error: { message, stack } };
864
+ }, renderReactError = async (conventionPath, errorProps) => {
865
+ const { createElement } = await import("react");
866
+ const { renderToReadableStream } = await import("react-dom/server");
867
+ const mod = await import(conventionPath);
868
+ const [firstKey] = Object.keys(mod);
869
+ const ErrorComponent = mod.default ?? (firstKey ? mod[firstKey] : undefined);
870
+ const element = createElement(ErrorComponent, errorProps);
871
+ const stream = await renderToReadableStream(element);
872
+ return new Response(stream, {
873
+ headers: { "Content-Type": "text/html" },
874
+ status: 500
875
+ });
876
+ }, renderSvelteError = async (conventionPath, errorProps) => {
877
+ const { render } = await import("svelte/server");
878
+ const mod = await import(conventionPath);
879
+ const ErrorComponent = mod.default;
880
+ const { head, body } = render(ErrorComponent, {
881
+ props: errorProps
882
+ });
883
+ const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
884
+ return new Response(html, {
885
+ headers: { "Content-Type": "text/html" },
886
+ status: 500
887
+ });
888
+ }, unescapeVueStyles = (ssrBody) => {
889
+ let styles = "";
890
+ const body = ssrBody.replace(/<style>([\s\S]*?)<\/style>/g, (_, css) => {
891
+ styles += `<style>${css.replace(/&quot;/g, '"').replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">")}</style>`;
892
+ return "";
893
+ });
894
+ return { body, styles };
895
+ }, renderVueError = async (conventionPath, errorProps) => {
896
+ const { createSSRApp, h: h3 } = await import("vue");
897
+ const { renderToString } = await import("vue/server-renderer");
898
+ const mod = await import(conventionPath);
899
+ const ErrorComponent = mod.default;
900
+ const app = createSSRApp({
901
+ render: () => h3(ErrorComponent, errorProps)
902
+ });
903
+ const rawBody = await renderToString(app);
904
+ const { styles, body } = unescapeVueStyles(rawBody);
905
+ const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
906
+ return new Response(html, {
907
+ headers: { "Content-Type": "text/html" },
908
+ status: 500
909
+ });
910
+ }, renderAngularError = async (conventionPath, errorProps) => {
911
+ const mod = await import(conventionPath);
912
+ const renderError = mod.default ?? mod.renderError;
913
+ if (typeof renderError !== "function")
914
+ return null;
915
+ const html = renderError(errorProps);
916
+ return new Response(html, {
917
+ headers: { "Content-Type": "text/html" },
918
+ status: 500
919
+ });
920
+ }, logConventionRenderError = (framework, label, renderError) => {
921
+ const message = renderError instanceof Error ? renderError.message : "";
922
+ if (message.includes("Cannot find module") || message.includes("Cannot find package") || message.includes("not found in module")) {
923
+ 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}).`);
924
+ return;
895
925
  }
896
- const scriptArray = Array.from(scripts);
897
- scriptRegistry.delete(id);
898
- return scriptArray;
899
- };
900
- var init_registerClientScript = __esm(() => {
901
- scriptRegistry = new Map;
902
- if (typeof globalThis !== "undefined") {
903
- Object.assign(globalThis, { registerClientScript });
926
+ console.error(`[SSR] Failed to render ${framework} convention ${label} page:`, renderError);
927
+ }, ERROR_RENDERERS, renderConventionError = async (framework, pageName, error) => {
928
+ const conventionPath = resolveErrorConventionPath(framework, pageName);
929
+ if (!conventionPath)
930
+ return null;
931
+ const errorProps = buildErrorProps(error);
932
+ const renderer = ERROR_RENDERERS[framework];
933
+ if (!renderer)
934
+ return null;
935
+ try {
936
+ return await renderer(conventionPath, errorProps);
937
+ } catch (renderError) {
938
+ logConventionRenderError(framework, "error", renderError);
904
939
  }
905
- });
906
-
907
- // src/angular/ssrRender.ts
908
- var routePropsCache, cacheRouteData = (pagePath, data) => {
909
- const cacheKey = pagePath.split("?")[0] ?? pagePath;
910
- routePropsCache.set(cacheKey, data);
911
- }, getCachedRouteData = (pagePath) => routePropsCache.get(pagePath), selectorCache, buildDeps = (ssrResult, baseDeps) => {
912
- if (!ssrResult?.core) {
913
- return baseDeps;
940
+ return null;
941
+ }, renderReactNotFound = async (conventionPath) => {
942
+ const { createElement } = await import("react");
943
+ const { renderToReadableStream } = await import("react-dom/server");
944
+ const mod = await import(conventionPath);
945
+ const [nfKey] = Object.keys(mod);
946
+ const NotFoundComponent = mod.default ?? (nfKey ? mod[nfKey] : undefined);
947
+ const element = createElement(NotFoundComponent);
948
+ const stream = await renderToReadableStream(element);
949
+ return new Response(stream, {
950
+ headers: { "Content-Type": "text/html" },
951
+ status: 404
952
+ });
953
+ }, renderSvelteNotFound = async (conventionPath) => {
954
+ const { render } = await import("svelte/server");
955
+ const mod = await import(conventionPath);
956
+ const NotFoundComponent = mod.default;
957
+ const { head, body } = render(NotFoundComponent);
958
+ const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
959
+ return new Response(html, {
960
+ headers: { "Content-Type": "text/html" },
961
+ status: 404
962
+ });
963
+ }, renderVueNotFound = async (conventionPath) => {
964
+ const { createSSRApp, h: h3 } = await import("vue");
965
+ const { renderToString } = await import("vue/server-renderer");
966
+ const mod = await import(conventionPath);
967
+ const NotFoundComponent = mod.default;
968
+ const app = createSSRApp({
969
+ render: () => h3(NotFoundComponent)
970
+ });
971
+ const rawBody = await renderToString(app);
972
+ const { styles, body } = unescapeVueStyles(rawBody);
973
+ const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
974
+ return new Response(html, {
975
+ headers: { "Content-Type": "text/html" },
976
+ status: 404
977
+ });
978
+ }, renderAngularNotFound = async (conventionPath) => {
979
+ const mod = await import(conventionPath);
980
+ const renderNotFound = mod.default ?? mod.renderNotFound;
981
+ if (typeof renderNotFound !== "function")
982
+ return null;
983
+ const html = renderNotFound();
984
+ return new Response(html, {
985
+ headers: { "Content-Type": "text/html" },
986
+ status: 404
987
+ });
988
+ }, NOT_FOUND_RENDERERS, renderConventionNotFound = async (framework) => {
989
+ const conventionPath = resolveNotFoundConventionPath(framework);
990
+ if (!conventionPath)
991
+ return null;
992
+ const renderer = NOT_FOUND_RENDERERS[framework];
993
+ if (!renderer)
994
+ return null;
995
+ try {
996
+ return await renderer(conventionPath);
997
+ } catch (renderError) {
998
+ logConventionRenderError(framework, "not-found", renderError);
914
999
  }
915
- const { common, core, platformBrowser, platformServer } = ssrResult;
916
- return {
917
- APP_BASE_HREF: common?.APP_BASE_HREF ?? baseDeps.APP_BASE_HREF,
918
- bootstrapApplication: platformBrowser?.bootstrapApplication ?? baseDeps.bootstrapApplication,
919
- DomSanitizer: platformBrowser?.DomSanitizer ?? baseDeps.DomSanitizer,
920
- provideClientHydration: platformBrowser?.provideClientHydration ?? baseDeps.provideClientHydration,
921
- provideServerRendering: platformServer?.provideServerRendering ?? baseDeps.provideServerRendering,
922
- provideZonelessChangeDetection: core.provideZonelessChangeDetection,
923
- reflectComponentType: core.reflectComponentType,
924
- renderApplication: platformServer?.renderApplication ?? baseDeps.renderApplication,
925
- Sanitizer: core.Sanitizer,
926
- SecurityContext: core.SecurityContext
1000
+ return null;
1001
+ }, NOT_FOUND_PRIORITY, renderFirstNotFound = async () => {
1002
+ for (const framework of NOT_FOUND_PRIORITY) {
1003
+ if (!getMap()[framework]?.defaults?.notFound)
1004
+ continue;
1005
+ const response = await renderConventionNotFound(framework);
1006
+ if (response)
1007
+ return response;
1008
+ }
1009
+ return null;
1010
+ };
1011
+ var init_resolveConvention = __esm(() => {
1012
+ ERROR_RENDERERS = {
1013
+ angular: renderAngularError,
1014
+ react: renderReactError,
1015
+ svelte: renderSvelteError,
1016
+ vue: renderVueError
927
1017
  };
928
- }, buildProviders = (deps, sanitizer, maybeProps, tokenMap) => {
929
- const providers = [
930
- deps.provideServerRendering(),
931
- deps.provideClientHydration(),
932
- deps.provideZonelessChangeDetection(),
933
- { provide: deps.APP_BASE_HREF, useValue: "/" },
934
- {
935
- provide: deps.DomSanitizer,
936
- useValue: sanitizer
937
- },
938
- { provide: deps.Sanitizer, useValue: sanitizer }
1018
+ NOT_FOUND_RENDERERS = {
1019
+ angular: renderAngularNotFound,
1020
+ react: renderReactNotFound,
1021
+ svelte: renderSvelteNotFound,
1022
+ vue: renderVueNotFound
1023
+ };
1024
+ NOT_FOUND_PRIORITY = [
1025
+ "react",
1026
+ "svelte",
1027
+ "vue",
1028
+ "angular"
939
1029
  ];
940
- if (!maybeProps) {
941
- return providers;
942
- }
943
- const propProviders = Object.entries(maybeProps).map(([propName, propValue]) => ({
944
- token: tokenMap.get(toScreamingSnake(propName)),
945
- value: propValue
946
- })).filter((entry) => entry.token).map((entry) => ({ provide: entry.token, useValue: entry.value }));
947
- return [...providers, ...propProviders];
948
- }, clearSelectorCache = () => selectorCache.clear(), isInjectionToken = (value) => {
949
- if (!value || typeof value !== "object") {
1030
+ });
1031
+
1032
+ // src/vue/pageHandler.ts
1033
+ import { readdir } from "fs/promises";
1034
+ import { basename as basename2, dirname } from "path";
1035
+ var ssrDirty = false, isRecord = (value) => typeof value === "object" && value !== null, isGenericVueComponent = (value) => typeof value === "function" || isRecord(value), readHasIslands = (value) => {
1036
+ if (!isRecord(value))
950
1037
  return false;
951
- }
952
- return "ngMetadataName" in value && value.ngMetadataName === "InjectionToken";
953
- }, discoverTokens = (pageModule) => new Map(Object.entries(pageModule).filter(([, value]) => isInjectionToken(value))), loadSsrDeps = async (pagePath) => {
954
- const ssrDepsPath = (pagePath.split("?")[0] ?? pagePath).replace(/\.js$/, ".ssr-deps.js");
1038
+ const hasIslands = value["__ABSOLUTE_PAGE_HAS_ISLANDS__"];
1039
+ return typeof hasIslands === "boolean" ? hasIslands : false;
1040
+ }, readDefaultExport = (value) => isRecord(value) ? value.default : undefined, resolveCurrentGeneratedVueModulePath = async (pagePath) => {
1041
+ const pageDirectory = dirname(pagePath);
1042
+ const expectedPrefix = `${basename2(pagePath, ".js").split(".")[0]}.`;
955
1043
  try {
956
- const ssrDeps = await import(ssrDepsPath);
957
- const result = {
958
- common: ssrDeps.__angularCommon,
959
- core: ssrDeps.__angularCore,
960
- platformBrowser: ssrDeps.__angularPlatformBrowser,
961
- platformServer: ssrDeps.__angularPlatformServer
962
- };
963
- return result;
1044
+ const pageEntries = await readdir(pageDirectory, {
1045
+ withFileTypes: true
1046
+ });
1047
+ const matchingEntry = pageEntries.find((entry) => entry.isFile() && entry.name.endsWith(".js") && (entry.name === `${expectedPrefix.slice(0, EXCLUDE_LAST_OFFSET)}.js` || entry.name.startsWith(expectedPrefix)));
1048
+ if (!matchingEntry) {
1049
+ return pagePath;
1050
+ }
1051
+ return `${pageDirectory}/${matchingEntry.name}`;
964
1052
  } catch {
965
- return null;
966
- }
967
- }, resolveSelector = (deps, pagePath, PageComponent) => {
968
- const cached = selectorCache.get(pagePath);
969
- if (cached) {
970
- return cached;
971
- }
972
- const selector = deps.reflectComponentType(PageComponent)?.selector ?? "ng-app";
973
- selectorCache.set(pagePath, selector);
974
- return selector;
975
- }, injectBeforeClose = (html, snippet) => {
976
- if (html.includes("</body>")) {
977
- return html.replace("</body>", `${snippet}</body>`);
978
- }
979
- if (html.includes("</html>")) {
980
- return html.replace("</html>", `${snippet}</html>`);
981
- }
982
- return html + snippet;
983
- }, injectSsrScripts = (html, requestId, indexPath, props) => {
984
- let result = html;
985
- const registeredScripts = getAndClearClientScripts(requestId);
986
- if (registeredScripts.length > 0) {
987
- result = injectBeforeClose(result, generateClientScriptCode(registeredScripts));
988
- }
989
- if (props) {
990
- result = injectBeforeClose(result, `<script>window.__ABS_ANGULAR_PAGE_PROPS__ = ${JSON.stringify(props)};</script>`);
991
- }
992
- if (indexPath) {
993
- result = injectBeforeClose(result, `<script type="module" src="${indexPath}"></script>`);
1053
+ return pagePath;
1054
+ }
1055
+ }, buildDirtyResponse = (headTag, indexPath, maybeProps) => {
1056
+ const propsScript = `window.__INITIAL_PROPS__=${JSON.stringify(maybeProps ?? {})};`;
1057
+ const dirtyFlag = "window.__SSR_DIRTY__=true;";
1058
+ const html = `<!DOCTYPE html><html>${headTag}<body><div id="root"></div>` + `<script>${propsScript}${dirtyFlag}</script>` + `<script type="module" src="${indexPath}"></script>` + `</body></html>`;
1059
+ return new Response(html, {
1060
+ headers: { "Content-Type": "text/html" }
1061
+ });
1062
+ }, handleVuePageRequest = async (_PageComponent, pagePath, indexPath, headTag = "<head></head>", ...props) => {
1063
+ const [maybeProps] = props;
1064
+ if (ssrDirty) {
1065
+ return buildDirtyResponse(headTag, indexPath, maybeProps);
994
1066
  }
995
- return result;
996
- }, renderAngularApp = async (deps, PageComponent, providers, document2) => {
997
- const bootstrap = (context) => deps.bootstrapApplication(PageComponent, { providers }, context);
998
- return withSuppressedAngularDevLogs(() => deps.renderApplication(bootstrap, {
999
- document: document2,
1000
- platformProviders: [],
1001
- url: "/"
1002
- }));
1003
- }, withSuppressedAngularDevLogs = async (render) => {
1004
- const origLog = console.log;
1005
- console.log = (...args) => {
1006
- if (typeof args[0] === "string" && args[0].includes("development mode")) {
1007
- return;
1008
- }
1009
- origLog.apply(console, args);
1010
- };
1011
1067
  try {
1012
- return await render();
1013
- } finally {
1014
- console.log = origLog;
1068
+ const resolvePageComponent = async () => {
1069
+ const passedPageComponent = _PageComponent;
1070
+ if (isGenericVueComponent(passedPageComponent)) {
1071
+ return {
1072
+ component: passedPageComponent,
1073
+ hasIslands: readHasIslands(passedPageComponent)
1074
+ };
1075
+ }
1076
+ const generatedPagePath = await resolveCurrentGeneratedVueModulePath(pagePath);
1077
+ const importedPageModule = await import(generatedPagePath);
1078
+ const importedPageComponent = readDefaultExport(importedPageModule) ?? importedPageModule;
1079
+ if (!isGenericVueComponent(importedPageComponent)) {
1080
+ throw new Error(`Invalid Vue page module: ${generatedPagePath}`);
1081
+ }
1082
+ return {
1083
+ component: importedPageComponent,
1084
+ hasIslands: readHasIslands(importedPageModule)
1085
+ };
1086
+ };
1087
+ const resolvedPage = await resolvePageComponent();
1088
+ const { createSSRApp, h: h3 } = await import("vue");
1089
+ const { renderToWebStream } = await import("vue/server-renderer");
1090
+ const app = createSSRApp({
1091
+ render: () => h3(resolvedPage.component, maybeProps ?? null)
1092
+ });
1093
+ const bodyStream = renderToWebStream(app);
1094
+ const head = `<!DOCTYPE html><html>${headTag}<body><div id="root">`;
1095
+ const tail = `</div><script>window.__INITIAL_PROPS__=${JSON.stringify(maybeProps ?? {})}</script><script type="module" src="${indexPath}"></script></body></html>`;
1096
+ const stream = new ReadableStream({
1097
+ start(controller) {
1098
+ controller.enqueue(head);
1099
+ const reader = bodyStream.getReader();
1100
+ const pumpLoop = () => {
1101
+ reader.read().then(({ done, value }) => done ? (controller.enqueue(tail), controller.close()) : (controller.enqueue(value), pumpLoop())).catch((err) => controller.error(err));
1102
+ };
1103
+ pumpLoop();
1104
+ }
1105
+ });
1106
+ const htmlStream = injectIslandPageContextStream(stream, {
1107
+ hasIslands: resolvedPage.hasIslands
1108
+ });
1109
+ return new Response(htmlStream, {
1110
+ headers: { "Content-Type": "text/html" }
1111
+ });
1112
+ } catch (error) {
1113
+ console.error("[SSR] Vue render error:", error);
1114
+ const pageName = derivePageName(pagePath);
1115
+ const conventionResponse = await renderConventionError("vue", pageName, error);
1116
+ if (conventionResponse)
1117
+ return conventionResponse;
1118
+ return new Response(ssrErrorPage("vue", error), {
1119
+ headers: { "Content-Type": "text/html" },
1120
+ status: 500
1121
+ });
1015
1122
  }
1123
+ }, invalidateVueSsrCache = () => {
1124
+ ssrDirty = true;
1016
1125
  };
1017
- var init_ssrRender = __esm(() => {
1018
- init_registerClientScript();
1019
- routePropsCache = new Map;
1020
- selectorCache = new Map;
1126
+ var init_pageHandler = __esm(() => {
1127
+ init_constants();
1128
+ init_resolveConvention();
1021
1129
  });
1022
1130
 
1023
- // src/angular/islands.ts
1024
- var angularIslandSelector = "abs-angular-island", getAngularIslandSelector = (_islandId) => angularIslandSelector, getSelectorFromRenderedIsland = (rootElement) => {
1025
- const firstChild = rootElement.firstElementChild;
1026
- if (!(firstChild instanceof HTMLElement)) {
1027
- return null;
1028
- }
1029
- const selector = firstChild.tagName.toLowerCase();
1030
- return selector.length > 0 ? selector : null;
1031
- }, getClientAngularComponentSelector = (component) => {
1032
- const maybeDef = Reflect.get(component, "\u0275cmp");
1033
- if (typeof maybeDef !== "object" || maybeDef === null) {
1034
- return null;
1035
- }
1036
- const maybeSelectors = Reflect.get(maybeDef, "selectors");
1037
- if (!Array.isArray(maybeSelectors)) {
1038
- return null;
1039
- }
1040
- const [firstSelectorGroup] = maybeSelectors;
1041
- if (!Array.isArray(firstSelectorGroup)) {
1042
- return null;
1043
- }
1044
- const [selector] = firstSelectorGroup;
1045
- return typeof selector === "string" && selector.length > 0 ? selector : null;
1046
- }, createAngularIslandApp = async () => {
1047
- const { EnvironmentInjector, provideZonelessChangeDetection } = await import("@angular/core");
1048
- const { createApplication } = await import("@angular/platform-browser");
1049
- const app = await createApplication({
1050
- providers: [provideZonelessChangeDetection()]
1051
- });
1052
- const environmentInjector = app.injector.get(EnvironmentInjector);
1053
- return { app, environmentInjector };
1054
- }, angularIslandAppPromise = null, getAngularIslandApp = async () => {
1055
- if (!angularIslandAppPromise) {
1056
- angularIslandAppPromise = createAngularIslandApp();
1057
- }
1058
- return angularIslandAppPromise;
1059
- }, wrapperMetadataCache, requestRenderCache, getRequestRenderCache = () => {
1060
- const requestId = getSsrContextId();
1061
- if (!requestId) {
1062
- return null;
1131
+ // src/core/islandManifest.ts
1132
+ var toIslandFrameworkSegment = (framework) => framework[0]?.toUpperCase() + framework.slice(1), collectFrameworkIslands = (manifest, prefix) => {
1133
+ const entries = {};
1134
+ let found = false;
1135
+ for (const [key, value] of Object.entries(manifest)) {
1136
+ if (!key.startsWith(prefix))
1137
+ continue;
1138
+ const component = key.slice(prefix.length);
1139
+ if (!component)
1140
+ continue;
1141
+ entries[component] = value;
1142
+ found = true;
1063
1143
  }
1064
- const cached = requestRenderCache.get(requestId);
1065
- if (cached) {
1066
- return cached;
1144
+ return found ? entries : undefined;
1145
+ }, getIslandManifestEntries = (manifest) => {
1146
+ const islands = {};
1147
+ const frameworks = ["react", "svelte", "vue", "angular"];
1148
+ for (const framework of frameworks) {
1149
+ const prefix = `Island${toIslandFrameworkSegment(framework)}`;
1150
+ const entries = collectFrameworkIslands(manifest, prefix);
1151
+ if (entries)
1152
+ islands[framework] = entries;
1067
1153
  }
1068
- const renderCache = new Map;
1069
- requestRenderCache.set(requestId, renderCache);
1070
- return renderCache;
1071
- }, getAngularIslandWrapperKey = (component, _islandId) => {
1072
- const componentName = typeof component.name === "string" && component.name.length > 0 ? component.name : "AngularIsland";
1073
- return `${componentName}:${angularIslandSelector}`;
1074
- }, getIslandRenderCacheKey = (component, props) => {
1075
- const componentName = typeof component.name === "string" && component.name.length > 0 ? component.name : "AngularIsland";
1076
- return `${componentName}:${JSON.stringify(props)}`;
1077
- }, buildAngularIslandWrapperMetadata = async (component, islandId, wrapperKey) => {
1078
- const { Component, InjectionToken, inject } = await import("@angular/core");
1079
- const { NgComponentOutlet } = await import("@angular/common");
1080
- const deps = await getAngularDeps();
1081
- const selector = getAngularIslandSelector(islandId);
1082
- const propsToken = new InjectionToken(`${wrapperKey}:props`);
1154
+ return islands;
1155
+ }, getIslandManifestKey = (framework, component) => `Island${toIslandFrameworkSegment(framework)}${component}`;
1083
1156
 
1084
- class AngularIslandWrapperComponent {
1085
- component = component;
1086
- props = inject(propsToken);
1157
+ // src/core/islands.ts
1158
+ function getIslandComponent(component) {
1159
+ if (isIslandComponentDefinition(component)) {
1160
+ return component.component;
1087
1161
  }
1162
+ return component;
1163
+ }
1164
+ var defineIslandComponent = (component, options) => ({
1165
+ component,
1166
+ export: options.export,
1167
+ source: options.source
1168
+ }), defineIslandRegistry = (registry) => registry, isRecord2 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component) => {
1169
+ if (!isIslandComponentDefinition(component))
1170
+ return null;
1088
1171
  return {
1089
- deps,
1090
- propsToken,
1091
- selector,
1092
- WrapperComponent: Component({
1093
- imports: [NgComponentOutlet, component],
1094
- selector,
1095
- standalone: true,
1096
- template: '<ng-container *ngComponentOutlet="component; inputs: props"></ng-container>'
1097
- })(AngularIslandWrapperComponent)
1172
+ export: component.export,
1173
+ source: component.source
1098
1174
  };
1099
- }, createAngularIslandWrapper = async (component, islandId) => {
1100
- const wrapperKey = getAngularIslandWrapperKey(component, islandId);
1101
- const cached = wrapperMetadataCache.get(wrapperKey);
1102
- if (cached) {
1103
- return cached;
1104
- }
1105
- const metadataPromise = buildAngularIslandWrapperMetadata(component, islandId, wrapperKey);
1106
- wrapperMetadataCache.set(wrapperKey, metadataPromise);
1107
- return metadataPromise;
1108
- }, extractAngularIslandRoot = (html, selector) => {
1109
- const openTag = `<${selector}`;
1110
- const start = html.indexOf(openTag);
1111
- if (start < 0) {
1112
- throw new Error(`Could not find Angular island root "${selector}".`);
1175
+ }, isIslandComponentDefinition = (value) => isRecord2(value) && ("component" in value) && ("source" in value) && typeof value.source === "string", parseIslandProps = (rawProps) => {
1176
+ if (!rawProps)
1177
+ return {};
1178
+ return JSON.parse(rawProps);
1179
+ }, serializeIslandProps = (props) => JSON.stringify(props ?? {});
1180
+ var init_islands = () => {};
1181
+
1182
+ // src/core/islandMarkupAttributes.ts
1183
+ var getIslandMarkerAttributes = (props, islandId) => ({
1184
+ "data-component": props.component,
1185
+ "data-framework": props.framework,
1186
+ "data-hydrate": props.hydrate ?? "load",
1187
+ "data-island": "true",
1188
+ ...islandId ? { "data-island-id": islandId } : {},
1189
+ "data-props": serializeIslandProps(props.props)
1190
+ }), escapeHtmlAttribute = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;"), serializeIslandAttributes = (attributes) => Object.entries(attributes).map(([key, value]) => `${key}="${escapeHtmlAttribute(value)}"`).join(" ");
1191
+ var init_islandMarkupAttributes = __esm(() => {
1192
+ init_islands();
1193
+ });
1194
+
1195
+ // src/core/currentIslandRegistry.ts
1196
+ var requireCurrentIslandRegistry = () => {
1197
+ const registry = globalThis.__absoluteIslandRegistry;
1198
+ if (!registry) {
1199
+ throw new Error("No island registry is active. Configure `islands.registry` in absolute.config.ts before rendering <Island />.");
1113
1200
  }
1114
- const endTag = `</${selector}>`;
1115
- const end = html.indexOf(endTag, start);
1116
- if (end < 0) {
1117
- throw new Error(`Could not close Angular island root "${selector}".`);
1201
+ return registry;
1202
+ }, setCurrentIslandRegistry = (registry) => {
1203
+ globalThis.__absoluteIslandRegistry = registry;
1204
+ };
1205
+
1206
+ // src/angular/injectorPatch.ts
1207
+ import { existsSync, readFileSync, writeFileSync } from "fs";
1208
+ import { dirname as dirname2, join, resolve } from "path";
1209
+ var applyInjectorPatch = (chunkPath, content) => {
1210
+ if (content.includes('Symbol.for("angular.currentInjector")')) {
1211
+ return;
1118
1212
  }
1119
- return html.slice(start, end + endTag.length);
1120
- }, mountAngularIsland = async (component, element, props, islandId) => {
1121
- await import("@angular/compiler");
1122
- const { createComponent, inputBinding } = await import("@angular/core");
1123
- const selector = getAngularIslandSelector(islandId);
1124
- const { app, environmentInjector } = await getAngularIslandApp();
1125
- let rootElement = element.querySelector(selector);
1126
- if (!(rootElement instanceof HTMLElement)) {
1127
- element.innerHTML = `<${selector}></${selector}>`;
1128
- rootElement = element.querySelector(selector);
1213
+ const original = [
1214
+ "let _currentInjector = undefined;",
1215
+ "function getCurrentInjector() {",
1216
+ " return _currentInjector;",
1217
+ "}",
1218
+ "function setCurrentInjector(injector) {",
1219
+ " const former = _currentInjector;",
1220
+ " _currentInjector = injector;",
1221
+ " return former;",
1222
+ "}"
1223
+ ].join(`
1224
+ `);
1225
+ const replacement = [
1226
+ 'const _injSym = Symbol.for("angular.currentInjector");',
1227
+ "if (!globalThis[_injSym]) globalThis[_injSym] = { v: undefined };",
1228
+ "function getCurrentInjector() {",
1229
+ " return globalThis[_injSym].v;",
1230
+ "}",
1231
+ "function setCurrentInjector(injector) {",
1232
+ " const former = globalThis[_injSym].v;",
1233
+ " globalThis[_injSym].v = injector;",
1234
+ " return former;",
1235
+ "}"
1236
+ ].join(`
1237
+ `);
1238
+ const patched = content.replace(original, replacement);
1239
+ if (patched === content) {
1240
+ return;
1129
1241
  }
1130
- if (!(rootElement instanceof HTMLElement))
1131
- return app;
1132
- const componentSelector = getClientAngularComponentSelector(component) ?? getSelectorFromRenderedIsland(rootElement);
1133
- if (!componentSelector)
1134
- return app;
1135
- rootElement.innerHTML = `<${componentSelector}></${componentSelector}>`;
1136
- const hostElement = rootElement.querySelector(componentSelector);
1137
- if (!(hostElement instanceof HTMLElement))
1138
- return app;
1139
- const bindings = Object.entries(props).map(([key, value]) => inputBinding(key, () => value));
1140
- const componentRef = createComponent(component, {
1141
- bindings,
1142
- environmentInjector,
1143
- hostElement
1144
- });
1145
- app.attachView(componentRef.hostView);
1146
- componentRef.changeDetectorRef.detectChanges();
1147
- window.__ABS_ANGULAR_ISLAND_APPS__ ??= [];
1148
- window.__ABS_ANGULAR_ISLAND_APPS__.push(app);
1149
- return app;
1150
- }, renderAngularIslandToHtml = async (component, props, islandId) => {
1151
- const requestCache = getRequestRenderCache();
1152
- const renderCacheKey = getIslandRenderCacheKey(component, props);
1153
- const cachedHtml = requestCache?.get(renderCacheKey);
1154
- if (cachedHtml) {
1155
- return cachedHtml;
1242
+ writeFileSync(chunkPath, patched, "utf-8");
1243
+ }, resolveAngularCoreDir = () => {
1244
+ const fromProject = resolve(process.cwd(), "node_modules/@angular/core");
1245
+ if (existsSync(join(fromProject, "package.json"))) {
1246
+ return fromProject;
1156
1247
  }
1157
- const { deps, propsToken, selector, WrapperComponent } = await createAngularIslandWrapper(component, islandId);
1158
- const providers = [
1159
- deps.provideServerRendering(),
1160
- deps.provideZonelessChangeDetection(),
1161
- { provide: deps.APP_BASE_HREF, useValue: "/" },
1162
- { provide: propsToken, useValue: props }
1163
- ];
1164
- const document2 = `<!DOCTYPE html><html><body><${selector}></${selector}></body></html>`;
1165
- const html = await withSuppressedAngularDevLogs(() => deps.renderApplication((context) => deps.bootstrapApplication(WrapperComponent, { providers }, context), {
1166
- document: document2,
1167
- platformProviders: [],
1168
- url: "/"
1169
- }));
1170
- const islandHtml = extractAngularIslandRoot(html, selector);
1171
- requestCache?.set(renderCacheKey, islandHtml);
1172
- return islandHtml;
1248
+ return dirname2(__require.resolve("@angular/core/package.json"));
1249
+ }, patchAngularInjectorSingleton = () => {
1250
+ try {
1251
+ const coreDir = resolveAngularCoreDir();
1252
+ const chunkPath = join(coreDir, "fesm2022", "_not_found-chunk.mjs");
1253
+ const content = readFileSync(chunkPath, "utf-8");
1254
+ applyInjectorPatch(chunkPath, content);
1255
+ } catch {}
1173
1256
  };
1174
- var init_islands2 = __esm(() => {
1175
- init_angularDeps();
1176
- init_ssrRender();
1177
- init_registerClientScript();
1178
- wrapperMetadataCache = new Map;
1179
- requestRenderCache = new Map;
1257
+ var init_injectorPatch = __esm(() => {
1258
+ patchAngularInjectorSingleton();
1180
1259
  });
1181
1260
 
1182
- // src/core/islandSsr.ts
1183
- import { createElement } from "react";
1184
- import { renderToStaticMarkup } from "react-dom/server";
1185
- import { render as renderSvelte } from "svelte/server";
1186
- import { createSSRApp, h as h3 } from "vue";
1187
- import { renderToString as renderVueToString } from "vue/server-renderer";
1188
- var renderReactIslandToHtml = (component, props) => renderToStaticMarkup(createElement(component, props)), renderSvelteIslandToHtml = (component, props) => {
1189
- const { body } = renderSvelte(component, { props });
1190
- return body;
1191
- }, renderVueIslandToHtml = (component, props) => {
1192
- const app = createSSRApp({
1193
- render: () => h3(component, props)
1194
- });
1195
- return renderVueToString(app);
1261
+ // src/angular/resolveAngularPackage.ts
1262
+ import { existsSync as existsSync2 } from "fs";
1263
+ import { resolve as resolve2 } from "path";
1264
+ var resolveAngularPackage = (specifier) => {
1265
+ const fromProject = resolve2(process.cwd(), "node_modules", specifier);
1266
+ if (existsSync2(fromProject)) {
1267
+ return fromProject;
1268
+ }
1269
+ return specifier;
1196
1270
  };
1197
- var init_islandSsr = __esm(() => {
1198
- init_islands2();
1199
- });
1271
+ var init_resolveAngularPackage = () => {};
1200
1272
 
1201
- // src/svelte/lowerIslandSyntax.ts
1202
- var ISLAND_TAG_RE, extractBracedExpression = (text, braceStart) => {
1203
- let depth = 0;
1204
- for (let index = braceStart;index < text.length; index += 1) {
1205
- const char = text[index];
1206
- if (char === "{")
1207
- depth += 1;
1208
- if (char === "}")
1209
- depth -= 1;
1210
- if (depth === 0) {
1211
- return text.slice(braceStart + 1, index).trim();
1212
- }
1273
+ // src/angular/angularPatch.ts
1274
+ var exports_angularPatch = {};
1275
+ __export(exports_angularPatch, {
1276
+ applyPatches: () => applyPatches
1277
+ });
1278
+ var ensureHead = (doc) => {
1279
+ if (!doc || doc.head || !doc.documentElement) {
1280
+ return;
1213
1281
  }
1214
- return null;
1215
- }, extractIslandAttribute = (attributeString, name) => {
1216
- const quotedMatch = attributeString.match(new RegExp(`\\b${name}\\s*=\\s*["']([^"']+)["']`));
1217
- if (quotedMatch?.[1]) {
1218
- return { expression: JSON.stringify(quotedMatch[1]), found: true };
1282
+ const head = doc.createElement("head");
1283
+ doc.documentElement.insertBefore(head, doc.documentElement.firstChild);
1284
+ }, applyPatches = async () => {
1285
+ const { \u{275}DominoAdapter } = await import(resolveAngularPackage("@angular/platform-server"));
1286
+ if (!\u{275}DominoAdapter?.prototype) {
1287
+ console.warn("[Angular Patch] \u0275DominoAdapter not found, skipping patches");
1288
+ return false;
1219
1289
  }
1220
- const attributeIndex = attributeString.search(new RegExp(`\\b${name}\\s*=\\s*\\{`));
1221
- if (attributeIndex < 0) {
1222
- return { expression: "", found: false };
1290
+ const proto = \u{275}DominoAdapter.prototype;
1291
+ const origGetBaseHref = proto.getBaseHref;
1292
+ proto.getBaseHref = function(doc) {
1293
+ if (!doc || !doc.head || typeof doc.head.children === "undefined") {
1294
+ return "";
1295
+ }
1296
+ return origGetBaseHref.call(this, doc);
1297
+ };
1298
+ const origCreateHtmlDocument = proto.createHtmlDocument;
1299
+ proto.createHtmlDocument = function() {
1300
+ const doc = origCreateHtmlDocument.call(this);
1301
+ ensureHead(doc);
1302
+ return doc;
1303
+ };
1304
+ const origGetDefaultDocument = proto.getDefaultDocument;
1305
+ proto.getDefaultDocument = function() {
1306
+ const doc = origGetDefaultDocument.call(this);
1307
+ ensureHead(doc);
1308
+ return doc;
1309
+ };
1310
+ return true;
1311
+ };
1312
+ var init_angularPatch = __esm(() => {
1313
+ init_resolveAngularPackage();
1314
+ });
1315
+
1316
+ // src/angular/angularDeps.ts
1317
+ var initDominoAdapter = (platformServer) => {
1318
+ try {
1319
+ const DominoAdapter = platformServer.\u{275}DominoAdapter;
1320
+ DominoAdapter?.makeCurrent?.();
1321
+ } catch (err) {
1322
+ console.error("Failed to initialize DominoAdapter:", err);
1223
1323
  }
1224
- const braceStart = attributeString.indexOf("{", attributeIndex);
1225
- if (braceStart < 0) {
1226
- return { expression: "", found: false };
1324
+ }, loadAngularDeps = async () => {
1325
+ patchAngularInjectorSingleton();
1326
+ await import(resolveAngularPackage("@angular/compiler"));
1327
+ const { applyPatches: applyPatches2 } = await Promise.resolve().then(() => (init_angularPatch(), exports_angularPatch));
1328
+ await applyPatches2();
1329
+ const [platformBrowser, platformServer, common, core] = await Promise.all([
1330
+ import(resolveAngularPackage("@angular/platform-browser")),
1331
+ import(resolveAngularPackage("@angular/platform-server")),
1332
+ import(resolveAngularPackage("@angular/common")),
1333
+ import(resolveAngularPackage("@angular/core"))
1334
+ ]);
1335
+ if (false) {}
1336
+ initDominoAdapter(platformServer);
1337
+ return {
1338
+ APP_BASE_HREF: common.APP_BASE_HREF,
1339
+ bootstrapApplication: platformBrowser.bootstrapApplication,
1340
+ DomSanitizer: platformBrowser.DomSanitizer,
1341
+ provideClientHydration: platformBrowser.provideClientHydration,
1342
+ provideServerRendering: platformServer.provideServerRendering,
1343
+ provideZonelessChangeDetection: core.provideZonelessChangeDetection,
1344
+ reflectComponentType: core.reflectComponentType,
1345
+ renderApplication: platformServer.renderApplication,
1346
+ Sanitizer: core.Sanitizer,
1347
+ SecurityContext: core.SecurityContext
1348
+ };
1349
+ }, angularDeps = null, getAngularDeps = () => {
1350
+ if (!angularDeps) {
1351
+ angularDeps = loadAngularDeps();
1227
1352
  }
1228
- const expression = extractBracedExpression(attributeString, braceStart);
1229
- if (expression === null) {
1230
- return { expression: "", found: false };
1353
+ return angularDeps;
1354
+ };
1355
+ var init_angularDeps = __esm(() => {
1356
+ init_injectorPatch();
1357
+ init_resolveAngularPackage();
1358
+ });
1359
+
1360
+ // src/utils/registerClientScript.ts
1361
+ var scriptRegistry, requestCounter = 0, getRequestId = () => `req_${Date.now()}_${++requestCounter}`, ssrContextGetter = null, getSsrContextId = () => ssrContextGetter?.() || Object.getOwnPropertyDescriptor(globalThis, "__absolutejs_requestId")?.value, registerClientScript = (script, requestId) => {
1362
+ const id = requestId || getSsrContextId() || getRequestId();
1363
+ if (!scriptRegistry.has(id)) {
1364
+ scriptRegistry.set(id, new Set);
1231
1365
  }
1232
- return { expression, found: true };
1233
- }, lowerSvelteIslandSyntax = (source, _mode = "server") => {
1234
- if (!source.includes("<Island")) {
1235
- return { code: source, transformed: false };
1366
+ scriptRegistry.get(id)?.add(script);
1367
+ return id;
1368
+ }, setSsrContextGetter = (getter) => {
1369
+ ssrContextGetter = getter;
1370
+ }, clearAllClientScripts = () => {
1371
+ scriptRegistry.clear();
1372
+ }, generateClientScriptCode = (scripts) => {
1373
+ if (scripts.length === 0) {
1374
+ return "";
1236
1375
  }
1237
- let islandIndex = 0;
1238
- const transformedMarkup = source.replace(ISLAND_TAG_RE, (fullMatch, attributeString) => {
1239
- const framework = extractIslandAttribute(attributeString, "framework");
1240
- const component = extractIslandAttribute(attributeString, "component");
1241
- if (!framework.found || !component.found) {
1242
- return fullMatch;
1243
- }
1244
- const hydrate = extractIslandAttribute(attributeString, "hydrate");
1245
- const props = extractIslandAttribute(attributeString, "props");
1246
- const slotId = `absolute-svelte-island-${islandIndex.toString(BASE_36_RADIX)}`;
1247
- islandIndex += 1;
1248
- 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 : "{}"} })`;
1249
- return `<div data-absolute-island-slot="${slotId}" style="display: contents">{@html ${resolveExpression}}</div>`;
1250
- });
1251
- const importLine = 'import { resolveIslandHtml as __absoluteResolveIslandHtml } from "@absolutejs/absolute/svelte";';
1252
- if (transformedMarkup.includes("<script")) {
1253
- return {
1254
- code: transformedMarkup.replace(/<script(\s[^>]*)?>/, (match) => `${match}
1255
- ${importLine}
1256
- `),
1257
- transformed: true
1258
- };
1376
+ const scriptCode = scripts.map((script, index) => {
1377
+ const funcString = script.toString();
1378
+ const bodyMatch = funcString.match(/\{([\s\S]*)\}/);
1379
+ if (!bodyMatch || !bodyMatch[1]) {
1380
+ return "";
1381
+ }
1382
+ const body = bodyMatch[1].trim();
1383
+ return `
1384
+ (function() {
1385
+ var executed = false;
1386
+ function executeScript_${index}() {
1387
+ if (executed) return;
1388
+ executed = true;
1389
+ ${body}
1390
+ }
1391
+
1392
+ if (document.readyState === 'complete' || document.readyState === 'interactive') {
1393
+ executeScript_${index}();
1394
+ } else {
1395
+ document.addEventListener('DOMContentLoaded', executeScript_${index});
1396
+ }
1397
+
1398
+ // Watch for hydration-added elements
1399
+ var observer = new MutationObserver(function() {
1400
+ executeScript_${index}();
1401
+ if (executed) observer.disconnect();
1402
+ });
1403
+ if (!executed) {
1404
+ observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
1405
+ }
1406
+
1407
+ // Single fallback timeout
1408
+ setTimeout(function() {
1409
+ executeScript_${index}();
1410
+ observer.disconnect();
1411
+ }, 1000);
1412
+ })();`;
1413
+ }).join(`
1414
+ `);
1415
+ return `<script>
1416
+ (function() {
1417
+ ${scriptCode}
1418
+ })();
1419
+ </script>`;
1420
+ }, getAndClearClientScripts = (requestId) => {
1421
+ const id = requestId || ssrContextGetter?.();
1422
+ if (!id)
1423
+ return [];
1424
+ const scripts = scriptRegistry.get(id);
1425
+ if (!scripts) {
1426
+ return [];
1259
1427
  }
1260
- return {
1261
- code: `<script lang="ts">
1262
- ${importLine}
1263
- </script>
1264
- ${transformedMarkup}`,
1265
- transformed: true
1266
- };
1428
+ const scriptArray = Array.from(scripts);
1429
+ scriptRegistry.delete(id);
1430
+ return scriptArray;
1267
1431
  };
1268
- var init_lowerIslandSyntax = __esm(() => {
1269
- init_constants();
1270
- ISLAND_TAG_RE = /<Island\b([\s\S]*?)\/>/g;
1432
+ var init_registerClientScript = __esm(() => {
1433
+ scriptRegistry = new Map;
1434
+ if (typeof globalThis !== "undefined") {
1435
+ Object.assign(globalThis, { registerClientScript });
1436
+ }
1271
1437
  });
1272
1438
 
1273
- // src/core/svelteServerModule.ts
1274
- import { mkdir, readdir as readdir2 } from "fs/promises";
1275
- import { basename as basename3, dirname as dirname3, extname, join as join2, relative, resolve as resolve3 } from "path";
1276
- var serverCacheRoot, compiledModuleCache, originalSourcePathCache, transpiler, ensureRelativeImportPath = (from, target) => {
1277
- const importPath = relative(dirname3(from), target).replace(/\\/g, "/");
1278
- return importPath.startsWith(".") ? importPath : `./${importPath}`;
1279
- }, processDirectoryEntries = (entries, dir, targetFileName, stack) => {
1280
- for (const entry of entries) {
1281
- const entryPath = join2(dir, entry.name);
1282
- if (entry.isDirectory())
1283
- stack.push(entryPath);
1284
- if (entry.isFile() && entry.name === targetFileName) {
1285
- return entryPath;
1286
- }
1287
- }
1288
- return null;
1289
- }, searchDirectoryLevel = async (dirs, targetFileName) => {
1290
- if (dirs.length === 0)
1291
- return null;
1292
- const nextStack = [];
1293
- const dirEntries = await Promise.all(dirs.map(async (dir) => ({
1294
- dir,
1295
- entries: await readdir2(dir, {
1296
- encoding: "utf-8",
1297
- withFileTypes: true
1298
- })
1299
- })));
1300
- for (const { dir, entries } of dirEntries) {
1301
- const found = processDirectoryEntries(entries, dir, targetFileName, nextStack);
1302
- if (found)
1303
- return found;
1439
+ // src/angular/ssrRender.ts
1440
+ var routePropsCache, cacheRouteData = (pagePath, data) => {
1441
+ const cacheKey = pagePath.split("?")[0] ?? pagePath;
1442
+ routePropsCache.set(cacheKey, data);
1443
+ }, getCachedRouteData = (pagePath) => routePropsCache.get(pagePath), selectorCache, buildDeps = (ssrResult, baseDeps) => {
1444
+ if (!ssrResult?.core) {
1445
+ return baseDeps;
1304
1446
  }
1305
- return searchDirectoryLevel(nextStack, targetFileName);
1306
- }, findSourceFileByBasename = async (searchRoot, targetFileName) => searchDirectoryLevel([searchRoot], targetFileName), normalizeBuiltSvelteFileName = (sourcePath) => basename3(sourcePath).replace(/-[a-z0-9]{6,}(?=\.svelte$)/i, ""), resolveOriginalSourcePath = async (sourcePath) => {
1307
- const cachedPath = originalSourcePathCache.get(sourcePath);
1308
- if (cachedPath !== undefined) {
1309
- return cachedPath;
1447
+ const { common, core, platformBrowser, platformServer } = ssrResult;
1448
+ return {
1449
+ APP_BASE_HREF: common?.APP_BASE_HREF ?? baseDeps.APP_BASE_HREF,
1450
+ bootstrapApplication: platformBrowser?.bootstrapApplication ?? baseDeps.bootstrapApplication,
1451
+ DomSanitizer: platformBrowser?.DomSanitizer ?? baseDeps.DomSanitizer,
1452
+ provideClientHydration: platformBrowser?.provideClientHydration ?? baseDeps.provideClientHydration,
1453
+ provideServerRendering: platformServer?.provideServerRendering ?? baseDeps.provideServerRendering,
1454
+ provideZonelessChangeDetection: core.provideZonelessChangeDetection,
1455
+ reflectComponentType: core.reflectComponentType,
1456
+ renderApplication: platformServer?.renderApplication ?? baseDeps.renderApplication,
1457
+ Sanitizer: core.Sanitizer,
1458
+ SecurityContext: core.SecurityContext
1459
+ };
1460
+ }, buildProviders = (deps, sanitizer, maybeProps, tokenMap) => {
1461
+ const providers = [
1462
+ deps.provideServerRendering(),
1463
+ deps.provideClientHydration(),
1464
+ deps.provideZonelessChangeDetection(),
1465
+ { provide: deps.APP_BASE_HREF, useValue: "/" },
1466
+ {
1467
+ provide: deps.DomSanitizer,
1468
+ useValue: sanitizer
1469
+ },
1470
+ { provide: deps.Sanitizer, useValue: sanitizer }
1471
+ ];
1472
+ if (!maybeProps) {
1473
+ return providers;
1310
1474
  }
1311
- if (!sourcePath.includes(`${join2(process.cwd(), "build")}${process.platform === "win32" ? "" : "/"}`) && !sourcePath.includes("/build/")) {
1312
- originalSourcePathCache.set(sourcePath, sourcePath);
1313
- return sourcePath;
1475
+ const propProviders = Object.entries(maybeProps).map(([propName, propValue]) => ({
1476
+ token: tokenMap.get(toScreamingSnake(propName)),
1477
+ value: propValue
1478
+ })).filter((entry) => entry.token).map((entry) => ({ provide: entry.token, useValue: entry.value }));
1479
+ return [...providers, ...propProviders];
1480
+ }, clearSelectorCache = () => selectorCache.clear(), isInjectionToken = (value) => {
1481
+ if (!value || typeof value !== "object") {
1482
+ return false;
1314
1483
  }
1315
- const resolvedSourcePath = await findSourceFileByBasename(join2(process.cwd(), "src"), normalizeBuiltSvelteFileName(sourcePath));
1316
- const nextPath = resolvedSourcePath ?? sourcePath;
1317
- originalSourcePathCache.set(sourcePath, nextPath);
1318
- return nextPath;
1319
- }, resolveRelativeModule = async (spec, from) => {
1320
- if (!spec.startsWith(".")) {
1484
+ return "ngMetadataName" in value && value.ngMetadataName === "InjectionToken";
1485
+ }, discoverTokens = (pageModule) => new Map(Object.entries(pageModule).filter(([, value]) => isInjectionToken(value))), loadSsrDeps = async (pagePath) => {
1486
+ const ssrDepsPath = (pagePath.split("?")[0] ?? pagePath).replace(/\.js$/, ".ssr-deps.js");
1487
+ try {
1488
+ const ssrDeps = await import(ssrDepsPath);
1489
+ const result = {
1490
+ common: ssrDeps.__angularCommon,
1491
+ core: ssrDeps.__angularCore,
1492
+ platformBrowser: ssrDeps.__angularPlatformBrowser,
1493
+ platformServer: ssrDeps.__angularPlatformServer
1494
+ };
1495
+ return result;
1496
+ } catch {
1321
1497
  return null;
1322
1498
  }
1323
- const basePath = resolve3(dirname3(from), spec);
1324
- const candidates = [
1325
- basePath,
1326
- `${basePath}.ts`,
1327
- `${basePath}.js`,
1328
- `${basePath}.mjs`,
1329
- `${basePath}.cjs`,
1330
- `${basePath}.json`,
1331
- join2(basePath, "index.ts"),
1332
- join2(basePath, "index.js"),
1333
- join2(basePath, "index.mjs"),
1334
- join2(basePath, "index.cjs"),
1335
- join2(basePath, "index.json")
1336
- ];
1337
- const existResults = await Promise.all(candidates.map((candidate) => Bun.file(candidate).exists()));
1338
- const foundIndex = existResults.indexOf(true);
1339
- return foundIndex >= 0 ? candidates[foundIndex] ?? null : null;
1340
- }, getCachedModulePath = (sourcePath) => {
1341
- const relativeSourcePath = relative(process.cwd(), sourcePath).replace(/\\/g, "/");
1342
- const normalizedSourcePath = relativeSourcePath.startsWith("..") ? sourcePath.replace(/[:\\/]/g, "_") : relativeSourcePath;
1343
- return join2(serverCacheRoot, `${normalizedSourcePath}.server.js`);
1344
- }, resolveSvelteImport = async (spec, from) => {
1345
- if (spec.startsWith("/")) {
1346
- return spec;
1347
- }
1348
- if (!spec.startsWith(".")) {
1349
- return null;
1499
+ }, resolveSelector = (deps, pagePath, PageComponent) => {
1500
+ const cached = selectorCache.get(pagePath);
1501
+ if (cached) {
1502
+ return cached;
1350
1503
  }
1351
- const explicitPath = resolve3(dirname3(from), spec);
1352
- if (extname(explicitPath) === ".svelte") {
1353
- return explicitPath;
1504
+ const selector = deps.reflectComponentType(PageComponent)?.selector ?? "ng-app";
1505
+ selectorCache.set(pagePath, selector);
1506
+ return selector;
1507
+ }, injectBeforeClose = (html, snippet) => {
1508
+ if (html.includes("</body>")) {
1509
+ return html.replace("</body>", `${snippet}</body>`);
1354
1510
  }
1355
- const candidate = `${explicitPath}.svelte`;
1356
- if (await Bun.file(candidate).exists() === true) {
1357
- return candidate;
1511
+ if (html.includes("</html>")) {
1512
+ return html.replace("</html>", `${snippet}</html>`);
1358
1513
  }
1359
- return null;
1360
- }, writeIfChanged = async (path, content) => {
1361
- const targetFile = Bun.file(path);
1362
- const exists = await targetFile.exists();
1363
- if (exists) {
1364
- const currentContent = await targetFile.text();
1365
- if (currentContent === content) {
1366
- return;
1367
- }
1514
+ return html + snippet;
1515
+ }, injectSsrScripts = (html, requestId, indexPath, props) => {
1516
+ let result = html;
1517
+ const registeredScripts = getAndClearClientScripts(requestId);
1518
+ if (registeredScripts.length > 0) {
1519
+ result = injectBeforeClose(result, generateClientScriptCode(registeredScripts));
1368
1520
  }
1369
- await Bun.write(path, content);
1370
- }, compileSvelteServerModule = async (sourcePath) => {
1371
- const cachedModulePath = compiledModuleCache.get(sourcePath);
1372
- if (cachedModulePath) {
1373
- return cachedModulePath;
1521
+ if (props) {
1522
+ result = injectBeforeClose(result, `<script>window.__ABS_ANGULAR_PAGE_PROPS__ = ${JSON.stringify(props)};</script>`);
1374
1523
  }
1375
- const resolutionSourcePath = await resolveOriginalSourcePath(sourcePath);
1376
- const source = await Bun.file(sourcePath).text();
1377
- const { compile, preprocess } = await import("svelte/compiler");
1378
- const loweredSource = lowerSvelteIslandSyntax(source, "server");
1379
- const preprocessed = await preprocess(loweredSource.code, {});
1380
- let transpiled = sourcePath.endsWith(".ts") || sourcePath.endsWith(".svelte.ts") ? transpiler.transformSync(preprocessed.code) : preprocessed.code;
1381
- const childImportSpecs = Array.from(transpiled.matchAll(/from\s+['"]([^'"]+)['"]/g)).map((match) => match[1]).filter((value) => value !== undefined);
1382
- const resolvedChildModules = await Promise.all(childImportSpecs.map((spec) => resolveSvelteImport(spec, resolutionSourcePath)));
1383
- const resolvedModuleImports = await Promise.all(childImportSpecs.map((spec) => resolveRelativeModule(spec, resolutionSourcePath)));
1384
- const childModulePaths = new Map;
1385
- const rewrittenModulePaths = new Map;
1386
- const compiledChildren = await Promise.all(childImportSpecs.map(async (spec, index) => {
1387
- const resolvedChild = resolvedChildModules[index];
1388
- if (!spec || !resolvedChild)
1389
- return null;
1390
- return {
1391
- compiledPath: await compileSvelteServerModule(resolvedChild),
1392
- spec
1393
- };
1524
+ if (indexPath) {
1525
+ result = injectBeforeClose(result, `<script type="module" src="${indexPath}"></script>`);
1526
+ }
1527
+ return result;
1528
+ }, renderAngularApp = async (deps, PageComponent, providers, document2) => {
1529
+ const bootstrap = (context) => deps.bootstrapApplication(PageComponent, { providers }, context);
1530
+ return withSuppressedAngularDevLogs(() => deps.renderApplication(bootstrap, {
1531
+ document: document2,
1532
+ platformProviders: [],
1533
+ url: "/"
1394
1534
  }));
1395
- for (const result of compiledChildren) {
1396
- if (result)
1397
- childModulePaths.set(result.spec, result.compiledPath);
1535
+ }, withSuppressedAngularDevLogs = async (render) => {
1536
+ const origLog = console.log;
1537
+ console.log = (...args) => {
1538
+ if (typeof args[0] === "string" && args[0].includes("development mode")) {
1539
+ return;
1540
+ }
1541
+ origLog.apply(console, args);
1542
+ };
1543
+ try {
1544
+ return await render();
1545
+ } finally {
1546
+ console.log = origLog;
1398
1547
  }
1399
- for (let index = 0;index < childImportSpecs.length; index += 1) {
1400
- const spec = childImportSpecs[index];
1401
- const resolvedModuleImport = resolvedModuleImports[index];
1402
- if (!spec || !resolvedModuleImport)
1403
- continue;
1404
- if (resolvedChildModules[index])
1405
- continue;
1406
- rewrittenModulePaths.set(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), resolvedModuleImport));
1548
+ };
1549
+ var init_ssrRender = __esm(() => {
1550
+ init_registerClientScript();
1551
+ routePropsCache = new Map;
1552
+ selectorCache = new Map;
1553
+ });
1554
+
1555
+ // src/angular/islands.ts
1556
+ var angularIslandSelector = "abs-angular-island", getAngularIslandSelector = (_islandId) => angularIslandSelector, getSelectorFromRenderedIsland = (rootElement) => {
1557
+ const firstChild = rootElement.firstElementChild;
1558
+ if (!(firstChild instanceof HTMLElement)) {
1559
+ return null;
1407
1560
  }
1408
- for (const [spec, resolvedModuleImport] of rewrittenModulePaths) {
1409
- transpiled = transpiled.replaceAll(spec, resolvedModuleImport);
1561
+ const selector = firstChild.tagName.toLowerCase();
1562
+ return selector.length > 0 ? selector : null;
1563
+ }, getClientAngularComponentSelector = (component) => {
1564
+ const maybeDef = Reflect.get(component, "\u0275cmp");
1565
+ if (typeof maybeDef !== "object" || maybeDef === null) {
1566
+ return null;
1410
1567
  }
1411
- let compiledCode = compile(transpiled, {
1412
- css: "injected",
1413
- experimental: {
1414
- async: loweredSource.transformed
1415
- },
1416
- filename: resolutionSourcePath,
1417
- generate: "server"
1418
- }).js.code;
1419
- for (const [spec, compiledChildPath] of childModulePaths) {
1420
- compiledCode = compiledCode.replaceAll(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), compiledChildPath));
1568
+ const maybeSelectors = Reflect.get(maybeDef, "selectors");
1569
+ if (!Array.isArray(maybeSelectors)) {
1570
+ return null;
1421
1571
  }
1422
- for (const [spec, resolvedModuleImport] of rewrittenModulePaths) {
1423
- compiledCode = compiledCode.replaceAll(spec, resolvedModuleImport);
1572
+ const [firstSelectorGroup] = maybeSelectors;
1573
+ if (!Array.isArray(firstSelectorGroup)) {
1574
+ return null;
1424
1575
  }
1425
- const compiledModulePath = getCachedModulePath(sourcePath);
1426
- await mkdir(dirname3(compiledModulePath), { recursive: true });
1427
- await writeIfChanged(compiledModulePath, compiledCode);
1428
- compiledModuleCache.set(sourcePath, compiledModulePath);
1429
- return compiledModulePath;
1430
- };
1431
- var init_svelteServerModule = __esm(() => {
1432
- init_lowerIslandSyntax();
1433
- serverCacheRoot = join2(process.cwd(), ".absolutejs", "islands", "svelte");
1434
- compiledModuleCache = new Map;
1435
- originalSourcePathCache = new Map;
1436
- transpiler = new Bun.Transpiler({
1437
- loader: "ts",
1438
- target: "browser"
1576
+ const [selector] = firstSelectorGroup;
1577
+ return typeof selector === "string" && selector.length > 0 ? selector : null;
1578
+ }, createAngularIslandApp = async () => {
1579
+ const { EnvironmentInjector, provideZonelessChangeDetection } = await import("@angular/core");
1580
+ const { createApplication } = await import("@angular/platform-browser");
1581
+ const app = await createApplication({
1582
+ providers: [provideZonelessChangeDetection()]
1439
1583
  });
1440
- });
1584
+ const environmentInjector = app.injector.get(EnvironmentInjector);
1585
+ return { app, environmentInjector };
1586
+ }, angularIslandAppPromise = null, getAngularIslandApp = async () => {
1587
+ if (!angularIslandAppPromise) {
1588
+ angularIslandAppPromise = createAngularIslandApp();
1589
+ }
1590
+ return angularIslandAppPromise;
1591
+ }, wrapperMetadataCache, requestRenderCache, getRequestRenderCache = () => {
1592
+ const requestId = getSsrContextId();
1593
+ if (!requestId) {
1594
+ return null;
1595
+ }
1596
+ const cached = requestRenderCache.get(requestId);
1597
+ if (cached) {
1598
+ return cached;
1599
+ }
1600
+ const renderCache = new Map;
1601
+ requestRenderCache.set(requestId, renderCache);
1602
+ return renderCache;
1603
+ }, getAngularIslandWrapperKey = (component, _islandId) => {
1604
+ const componentName = typeof component.name === "string" && component.name.length > 0 ? component.name : "AngularIsland";
1605
+ return `${componentName}:${angularIslandSelector}`;
1606
+ }, getIslandRenderCacheKey = (component, props) => {
1607
+ const componentName = typeof component.name === "string" && component.name.length > 0 ? component.name : "AngularIsland";
1608
+ return `${componentName}:${JSON.stringify(props)}`;
1609
+ }, buildAngularIslandWrapperMetadata = async (component, islandId, wrapperKey) => {
1610
+ const { Component, InjectionToken, inject } = await import("@angular/core");
1611
+ const { NgComponentOutlet } = await import("@angular/common");
1612
+ const deps = await getAngularDeps();
1613
+ const selector = getAngularIslandSelector(islandId);
1614
+ const propsToken = new InjectionToken(`${wrapperKey}:props`);
1441
1615
 
1442
- // src/core/renderIslandMarkup.ts
1443
- var islandSequence = 0, resolvedServerComponentCache, resolvedServerBuildComponentCache, nextIslandId = () => {
1444
- islandSequence += 1;
1445
- return `island-${islandSequence}`;
1446
- }, isRecord3 = (value) => typeof value === "object" && value !== null, isReactServerIslandComponent = (value) => typeof value === "function", isSvelteServerIslandComponent = (value) => typeof value === "function", isVueServerIslandComponent = (value) => typeof value === "function" || isRecord3(value), isAngularServerIslandComponent = (value) => typeof value === "function", resolveBuildReferencePath = (source, registryPath) => {
1447
- if (source.startsWith("file://"))
1448
- return new URL(source).pathname;
1449
- if (source.startsWith("."))
1450
- return new URL(source, registryPath).pathname;
1451
- return source;
1452
- }, loadAndCompileServerBuildComponent = async (buildReferencePath) => {
1453
- const compiledModulePath = await compileSvelteServerModule(buildReferencePath);
1454
- const loadedModule = await import(compiledModulePath);
1455
- return "default" in loadedModule ? loadedModule.default : loadedModule;
1456
- }, loadServerBuildComponent = async (buildReferencePath) => {
1457
- const cachedBuildComponent = resolvedServerBuildComponentCache.get(buildReferencePath);
1458
- if (cachedBuildComponent) {
1459
- return cachedBuildComponent;
1616
+ class AngularIslandWrapperComponent {
1617
+ component = component;
1618
+ props = inject(propsToken);
1460
1619
  }
1461
- const loadPromise = loadAndCompileServerBuildComponent(buildReferencePath);
1462
- resolvedServerBuildComponentCache.set(buildReferencePath, loadPromise);
1463
- return loadPromise;
1464
- }, loadServerImportComponent = async (resolvedComponent) => {
1465
- const resolvedModulePath = resolvedComponent.startsWith(".") ? new URL(resolvedComponent, import.meta.url).pathname : resolvedComponent;
1466
- const importTarget = resolvedModulePath.endsWith(".svelte") ? await compileSvelteServerModule(resolvedModulePath) : resolvedModulePath;
1467
- const loadedModule = await import(importTarget);
1468
- return "default" in loadedModule ? loadedModule.default : loadedModule;
1469
- }, resolveIslandComponent = async (component) => {
1470
- const buildReference = getIslandBuildReference(component);
1471
- const buildReferencePath = buildReference?.source ? resolveBuildReferencePath(buildReference.source, import.meta.url) : null;
1472
- if (buildReferencePath?.endsWith(".svelte")) {
1473
- return loadServerBuildComponent(buildReferencePath);
1620
+ return {
1621
+ deps,
1622
+ propsToken,
1623
+ selector,
1624
+ WrapperComponent: Component({
1625
+ imports: [NgComponentOutlet, component],
1626
+ selector,
1627
+ standalone: true,
1628
+ template: '<ng-container *ngComponentOutlet="component; inputs: props"></ng-container>'
1629
+ })(AngularIslandWrapperComponent)
1630
+ };
1631
+ }, createAngularIslandWrapper = async (component, islandId) => {
1632
+ const wrapperKey = getAngularIslandWrapperKey(component, islandId);
1633
+ const cached = wrapperMetadataCache.get(wrapperKey);
1634
+ if (cached) {
1635
+ return cached;
1474
1636
  }
1475
- const resolvedComponent = getIslandComponent(component);
1476
- if (typeof resolvedComponent !== "string") {
1477
- return resolvedComponent;
1637
+ const metadataPromise = buildAngularIslandWrapperMetadata(component, islandId, wrapperKey);
1638
+ wrapperMetadataCache.set(wrapperKey, metadataPromise);
1639
+ return metadataPromise;
1640
+ }, extractAngularIslandRoot = (html, selector) => {
1641
+ const openTag = `<${selector}`;
1642
+ const start = html.indexOf(openTag);
1643
+ if (start < 0) {
1644
+ throw new Error(`Could not find Angular island root "${selector}".`);
1478
1645
  }
1479
- return loadServerImportComponent(resolvedComponent);
1480
- }, resolveServerIslandComponent = async (component) => {
1481
- const cachedResolvedComponent = resolvedServerComponentCache.get(component);
1482
- if (cachedResolvedComponent) {
1483
- return cachedResolvedComponent;
1646
+ const endTag = `</${selector}>`;
1647
+ const end = html.indexOf(endTag, start);
1648
+ if (end < 0) {
1649
+ throw new Error(`Could not close Angular island root "${selector}".`);
1484
1650
  }
1485
- const resolutionPromise = resolveIslandComponent(component);
1486
- resolvedServerComponentCache.set(component, resolutionPromise);
1487
- return resolutionPromise;
1488
- }, resolveReactServerIslandComponent = async (component) => {
1489
- const resolvedComponent = await resolveServerIslandComponent(component);
1490
- if (!isReactServerIslandComponent(resolvedComponent)) {
1491
- throw new Error("Resolved React island is not a valid React component.");
1651
+ return html.slice(start, end + endTag.length);
1652
+ }, mountAngularIsland = async (component, element, props, islandId) => {
1653
+ await import("@angular/compiler");
1654
+ const { createComponent, inputBinding } = await import("@angular/core");
1655
+ const selector = getAngularIslandSelector(islandId);
1656
+ const { app, environmentInjector } = await getAngularIslandApp();
1657
+ let rootElement = element.querySelector(selector);
1658
+ if (!(rootElement instanceof HTMLElement)) {
1659
+ element.innerHTML = `<${selector}></${selector}>`;
1660
+ rootElement = element.querySelector(selector);
1492
1661
  }
1493
- return resolvedComponent;
1494
- }, resolveSvelteServerIslandComponent = async (component) => {
1495
- const resolvedComponent = await resolveServerIslandComponent(component);
1496
- if (!isSvelteServerIslandComponent(resolvedComponent)) {
1497
- throw new Error("Resolved Svelte island is not a valid Svelte component.");
1662
+ if (!(rootElement instanceof HTMLElement))
1663
+ return app;
1664
+ const componentSelector = getClientAngularComponentSelector(component) ?? getSelectorFromRenderedIsland(rootElement);
1665
+ if (!componentSelector)
1666
+ return app;
1667
+ rootElement.innerHTML = `<${componentSelector}></${componentSelector}>`;
1668
+ const hostElement = rootElement.querySelector(componentSelector);
1669
+ if (!(hostElement instanceof HTMLElement))
1670
+ return app;
1671
+ const bindings = Object.entries(props).map(([key, value]) => inputBinding(key, () => value));
1672
+ const componentRef = createComponent(component, {
1673
+ bindings,
1674
+ environmentInjector,
1675
+ hostElement
1676
+ });
1677
+ app.attachView(componentRef.hostView);
1678
+ componentRef.changeDetectorRef.detectChanges();
1679
+ window.__ABS_ANGULAR_ISLAND_APPS__ ??= [];
1680
+ window.__ABS_ANGULAR_ISLAND_APPS__.push(app);
1681
+ return app;
1682
+ }, renderAngularIslandToHtml = async (component, props, islandId) => {
1683
+ const requestCache = getRequestRenderCache();
1684
+ const renderCacheKey = getIslandRenderCacheKey(component, props);
1685
+ const cachedHtml = requestCache?.get(renderCacheKey);
1686
+ if (cachedHtml) {
1687
+ return cachedHtml;
1688
+ }
1689
+ const { deps, propsToken, selector, WrapperComponent } = await createAngularIslandWrapper(component, islandId);
1690
+ const providers = [
1691
+ deps.provideServerRendering(),
1692
+ deps.provideZonelessChangeDetection(),
1693
+ { provide: deps.APP_BASE_HREF, useValue: "/" },
1694
+ { provide: propsToken, useValue: props }
1695
+ ];
1696
+ const document2 = `<!DOCTYPE html><html><body><${selector}></${selector}></body></html>`;
1697
+ const html = await withSuppressedAngularDevLogs(() => deps.renderApplication((context) => deps.bootstrapApplication(WrapperComponent, { providers }, context), {
1698
+ document: document2,
1699
+ platformProviders: [],
1700
+ url: "/"
1701
+ }));
1702
+ const islandHtml = extractAngularIslandRoot(html, selector);
1703
+ requestCache?.set(renderCacheKey, islandHtml);
1704
+ return islandHtml;
1705
+ };
1706
+ var init_islands2 = __esm(() => {
1707
+ init_angularDeps();
1708
+ init_ssrRender();
1709
+ init_registerClientScript();
1710
+ wrapperMetadataCache = new Map;
1711
+ requestRenderCache = new Map;
1712
+ });
1713
+
1714
+ // src/core/islandSsr.ts
1715
+ import { createElement } from "react";
1716
+ import { renderToStaticMarkup } from "react-dom/server";
1717
+ import { render as renderSvelte } from "svelte/server";
1718
+ import { createSSRApp, h as h3 } from "vue";
1719
+ import { renderToString as renderVueToString } from "vue/server-renderer";
1720
+ var renderReactIslandToHtml = (component, props) => renderToStaticMarkup(createElement(component, props)), renderSvelteIslandToHtml = (component, props) => {
1721
+ const { body } = renderSvelte(component, { props });
1722
+ return body;
1723
+ }, renderVueIslandToHtml = (component, props) => {
1724
+ const app = createSSRApp({
1725
+ render: () => h3(component, props)
1726
+ });
1727
+ return renderVueToString(app);
1728
+ };
1729
+ var init_islandSsr = __esm(() => {
1730
+ init_islands2();
1731
+ });
1732
+
1733
+ // src/svelte/lowerIslandSyntax.ts
1734
+ var ISLAND_TAG_RE, extractBracedExpression = (text, braceStart) => {
1735
+ let depth = 0;
1736
+ for (let index = braceStart;index < text.length; index += 1) {
1737
+ const char = text[index];
1738
+ if (char === "{")
1739
+ depth += 1;
1740
+ if (char === "}")
1741
+ depth -= 1;
1742
+ if (depth === 0) {
1743
+ return text.slice(braceStart + 1, index).trim();
1744
+ }
1498
1745
  }
1499
- return resolvedComponent;
1500
- }, resolveVueServerIslandComponent = async (component) => {
1501
- const resolvedComponent = await resolveServerIslandComponent(component);
1502
- if (!isVueServerIslandComponent(resolvedComponent)) {
1503
- throw new Error("Resolved Vue island is not a valid Vue component.");
1746
+ return null;
1747
+ }, extractIslandAttribute = (attributeString, name) => {
1748
+ const quotedMatch = attributeString.match(new RegExp(`\\b${name}\\s*=\\s*["']([^"']+)["']`));
1749
+ if (quotedMatch?.[1]) {
1750
+ return { expression: JSON.stringify(quotedMatch[1]), found: true };
1504
1751
  }
1505
- return resolvedComponent;
1506
- }, resolveAngularServerIslandComponent = async (component) => {
1507
- const resolvedComponent = await resolveServerIslandComponent(component);
1508
- if (!isAngularServerIslandComponent(resolvedComponent)) {
1509
- throw new Error("Resolved Angular island is not a valid Angular component.");
1752
+ const attributeIndex = attributeString.search(new RegExp(`\\b${name}\\s*=\\s*\\{`));
1753
+ if (attributeIndex < 0) {
1754
+ return { expression: "", found: false };
1510
1755
  }
1511
- return resolvedComponent;
1512
- }, renderIslandMarkup = async (registry, props) => {
1513
- const result = await renderIslandResult(registry, props);
1514
- return `<div ${serializeIslandAttributes(result.attributes)}>${result.html}</div>`;
1515
- }, renderIslandResult = async (registry, props) => {
1516
- const islandId = nextIslandId();
1517
- const attributes = getIslandMarkerAttributes(props);
1518
- if (props.framework === "react") {
1519
- const entry = registry.react?.[props.component];
1520
- if (!entry) {
1521
- throw new Error(`Island component "${props.component}" is not registered for framework "react".`);
1522
- }
1523
- const component = await resolveReactServerIslandComponent(entry);
1524
- const html = renderReactIslandToHtml(component, props.props);
1525
- return { attributes, html };
1756
+ const braceStart = attributeString.indexOf("{", attributeIndex);
1757
+ if (braceStart < 0) {
1758
+ return { expression: "", found: false };
1526
1759
  }
1527
- if (props.framework === "svelte") {
1528
- const entry = registry.svelte?.[props.component];
1529
- if (!entry) {
1530
- throw new Error(`Island component "${props.component}" is not registered for framework "svelte".`);
1531
- }
1532
- const component = await resolveSvelteServerIslandComponent(entry);
1533
- const html = renderSvelteIslandToHtml(component, props.props);
1534
- return { attributes, html };
1760
+ const expression = extractBracedExpression(attributeString, braceStart);
1761
+ if (expression === null) {
1762
+ return { expression: "", found: false };
1535
1763
  }
1536
- if (props.framework === "vue") {
1537
- const entry = registry.vue?.[props.component];
1538
- if (!entry) {
1539
- throw new Error(`Island component "${props.component}" is not registered for framework "vue".`);
1540
- }
1541
- const component = await resolveVueServerIslandComponent(entry);
1542
- const html = await renderVueIslandToHtml(component, props.props);
1543
- return { attributes, html };
1764
+ return { expression, found: true };
1765
+ }, lowerSvelteIslandSyntax = (source, _mode = "server") => {
1766
+ if (!source.includes("<Island")) {
1767
+ return { code: source, transformed: false };
1544
1768
  }
1545
- if (props.framework === "angular") {
1546
- const entry = registry.angular?.[props.component];
1547
- if (!entry) {
1548
- throw new Error(`Island component "${props.component}" is not registered for framework "angular".`);
1769
+ let islandIndex = 0;
1770
+ const transformedMarkup = source.replace(ISLAND_TAG_RE, (fullMatch, attributeString) => {
1771
+ const framework = extractIslandAttribute(attributeString, "framework");
1772
+ const component = extractIslandAttribute(attributeString, "component");
1773
+ if (!framework.found || !component.found) {
1774
+ return fullMatch;
1549
1775
  }
1550
- const component = await resolveAngularServerIslandComponent(entry);
1551
- const html = await renderAngularIslandToHtml(component, props.props, islandId);
1776
+ const hydrate = extractIslandAttribute(attributeString, "hydrate");
1777
+ const props = extractIslandAttribute(attributeString, "props");
1778
+ const slotId = `absolute-svelte-island-${islandIndex.toString(BASE_36_RADIX)}`;
1779
+ islandIndex += 1;
1780
+ 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 : "{}"} })`;
1781
+ return `<div data-absolute-island-slot="${slotId}" style="display: contents">{@html ${resolveExpression}}</div>`;
1782
+ });
1783
+ const importLine = 'import { resolveIslandHtml as __absoluteResolveIslandHtml } from "@absolutejs/absolute/svelte";';
1784
+ if (transformedMarkup.includes("<script")) {
1552
1785
  return {
1553
- attributes: {
1554
- ...getIslandMarkerAttributes(props, islandId)
1555
- },
1556
- html
1786
+ code: transformedMarkup.replace(/<script(\s[^>]*)?>/, (match) => `${match}
1787
+ ${importLine}
1788
+ `),
1789
+ transformed: true
1557
1790
  };
1558
1791
  }
1559
- throw new Error(`Framework "${props.framework}" is not implemented in this prototype.`);
1792
+ return {
1793
+ code: `<script lang="ts">
1794
+ ${importLine}
1795
+ </script>
1796
+ ${transformedMarkup}`,
1797
+ transformed: true
1798
+ };
1560
1799
  };
1561
- var init_renderIslandMarkup = __esm(() => {
1562
- init_islandSsr();
1563
- init_svelteServerModule();
1564
- init_islandMarkupAttributes();
1565
- init_islands();
1566
- resolvedServerComponentCache = new Map;
1567
- resolvedServerBuildComponentCache = new Map;
1800
+ var init_lowerIslandSyntax = __esm(() => {
1801
+ init_constants();
1802
+ ISLAND_TAG_RE = /<Island\b([\s\S]*?)\/>/g;
1568
1803
  });
1569
1804
 
1570
- // src/vue/components/Image.vue
1571
- var Image_default = "../Image-0pe96k20.vue";
1572
- // src/vue/components/SuspenseSlot.ts
1573
- import { defineComponent as defineComponent2, h as h2 } from "vue";
1574
-
1575
- // src/vue/components/StreamSlot.ts
1576
- import { defineComponent, h } from "vue";
1577
-
1578
- // src/core/streamingSlotRegistrar.ts
1579
- var registrar = null;
1580
- var setStreamingSlotRegistrar = (nextRegistrar) => {
1581
- registrar = nextRegistrar;
1582
- };
1583
- var registerStreamingSlot = (slot) => {
1584
- registrar?.(slot);
1585
- };
1586
-
1587
- // src/vue/components/StreamSlot.ts
1588
- var StreamSlot = defineComponent({
1589
- name: "AbsoluteStreamSlot",
1590
- props: {
1591
- className: { default: undefined, type: String },
1592
- errorHtml: { default: undefined, type: String },
1593
- fallbackHtml: { default: "", type: String },
1594
- id: { required: true, type: String },
1595
- resolve: {
1596
- required: true,
1597
- type: Function
1598
- },
1599
- timeoutMs: { default: undefined, type: Number }
1600
- },
1601
- setup(props) {
1602
- if (typeof window === "undefined") {
1603
- registerStreamingSlot({
1604
- errorHtml: props.errorHtml,
1605
- fallbackHtml: props.fallbackHtml,
1606
- id: props.id,
1607
- resolve: props.resolve,
1608
- timeoutMs: props.timeoutMs
1609
- });
1805
+ // src/core/svelteServerModule.ts
1806
+ import { mkdir, readdir as readdir2 } from "fs/promises";
1807
+ import { basename as basename3, dirname as dirname3, extname, join as join2, relative, resolve as resolve3 } from "path";
1808
+ var serverCacheRoot, compiledModuleCache, originalSourcePathCache, transpiler, ensureRelativeImportPath = (from, target) => {
1809
+ const importPath = relative(dirname3(from), target).replace(/\\/g, "/");
1810
+ return importPath.startsWith(".") ? importPath : `./${importPath}`;
1811
+ }, processDirectoryEntries = (entries, dir, targetFileName, stack) => {
1812
+ for (const entry of entries) {
1813
+ const entryPath = join2(dir, entry.name);
1814
+ if (entry.isDirectory())
1815
+ stack.push(entryPath);
1816
+ if (entry.isFile() && entry.name === targetFileName) {
1817
+ return entryPath;
1610
1818
  }
1611
- return () => h("div", {
1612
- class: props.className,
1613
- "data-absolute-slot": "true",
1614
- id: `slot-${props.id}`,
1615
- innerHTML: props.fallbackHtml
1616
- });
1617
- }
1618
- });
1619
-
1620
- // src/vue/components/SuspenseSlot.ts
1621
- var SuspenseSlot = defineComponent2({
1622
- name: "AbsoluteSuspenseSlot",
1623
- props: {
1624
- className: { default: undefined, type: String },
1625
- errorHtml: { default: undefined, type: String },
1626
- fallbackHtml: { default: "", type: String },
1627
- id: { required: true, type: String },
1628
- resolve: {
1629
- required: true,
1630
- type: Function
1631
- },
1632
- timeoutMs: { default: undefined, type: Number }
1633
- },
1634
- setup(props) {
1635
- return () => h2(StreamSlot, props);
1636
1819
  }
1637
- });
1638
- // src/client/streamSwap.ts
1639
- var streamSwapRuntime = () => {
1640
- if (window.__ABS_SLOT_RUNTIME__ === true)
1641
- return;
1642
- window.__ABS_SLOT_RUNTIME__ = true;
1643
- window.__ABS_SLOT_PENDING__ = window.__ABS_SLOT_PENDING__ ?? {};
1644
- const pending = window.__ABS_SLOT_PENDING__;
1645
- const apply = (id, html) => {
1646
- const node = document.getElementById(`slot-${id}`);
1647
- if (!node) {
1648
- pending[id] = html;
1649
- return;
1650
- }
1651
- node.innerHTML = html;
1652
- delete pending[id];
1653
- };
1654
- const flush = () => {
1655
- for (const id in pending) {
1656
- if (!Object.prototype.hasOwnProperty.call(pending, id))
1657
- continue;
1658
- apply(id, pending[id] ?? "");
1659
- }
1660
- };
1661
- window.__ABS_SLOT_ENQUEUE__ = (id, html) => {
1662
- apply(id, html);
1663
- };
1664
- if (typeof MutationObserver === "function") {
1665
- const observer = new MutationObserver(flush);
1666
- const root = document.documentElement ?? document.body ?? document;
1667
- observer.observe(root, { childList: true, subtree: true });
1820
+ return null;
1821
+ }, searchDirectoryLevel = async (dirs, targetFileName) => {
1822
+ if (dirs.length === 0)
1823
+ return null;
1824
+ const nextStack = [];
1825
+ const dirEntries = await Promise.all(dirs.map(async (dir) => ({
1826
+ dir,
1827
+ entries: await readdir2(dir, {
1828
+ encoding: "utf-8",
1829
+ withFileTypes: true
1830
+ })
1831
+ })));
1832
+ for (const { dir, entries } of dirEntries) {
1833
+ const found = processDirectoryEntries(entries, dir, targetFileName, nextStack);
1834
+ if (found)
1835
+ return found;
1668
1836
  }
1669
- if (document.readyState === "loading") {
1670
- document.addEventListener("DOMContentLoaded", flush, { once: true });
1837
+ return searchDirectoryLevel(nextStack, targetFileName);
1838
+ }, findSourceFileByBasename = async (searchRoot, targetFileName) => searchDirectoryLevel([searchRoot], targetFileName), normalizeBuiltSvelteFileName = (sourcePath) => basename3(sourcePath).replace(/-[a-z0-9]{6,}(?=\.svelte$)/i, ""), resolveOriginalSourcePath = async (sourcePath) => {
1839
+ const cachedPath = originalSourcePathCache.get(sourcePath);
1840
+ if (cachedPath !== undefined) {
1841
+ return cachedPath;
1671
1842
  }
1672
- flush();
1673
- };
1674
- var stripFunctionWrapper = (value) => {
1675
- const start = value.indexOf("{");
1676
- const end = value.lastIndexOf("}");
1677
- if (start < 0 || end <= start)
1678
- return "";
1679
- return value.slice(start + 1, end);
1680
- };
1681
- var getStreamSwapRuntimeScript = () => `(function(){${stripFunctionWrapper(streamSwapRuntime.toString())}})();`;
1682
-
1683
- // src/utils/streamingSlots.ts
1684
- init_escapeScriptContent();
1685
- var SLOT_ID_PREFIX = "abs-slot-";
1686
- var SLOT_PLACEHOLDER_PREFIX = "slot-";
1687
- var CLOSING_HEAD_TAG = "</head>";
1688
- var CLOSING_HEAD_TAG_LENGTH = CLOSING_HEAD_TAG.length;
1689
- var CLOSING_PAGE_TAG_REGEX = /<\/body>\s*<\/html>\s*$/i;
1690
- var STREAMING_RUNTIME_GLOBAL = "__ABS_SLOT_ENQUEUE__";
1691
- var STREAMING_PENDING_GLOBAL = "__ABS_SLOT_PENDING__";
1692
- var STREAM_TAIL_LOOKBEHIND = 128;
1693
- var STREAMING_SLOT_TIMEOUT_MS = 5000;
1694
- var STREAMING_SLOT_MAX_PER_RESPONSE = 128;
1695
- var STREAMING_SLOT_MAX_HTML_BYTES = 64000;
1696
- var createSlotPlaceholderId = (id) => `${SLOT_PLACEHOLDER_PREFIX}${id}`;
1697
- 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)});`;
1698
- var createNonceAttr = (nonce) => nonce ? ` nonce="${nonce}"` : "";
1699
- var createStreamingSlotId = () => `${SLOT_ID_PREFIX}${Math.random().toString(36).slice(2, 10)}`;
1700
- var getStreamingSlotsRuntimeScript = () => getStreamSwapRuntimeScript();
1701
- var renderStreamingSlotsRuntimeTag = (nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(getStreamingSlotsRuntimeScript())}</script>`;
1702
- var renderStreamingSlotPlaceholder = (id, fallbackHtml = "") => `<div id="${createSlotPlaceholderId(id)}" data-absolute-slot="true">${fallbackHtml}</div>`;
1703
- var renderStreamingSlotPatchTag = (id, html, nonce) => `<script${createNonceAttr(nonce)}>${escapeScriptContent(createSlotPatchStatement(id, html))}</script>`;
1704
- var injectHtmlIntoHead = (html, injection) => {
1705
- const closingHeadIndex = html.indexOf(CLOSING_HEAD_TAG);
1706
- if (closingHeadIndex >= 0) {
1707
- return `${html.slice(0, closingHeadIndex)}${injection}${html.slice(closingHeadIndex)}`;
1843
+ if (!sourcePath.includes(`${join2(process.cwd(), "build")}${process.platform === "win32" ? "" : "/"}`) && !sourcePath.includes("/build/")) {
1844
+ originalSourcePathCache.set(sourcePath, sourcePath);
1845
+ return sourcePath;
1708
1846
  }
1709
- return `${html}${injection}`;
1710
- };
1711
- var toUint8 = (value, encoder) => encoder.encode(value);
1712
- var currentStreamingSlotPolicy = {
1713
- timeoutMs: STREAMING_SLOT_TIMEOUT_MS,
1714
- fallbackHtml: "",
1715
- errorHtml: undefined,
1716
- maxSlotsPerResponse: STREAMING_SLOT_MAX_PER_RESPONSE,
1717
- maxSlotHtmlSizeBytes: STREAMING_SLOT_MAX_HTML_BYTES
1718
- };
1719
- var clonePolicy = (policy) => ({
1720
- ...policy
1721
- });
1722
- var normalizeSlotBytes = (value, fallback) => {
1723
- if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
1724
- return Math.floor(value);
1847
+ const resolvedSourcePath = await findSourceFileByBasename(join2(process.cwd(), "src"), normalizeBuiltSvelteFileName(sourcePath));
1848
+ const nextPath = resolvedSourcePath ?? sourcePath;
1849
+ originalSourcePathCache.set(sourcePath, nextPath);
1850
+ return nextPath;
1851
+ }, resolveRelativeModule = async (spec, from) => {
1852
+ if (!spec.startsWith(".")) {
1853
+ return null;
1725
1854
  }
1726
- return fallback;
1727
- };
1728
- var normalizeSlotText = (value, fallback) => typeof value === "string" ? value : fallback;
1729
- var normalizeSlotError = (value, fallback) => typeof value === "string" ? value : fallback;
1730
- var hasPolicyValue = (policy, key) => Object.prototype.hasOwnProperty.call(policy, key);
1731
- var applyStreamingSlotPolicyOverrides = (base, overridePolicy = {}) => ({
1732
- timeoutMs: hasPolicyValue(overridePolicy, "timeoutMs") ? normalizeSlotBytes(overridePolicy.timeoutMs, base.timeoutMs) : base.timeoutMs,
1733
- fallbackHtml: hasPolicyValue(overridePolicy, "fallbackHtml") ? normalizeSlotText(overridePolicy.fallbackHtml, "") : base.fallbackHtml,
1734
- errorHtml: hasPolicyValue(overridePolicy, "errorHtml") ? normalizeSlotError(overridePolicy.errorHtml) : base.errorHtml,
1735
- maxSlotsPerResponse: hasPolicyValue(overridePolicy, "maxSlotsPerResponse") ? normalizeSlotBytes(overridePolicy.maxSlotsPerResponse, base.maxSlotsPerResponse) : base.maxSlotsPerResponse,
1736
- maxSlotHtmlSizeBytes: hasPolicyValue(overridePolicy, "maxSlotHtmlSizeBytes") ? normalizeSlotBytes(overridePolicy.maxSlotHtmlSizeBytes, base.maxSlotHtmlSizeBytes) : base.maxSlotHtmlSizeBytes,
1737
- onError: hasPolicyValue(overridePolicy, "onError") ? overridePolicy.onError : base.onError,
1738
- onSlotMetric: hasPolicyValue(overridePolicy, "onSlotMetric") ? overridePolicy.onSlotMetric : base.onSlotMetric
1739
- });
1740
- var createCombinedSlotErrorHandler = (policyOnError, enhancerOnError) => {
1741
- if (!policyOnError && !enhancerOnError)
1742
- return;
1743
- return (error, slot) => {
1744
- policyOnError?.(error, slot);
1745
- enhancerOnError?.(error, slot);
1746
- };
1747
- };
1748
- var createCombinedSlotMetricHandler = (policyOnSlotMetric, callOnSlotMetric) => {
1749
- if (!policyOnSlotMetric && !callOnSlotMetric)
1750
- return;
1751
- return (metric) => {
1752
- policyOnSlotMetric?.(metric);
1753
- callOnSlotMetric?.(metric);
1754
- };
1755
- };
1756
- var resolveStreamingSlotPolicy = (overridePolicy = {}) => {
1757
- const base = getStreamingSlotPolicy();
1758
- return applyStreamingSlotPolicyOverrides(base, overridePolicy);
1759
- };
1760
- var getStreamingSlotPolicy = () => clonePolicy(currentStreamingSlotPolicy);
1761
- var setStreamingSlotPolicy = (policy = {}) => {
1762
- const base = getStreamingSlotPolicy();
1763
- currentStreamingSlotPolicy = applyStreamingSlotPolicyOverrides(base, policy);
1764
- };
1765
- var withStreamingSlotPolicy = async (policy, callback) => {
1766
- const previous = getStreamingSlotPolicy();
1767
- setStreamingSlotPolicy(policy);
1768
- try {
1769
- return await callback();
1770
- } finally {
1771
- currentStreamingSlotPolicy = previous;
1855
+ const basePath = resolve3(dirname3(from), spec);
1856
+ const candidates = [
1857
+ basePath,
1858
+ `${basePath}.ts`,
1859
+ `${basePath}.js`,
1860
+ `${basePath}.mjs`,
1861
+ `${basePath}.cjs`,
1862
+ `${basePath}.json`,
1863
+ join2(basePath, "index.ts"),
1864
+ join2(basePath, "index.js"),
1865
+ join2(basePath, "index.mjs"),
1866
+ join2(basePath, "index.cjs"),
1867
+ join2(basePath, "index.json")
1868
+ ];
1869
+ const existResults = await Promise.all(candidates.map((candidate) => Bun.file(candidate).exists()));
1870
+ const foundIndex = existResults.indexOf(true);
1871
+ return foundIndex >= 0 ? candidates[foundIndex] ?? null : null;
1872
+ }, getCachedModulePath = (sourcePath) => {
1873
+ const relativeSourcePath = relative(process.cwd(), sourcePath).replace(/\\/g, "/");
1874
+ const normalizedSourcePath = relativeSourcePath.startsWith("..") ? sourcePath.replace(/[:\\/]/g, "_") : relativeSourcePath;
1875
+ return join2(serverCacheRoot, `${normalizedSourcePath}.server.js`);
1876
+ }, resolveSvelteImport = async (spec, from) => {
1877
+ if (spec.startsWith("/")) {
1878
+ return spec;
1772
1879
  }
1773
- };
1774
- var emitSlotMetric = (metric, onSlotMetric) => {
1775
- onSlotMetric?.(metric);
1776
- };
1777
- var createTimeoutError = (slot, timeoutMs) => {
1778
- const error = new Error(`Streaming slot "${slot.id}" timed out after ${timeoutMs}ms`);
1779
- error.__absTimeout = true;
1780
- return error;
1781
- };
1782
- var toStreamingSlot = (slot, policy) => ({
1783
- errorHtml: slot.errorHtml === undefined ? policy.errorHtml : slot.errorHtml,
1784
- fallbackHtml: normalizeSlotText(slot.fallbackHtml, policy.fallbackHtml),
1785
- id: slot.id ?? createStreamingSlotId(),
1786
- timeoutMs: normalizeSlotBytes(slot.timeoutMs, policy.timeoutMs),
1787
- resolve: slot.resolve
1788
- });
1789
- var prepareSlots = ({
1790
- policy,
1791
- slots,
1792
- onError,
1793
- onSlotMetric
1794
- }) => {
1795
- const preparedSlots = slots.map((slot) => toStreamingSlot(slot, policy));
1796
- const maxSlotsPerResponse = policy.maxSlotsPerResponse;
1797
- if (maxSlotsPerResponse === 0) {
1798
- const error = new Error("Streaming slot limit is set to 0");
1799
- for (const slot of preparedSlots) {
1800
- onError?.(error, slot);
1801
- emitSlotMetric({
1802
- type: "dropped",
1803
- slotId: slot.id,
1804
- reason: "maxSlotsPerResponse is 0"
1805
- }, onSlotMetric);
1806
- }
1807
- return [];
1880
+ if (!spec.startsWith(".")) {
1881
+ return null;
1808
1882
  }
1809
- if (preparedSlots.length <= maxSlotsPerResponse) {
1810
- preparedSlots.forEach((slot) => emitSlotMetric({
1811
- type: "prepared",
1812
- slotId: slot.id
1813
- }, onSlotMetric));
1814
- return preparedSlots;
1883
+ const explicitPath = resolve3(dirname3(from), spec);
1884
+ if (extname(explicitPath) === ".svelte") {
1885
+ return explicitPath;
1815
1886
  }
1816
- const keptSlots = preparedSlots.slice(0, maxSlotsPerResponse);
1817
- const droppedSlots = preparedSlots.slice(maxSlotsPerResponse);
1818
- droppedSlots.forEach((slot) => {
1819
- onError?.(new Error(`Streaming slot "${slot.id}" dropped because ${maxSlotsPerResponse} slots is the configured maximum`), slot);
1820
- emitSlotMetric({
1821
- type: "dropped",
1822
- slotId: slot.id,
1823
- reason: `maxSlotsPerResponse is ${maxSlotsPerResponse}`
1824
- }, onSlotMetric);
1825
- });
1826
- keptSlots.forEach((slot) => emitSlotMetric({
1827
- type: "prepared",
1828
- slotId: slot.id
1829
- }, onSlotMetric));
1830
- return keptSlots;
1831
- };
1832
- var htmlByteLength = (value, encoder) => encoder.encode(value).length;
1833
- var resolveSlot = async (slot, onError, policy, onSlotMetric) => {
1834
- const safePolicy = policy ?? getStreamingSlotPolicy();
1835
- const encoder = new TextEncoder;
1836
- const start = Date.now();
1837
- try {
1838
- const maybeAsyncValue = Promise.resolve(slot.resolve());
1839
- const resolved = typeof slot.timeoutMs === "number" && slot.timeoutMs > 0 ? await Promise.race([
1840
- maybeAsyncValue,
1841
- new Promise((_, reject) => setTimeout(() => {
1842
- reject(createTimeoutError(slot, slot.timeoutMs ?? 0));
1843
- }, slot.timeoutMs))
1844
- ]) : await maybeAsyncValue;
1845
- const html = typeof resolved === "string" ? resolved : `${resolved}`;
1846
- if (safePolicy.maxSlotHtmlSizeBytes > 0 && htmlByteLength(html, encoder) > safePolicy.maxSlotHtmlSizeBytes) {
1847
- const bytes2 = htmlByteLength(html, encoder);
1848
- const error = new Error(`Streaming slot "${slot.id}" exceeded max payload size of ${safePolicy.maxSlotHtmlSizeBytes} bytes`);
1849
- const durationMs2 = Date.now() - start;
1850
- onError?.(error, slot);
1851
- emitSlotMetric({
1852
- type: "size_exceeded",
1853
- slotId: slot.id,
1854
- durationMs: durationMs2,
1855
- bytes: bytes2,
1856
- error
1857
- }, onSlotMetric);
1858
- const fallbackHtml = typeof slot.errorHtml === "string" ? slot.errorHtml : null;
1859
- return {
1860
- html: fallbackHtml,
1861
- id: slot.id,
1862
- durationMs: durationMs2,
1863
- bytes: fallbackHtml === null ? 0 : htmlByteLength(fallbackHtml, encoder)
1864
- };
1865
- }
1866
- const durationMs = Date.now() - start;
1867
- const bytes = htmlByteLength(html, encoder);
1868
- emitSlotMetric({
1869
- type: "resolved",
1870
- slotId: slot.id,
1871
- durationMs,
1872
- bytes
1873
- }, onSlotMetric);
1874
- return {
1875
- html,
1876
- id: slot.id,
1877
- durationMs,
1878
- bytes
1879
- };
1880
- } catch (error) {
1881
- const durationMs = Date.now() - start;
1882
- onError?.(error, slot);
1883
- emitSlotMetric({
1884
- type: error?.__absTimeout === true ? "timeout" : "error",
1885
- slotId: slot.id,
1886
- durationMs,
1887
- error
1888
- }, onSlotMetric);
1889
- if (typeof slot.errorHtml === "string") {
1890
- const html = slot.errorHtml;
1891
- return {
1892
- html,
1893
- id: slot.id,
1894
- durationMs,
1895
- bytes: htmlByteLength(html, encoder)
1896
- };
1887
+ const candidate = `${explicitPath}.svelte`;
1888
+ if (await Bun.file(candidate).exists() === true) {
1889
+ return candidate;
1890
+ }
1891
+ return null;
1892
+ }, writeIfChanged = async (path, content) => {
1893
+ const targetFile = Bun.file(path);
1894
+ const exists = await targetFile.exists();
1895
+ if (exists) {
1896
+ const currentContent = await targetFile.text();
1897
+ if (currentContent === content) {
1898
+ return;
1897
1899
  }
1900
+ }
1901
+ await Bun.write(path, content);
1902
+ }, compileSvelteServerModule = async (sourcePath) => {
1903
+ const cachedModulePath = compiledModuleCache.get(sourcePath);
1904
+ if (cachedModulePath) {
1905
+ return cachedModulePath;
1906
+ }
1907
+ const resolutionSourcePath = await resolveOriginalSourcePath(sourcePath);
1908
+ const source = await Bun.file(sourcePath).text();
1909
+ const { compile, preprocess } = await import("svelte/compiler");
1910
+ const loweredSource = lowerSvelteIslandSyntax(source, "server");
1911
+ const preprocessed = await preprocess(loweredSource.code, {});
1912
+ let transpiled = sourcePath.endsWith(".ts") || sourcePath.endsWith(".svelte.ts") ? transpiler.transformSync(preprocessed.code) : preprocessed.code;
1913
+ const childImportSpecs = Array.from(transpiled.matchAll(/from\s+['"]([^'"]+)['"]/g)).map((match) => match[1]).filter((value) => value !== undefined);
1914
+ const resolvedChildModules = await Promise.all(childImportSpecs.map((spec) => resolveSvelteImport(spec, resolutionSourcePath)));
1915
+ const resolvedModuleImports = await Promise.all(childImportSpecs.map((spec) => resolveRelativeModule(spec, resolutionSourcePath)));
1916
+ const childModulePaths = new Map;
1917
+ const rewrittenModulePaths = new Map;
1918
+ const compiledChildren = await Promise.all(childImportSpecs.map(async (spec, index) => {
1919
+ const resolvedChild = resolvedChildModules[index];
1920
+ if (!spec || !resolvedChild)
1921
+ return null;
1898
1922
  return {
1899
- html: null,
1900
- id: slot.id,
1901
- durationMs,
1902
- bytes: 0
1923
+ compiledPath: await compileSvelteServerModule(resolvedChild),
1924
+ spec
1903
1925
  };
1926
+ }));
1927
+ for (const result of compiledChildren) {
1928
+ if (result)
1929
+ childModulePaths.set(result.spec, result.compiledPath);
1904
1930
  }
1931
+ for (let index = 0;index < childImportSpecs.length; index += 1) {
1932
+ const spec = childImportSpecs[index];
1933
+ const resolvedModuleImport = resolvedModuleImports[index];
1934
+ if (!spec || !resolvedModuleImport)
1935
+ continue;
1936
+ if (resolvedChildModules[index])
1937
+ continue;
1938
+ rewrittenModulePaths.set(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), resolvedModuleImport));
1939
+ }
1940
+ for (const [spec, resolvedModuleImport] of rewrittenModulePaths) {
1941
+ transpiled = transpiled.replaceAll(spec, resolvedModuleImport);
1942
+ }
1943
+ let compiledCode = compile(transpiled, {
1944
+ css: "injected",
1945
+ experimental: {
1946
+ async: loweredSource.transformed
1947
+ },
1948
+ filename: resolutionSourcePath,
1949
+ generate: "server"
1950
+ }).js.code;
1951
+ for (const [spec, compiledChildPath] of childModulePaths) {
1952
+ compiledCode = compiledCode.replaceAll(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), compiledChildPath));
1953
+ }
1954
+ for (const [spec, resolvedModuleImport] of rewrittenModulePaths) {
1955
+ compiledCode = compiledCode.replaceAll(spec, resolvedModuleImport);
1956
+ }
1957
+ const compiledModulePath = getCachedModulePath(sourcePath);
1958
+ await mkdir(dirname3(compiledModulePath), { recursive: true });
1959
+ await writeIfChanged(compiledModulePath, compiledCode);
1960
+ compiledModuleCache.set(sourcePath, compiledModulePath);
1961
+ return compiledModulePath;
1905
1962
  };
1906
- var nextResolvedSlot = async (pending) => {
1907
- const wrapped = pending.map((promise) => promise.then((result) => ({
1908
- original: promise,
1909
- result
1910
- })));
1911
- return Promise.race(wrapped);
1912
- };
1913
- var streamChunkToString = (value, decoder) => typeof value === "string" ? value : decoder.decode(value, { stream: true });
1914
- var streamOutOfOrderSlots = ({
1915
- footerHtml = "",
1916
- headerHtml = "",
1917
- nonce,
1918
- policy,
1919
- onSlotMetric,
1920
- onError,
1921
- slots
1922
- }) => {
1923
- const resolvedPolicy = resolveStreamingSlotPolicy(policy);
1924
- const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
1925
- const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
1926
- const effectivePolicy = {
1927
- ...resolvedPolicy,
1928
- onSlotMetric: combinedOnSlotMetric
1929
- };
1930
- const preparedSlots = prepareSlots({
1931
- policy: effectivePolicy,
1932
- slots,
1933
- onError: combinedOnError,
1934
- onSlotMetric: combinedOnSlotMetric
1935
- });
1936
- const encoder = new TextEncoder;
1937
- return new ReadableStream({
1938
- async start(controller) {
1939
- try {
1940
- let header = headerHtml;
1941
- if (preparedSlots.length > 0 && !header.includes(STREAMING_RUNTIME_GLOBAL)) {
1942
- header = injectHtmlIntoHead(header, renderStreamingSlotsRuntimeTag(nonce));
1943
- }
1944
- controller.enqueue(toUint8(header, encoder));
1945
- const pending = preparedSlots.map((slot) => {
1946
- const fallback = renderStreamingSlotPlaceholder(slot.id, slot.fallbackHtml ?? "");
1947
- controller.enqueue(toUint8(fallback, encoder));
1948
- return resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric);
1949
- });
1950
- while (pending.length > 0) {
1951
- const { original, result } = await nextResolvedSlot(pending);
1952
- const index = pending.indexOf(original);
1953
- if (index >= 0)
1954
- pending.splice(index, 1);
1955
- if (result.html === null)
1956
- continue;
1957
- emitSlotMetric({
1958
- type: "patched",
1959
- slotId: result.id,
1960
- durationMs: result.durationMs,
1961
- bytes: result.bytes
1962
- }, combinedOnSlotMetric);
1963
- controller.enqueue(toUint8(renderStreamingSlotPatchTag(result.id, result.html, nonce), encoder));
1964
- }
1965
- if (footerHtml.length > 0) {
1966
- controller.enqueue(toUint8(footerHtml, encoder));
1967
- }
1968
- controller.close();
1969
- } catch (error) {
1970
- controller.error(error);
1971
- }
1972
- }
1973
- });
1974
- };
1975
- var injectStreamingRuntimeIntoStream = (stream, nonce) => {
1976
- const runtimeTag = renderStreamingSlotsRuntimeTag(nonce);
1977
- const encoder = new TextEncoder;
1978
- const decoder = new TextDecoder;
1979
- const lookbehind = CLOSING_HEAD_TAG_LENGTH - 1;
1980
- return new ReadableStream({
1981
- async start(controller) {
1982
- const reader = stream.getReader();
1983
- let injected = false;
1984
- let pending = "";
1985
- try {
1986
- for (;; ) {
1987
- const { done, value } = await reader.read();
1988
- if (done)
1989
- break;
1990
- if (!value)
1991
- continue;
1992
- pending += streamChunkToString(value, decoder);
1993
- if (injected) {
1994
- controller.enqueue(encoder.encode(pending));
1995
- pending = "";
1996
- continue;
1997
- }
1998
- const headIndex = pending.indexOf(CLOSING_HEAD_TAG);
1999
- if (headIndex >= 0) {
2000
- const withRuntime = `${pending.slice(0, headIndex)}${runtimeTag}${pending.slice(headIndex)}`;
2001
- controller.enqueue(encoder.encode(withRuntime));
2002
- pending = "";
2003
- injected = true;
2004
- continue;
2005
- }
2006
- if (pending.length > lookbehind) {
2007
- const safeText = pending.slice(0, pending.length - lookbehind);
2008
- controller.enqueue(encoder.encode(safeText));
2009
- pending = pending.slice(-lookbehind);
2010
- }
2011
- }
2012
- pending += decoder.decode();
2013
- if (!injected) {
2014
- pending = injectHtmlIntoHead(pending, runtimeTag);
2015
- }
2016
- if (pending.length > 0) {
2017
- controller.enqueue(encoder.encode(pending));
2018
- }
2019
- controller.close();
2020
- } catch (error) {
2021
- controller.error(error);
2022
- }
2023
- }
2024
- });
2025
- };
2026
- var appendStreamingSlotPatchesToStream = (stream, slots = [], {
2027
- injectRuntime = true,
2028
- nonce,
2029
- onError,
2030
- onSlotMetric,
2031
- policy
2032
- } = {}) => {
2033
- const resolvedPolicy = resolveStreamingSlotPolicy(policy);
2034
- const combinedOnError = createCombinedSlotErrorHandler(resolvedPolicy.onError, onError);
2035
- const combinedOnSlotMetric = createCombinedSlotMetricHandler(resolvedPolicy.onSlotMetric, onSlotMetric);
2036
- const effectivePolicy = {
2037
- ...resolvedPolicy,
2038
- onSlotMetric: combinedOnSlotMetric
2039
- };
2040
- const preparedSlots = prepareSlots({
2041
- policy: effectivePolicy,
2042
- slots,
2043
- onError: combinedOnError,
2044
- onSlotMetric: combinedOnSlotMetric
2045
- });
2046
- if (preparedSlots.length === 0)
2047
- return stream;
2048
- const source = injectRuntime ? injectStreamingRuntimeIntoStream(stream, nonce) : stream;
2049
- const encoder = new TextEncoder;
2050
- const decoder = new TextDecoder;
2051
- const reader = source.getReader();
2052
- const pending = preparedSlots.map((slot) => resolveSlot(slot, combinedOnError, effectivePolicy, combinedOnSlotMetric));
2053
- return new ReadableStream({
2054
- async start(controller) {
2055
- let baseDone = false;
2056
- let baseRead = reader.read();
2057
- let tail = "";
2058
- let footer = "";
2059
- try {
2060
- while (!baseDone || pending.length > 0) {
2061
- const racers = [];
2062
- if (!baseDone) {
2063
- racers.push(baseRead.then(({ done, value }) => ({
2064
- done,
2065
- kind: "base",
2066
- value
2067
- })));
2068
- }
2069
- if (pending.length > 0) {
2070
- racers.push(nextResolvedSlot(pending).then((resolved) => ({
2071
- kind: "slot",
2072
- ...resolved
2073
- })));
2074
- }
2075
- if (racers.length === 0)
2076
- break;
2077
- const winner = await Promise.race(racers);
2078
- if (winner.kind === "base") {
2079
- if (winner.done) {
2080
- baseDone = true;
2081
- tail += decoder.decode();
2082
- const footerStart = tail.search(CLOSING_PAGE_TAG_REGEX);
2083
- if (footerStart >= 0) {
2084
- const content = tail.slice(0, footerStart);
2085
- footer = tail.slice(footerStart);
2086
- if (content.length > 0) {
2087
- controller.enqueue(encoder.encode(content));
2088
- }
2089
- } else if (tail.length > 0) {
2090
- controller.enqueue(encoder.encode(tail));
2091
- }
2092
- tail = "";
2093
- } else if (winner.value) {
2094
- tail += streamChunkToString(winner.value, decoder);
2095
- if (tail.length > STREAM_TAIL_LOOKBEHIND) {
2096
- const content = tail.slice(0, tail.length - STREAM_TAIL_LOOKBEHIND);
2097
- controller.enqueue(encoder.encode(content));
2098
- tail = tail.slice(-STREAM_TAIL_LOOKBEHIND);
2099
- }
2100
- baseRead = reader.read();
2101
- }
2102
- continue;
2103
- }
2104
- const index = pending.indexOf(winner.original);
2105
- if (index >= 0)
2106
- pending.splice(index, 1);
2107
- if (winner.result.html === null)
2108
- continue;
2109
- emitSlotMetric({
2110
- type: "patched",
2111
- slotId: winner.result.id,
2112
- durationMs: winner.result.durationMs,
2113
- bytes: winner.result.bytes
2114
- }, combinedOnSlotMetric);
2115
- controller.enqueue(encoder.encode(renderStreamingSlotPatchTag(winner.result.id, winner.result.html, nonce)));
2116
- }
2117
- if (footer.length > 0)
2118
- controller.enqueue(encoder.encode(footer));
2119
- controller.close();
2120
- } catch (error) {
2121
- controller.error(error);
2122
- }
2123
- }
2124
- });
2125
- };
1963
+ var init_svelteServerModule = __esm(() => {
1964
+ init_lowerIslandSyntax();
1965
+ serverCacheRoot = join2(process.cwd(), ".absolutejs", "islands", "svelte");
1966
+ compiledModuleCache = new Map;
1967
+ originalSourcePathCache = new Map;
1968
+ transpiler = new Bun.Transpiler({
1969
+ loader: "ts",
1970
+ target: "browser"
1971
+ });
1972
+ });
2126
1973
 
2127
- // src/core/streamingSlotRegistry.ts
2128
- var asyncLocalStorage;
2129
- var isServerRuntime = () => typeof process !== "undefined" && typeof process.versions?.node === "string";
2130
- var ensureAsyncLocalStorage = async () => {
2131
- if (typeof asyncLocalStorage !== "undefined")
2132
- return asyncLocalStorage;
2133
- if (!isServerRuntime()) {
2134
- asyncLocalStorage = null;
2135
- return asyncLocalStorage;
1974
+ // src/core/renderIslandMarkup.ts
1975
+ var islandSequence = 0, resolvedServerComponentCache, resolvedServerBuildComponentCache, nextIslandId = () => {
1976
+ islandSequence += 1;
1977
+ return `island-${islandSequence}`;
1978
+ }, isRecord3 = (value) => typeof value === "object" && value !== null, isReactServerIslandComponent = (value) => typeof value === "function", isSvelteServerIslandComponent = (value) => typeof value === "function", isVueServerIslandComponent = (value) => typeof value === "function" || isRecord3(value), isAngularServerIslandComponent = (value) => typeof value === "function", resolveBuildReferencePath = (source, registryPath) => {
1979
+ if (source.startsWith("file://"))
1980
+ return new URL(source).pathname;
1981
+ if (source.startsWith("."))
1982
+ return new URL(source, registryPath).pathname;
1983
+ return source;
1984
+ }, loadAndCompileServerBuildComponent = async (buildReferencePath) => {
1985
+ const compiledModulePath = await compileSvelteServerModule(buildReferencePath);
1986
+ const loadedModule = await import(compiledModulePath);
1987
+ return "default" in loadedModule ? loadedModule.default : loadedModule;
1988
+ }, loadServerBuildComponent = async (buildReferencePath) => {
1989
+ const cachedBuildComponent = resolvedServerBuildComponentCache.get(buildReferencePath);
1990
+ if (cachedBuildComponent) {
1991
+ return cachedBuildComponent;
2136
1992
  }
2137
- const mod = await import("async_hooks");
2138
- asyncLocalStorage = new mod.AsyncLocalStorage;
2139
- return asyncLocalStorage;
2140
- };
2141
- var registerStreamingSlot2 = (slot) => {
2142
- if (!asyncLocalStorage)
2143
- return;
2144
- const store = asyncLocalStorage.getStore();
2145
- if (!store)
2146
- return;
2147
- store.set(slot.id, slot);
2148
- };
2149
- setStreamingSlotRegistrar(registerStreamingSlot2);
2150
- var runWithStreamingSlotRegistry = async (task) => {
2151
- const storage = await ensureAsyncLocalStorage();
2152
- if (!storage) {
2153
- return {
2154
- result: await task(),
2155
- slots: []
2156
- };
1993
+ const loadPromise = loadAndCompileServerBuildComponent(buildReferencePath);
1994
+ resolvedServerBuildComponentCache.set(buildReferencePath, loadPromise);
1995
+ return loadPromise;
1996
+ }, loadServerImportComponent = async (resolvedComponent) => {
1997
+ const resolvedModulePath = resolvedComponent.startsWith(".") ? new URL(resolvedComponent, import.meta.url).pathname : resolvedComponent;
1998
+ const importTarget = resolvedModulePath.endsWith(".svelte") ? await compileSvelteServerModule(resolvedModulePath) : resolvedModulePath;
1999
+ const loadedModule = await import(importTarget);
2000
+ return "default" in loadedModule ? loadedModule.default : loadedModule;
2001
+ }, resolveIslandComponent = async (component) => {
2002
+ const buildReference = getIslandBuildReference(component);
2003
+ const buildReferencePath = buildReference?.source ? resolveBuildReferencePath(buildReference.source, import.meta.url) : null;
2004
+ if (buildReferencePath?.endsWith(".svelte")) {
2005
+ return loadServerBuildComponent(buildReferencePath);
2157
2006
  }
2158
- return storage.run(new Map, async () => {
2159
- const result = await task();
2160
- const store = storage.getStore();
2007
+ const resolvedComponent = getIslandComponent(component);
2008
+ if (typeof resolvedComponent !== "string") {
2009
+ return resolvedComponent;
2010
+ }
2011
+ return loadServerImportComponent(resolvedComponent);
2012
+ }, resolveServerIslandComponent = async (component) => {
2013
+ const cachedResolvedComponent = resolvedServerComponentCache.get(component);
2014
+ if (cachedResolvedComponent) {
2015
+ return cachedResolvedComponent;
2016
+ }
2017
+ const resolutionPromise = resolveIslandComponent(component);
2018
+ resolvedServerComponentCache.set(component, resolutionPromise);
2019
+ return resolutionPromise;
2020
+ }, resolveReactServerIslandComponent = async (component) => {
2021
+ const resolvedComponent = await resolveServerIslandComponent(component);
2022
+ if (!isReactServerIslandComponent(resolvedComponent)) {
2023
+ throw new Error("Resolved React island is not a valid React component.");
2024
+ }
2025
+ return resolvedComponent;
2026
+ }, resolveSvelteServerIslandComponent = async (component) => {
2027
+ const resolvedComponent = await resolveServerIslandComponent(component);
2028
+ if (!isSvelteServerIslandComponent(resolvedComponent)) {
2029
+ throw new Error("Resolved Svelte island is not a valid Svelte component.");
2030
+ }
2031
+ return resolvedComponent;
2032
+ }, resolveVueServerIslandComponent = async (component) => {
2033
+ const resolvedComponent = await resolveServerIslandComponent(component);
2034
+ if (!isVueServerIslandComponent(resolvedComponent)) {
2035
+ throw new Error("Resolved Vue island is not a valid Vue component.");
2036
+ }
2037
+ return resolvedComponent;
2038
+ }, resolveAngularServerIslandComponent = async (component) => {
2039
+ const resolvedComponent = await resolveServerIslandComponent(component);
2040
+ if (!isAngularServerIslandComponent(resolvedComponent)) {
2041
+ throw new Error("Resolved Angular island is not a valid Angular component.");
2042
+ }
2043
+ return resolvedComponent;
2044
+ }, renderIslandMarkup = async (registry, props) => {
2045
+ const result = await renderIslandResult(registry, props);
2046
+ return `<div ${serializeIslandAttributes(result.attributes)}>${result.html}</div>`;
2047
+ }, renderIslandResult = async (registry, props) => {
2048
+ const islandId = nextIslandId();
2049
+ const attributes = getIslandMarkerAttributes(props);
2050
+ if (props.framework === "react") {
2051
+ const entry = registry.react?.[props.component];
2052
+ if (!entry) {
2053
+ throw new Error(`Island component "${props.component}" is not registered for framework "react".`);
2054
+ }
2055
+ const component = await resolveReactServerIslandComponent(entry);
2056
+ const html = renderReactIslandToHtml(component, props.props);
2057
+ return { attributes, html };
2058
+ }
2059
+ if (props.framework === "svelte") {
2060
+ const entry = registry.svelte?.[props.component];
2061
+ if (!entry) {
2062
+ throw new Error(`Island component "${props.component}" is not registered for framework "svelte".`);
2063
+ }
2064
+ const component = await resolveSvelteServerIslandComponent(entry);
2065
+ const html = renderSvelteIslandToHtml(component, props.props);
2066
+ return { attributes, html };
2067
+ }
2068
+ if (props.framework === "vue") {
2069
+ const entry = registry.vue?.[props.component];
2070
+ if (!entry) {
2071
+ throw new Error(`Island component "${props.component}" is not registered for framework "vue".`);
2072
+ }
2073
+ const component = await resolveVueServerIslandComponent(entry);
2074
+ const html = await renderVueIslandToHtml(component, props.props);
2075
+ return { attributes, html };
2076
+ }
2077
+ if (props.framework === "angular") {
2078
+ const entry = registry.angular?.[props.component];
2079
+ if (!entry) {
2080
+ throw new Error(`Island component "${props.component}" is not registered for framework "angular".`);
2081
+ }
2082
+ const component = await resolveAngularServerIslandComponent(entry);
2083
+ const html = await renderAngularIslandToHtml(component, props.props, islandId);
2161
2084
  return {
2162
- result,
2163
- slots: store ? [...store.values()] : []
2085
+ attributes: {
2086
+ ...getIslandMarkerAttributes(props, islandId)
2087
+ },
2088
+ html
2164
2089
  };
2165
- });
2090
+ }
2091
+ throw new Error(`Framework "${props.framework}" is not implemented in this prototype.`);
2166
2092
  };
2093
+ var init_renderIslandMarkup = __esm(() => {
2094
+ init_islandSsr();
2095
+ init_svelteServerModule();
2096
+ init_islandMarkupAttributes();
2097
+ init_islands();
2098
+ resolvedServerComponentCache = new Map;
2099
+ resolvedServerBuildComponentCache = new Map;
2100
+ });
2167
2101
 
2168
- // src/core/responseEnhancers.ts
2169
- var toResponse = async (responseLike) => await responseLike;
2170
- var cloneHeaders = (response) => {
2171
- const headers = new Headers(response.headers);
2172
- return headers;
2173
- };
2174
- var enhanceHtmlResponseWithStreamingSlots = (response, { nonce, onError, streamingSlots = [], policy } = {}) => {
2175
- if (!response.body || streamingSlots.length === 0) {
2176
- return response;
2102
+ // src/vue/components/Image.vue
2103
+ var Image_default = "../Image-0pe96k20.vue";
2104
+ // src/vue/components/SuspenseSlot.ts
2105
+ import { defineComponent as defineComponent2, h as h2 } from "vue";
2106
+
2107
+ // src/vue/components/StreamSlot.ts
2108
+ import { defineComponent, h } from "vue";
2109
+ var StreamSlot = defineComponent({
2110
+ name: "AbsoluteStreamSlot",
2111
+ props: {
2112
+ className: { default: undefined, type: String },
2113
+ errorHtml: { default: undefined, type: String },
2114
+ fallbackHtml: { default: "", type: String },
2115
+ id: { required: true, type: String },
2116
+ resolve: {
2117
+ required: true,
2118
+ type: Function
2119
+ },
2120
+ timeoutMs: { default: undefined, type: Number }
2121
+ },
2122
+ setup(props) {
2123
+ if (typeof window === "undefined") {
2124
+ registerStreamingSlot({
2125
+ errorHtml: props.errorHtml,
2126
+ fallbackHtml: props.fallbackHtml,
2127
+ id: props.id,
2128
+ resolve: props.resolve,
2129
+ timeoutMs: props.timeoutMs
2130
+ });
2131
+ }
2132
+ return () => h("div", {
2133
+ class: props.className,
2134
+ "data-absolute-slot": "true",
2135
+ id: `slot-${props.id}`,
2136
+ innerHTML: props.fallbackHtml
2137
+ });
2177
2138
  }
2178
- const body = appendStreamingSlotPatchesToStream(response.body, streamingSlots, {
2179
- nonce,
2180
- onError,
2181
- policy
2182
- });
2183
- return new Response(body, {
2184
- headers: cloneHeaders(response),
2185
- status: response.status,
2186
- statusText: response.statusText
2187
- });
2188
- };
2189
- var withStreamingSlots = async (responseLike, options = {}) => enhanceHtmlResponseWithStreamingSlots(await toResponse(responseLike), options);
2190
- var mergeStreamingSlots = (registered, explicit) => {
2191
- const merged = new Map;
2192
- for (const slot of registered)
2193
- merged.set(slot.id, slot);
2194
- for (const slot of explicit)
2195
- merged.set(slot.id, slot);
2196
- return [...merged.values()];
2197
- };
2198
- var withRegisteredStreamingSlots = async (renderResponse, options = {}) => {
2199
- const { result, slots } = await runWithStreamingSlotRegistry(renderResponse);
2200
- const explicit = options.streamingSlots ?? [];
2201
- return withStreamingSlots(result, {
2202
- ...options,
2203
- streamingSlots: mergeStreamingSlots(slots, explicit)
2204
- });
2205
- };
2139
+ });
2206
2140
 
2141
+ // src/vue/components/SuspenseSlot.ts
2142
+ var SuspenseSlot = defineComponent2({
2143
+ name: "AbsoluteSuspenseSlot",
2144
+ props: {
2145
+ className: { default: undefined, type: String },
2146
+ errorHtml: { default: undefined, type: String },
2147
+ fallbackHtml: { default: "", type: String },
2148
+ id: { required: true, type: String },
2149
+ resolve: {
2150
+ required: true,
2151
+ type: Function
2152
+ },
2153
+ timeoutMs: { default: undefined, type: Number }
2154
+ },
2155
+ setup(props) {
2156
+ return () => h2(StreamSlot, props);
2157
+ }
2158
+ });
2207
2159
  // src/core/wrapPageHandlerWithStreamingSlots.ts
2160
+ init_responseEnhancers();
2208
2161
  var wrapPageHandlerWithStreamingSlots = (handler) => (...args) => withRegisteredStreamingSlots(() => handler(...args));
2209
2162
 
2210
2163
  // src/vue/index.ts
@@ -2529,5 +2482,5 @@ export {
2529
2482
  Image_default as Image
2530
2483
  };
2531
2484
 
2532
- //# debugId=63023D8600E282C964756E2164756E21
2485
+ //# debugId=5177CF90AA9AA8F464756E2164756E21
2533
2486
  //# sourceMappingURL=index.js.map