@vertz/ui-server 0.2.42 → 0.2.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bun-dev-server.js +188 -318
- package/dist/index.d.ts +21 -38
- package/dist/index.js +5 -7
- package/dist/node-handler.js +2 -2
- package/dist/shared/{chunk-tnsz18ac.js → chunk-05hadnnd.js} +1 -1
- package/dist/shared/{chunk-r1pf7cf2.js → chunk-1v0wakka.js} +188 -365
- package/dist/shared/{chunk-phe490jc.js → chunk-hd03dkxf.js} +1 -1
- package/dist/ssr/index.d.ts +48 -32
- package/dist/ssr/index.js +8 -8
- package/package.json +1 -1
package/dist/bun-dev-server.js
CHANGED
|
@@ -1535,9 +1535,16 @@ function createPrefetchManifestManager(options) {
|
|
|
1535
1535
|
};
|
|
1536
1536
|
}
|
|
1537
1537
|
|
|
1538
|
-
// src/ssr-
|
|
1539
|
-
|
|
1540
|
-
|
|
1538
|
+
// src/ssr-session.ts
|
|
1539
|
+
function createSessionScript(session, nonce) {
|
|
1540
|
+
const json = JSON.stringify(session);
|
|
1541
|
+
const escaped = json.replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
1542
|
+
const nonceAttr = nonce ? ` nonce="${escapeAttr2(nonce)}"` : "";
|
|
1543
|
+
return `<script${nonceAttr}>window.__VERTZ_SESSION__=${escaped}</script>`;
|
|
1544
|
+
}
|
|
1545
|
+
function escapeAttr2(s) {
|
|
1546
|
+
return s.replace(/[&"'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
1547
|
+
}
|
|
1541
1548
|
|
|
1542
1549
|
// src/css-filter.ts
|
|
1543
1550
|
function extractClassNamesFromHTML(html) {
|
|
@@ -2382,14 +2389,14 @@ var RAW_TEXT_ELEMENTS = new Set(["script", "style"]);
|
|
|
2382
2389
|
function escapeHtml(text) {
|
|
2383
2390
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2384
2391
|
}
|
|
2385
|
-
function
|
|
2392
|
+
function escapeAttr3(value) {
|
|
2386
2393
|
const str = typeof value === "string" ? value : String(value);
|
|
2387
2394
|
return str.replace(/&/g, "&").replace(/"/g, """);
|
|
2388
2395
|
}
|
|
2389
2396
|
function serializeAttrs(attrs) {
|
|
2390
2397
|
const parts = [];
|
|
2391
2398
|
for (const [key, value] of Object.entries(attrs)) {
|
|
2392
|
-
parts.push(` ${key}="${
|
|
2399
|
+
parts.push(` ${key}="${escapeAttr3(value)}"`);
|
|
2393
2400
|
}
|
|
2394
2401
|
return parts.join("");
|
|
2395
2402
|
}
|
|
@@ -2457,7 +2464,7 @@ async function streamToString(stream) {
|
|
|
2457
2464
|
function createTemplateChunk(slotId, resolvedHtml, nonce) {
|
|
2458
2465
|
const tmplId = `v-tmpl-${slotId}`;
|
|
2459
2466
|
const slotRef = `v-slot-${slotId}`;
|
|
2460
|
-
const nonceAttr = nonce != null ? ` nonce="${
|
|
2467
|
+
const nonceAttr = nonce != null ? ` nonce="${escapeAttr3(nonce)}"` : "";
|
|
2461
2468
|
return `<template id="${tmplId}">${resolvedHtml}</template>` + `<script${nonceAttr}>` + `(function(){` + `var s=document.getElementById("${slotRef}");` + `var t=document.getElementById("${tmplId}");` + `if(s&&t){s.replaceWith(t.content.cloneNode(true));t.remove()}` + `})()` + "</script>";
|
|
2462
2469
|
}
|
|
2463
2470
|
|
|
@@ -2487,7 +2494,7 @@ function renderToStream(tree, options) {
|
|
|
2487
2494
|
return children.map((child) => walkAndSerialize(child)).join("");
|
|
2488
2495
|
}
|
|
2489
2496
|
const isRawText = RAW_TEXT_ELEMENTS.has(tag);
|
|
2490
|
-
const attrStr = Object.entries(attrs).map(([k, v]) => ` ${k}="${
|
|
2497
|
+
const attrStr = Object.entries(attrs).map(([k, v]) => ` ${k}="${escapeAttr3(v)}"`).join("");
|
|
2491
2498
|
if (VOID_ELEMENTS.has(tag)) {
|
|
2492
2499
|
return `<${tag}${attrStr}>`;
|
|
2493
2500
|
}
|
|
@@ -2525,276 +2532,6 @@ function renderToStream(tree, options) {
|
|
|
2525
2532
|
});
|
|
2526
2533
|
}
|
|
2527
2534
|
|
|
2528
|
-
// src/ssr-streaming-runtime.ts
|
|
2529
|
-
function safeSerialize(data) {
|
|
2530
|
-
return JSON.stringify(data).replace(/</g, "\\u003c");
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
// src/ssr-render.ts
|
|
2534
|
-
var compiledThemeCache = new WeakMap;
|
|
2535
|
-
var compiledThemeWithMetricsCache = new WeakMap;
|
|
2536
|
-
function compileThemeCached(theme, fallbackMetrics) {
|
|
2537
|
-
const cache = fallbackMetrics ? compiledThemeWithMetricsCache : compiledThemeCache;
|
|
2538
|
-
const cached = cache.get(theme);
|
|
2539
|
-
if (cached)
|
|
2540
|
-
return cached;
|
|
2541
|
-
const compiled = compileTheme(theme, { fallbackMetrics });
|
|
2542
|
-
cache.set(theme, compiled);
|
|
2543
|
-
return compiled;
|
|
2544
|
-
}
|
|
2545
|
-
function createRequestContext(url) {
|
|
2546
|
-
return {
|
|
2547
|
-
url,
|
|
2548
|
-
adapter: createSSRAdapter(),
|
|
2549
|
-
subscriber: null,
|
|
2550
|
-
readValueCb: null,
|
|
2551
|
-
cleanupStack: [],
|
|
2552
|
-
batchDepth: 0,
|
|
2553
|
-
pendingEffects: new Map,
|
|
2554
|
-
contextScope: null,
|
|
2555
|
-
entityStore: new EntityStore,
|
|
2556
|
-
envelopeStore: new QueryEnvelopeStore,
|
|
2557
|
-
queryCache: new MemoryCache({ maxSize: Infinity }),
|
|
2558
|
-
inflight: new Map,
|
|
2559
|
-
queries: [],
|
|
2560
|
-
errors: [],
|
|
2561
|
-
cssTracker: new Set
|
|
2562
|
-
};
|
|
2563
|
-
}
|
|
2564
|
-
var domShimInstalled = false;
|
|
2565
|
-
function ensureDomShim() {
|
|
2566
|
-
if (domShimInstalled && typeof document !== "undefined")
|
|
2567
|
-
return;
|
|
2568
|
-
domShimInstalled = true;
|
|
2569
|
-
installDomShim();
|
|
2570
|
-
}
|
|
2571
|
-
function resolveAppFactory(module) {
|
|
2572
|
-
const createApp = module.default || module.App;
|
|
2573
|
-
if (typeof createApp !== "function") {
|
|
2574
|
-
throw new Error("App entry must export a default function or named App function");
|
|
2575
|
-
}
|
|
2576
|
-
return createApp;
|
|
2577
|
-
}
|
|
2578
|
-
function collectCSS(themeCss, module, renderedHtml) {
|
|
2579
|
-
const alreadyIncluded = new Set;
|
|
2580
|
-
if (themeCss)
|
|
2581
|
-
alreadyIncluded.add(themeCss);
|
|
2582
|
-
if (module.styles) {
|
|
2583
|
-
for (const s of module.styles)
|
|
2584
|
-
alreadyIncluded.add(s);
|
|
2585
|
-
}
|
|
2586
|
-
const ssrCtx = ssrStorage.getStore();
|
|
2587
|
-
const tracker = ssrCtx?.cssTracker;
|
|
2588
|
-
const useTracker = tracker && tracker.size > 0;
|
|
2589
|
-
const rawComponentCss = useTracker ? Array.from(tracker) : module.getInjectedCSS?.() ?? [];
|
|
2590
|
-
let componentCss = rawComponentCss.filter((s) => !alreadyIncluded.has(s));
|
|
2591
|
-
if (!useTracker && componentCss.length > 0 && renderedHtml) {
|
|
2592
|
-
componentCss = filterCSSByHTML(renderedHtml, componentCss);
|
|
2593
|
-
}
|
|
2594
|
-
const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
|
|
2595
|
-
const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
|
|
2596
|
-
`)}</style>` : "";
|
|
2597
|
-
const componentTag = componentCss.length > 0 ? `<style data-vertz-css>${componentCss.join(`
|
|
2598
|
-
`)}</style>` : "";
|
|
2599
|
-
return [themeTag, globalTag, componentTag].filter(Boolean).join(`
|
|
2600
|
-
`);
|
|
2601
|
-
}
|
|
2602
|
-
async function ssrRenderToString(module, url, options) {
|
|
2603
|
-
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
2604
|
-
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
2605
|
-
ensureDomShim();
|
|
2606
|
-
const ctx = createRequestContext(normalizedUrl);
|
|
2607
|
-
if (options?.ssrAuth) {
|
|
2608
|
-
ctx.ssrAuth = options.ssrAuth;
|
|
2609
|
-
}
|
|
2610
|
-
return ssrStorage.run(ctx, async () => {
|
|
2611
|
-
try {
|
|
2612
|
-
setGlobalSSRTimeout(ssrTimeout);
|
|
2613
|
-
const createApp = resolveAppFactory(module);
|
|
2614
|
-
let themeCss = "";
|
|
2615
|
-
let themePreloadTags = "";
|
|
2616
|
-
if (module.theme) {
|
|
2617
|
-
try {
|
|
2618
|
-
const compiled = compileThemeCached(module.theme, options?.fallbackMetrics);
|
|
2619
|
-
themeCss = compiled.css;
|
|
2620
|
-
themePreloadTags = compiled.preloadTags;
|
|
2621
|
-
} catch (e) {
|
|
2622
|
-
console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2625
|
-
createApp();
|
|
2626
|
-
if (ctx.ssrRedirect) {
|
|
2627
|
-
return {
|
|
2628
|
-
html: "",
|
|
2629
|
-
css: "",
|
|
2630
|
-
ssrData: [],
|
|
2631
|
-
headTags: "",
|
|
2632
|
-
redirect: ctx.ssrRedirect
|
|
2633
|
-
};
|
|
2634
|
-
}
|
|
2635
|
-
const store = ssrStorage.getStore();
|
|
2636
|
-
if (store) {
|
|
2637
|
-
if (store.pendingRouteComponents?.size) {
|
|
2638
|
-
const entries = Array.from(store.pendingRouteComponents.entries());
|
|
2639
|
-
const results = await Promise.allSettled(entries.map(([route, promise]) => Promise.race([
|
|
2640
|
-
promise.then((mod) => ({ route, factory: mod.default })),
|
|
2641
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("lazy route timeout")), ssrTimeout))
|
|
2642
|
-
])));
|
|
2643
|
-
store.resolvedComponents = new Map;
|
|
2644
|
-
for (const result of results) {
|
|
2645
|
-
if (result.status === "fulfilled") {
|
|
2646
|
-
const { route, factory } = result.value;
|
|
2647
|
-
store.resolvedComponents.set(route, factory);
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
store.pendingRouteComponents = undefined;
|
|
2651
|
-
}
|
|
2652
|
-
if (!store.resolvedComponents) {
|
|
2653
|
-
store.resolvedComponents = new Map;
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
const queries = getSSRQueries();
|
|
2657
|
-
const resolvedQueries = [];
|
|
2658
|
-
if (queries.length > 0) {
|
|
2659
|
-
await Promise.allSettled(queries.map(({ promise, timeout, resolve, key }) => Promise.race([
|
|
2660
|
-
promise.then((data) => {
|
|
2661
|
-
resolve(data);
|
|
2662
|
-
resolvedQueries.push({ key, data });
|
|
2663
|
-
return "resolved";
|
|
2664
|
-
}),
|
|
2665
|
-
new Promise((r) => setTimeout(r, timeout || ssrTimeout)).then(() => "timeout")
|
|
2666
|
-
])));
|
|
2667
|
-
if (store)
|
|
2668
|
-
store.queries = [];
|
|
2669
|
-
}
|
|
2670
|
-
const app = createApp();
|
|
2671
|
-
const vnode = toVNode(app);
|
|
2672
|
-
const stream = renderToStream(vnode);
|
|
2673
|
-
const html = await streamToString(stream);
|
|
2674
|
-
const css = collectCSS(themeCss, module, html);
|
|
2675
|
-
const ssrData = resolvedQueries.length > 0 ? resolvedQueries.map(({ key, data }) => ({ key, data })) : [];
|
|
2676
|
-
return {
|
|
2677
|
-
html,
|
|
2678
|
-
css,
|
|
2679
|
-
ssrData,
|
|
2680
|
-
headTags: themePreloadTags,
|
|
2681
|
-
discoveredRoutes: ctx.discoveredRoutes,
|
|
2682
|
-
matchedRoutePatterns: ctx.matchedRoutePatterns
|
|
2683
|
-
};
|
|
2684
|
-
} finally {
|
|
2685
|
-
clearGlobalSSRTimeout();
|
|
2686
|
-
}
|
|
2687
|
-
});
|
|
2688
|
-
}
|
|
2689
|
-
async function ssrStreamNavQueries(module, url, options) {
|
|
2690
|
-
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
2691
|
-
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
2692
|
-
const navTimeout = options?.navSsrTimeout ?? 5000;
|
|
2693
|
-
ensureDomShim();
|
|
2694
|
-
const ctx = createRequestContext(normalizedUrl);
|
|
2695
|
-
const queries = await ssrStorage.run(ctx, async () => {
|
|
2696
|
-
try {
|
|
2697
|
-
setGlobalSSRTimeout(ssrTimeout);
|
|
2698
|
-
const createApp = resolveAppFactory(module);
|
|
2699
|
-
createApp();
|
|
2700
|
-
const discovered = getSSRQueries();
|
|
2701
|
-
return discovered.map((q) => ({
|
|
2702
|
-
promise: q.promise,
|
|
2703
|
-
timeout: q.timeout || ssrTimeout,
|
|
2704
|
-
resolve: q.resolve,
|
|
2705
|
-
key: q.key
|
|
2706
|
-
}));
|
|
2707
|
-
} finally {
|
|
2708
|
-
clearGlobalSSRTimeout();
|
|
2709
|
-
}
|
|
2710
|
-
});
|
|
2711
|
-
if (queries.length === 0) {
|
|
2712
|
-
const encoder3 = new TextEncoder;
|
|
2713
|
-
return new ReadableStream({
|
|
2714
|
-
start(controller) {
|
|
2715
|
-
controller.enqueue(encoder3.encode(`event: done
|
|
2716
|
-
data: {}
|
|
2717
|
-
|
|
2718
|
-
`));
|
|
2719
|
-
controller.close();
|
|
2720
|
-
}
|
|
2721
|
-
});
|
|
2722
|
-
}
|
|
2723
|
-
const encoder2 = new TextEncoder;
|
|
2724
|
-
let remaining = queries.length;
|
|
2725
|
-
return new ReadableStream({
|
|
2726
|
-
start(controller) {
|
|
2727
|
-
let closed = false;
|
|
2728
|
-
function safeEnqueue(chunk) {
|
|
2729
|
-
if (closed)
|
|
2730
|
-
return;
|
|
2731
|
-
try {
|
|
2732
|
-
controller.enqueue(chunk);
|
|
2733
|
-
} catch {
|
|
2734
|
-
closed = true;
|
|
2735
|
-
}
|
|
2736
|
-
}
|
|
2737
|
-
function safeClose() {
|
|
2738
|
-
if (closed)
|
|
2739
|
-
return;
|
|
2740
|
-
closed = true;
|
|
2741
|
-
try {
|
|
2742
|
-
controller.close();
|
|
2743
|
-
} catch {}
|
|
2744
|
-
}
|
|
2745
|
-
function checkDone() {
|
|
2746
|
-
if (remaining === 0) {
|
|
2747
|
-
safeEnqueue(encoder2.encode(`event: done
|
|
2748
|
-
data: {}
|
|
2749
|
-
|
|
2750
|
-
`));
|
|
2751
|
-
safeClose();
|
|
2752
|
-
}
|
|
2753
|
-
}
|
|
2754
|
-
for (const { promise, resolve, key } of queries) {
|
|
2755
|
-
let settled = false;
|
|
2756
|
-
promise.then((data) => {
|
|
2757
|
-
if (settled)
|
|
2758
|
-
return;
|
|
2759
|
-
settled = true;
|
|
2760
|
-
resolve(data);
|
|
2761
|
-
const entry = { key, data };
|
|
2762
|
-
safeEnqueue(encoder2.encode(`event: data
|
|
2763
|
-
data: ${safeSerialize(entry)}
|
|
2764
|
-
|
|
2765
|
-
`));
|
|
2766
|
-
remaining--;
|
|
2767
|
-
checkDone();
|
|
2768
|
-
}, () => {
|
|
2769
|
-
if (settled)
|
|
2770
|
-
return;
|
|
2771
|
-
settled = true;
|
|
2772
|
-
remaining--;
|
|
2773
|
-
checkDone();
|
|
2774
|
-
});
|
|
2775
|
-
setTimeout(() => {
|
|
2776
|
-
if (settled)
|
|
2777
|
-
return;
|
|
2778
|
-
settled = true;
|
|
2779
|
-
remaining--;
|
|
2780
|
-
checkDone();
|
|
2781
|
-
}, navTimeout);
|
|
2782
|
-
}
|
|
2783
|
-
}
|
|
2784
|
-
});
|
|
2785
|
-
}
|
|
2786
|
-
|
|
2787
|
-
// src/ssr-session.ts
|
|
2788
|
-
function createSessionScript(session, nonce) {
|
|
2789
|
-
const json = JSON.stringify(session);
|
|
2790
|
-
const escaped = json.replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
2791
|
-
const nonceAttr = nonce ? ` nonce="${escapeAttr3(nonce)}"` : "";
|
|
2792
|
-
return `<script${nonceAttr}>window.__VERTZ_SESSION__=${escaped}</script>`;
|
|
2793
|
-
}
|
|
2794
|
-
function escapeAttr3(s) {
|
|
2795
|
-
return s.replace(/[&"'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
2796
|
-
}
|
|
2797
|
-
|
|
2798
2535
|
// src/ssr-manifest-prefetch.ts
|
|
2799
2536
|
function reconstructDescriptors(queries, routeParams, apiClient) {
|
|
2800
2537
|
if (!apiClient)
|
|
@@ -2884,6 +2621,40 @@ function resolveQueryBindings(bindings, routeParams) {
|
|
|
2884
2621
|
return resolved;
|
|
2885
2622
|
}
|
|
2886
2623
|
|
|
2624
|
+
// src/ssr-shared.ts
|
|
2625
|
+
import { compileTheme } from "@vertz/ui";
|
|
2626
|
+
import { EntityStore, MemoryCache, QueryEnvelopeStore } from "@vertz/ui/internals";
|
|
2627
|
+
var compiledThemeCache = new WeakMap;
|
|
2628
|
+
var compiledThemeWithMetricsCache = new WeakMap;
|
|
2629
|
+
function compileThemeCached(theme, fallbackMetrics) {
|
|
2630
|
+
const cache = fallbackMetrics ? compiledThemeWithMetricsCache : compiledThemeCache;
|
|
2631
|
+
const cached = cache.get(theme);
|
|
2632
|
+
if (cached)
|
|
2633
|
+
return cached;
|
|
2634
|
+
const compiled = compileTheme(theme, { fallbackMetrics });
|
|
2635
|
+
cache.set(theme, compiled);
|
|
2636
|
+
return compiled;
|
|
2637
|
+
}
|
|
2638
|
+
function createRequestContext(url) {
|
|
2639
|
+
return {
|
|
2640
|
+
url,
|
|
2641
|
+
adapter: createSSRAdapter(),
|
|
2642
|
+
subscriber: null,
|
|
2643
|
+
readValueCb: null,
|
|
2644
|
+
cleanupStack: [],
|
|
2645
|
+
batchDepth: 0,
|
|
2646
|
+
pendingEffects: new Map,
|
|
2647
|
+
contextScope: null,
|
|
2648
|
+
entityStore: new EntityStore,
|
|
2649
|
+
envelopeStore: new QueryEnvelopeStore,
|
|
2650
|
+
queryCache: new MemoryCache({ maxSize: Infinity }),
|
|
2651
|
+
inflight: new Map,
|
|
2652
|
+
queries: [],
|
|
2653
|
+
errors: [],
|
|
2654
|
+
cssTracker: new Set
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2887
2658
|
// src/ssr-route-matcher.ts
|
|
2888
2659
|
function matchUrlToPatterns(url, patterns, options) {
|
|
2889
2660
|
const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
|
|
@@ -2925,14 +2696,16 @@ function matchPattern(path, pattern, exact) {
|
|
|
2925
2696
|
return { pattern, params };
|
|
2926
2697
|
}
|
|
2927
2698
|
|
|
2699
|
+
// src/ssr-streaming-runtime.ts
|
|
2700
|
+
function safeSerialize(data) {
|
|
2701
|
+
return JSON.stringify(data).replace(/</g, "\\u003c");
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2928
2704
|
// src/ssr-single-pass.ts
|
|
2929
2705
|
async function ssrRenderSinglePass(module, url, options) {
|
|
2930
|
-
if (options?.prefetch === false) {
|
|
2931
|
-
return ssrRenderToString(module, url, options);
|
|
2932
|
-
}
|
|
2933
2706
|
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
2934
2707
|
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
2935
|
-
|
|
2708
|
+
ensureDomShim();
|
|
2936
2709
|
const zeroDiscoveryData = attemptZeroDiscovery(normalizedUrl, module, options, ssrTimeout);
|
|
2937
2710
|
if (zeroDiscoveryData) {
|
|
2938
2711
|
return renderWithPrefetchedData(module, normalizedUrl, zeroDiscoveryData, options);
|
|
@@ -2961,7 +2734,7 @@ async function ssrRenderSinglePass(module, url, options) {
|
|
|
2961
2734
|
return ssrStorage.run(renderCtx, async () => {
|
|
2962
2735
|
try {
|
|
2963
2736
|
setGlobalSSRTimeout(ssrTimeout);
|
|
2964
|
-
const createApp =
|
|
2737
|
+
const createApp = resolveAppFactory(module);
|
|
2965
2738
|
let themeCss = "";
|
|
2966
2739
|
let themePreloadTags = "";
|
|
2967
2740
|
if (module.theme) {
|
|
@@ -2977,7 +2750,7 @@ async function ssrRenderSinglePass(module, url, options) {
|
|
|
2977
2750
|
const vnode = toVNode(app);
|
|
2978
2751
|
const stream = renderToStream(vnode);
|
|
2979
2752
|
const html = await streamToString(stream);
|
|
2980
|
-
const css =
|
|
2753
|
+
const css = collectCSS(themeCss, module, html);
|
|
2981
2754
|
const ssrData = discoveredData.resolvedQueries.map(({ key, data }) => ({
|
|
2982
2755
|
key,
|
|
2983
2756
|
data
|
|
@@ -2995,59 +2768,75 @@ async function ssrRenderSinglePass(module, url, options) {
|
|
|
2995
2768
|
}
|
|
2996
2769
|
});
|
|
2997
2770
|
}
|
|
2998
|
-
async function
|
|
2999
|
-
|
|
2771
|
+
async function runQueryDiscovery(normalizedUrl, ssrTimeout, module, options) {
|
|
2772
|
+
ensureDomShim();
|
|
2773
|
+
const ctx = createRequestContext(normalizedUrl);
|
|
3000
2774
|
if (options?.ssrAuth) {
|
|
3001
|
-
|
|
2775
|
+
ctx.ssrAuth = options.ssrAuth;
|
|
3002
2776
|
}
|
|
3003
2777
|
if (options?.cookies) {
|
|
3004
|
-
|
|
2778
|
+
ctx.cookies = options.cookies;
|
|
3005
2779
|
}
|
|
3006
|
-
return ssrStorage.run(
|
|
2780
|
+
return ssrStorage.run(ctx, async () => {
|
|
3007
2781
|
try {
|
|
3008
2782
|
setGlobalSSRTimeout(ssrTimeout);
|
|
3009
|
-
const createApp =
|
|
2783
|
+
const createApp = resolveAppFactory(module);
|
|
3010
2784
|
createApp();
|
|
3011
|
-
if (
|
|
3012
|
-
return { redirect:
|
|
2785
|
+
if (ctx.ssrRedirect) {
|
|
2786
|
+
return { redirect: ctx.ssrRedirect, queries: [] };
|
|
3013
2787
|
}
|
|
3014
|
-
if (
|
|
3015
|
-
const entries = Array.from(
|
|
2788
|
+
if (ctx.pendingRouteComponents?.size) {
|
|
2789
|
+
const entries = Array.from(ctx.pendingRouteComponents.entries());
|
|
3016
2790
|
const results = await Promise.allSettled(entries.map(([route, promise]) => Promise.race([
|
|
3017
2791
|
promise.then((mod) => ({ route, factory: mod.default })),
|
|
3018
2792
|
new Promise((_, reject) => setTimeout(() => reject(new Error("lazy route timeout")), ssrTimeout))
|
|
3019
2793
|
])));
|
|
3020
|
-
|
|
2794
|
+
ctx.resolvedComponents = new Map;
|
|
3021
2795
|
for (const result of results) {
|
|
3022
2796
|
if (result.status === "fulfilled") {
|
|
3023
2797
|
const { route, factory } = result.value;
|
|
3024
|
-
|
|
2798
|
+
ctx.resolvedComponents.set(route, factory);
|
|
3025
2799
|
}
|
|
3026
2800
|
}
|
|
3027
|
-
|
|
2801
|
+
ctx.pendingRouteComponents = undefined;
|
|
3028
2802
|
}
|
|
3029
2803
|
const queries = getSSRQueries();
|
|
3030
|
-
const eligibleQueries = filterByEntityAccess(queries, options?.manifest?.entityAccess, options?.prefetchSession);
|
|
3031
|
-
const resolvedQueries = [];
|
|
3032
|
-
if (eligibleQueries.length > 0) {
|
|
3033
|
-
await Promise.allSettled(eligibleQueries.map(({ promise, timeout, resolve, key }) => Promise.race([
|
|
3034
|
-
promise.then((data) => {
|
|
3035
|
-
resolve(data);
|
|
3036
|
-
resolvedQueries.push({ key, data });
|
|
3037
|
-
return "resolved";
|
|
3038
|
-
}),
|
|
3039
|
-
new Promise((r) => setTimeout(r, timeout || ssrTimeout)).then(() => "timeout")
|
|
3040
|
-
])));
|
|
3041
|
-
}
|
|
3042
2804
|
return {
|
|
3043
|
-
|
|
3044
|
-
|
|
2805
|
+
queries: queries.map((q) => ({
|
|
2806
|
+
promise: q.promise,
|
|
2807
|
+
timeout: q.timeout || ssrTimeout,
|
|
2808
|
+
resolve: q.resolve,
|
|
2809
|
+
key: q.key
|
|
2810
|
+
})),
|
|
2811
|
+
resolvedComponents: ctx.resolvedComponents
|
|
3045
2812
|
};
|
|
3046
2813
|
} finally {
|
|
3047
2814
|
clearGlobalSSRTimeout();
|
|
3048
2815
|
}
|
|
3049
2816
|
});
|
|
3050
2817
|
}
|
|
2818
|
+
async function runDiscoveryPhase(normalizedUrl, ssrTimeout, module, options) {
|
|
2819
|
+
const discovery = await runQueryDiscovery(normalizedUrl, ssrTimeout, module, options);
|
|
2820
|
+
if ("redirect" in discovery) {
|
|
2821
|
+
return { redirect: discovery.redirect };
|
|
2822
|
+
}
|
|
2823
|
+
const eligibleQueries = filterByEntityAccess(discovery.queries, options?.manifest?.entityAccess, options?.prefetchSession);
|
|
2824
|
+
const resolvedQueries = [];
|
|
2825
|
+
if (eligibleQueries.length > 0) {
|
|
2826
|
+
await Promise.allSettled(eligibleQueries.map(({ promise, timeout, resolve, key }) => Promise.race([
|
|
2827
|
+
promise.then((data) => {
|
|
2828
|
+
resolve(data);
|
|
2829
|
+
resolvedQueries.push({ key, data });
|
|
2830
|
+
return "resolved";
|
|
2831
|
+
}),
|
|
2832
|
+
new Promise((r) => setTimeout(r, timeout)).then(() => "timeout")
|
|
2833
|
+
])));
|
|
2834
|
+
}
|
|
2835
|
+
return {
|
|
2836
|
+
resolvedQueries,
|
|
2837
|
+
resolvedComponents: discovery.resolvedComponents
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
3051
2840
|
function attemptZeroDiscovery(url, module, options, ssrTimeout) {
|
|
3052
2841
|
const manifest = options?.manifest;
|
|
3053
2842
|
if (!manifest?.routeEntries || !module.api)
|
|
@@ -3108,7 +2897,7 @@ async function renderWithPrefetchedData(module, normalizedUrl, prefetchedData, o
|
|
|
3108
2897
|
return ssrStorage.run(renderCtx, async () => {
|
|
3109
2898
|
try {
|
|
3110
2899
|
setGlobalSSRTimeout(ssrTimeout);
|
|
3111
|
-
const createApp =
|
|
2900
|
+
const createApp = resolveAppFactory(module);
|
|
3112
2901
|
let themeCss = "";
|
|
3113
2902
|
let themePreloadTags = "";
|
|
3114
2903
|
if (module.theme) {
|
|
@@ -3135,7 +2924,7 @@ async function renderWithPrefetchedData(module, normalizedUrl, prefetchedData, o
|
|
|
3135
2924
|
matchedRoutePatterns: renderCtx.matchedRoutePatterns
|
|
3136
2925
|
};
|
|
3137
2926
|
}
|
|
3138
|
-
const css =
|
|
2927
|
+
const css = collectCSS(themeCss, module, html);
|
|
3139
2928
|
const ssrData = data.resolvedQueries.map(({ key, data: d }) => ({
|
|
3140
2929
|
key,
|
|
3141
2930
|
data: d
|
|
@@ -3153,14 +2942,95 @@ async function renderWithPrefetchedData(module, normalizedUrl, prefetchedData, o
|
|
|
3153
2942
|
}
|
|
3154
2943
|
});
|
|
3155
2944
|
}
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
2945
|
+
async function ssrStreamNavQueries(module, url, options) {
|
|
2946
|
+
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
2947
|
+
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
2948
|
+
const navTimeout = options?.navSsrTimeout ?? 5000;
|
|
2949
|
+
const discovery = await runQueryDiscovery(normalizedUrl, ssrTimeout, module);
|
|
2950
|
+
if ("redirect" in discovery || discovery.queries.length === 0) {
|
|
2951
|
+
const encoder3 = new TextEncoder;
|
|
2952
|
+
return new ReadableStream({
|
|
2953
|
+
start(controller) {
|
|
2954
|
+
controller.enqueue(encoder3.encode(`event: done
|
|
2955
|
+
data: {}
|
|
2956
|
+
|
|
2957
|
+
`));
|
|
2958
|
+
controller.close();
|
|
2959
|
+
}
|
|
2960
|
+
});
|
|
2961
|
+
}
|
|
2962
|
+
const queries = discovery.queries;
|
|
2963
|
+
const encoder2 = new TextEncoder;
|
|
2964
|
+
let remaining = queries.length;
|
|
2965
|
+
return new ReadableStream({
|
|
2966
|
+
start(controller) {
|
|
2967
|
+
let closed = false;
|
|
2968
|
+
function safeEnqueue(chunk) {
|
|
2969
|
+
if (closed)
|
|
2970
|
+
return;
|
|
2971
|
+
try {
|
|
2972
|
+
controller.enqueue(chunk);
|
|
2973
|
+
} catch {
|
|
2974
|
+
closed = true;
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
function safeClose() {
|
|
2978
|
+
if (closed)
|
|
2979
|
+
return;
|
|
2980
|
+
closed = true;
|
|
2981
|
+
try {
|
|
2982
|
+
controller.close();
|
|
2983
|
+
} catch {}
|
|
2984
|
+
}
|
|
2985
|
+
function checkDone() {
|
|
2986
|
+
if (remaining === 0) {
|
|
2987
|
+
safeEnqueue(encoder2.encode(`event: done
|
|
2988
|
+
data: {}
|
|
2989
|
+
|
|
2990
|
+
`));
|
|
2991
|
+
safeClose();
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
for (const { promise, resolve, key } of queries) {
|
|
2995
|
+
let settled = false;
|
|
2996
|
+
promise.then((data) => {
|
|
2997
|
+
if (settled)
|
|
2998
|
+
return;
|
|
2999
|
+
settled = true;
|
|
3000
|
+
resolve(data);
|
|
3001
|
+
const entry = { key, data };
|
|
3002
|
+
safeEnqueue(encoder2.encode(`event: data
|
|
3003
|
+
data: ${safeSerialize(entry)}
|
|
3004
|
+
|
|
3005
|
+
`));
|
|
3006
|
+
remaining--;
|
|
3007
|
+
checkDone();
|
|
3008
|
+
}, () => {
|
|
3009
|
+
if (settled)
|
|
3010
|
+
return;
|
|
3011
|
+
settled = true;
|
|
3012
|
+
remaining--;
|
|
3013
|
+
checkDone();
|
|
3014
|
+
});
|
|
3015
|
+
setTimeout(() => {
|
|
3016
|
+
if (settled)
|
|
3017
|
+
return;
|
|
3018
|
+
settled = true;
|
|
3019
|
+
remaining--;
|
|
3020
|
+
checkDone();
|
|
3021
|
+
}, navTimeout);
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3026
|
+
var domShimInstalled = false;
|
|
3027
|
+
function ensureDomShim() {
|
|
3028
|
+
if (domShimInstalled && typeof document !== "undefined")
|
|
3159
3029
|
return;
|
|
3160
|
-
|
|
3030
|
+
domShimInstalled = true;
|
|
3161
3031
|
installDomShim();
|
|
3162
3032
|
}
|
|
3163
|
-
function
|
|
3033
|
+
function resolveAppFactory(module) {
|
|
3164
3034
|
const createApp = module.default || module.App;
|
|
3165
3035
|
if (typeof createApp !== "function") {
|
|
3166
3036
|
throw new Error("App entry must export a default function or named App function");
|
|
@@ -3208,7 +3078,7 @@ function extractMethodFromKey(key) {
|
|
|
3208
3078
|
const segments = cleanPath.split("/").filter(Boolean);
|
|
3209
3079
|
return segments.length > 1 ? "get" : "list";
|
|
3210
3080
|
}
|
|
3211
|
-
function
|
|
3081
|
+
function collectCSS(themeCss, module, renderedHtml) {
|
|
3212
3082
|
const alreadyIncluded = new Set;
|
|
3213
3083
|
if (themeCss)
|
|
3214
3084
|
alreadyIncluded.add(themeCss);
|