@vertz/ui-server 0.2.42 → 0.2.44
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 +4 -4
|
@@ -200,6 +200,47 @@ function resolveQueryBindings(bindings, routeParams) {
|
|
|
200
200
|
return resolved;
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
// src/ssr-route-matcher.ts
|
|
204
|
+
function matchUrlToPatterns(url, patterns, options) {
|
|
205
|
+
const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
|
|
206
|
+
const exact = options?.exact ?? false;
|
|
207
|
+
const matches = [];
|
|
208
|
+
for (const pattern of patterns) {
|
|
209
|
+
const result = matchPattern(path, pattern, exact);
|
|
210
|
+
if (result) {
|
|
211
|
+
matches.push(result);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
matches.sort((a, b) => {
|
|
215
|
+
const aSegments = a.pattern.split("/").length;
|
|
216
|
+
const bSegments = b.pattern.split("/").length;
|
|
217
|
+
return aSegments - bSegments;
|
|
218
|
+
});
|
|
219
|
+
return matches;
|
|
220
|
+
}
|
|
221
|
+
function matchPattern(path, pattern, exact) {
|
|
222
|
+
const pathSegments = path.split("/").filter(Boolean);
|
|
223
|
+
const patternSegments = pattern.split("/").filter(Boolean);
|
|
224
|
+
if (exact) {
|
|
225
|
+
if (patternSegments.length !== pathSegments.length)
|
|
226
|
+
return;
|
|
227
|
+
} else {
|
|
228
|
+
if (patternSegments.length > pathSegments.length)
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const params = {};
|
|
232
|
+
for (let i = 0;i < patternSegments.length; i++) {
|
|
233
|
+
const seg = patternSegments[i];
|
|
234
|
+
const val = pathSegments[i];
|
|
235
|
+
if (seg.startsWith(":")) {
|
|
236
|
+
params[seg.slice(1)] = val;
|
|
237
|
+
} else if (seg !== val) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return { pattern, params };
|
|
242
|
+
}
|
|
243
|
+
|
|
203
244
|
// src/slot-placeholder.ts
|
|
204
245
|
var slotCounter = 0;
|
|
205
246
|
function resetSlotCounter() {
|
|
@@ -332,10 +373,6 @@ function createSSRDataChunk(key, data, nonce) {
|
|
|
332
373
|
return `<script${nonceAttr}>window.__VERTZ_SSR_PUSH__(${safeSerialize(key)},${serialized})</script>`;
|
|
333
374
|
}
|
|
334
375
|
|
|
335
|
-
// src/ssr-render.ts
|
|
336
|
-
import { compileTheme } from "@vertz/ui";
|
|
337
|
-
import { EntityStore, MemoryCache, QueryEnvelopeStore } from "@vertz/ui/internals";
|
|
338
|
-
|
|
339
376
|
// src/css-filter.ts
|
|
340
377
|
function extractClassNamesFromHTML(html) {
|
|
341
378
|
const classes = new Set;
|
|
@@ -421,7 +458,9 @@ function filterCSSByHTML(html, cssStrings) {
|
|
|
421
458
|
return kept;
|
|
422
459
|
}
|
|
423
460
|
|
|
424
|
-
// src/ssr-
|
|
461
|
+
// src/ssr-shared.ts
|
|
462
|
+
import { compileTheme } from "@vertz/ui";
|
|
463
|
+
import { EntityStore, MemoryCache, QueryEnvelopeStore } from "@vertz/ui/internals";
|
|
425
464
|
var compiledThemeCache = new WeakMap;
|
|
426
465
|
var compiledThemeWithMetricsCache = new WeakMap;
|
|
427
466
|
function compileThemeCached(theme, fallbackMetrics) {
|
|
@@ -452,325 +491,12 @@ function createRequestContext(url) {
|
|
|
452
491
|
cssTracker: new Set
|
|
453
492
|
};
|
|
454
493
|
}
|
|
455
|
-
var domShimInstalled = false;
|
|
456
|
-
function ensureDomShim() {
|
|
457
|
-
if (domShimInstalled && typeof document !== "undefined")
|
|
458
|
-
return;
|
|
459
|
-
domShimInstalled = true;
|
|
460
|
-
installDomShim();
|
|
461
|
-
}
|
|
462
|
-
function resolveAppFactory(module) {
|
|
463
|
-
const createApp = module.default || module.App;
|
|
464
|
-
if (typeof createApp !== "function") {
|
|
465
|
-
throw new Error("App entry must export a default function or named App function");
|
|
466
|
-
}
|
|
467
|
-
return createApp;
|
|
468
|
-
}
|
|
469
|
-
function collectCSS(themeCss, module, renderedHtml) {
|
|
470
|
-
const alreadyIncluded = new Set;
|
|
471
|
-
if (themeCss)
|
|
472
|
-
alreadyIncluded.add(themeCss);
|
|
473
|
-
if (module.styles) {
|
|
474
|
-
for (const s of module.styles)
|
|
475
|
-
alreadyIncluded.add(s);
|
|
476
|
-
}
|
|
477
|
-
const ssrCtx = ssrStorage.getStore();
|
|
478
|
-
const tracker = ssrCtx?.cssTracker;
|
|
479
|
-
const useTracker = tracker && tracker.size > 0;
|
|
480
|
-
const rawComponentCss = useTracker ? Array.from(tracker) : module.getInjectedCSS?.() ?? [];
|
|
481
|
-
let componentCss = rawComponentCss.filter((s) => !alreadyIncluded.has(s));
|
|
482
|
-
if (!useTracker && componentCss.length > 0 && renderedHtml) {
|
|
483
|
-
componentCss = filterCSSByHTML(renderedHtml, componentCss);
|
|
484
|
-
}
|
|
485
|
-
const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
|
|
486
|
-
const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
|
|
487
|
-
`)}</style>` : "";
|
|
488
|
-
const componentTag = componentCss.length > 0 ? `<style data-vertz-css>${componentCss.join(`
|
|
489
|
-
`)}</style>` : "";
|
|
490
|
-
return [themeTag, globalTag, componentTag].filter(Boolean).join(`
|
|
491
|
-
`);
|
|
492
|
-
}
|
|
493
|
-
async function ssrRenderToString(module, url, options) {
|
|
494
|
-
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
495
|
-
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
496
|
-
ensureDomShim();
|
|
497
|
-
const ctx = createRequestContext(normalizedUrl);
|
|
498
|
-
if (options?.ssrAuth) {
|
|
499
|
-
ctx.ssrAuth = options.ssrAuth;
|
|
500
|
-
}
|
|
501
|
-
return ssrStorage.run(ctx, async () => {
|
|
502
|
-
try {
|
|
503
|
-
setGlobalSSRTimeout(ssrTimeout);
|
|
504
|
-
const createApp = resolveAppFactory(module);
|
|
505
|
-
let themeCss = "";
|
|
506
|
-
let themePreloadTags = "";
|
|
507
|
-
if (module.theme) {
|
|
508
|
-
try {
|
|
509
|
-
const compiled = compileThemeCached(module.theme, options?.fallbackMetrics);
|
|
510
|
-
themeCss = compiled.css;
|
|
511
|
-
themePreloadTags = compiled.preloadTags;
|
|
512
|
-
} catch (e) {
|
|
513
|
-
console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
createApp();
|
|
517
|
-
if (ctx.ssrRedirect) {
|
|
518
|
-
return {
|
|
519
|
-
html: "",
|
|
520
|
-
css: "",
|
|
521
|
-
ssrData: [],
|
|
522
|
-
headTags: "",
|
|
523
|
-
redirect: ctx.ssrRedirect
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
const store = ssrStorage.getStore();
|
|
527
|
-
if (store) {
|
|
528
|
-
if (store.pendingRouteComponents?.size) {
|
|
529
|
-
const entries = Array.from(store.pendingRouteComponents.entries());
|
|
530
|
-
const results = await Promise.allSettled(entries.map(([route, promise]) => Promise.race([
|
|
531
|
-
promise.then((mod) => ({ route, factory: mod.default })),
|
|
532
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("lazy route timeout")), ssrTimeout))
|
|
533
|
-
])));
|
|
534
|
-
store.resolvedComponents = new Map;
|
|
535
|
-
for (const result of results) {
|
|
536
|
-
if (result.status === "fulfilled") {
|
|
537
|
-
const { route, factory } = result.value;
|
|
538
|
-
store.resolvedComponents.set(route, factory);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
store.pendingRouteComponents = undefined;
|
|
542
|
-
}
|
|
543
|
-
if (!store.resolvedComponents) {
|
|
544
|
-
store.resolvedComponents = new Map;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
const queries = getSSRQueries();
|
|
548
|
-
const resolvedQueries = [];
|
|
549
|
-
if (queries.length > 0) {
|
|
550
|
-
await Promise.allSettled(queries.map(({ promise, timeout, resolve, key }) => Promise.race([
|
|
551
|
-
promise.then((data) => {
|
|
552
|
-
resolve(data);
|
|
553
|
-
resolvedQueries.push({ key, data });
|
|
554
|
-
return "resolved";
|
|
555
|
-
}),
|
|
556
|
-
new Promise((r) => setTimeout(r, timeout || ssrTimeout)).then(() => "timeout")
|
|
557
|
-
])));
|
|
558
|
-
if (store)
|
|
559
|
-
store.queries = [];
|
|
560
|
-
}
|
|
561
|
-
const app = createApp();
|
|
562
|
-
const vnode = toVNode(app);
|
|
563
|
-
const stream = renderToStream(vnode);
|
|
564
|
-
const html = await streamToString(stream);
|
|
565
|
-
const css = collectCSS(themeCss, module, html);
|
|
566
|
-
const ssrData = resolvedQueries.length > 0 ? resolvedQueries.map(({ key, data }) => ({ key, data })) : [];
|
|
567
|
-
return {
|
|
568
|
-
html,
|
|
569
|
-
css,
|
|
570
|
-
ssrData,
|
|
571
|
-
headTags: themePreloadTags,
|
|
572
|
-
discoveredRoutes: ctx.discoveredRoutes,
|
|
573
|
-
matchedRoutePatterns: ctx.matchedRoutePatterns
|
|
574
|
-
};
|
|
575
|
-
} finally {
|
|
576
|
-
clearGlobalSSRTimeout();
|
|
577
|
-
}
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
async function ssrDiscoverQueries(module, url, options) {
|
|
581
|
-
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
582
|
-
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
583
|
-
ensureDomShim();
|
|
584
|
-
const ctx = createRequestContext(normalizedUrl);
|
|
585
|
-
return ssrStorage.run(ctx, async () => {
|
|
586
|
-
try {
|
|
587
|
-
setGlobalSSRTimeout(ssrTimeout);
|
|
588
|
-
const createApp = resolveAppFactory(module);
|
|
589
|
-
createApp();
|
|
590
|
-
const queries = getSSRQueries();
|
|
591
|
-
const resolvedQueries = [];
|
|
592
|
-
const pendingKeys = [];
|
|
593
|
-
if (queries.length > 0) {
|
|
594
|
-
await Promise.allSettled(queries.map(({ promise, timeout, resolve, key }) => {
|
|
595
|
-
let settled = false;
|
|
596
|
-
return Promise.race([
|
|
597
|
-
promise.then((data) => {
|
|
598
|
-
if (settled)
|
|
599
|
-
return "late";
|
|
600
|
-
settled = true;
|
|
601
|
-
resolve(data);
|
|
602
|
-
resolvedQueries.push({ key, data });
|
|
603
|
-
return "resolved";
|
|
604
|
-
}),
|
|
605
|
-
new Promise((r) => setTimeout(r, timeout || ssrTimeout)).then(() => {
|
|
606
|
-
if (settled)
|
|
607
|
-
return "already-resolved";
|
|
608
|
-
settled = true;
|
|
609
|
-
pendingKeys.push(key);
|
|
610
|
-
return "timeout";
|
|
611
|
-
})
|
|
612
|
-
]);
|
|
613
|
-
}));
|
|
614
|
-
}
|
|
615
|
-
return {
|
|
616
|
-
resolved: resolvedQueries.map(({ key, data }) => ({
|
|
617
|
-
key,
|
|
618
|
-
data: JSON.parse(JSON.stringify(data))
|
|
619
|
-
})),
|
|
620
|
-
pending: pendingKeys
|
|
621
|
-
};
|
|
622
|
-
} finally {
|
|
623
|
-
clearGlobalSSRTimeout();
|
|
624
|
-
}
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
async function ssrStreamNavQueries(module, url, options) {
|
|
628
|
-
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
629
|
-
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
630
|
-
const navTimeout = options?.navSsrTimeout ?? 5000;
|
|
631
|
-
ensureDomShim();
|
|
632
|
-
const ctx = createRequestContext(normalizedUrl);
|
|
633
|
-
const queries = await ssrStorage.run(ctx, async () => {
|
|
634
|
-
try {
|
|
635
|
-
setGlobalSSRTimeout(ssrTimeout);
|
|
636
|
-
const createApp = resolveAppFactory(module);
|
|
637
|
-
createApp();
|
|
638
|
-
const discovered = getSSRQueries();
|
|
639
|
-
return discovered.map((q) => ({
|
|
640
|
-
promise: q.promise,
|
|
641
|
-
timeout: q.timeout || ssrTimeout,
|
|
642
|
-
resolve: q.resolve,
|
|
643
|
-
key: q.key
|
|
644
|
-
}));
|
|
645
|
-
} finally {
|
|
646
|
-
clearGlobalSSRTimeout();
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
|
-
if (queries.length === 0) {
|
|
650
|
-
const encoder3 = new TextEncoder;
|
|
651
|
-
return new ReadableStream({
|
|
652
|
-
start(controller) {
|
|
653
|
-
controller.enqueue(encoder3.encode(`event: done
|
|
654
|
-
data: {}
|
|
655
|
-
|
|
656
|
-
`));
|
|
657
|
-
controller.close();
|
|
658
|
-
}
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
const encoder2 = new TextEncoder;
|
|
662
|
-
let remaining = queries.length;
|
|
663
|
-
return new ReadableStream({
|
|
664
|
-
start(controller) {
|
|
665
|
-
let closed = false;
|
|
666
|
-
function safeEnqueue(chunk) {
|
|
667
|
-
if (closed)
|
|
668
|
-
return;
|
|
669
|
-
try {
|
|
670
|
-
controller.enqueue(chunk);
|
|
671
|
-
} catch {
|
|
672
|
-
closed = true;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
function safeClose() {
|
|
676
|
-
if (closed)
|
|
677
|
-
return;
|
|
678
|
-
closed = true;
|
|
679
|
-
try {
|
|
680
|
-
controller.close();
|
|
681
|
-
} catch {}
|
|
682
|
-
}
|
|
683
|
-
function checkDone() {
|
|
684
|
-
if (remaining === 0) {
|
|
685
|
-
safeEnqueue(encoder2.encode(`event: done
|
|
686
|
-
data: {}
|
|
687
|
-
|
|
688
|
-
`));
|
|
689
|
-
safeClose();
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
for (const { promise, resolve, key } of queries) {
|
|
693
|
-
let settled = false;
|
|
694
|
-
promise.then((data) => {
|
|
695
|
-
if (settled)
|
|
696
|
-
return;
|
|
697
|
-
settled = true;
|
|
698
|
-
resolve(data);
|
|
699
|
-
const entry = { key, data };
|
|
700
|
-
safeEnqueue(encoder2.encode(`event: data
|
|
701
|
-
data: ${safeSerialize(entry)}
|
|
702
|
-
|
|
703
|
-
`));
|
|
704
|
-
remaining--;
|
|
705
|
-
checkDone();
|
|
706
|
-
}, () => {
|
|
707
|
-
if (settled)
|
|
708
|
-
return;
|
|
709
|
-
settled = true;
|
|
710
|
-
remaining--;
|
|
711
|
-
checkDone();
|
|
712
|
-
});
|
|
713
|
-
setTimeout(() => {
|
|
714
|
-
if (settled)
|
|
715
|
-
return;
|
|
716
|
-
settled = true;
|
|
717
|
-
remaining--;
|
|
718
|
-
checkDone();
|
|
719
|
-
}, navTimeout);
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
});
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// src/ssr-route-matcher.ts
|
|
726
|
-
function matchUrlToPatterns(url, patterns, options) {
|
|
727
|
-
const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
|
|
728
|
-
const exact = options?.exact ?? false;
|
|
729
|
-
const matches = [];
|
|
730
|
-
for (const pattern of patterns) {
|
|
731
|
-
const result = matchPattern(path, pattern, exact);
|
|
732
|
-
if (result) {
|
|
733
|
-
matches.push(result);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
matches.sort((a, b) => {
|
|
737
|
-
const aSegments = a.pattern.split("/").length;
|
|
738
|
-
const bSegments = b.pattern.split("/").length;
|
|
739
|
-
return aSegments - bSegments;
|
|
740
|
-
});
|
|
741
|
-
return matches;
|
|
742
|
-
}
|
|
743
|
-
function matchPattern(path, pattern, exact) {
|
|
744
|
-
const pathSegments = path.split("/").filter(Boolean);
|
|
745
|
-
const patternSegments = pattern.split("/").filter(Boolean);
|
|
746
|
-
if (exact) {
|
|
747
|
-
if (patternSegments.length !== pathSegments.length)
|
|
748
|
-
return;
|
|
749
|
-
} else {
|
|
750
|
-
if (patternSegments.length > pathSegments.length)
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
const params = {};
|
|
754
|
-
for (let i = 0;i < patternSegments.length; i++) {
|
|
755
|
-
const seg = patternSegments[i];
|
|
756
|
-
const val = pathSegments[i];
|
|
757
|
-
if (seg.startsWith(":")) {
|
|
758
|
-
params[seg.slice(1)] = val;
|
|
759
|
-
} else if (seg !== val) {
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
return { pattern, params };
|
|
764
|
-
}
|
|
765
494
|
|
|
766
495
|
// src/ssr-single-pass.ts
|
|
767
496
|
async function ssrRenderSinglePass(module, url, options) {
|
|
768
|
-
if (options?.prefetch === false) {
|
|
769
|
-
return ssrRenderToString(module, url, options);
|
|
770
|
-
}
|
|
771
497
|
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
772
498
|
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
773
|
-
|
|
499
|
+
ensureDomShim();
|
|
774
500
|
const zeroDiscoveryData = attemptZeroDiscovery(normalizedUrl, module, options, ssrTimeout);
|
|
775
501
|
if (zeroDiscoveryData) {
|
|
776
502
|
return renderWithPrefetchedData(module, normalizedUrl, zeroDiscoveryData, options);
|
|
@@ -799,7 +525,7 @@ async function ssrRenderSinglePass(module, url, options) {
|
|
|
799
525
|
return ssrStorage.run(renderCtx, async () => {
|
|
800
526
|
try {
|
|
801
527
|
setGlobalSSRTimeout(ssrTimeout);
|
|
802
|
-
const createApp =
|
|
528
|
+
const createApp = resolveAppFactory(module);
|
|
803
529
|
let themeCss = "";
|
|
804
530
|
let themePreloadTags = "";
|
|
805
531
|
if (module.theme) {
|
|
@@ -815,7 +541,7 @@ async function ssrRenderSinglePass(module, url, options) {
|
|
|
815
541
|
const vnode = toVNode(app);
|
|
816
542
|
const stream = renderToStream(vnode);
|
|
817
543
|
const html = await streamToString(stream);
|
|
818
|
-
const css =
|
|
544
|
+
const css = collectCSS(themeCss, module, html);
|
|
819
545
|
const ssrData = discoveredData.resolvedQueries.map(({ key, data }) => ({
|
|
820
546
|
key,
|
|
821
547
|
data
|
|
@@ -836,7 +562,7 @@ async function ssrRenderSinglePass(module, url, options) {
|
|
|
836
562
|
async function ssrRenderProgressive(module, url, options) {
|
|
837
563
|
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
838
564
|
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
839
|
-
|
|
565
|
+
ensureDomShim();
|
|
840
566
|
const discoveryResult = await runDiscoveryPhase(normalizedUrl, ssrTimeout, module, options);
|
|
841
567
|
if ("redirect" in discoveryResult) {
|
|
842
568
|
return {
|
|
@@ -860,7 +586,7 @@ async function ssrRenderProgressive(module, url, options) {
|
|
|
860
586
|
return ssrStorage.run(renderCtx, () => {
|
|
861
587
|
try {
|
|
862
588
|
setGlobalSSRTimeout(ssrTimeout);
|
|
863
|
-
const createApp =
|
|
589
|
+
const createApp = resolveAppFactory(module);
|
|
864
590
|
let themeCss = "";
|
|
865
591
|
let themePreloadTags = "";
|
|
866
592
|
if (module.theme) {
|
|
@@ -875,7 +601,7 @@ async function ssrRenderProgressive(module, url, options) {
|
|
|
875
601
|
const app = createApp();
|
|
876
602
|
const vnode = toVNode(app);
|
|
877
603
|
const renderStream = renderToStream(vnode);
|
|
878
|
-
const css =
|
|
604
|
+
const css = collectCSS(themeCss, module, "");
|
|
879
605
|
const ssrData = discoveryResult.resolvedQueries.map(({ key, data }) => ({
|
|
880
606
|
key,
|
|
881
607
|
data
|
|
@@ -892,59 +618,75 @@ async function ssrRenderProgressive(module, url, options) {
|
|
|
892
618
|
}
|
|
893
619
|
});
|
|
894
620
|
}
|
|
895
|
-
async function
|
|
896
|
-
|
|
621
|
+
async function runQueryDiscovery(normalizedUrl, ssrTimeout, module, options) {
|
|
622
|
+
ensureDomShim();
|
|
623
|
+
const ctx = createRequestContext(normalizedUrl);
|
|
897
624
|
if (options?.ssrAuth) {
|
|
898
|
-
|
|
625
|
+
ctx.ssrAuth = options.ssrAuth;
|
|
899
626
|
}
|
|
900
627
|
if (options?.cookies) {
|
|
901
|
-
|
|
628
|
+
ctx.cookies = options.cookies;
|
|
902
629
|
}
|
|
903
|
-
return ssrStorage.run(
|
|
630
|
+
return ssrStorage.run(ctx, async () => {
|
|
904
631
|
try {
|
|
905
632
|
setGlobalSSRTimeout(ssrTimeout);
|
|
906
|
-
const createApp =
|
|
633
|
+
const createApp = resolveAppFactory(module);
|
|
907
634
|
createApp();
|
|
908
|
-
if (
|
|
909
|
-
return { redirect:
|
|
635
|
+
if (ctx.ssrRedirect) {
|
|
636
|
+
return { redirect: ctx.ssrRedirect, queries: [] };
|
|
910
637
|
}
|
|
911
|
-
if (
|
|
912
|
-
const entries = Array.from(
|
|
638
|
+
if (ctx.pendingRouteComponents?.size) {
|
|
639
|
+
const entries = Array.from(ctx.pendingRouteComponents.entries());
|
|
913
640
|
const results = await Promise.allSettled(entries.map(([route, promise]) => Promise.race([
|
|
914
641
|
promise.then((mod) => ({ route, factory: mod.default })),
|
|
915
642
|
new Promise((_, reject) => setTimeout(() => reject(new Error("lazy route timeout")), ssrTimeout))
|
|
916
643
|
])));
|
|
917
|
-
|
|
644
|
+
ctx.resolvedComponents = new Map;
|
|
918
645
|
for (const result of results) {
|
|
919
646
|
if (result.status === "fulfilled") {
|
|
920
647
|
const { route, factory } = result.value;
|
|
921
|
-
|
|
648
|
+
ctx.resolvedComponents.set(route, factory);
|
|
922
649
|
}
|
|
923
650
|
}
|
|
924
|
-
|
|
651
|
+
ctx.pendingRouteComponents = undefined;
|
|
925
652
|
}
|
|
926
653
|
const queries = getSSRQueries();
|
|
927
|
-
const eligibleQueries = filterByEntityAccess(queries, options?.manifest?.entityAccess, options?.prefetchSession);
|
|
928
|
-
const resolvedQueries = [];
|
|
929
|
-
if (eligibleQueries.length > 0) {
|
|
930
|
-
await Promise.allSettled(eligibleQueries.map(({ promise, timeout, resolve, key }) => Promise.race([
|
|
931
|
-
promise.then((data) => {
|
|
932
|
-
resolve(data);
|
|
933
|
-
resolvedQueries.push({ key, data });
|
|
934
|
-
return "resolved";
|
|
935
|
-
}),
|
|
936
|
-
new Promise((r) => setTimeout(r, timeout || ssrTimeout)).then(() => "timeout")
|
|
937
|
-
])));
|
|
938
|
-
}
|
|
939
654
|
return {
|
|
940
|
-
|
|
941
|
-
|
|
655
|
+
queries: queries.map((q) => ({
|
|
656
|
+
promise: q.promise,
|
|
657
|
+
timeout: q.timeout || ssrTimeout,
|
|
658
|
+
resolve: q.resolve,
|
|
659
|
+
key: q.key
|
|
660
|
+
})),
|
|
661
|
+
resolvedComponents: ctx.resolvedComponents
|
|
942
662
|
};
|
|
943
663
|
} finally {
|
|
944
664
|
clearGlobalSSRTimeout();
|
|
945
665
|
}
|
|
946
666
|
});
|
|
947
667
|
}
|
|
668
|
+
async function runDiscoveryPhase(normalizedUrl, ssrTimeout, module, options) {
|
|
669
|
+
const discovery = await runQueryDiscovery(normalizedUrl, ssrTimeout, module, options);
|
|
670
|
+
if ("redirect" in discovery) {
|
|
671
|
+
return { redirect: discovery.redirect };
|
|
672
|
+
}
|
|
673
|
+
const eligibleQueries = filterByEntityAccess(discovery.queries, options?.manifest?.entityAccess, options?.prefetchSession);
|
|
674
|
+
const resolvedQueries = [];
|
|
675
|
+
if (eligibleQueries.length > 0) {
|
|
676
|
+
await Promise.allSettled(eligibleQueries.map(({ promise, timeout, resolve, key }) => Promise.race([
|
|
677
|
+
promise.then((data) => {
|
|
678
|
+
resolve(data);
|
|
679
|
+
resolvedQueries.push({ key, data });
|
|
680
|
+
return "resolved";
|
|
681
|
+
}),
|
|
682
|
+
new Promise((r) => setTimeout(r, timeout)).then(() => "timeout")
|
|
683
|
+
])));
|
|
684
|
+
}
|
|
685
|
+
return {
|
|
686
|
+
resolvedQueries,
|
|
687
|
+
resolvedComponents: discovery.resolvedComponents
|
|
688
|
+
};
|
|
689
|
+
}
|
|
948
690
|
function attemptZeroDiscovery(url, module, options, ssrTimeout) {
|
|
949
691
|
const manifest = options?.manifest;
|
|
950
692
|
if (!manifest?.routeEntries || !module.api)
|
|
@@ -1005,7 +747,7 @@ async function renderWithPrefetchedData(module, normalizedUrl, prefetchedData, o
|
|
|
1005
747
|
return ssrStorage.run(renderCtx, async () => {
|
|
1006
748
|
try {
|
|
1007
749
|
setGlobalSSRTimeout(ssrTimeout);
|
|
1008
|
-
const createApp =
|
|
750
|
+
const createApp = resolveAppFactory(module);
|
|
1009
751
|
let themeCss = "";
|
|
1010
752
|
let themePreloadTags = "";
|
|
1011
753
|
if (module.theme) {
|
|
@@ -1032,7 +774,7 @@ async function renderWithPrefetchedData(module, normalizedUrl, prefetchedData, o
|
|
|
1032
774
|
matchedRoutePatterns: renderCtx.matchedRoutePatterns
|
|
1033
775
|
};
|
|
1034
776
|
}
|
|
1035
|
-
const css =
|
|
777
|
+
const css = collectCSS(themeCss, module, html);
|
|
1036
778
|
const ssrData = data.resolvedQueries.map(({ key, data: d }) => ({
|
|
1037
779
|
key,
|
|
1038
780
|
data: d
|
|
@@ -1050,14 +792,95 @@ async function renderWithPrefetchedData(module, normalizedUrl, prefetchedData, o
|
|
|
1050
792
|
}
|
|
1051
793
|
});
|
|
1052
794
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
795
|
+
async function ssrStreamNavQueries(module, url, options) {
|
|
796
|
+
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
797
|
+
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
798
|
+
const navTimeout = options?.navSsrTimeout ?? 5000;
|
|
799
|
+
const discovery = await runQueryDiscovery(normalizedUrl, ssrTimeout, module);
|
|
800
|
+
if ("redirect" in discovery || discovery.queries.length === 0) {
|
|
801
|
+
const encoder3 = new TextEncoder;
|
|
802
|
+
return new ReadableStream({
|
|
803
|
+
start(controller) {
|
|
804
|
+
controller.enqueue(encoder3.encode(`event: done
|
|
805
|
+
data: {}
|
|
806
|
+
|
|
807
|
+
`));
|
|
808
|
+
controller.close();
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
const queries = discovery.queries;
|
|
813
|
+
const encoder2 = new TextEncoder;
|
|
814
|
+
let remaining = queries.length;
|
|
815
|
+
return new ReadableStream({
|
|
816
|
+
start(controller) {
|
|
817
|
+
let closed = false;
|
|
818
|
+
function safeEnqueue(chunk) {
|
|
819
|
+
if (closed)
|
|
820
|
+
return;
|
|
821
|
+
try {
|
|
822
|
+
controller.enqueue(chunk);
|
|
823
|
+
} catch {
|
|
824
|
+
closed = true;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
function safeClose() {
|
|
828
|
+
if (closed)
|
|
829
|
+
return;
|
|
830
|
+
closed = true;
|
|
831
|
+
try {
|
|
832
|
+
controller.close();
|
|
833
|
+
} catch {}
|
|
834
|
+
}
|
|
835
|
+
function checkDone() {
|
|
836
|
+
if (remaining === 0) {
|
|
837
|
+
safeEnqueue(encoder2.encode(`event: done
|
|
838
|
+
data: {}
|
|
839
|
+
|
|
840
|
+
`));
|
|
841
|
+
safeClose();
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
for (const { promise, resolve, key } of queries) {
|
|
845
|
+
let settled = false;
|
|
846
|
+
promise.then((data) => {
|
|
847
|
+
if (settled)
|
|
848
|
+
return;
|
|
849
|
+
settled = true;
|
|
850
|
+
resolve(data);
|
|
851
|
+
const entry = { key, data };
|
|
852
|
+
safeEnqueue(encoder2.encode(`event: data
|
|
853
|
+
data: ${safeSerialize(entry)}
|
|
854
|
+
|
|
855
|
+
`));
|
|
856
|
+
remaining--;
|
|
857
|
+
checkDone();
|
|
858
|
+
}, () => {
|
|
859
|
+
if (settled)
|
|
860
|
+
return;
|
|
861
|
+
settled = true;
|
|
862
|
+
remaining--;
|
|
863
|
+
checkDone();
|
|
864
|
+
});
|
|
865
|
+
setTimeout(() => {
|
|
866
|
+
if (settled)
|
|
867
|
+
return;
|
|
868
|
+
settled = true;
|
|
869
|
+
remaining--;
|
|
870
|
+
checkDone();
|
|
871
|
+
}, navTimeout);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
var domShimInstalled = false;
|
|
877
|
+
function ensureDomShim() {
|
|
878
|
+
if (domShimInstalled && typeof document !== "undefined")
|
|
1056
879
|
return;
|
|
1057
|
-
|
|
880
|
+
domShimInstalled = true;
|
|
1058
881
|
installDomShim();
|
|
1059
882
|
}
|
|
1060
|
-
function
|
|
883
|
+
function resolveAppFactory(module) {
|
|
1061
884
|
const createApp = module.default || module.App;
|
|
1062
885
|
if (typeof createApp !== "function") {
|
|
1063
886
|
throw new Error("App entry must export a default function or named App function");
|
|
@@ -1105,7 +928,7 @@ function extractMethodFromKey(key) {
|
|
|
1105
928
|
const segments = cleanPath.split("/").filter(Boolean);
|
|
1106
929
|
return segments.length > 1 ? "get" : "list";
|
|
1107
930
|
}
|
|
1108
|
-
function
|
|
931
|
+
function collectCSS(themeCss, module, renderedHtml) {
|
|
1109
932
|
const alreadyIncluded = new Set;
|
|
1110
933
|
if (themeCss)
|
|
1111
934
|
alreadyIncluded.add(themeCss);
|
|
@@ -1146,7 +969,7 @@ function createHoles(holeNames, module, url, queryCache, ssrAuth) {
|
|
|
1146
969
|
}
|
|
1147
970
|
holeCtx.resolvedComponents = new Map;
|
|
1148
971
|
return ssrStorage.run(holeCtx, () => {
|
|
1149
|
-
|
|
972
|
+
ensureDomShim2();
|
|
1150
973
|
const factory = resolveHoleComponent(module, name);
|
|
1151
974
|
if (!factory) {
|
|
1152
975
|
return `<!-- AOT hole: ${name} not found -->`;
|
|
@@ -1345,11 +1168,11 @@ function isAotDebugEnabled() {
|
|
|
1345
1168
|
return false;
|
|
1346
1169
|
return env === "1" || env.split(",").includes("aot");
|
|
1347
1170
|
}
|
|
1348
|
-
var
|
|
1349
|
-
function
|
|
1350
|
-
if (
|
|
1171
|
+
var domShimInstalled2 = false;
|
|
1172
|
+
function ensureDomShim2() {
|
|
1173
|
+
if (domShimInstalled2 && typeof document !== "undefined")
|
|
1351
1174
|
return;
|
|
1352
|
-
|
|
1175
|
+
domShimInstalled2 = true;
|
|
1353
1176
|
installDomShim();
|
|
1354
1177
|
}
|
|
1355
1178
|
function mergeRouteCss(routeCss, appCss) {
|
|
@@ -1696,4 +1519,4 @@ function replaceAppDivContent(template, appHtml) {
|
|
|
1696
1519
|
return template.slice(0, contentStart) + appHtml + template.slice(i);
|
|
1697
1520
|
}
|
|
1698
1521
|
|
|
1699
|
-
export { escapeHtml, escapeAttr, serializeToHtml, toPrefetchSession, evaluateAccessRule, reconstructDescriptors, resetSlotCounter, createSlotPlaceholder, encodeChunk, streamToString, collectStreamChunks, createTemplateChunk, renderToStream, safeSerialize, getStreamingRuntimeScript, createSSRDataChunk,
|
|
1522
|
+
export { escapeHtml, escapeAttr, serializeToHtml, toPrefetchSession, evaluateAccessRule, reconstructDescriptors, compileThemeCached, createRequestContext, matchUrlToPatterns, resetSlotCounter, createSlotPlaceholder, encodeChunk, streamToString, collectStreamChunks, createTemplateChunk, renderToStream, safeSerialize, getStreamingRuntimeScript, createSSRDataChunk, ssrRenderSinglePass, ssrRenderProgressive, ssrStreamNavQueries, createHoles, ssrRenderAot, isAotDebugEnabled, clearRouteCssCache, getAccessSetForSSR, createAccessSetScript, createSessionScript, resolveRouteModulepreload, precomputeHandlerState, resolveSession, injectIntoTemplate };
|