@vertz/ui-server 0.2.24 → 0.2.26
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 +894 -5
- package/dist/bun-plugin/index.d.ts +1 -1
- package/dist/dom-shim/index.d.ts +4 -0
- package/dist/dom-shim/index.js +1 -1
- package/dist/index.d.ts +463 -31
- package/dist/index.js +1089 -7
- package/dist/shared/{chunk-zs75v8qj.js → chunk-gcwqkynf.js} +4 -0
- package/dist/shared/{chunk-g0zqrb60.js → chunk-yr65qdge.js} +1 -1
- package/dist/ssr/index.d.ts +2 -0
- package/dist/ssr/index.js +2 -2
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -19,20 +19,78 @@ import {
|
|
|
19
19
|
ssrDiscoverQueries,
|
|
20
20
|
ssrRenderToString,
|
|
21
21
|
streamToString
|
|
22
|
-
} from "./shared/chunk-
|
|
22
|
+
} from "./shared/chunk-yr65qdge.js";
|
|
23
23
|
import {
|
|
24
24
|
clearGlobalSSRTimeout,
|
|
25
25
|
createSSRAdapter,
|
|
26
26
|
getGlobalSSRTimeout,
|
|
27
27
|
getSSRQueries,
|
|
28
28
|
getSSRUrl,
|
|
29
|
+
installDomShim,
|
|
29
30
|
isInSSR,
|
|
30
31
|
rawHtml,
|
|
31
32
|
registerSSRQuery,
|
|
32
33
|
setGlobalSSRTimeout,
|
|
33
|
-
ssrStorage
|
|
34
|
-
|
|
34
|
+
ssrStorage,
|
|
35
|
+
toVNode
|
|
36
|
+
} from "./shared/chunk-gcwqkynf.js";
|
|
35
37
|
|
|
38
|
+
// src/aot-manifest-build.ts
|
|
39
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
40
|
+
import { join } from "node:path";
|
|
41
|
+
import { compileForSSRAot } from "@vertz/ui-compiler";
|
|
42
|
+
function generateAotBuildManifest(srcDir) {
|
|
43
|
+
const components = {};
|
|
44
|
+
const classificationLog = [];
|
|
45
|
+
const tsxFiles = collectTsxFiles(srcDir);
|
|
46
|
+
for (const filePath of tsxFiles) {
|
|
47
|
+
try {
|
|
48
|
+
const source = readFileSync(filePath, "utf-8");
|
|
49
|
+
const result = compileForSSRAot(source, { filename: filePath });
|
|
50
|
+
for (const comp of result.components) {
|
|
51
|
+
components[comp.name] = {
|
|
52
|
+
tier: comp.tier,
|
|
53
|
+
holes: comp.holes
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
} catch (e) {
|
|
57
|
+
classificationLog.push(`⚠ ${filePath}: ${e instanceof Error ? e.message : "compilation failed"}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
let aotCount = 0;
|
|
61
|
+
let runtimeCount = 0;
|
|
62
|
+
for (const [name, entry] of Object.entries(components)) {
|
|
63
|
+
let line = `${name}: ${entry.tier}`;
|
|
64
|
+
if (entry.holes.length > 0) {
|
|
65
|
+
const holeLabel = entry.holes.length === 1 ? "hole" : "holes";
|
|
66
|
+
line += `, ${entry.holes.length} ${holeLabel} (${entry.holes.join(", ")})`;
|
|
67
|
+
}
|
|
68
|
+
classificationLog.push(line);
|
|
69
|
+
if (entry.tier === "runtime-fallback") {
|
|
70
|
+
runtimeCount++;
|
|
71
|
+
} else {
|
|
72
|
+
aotCount++;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const total = aotCount + runtimeCount;
|
|
76
|
+
if (total > 0) {
|
|
77
|
+
const pct = Math.round(aotCount / total * 100);
|
|
78
|
+
classificationLog.push(`Coverage: ${aotCount}/${total} components (${pct}%)`);
|
|
79
|
+
}
|
|
80
|
+
return { components, classificationLog };
|
|
81
|
+
}
|
|
82
|
+
function collectTsxFiles(dir) {
|
|
83
|
+
const files = [];
|
|
84
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
85
|
+
const fullPath = join(dir, entry.name);
|
|
86
|
+
if (entry.isDirectory()) {
|
|
87
|
+
files.push(...collectTsxFiles(fullPath));
|
|
88
|
+
} else if (entry.name.endsWith(".tsx")) {
|
|
89
|
+
files.push(fullPath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return files;
|
|
93
|
+
}
|
|
36
94
|
// src/asset-pipeline.ts
|
|
37
95
|
function renderAssetTags(assets) {
|
|
38
96
|
if (assets.length === 0)
|
|
@@ -61,7 +119,7 @@ function inlineCriticalCss(css) {
|
|
|
61
119
|
// src/font-metrics.ts
|
|
62
120
|
import { access as fsAccess } from "node:fs/promises";
|
|
63
121
|
import { readFile } from "node:fs/promises";
|
|
64
|
-
import { join } from "node:path";
|
|
122
|
+
import { join as join2 } from "node:path";
|
|
65
123
|
import { fromBuffer } from "@capsizecss/unpack";
|
|
66
124
|
var SYSTEM_FONT_METRICS = {
|
|
67
125
|
Arial: {
|
|
@@ -130,12 +188,12 @@ function getPrimarySrcPath(descriptor) {
|
|
|
130
188
|
}
|
|
131
189
|
async function resolveFilePath(urlPath, rootDir) {
|
|
132
190
|
const cleaned = urlPath.startsWith("/") ? urlPath.slice(1) : urlPath;
|
|
133
|
-
const direct =
|
|
191
|
+
const direct = join2(rootDir, cleaned);
|
|
134
192
|
try {
|
|
135
193
|
await fsAccess(direct);
|
|
136
194
|
return direct;
|
|
137
195
|
} catch {
|
|
138
|
-
return
|
|
196
|
+
return join2(rootDir, "public", cleaned);
|
|
139
197
|
}
|
|
140
198
|
}
|
|
141
199
|
async function extractFontMetrics(fonts, rootDir) {
|
|
@@ -425,6 +483,897 @@ async function renderToHTML(appOrOptions, maybeOptions) {
|
|
|
425
483
|
}
|
|
426
484
|
});
|
|
427
485
|
}
|
|
486
|
+
// src/ssr-access-evaluator.ts
|
|
487
|
+
function toPrefetchSession(ssrAuth) {
|
|
488
|
+
if (!ssrAuth || ssrAuth.status !== "authenticated" || !ssrAuth.user) {
|
|
489
|
+
return { status: "unauthenticated" };
|
|
490
|
+
}
|
|
491
|
+
const roles = ssrAuth.user.role ? [ssrAuth.user.role] : undefined;
|
|
492
|
+
return {
|
|
493
|
+
status: "authenticated",
|
|
494
|
+
roles,
|
|
495
|
+
tenantId: ssrAuth.user.tenantId
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function evaluateAccessRule(rule, session) {
|
|
499
|
+
switch (rule.type) {
|
|
500
|
+
case "public":
|
|
501
|
+
return true;
|
|
502
|
+
case "authenticated":
|
|
503
|
+
return session.status === "authenticated";
|
|
504
|
+
case "role":
|
|
505
|
+
if (session.status !== "authenticated")
|
|
506
|
+
return false;
|
|
507
|
+
return session.roles?.some((r) => rule.roles.includes(r)) === true;
|
|
508
|
+
case "entitlement":
|
|
509
|
+
if (session.status !== "authenticated")
|
|
510
|
+
return false;
|
|
511
|
+
return session.entitlements?.[rule.value] === true;
|
|
512
|
+
case "where":
|
|
513
|
+
return true;
|
|
514
|
+
case "fva":
|
|
515
|
+
return session.status === "authenticated";
|
|
516
|
+
case "deny":
|
|
517
|
+
return false;
|
|
518
|
+
case "all":
|
|
519
|
+
return rule.rules.every((r) => evaluateAccessRule(r, session));
|
|
520
|
+
case "any":
|
|
521
|
+
return rule.rules.some((r) => evaluateAccessRule(r, session));
|
|
522
|
+
default:
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
// src/ssr-aot-diagnostics.ts
|
|
527
|
+
var MAX_DIVERGENCES = 20;
|
|
528
|
+
|
|
529
|
+
class AotDiagnostics {
|
|
530
|
+
_components = new Map;
|
|
531
|
+
_divergences = [];
|
|
532
|
+
recordCompilation(components) {
|
|
533
|
+
for (const comp of components) {
|
|
534
|
+
this._components.set(comp.name, {
|
|
535
|
+
tier: comp.tier,
|
|
536
|
+
holes: comp.holes
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
recordDivergence(component, aotHtml, domHtml) {
|
|
541
|
+
this._divergences.push({
|
|
542
|
+
component,
|
|
543
|
+
aotHtml,
|
|
544
|
+
domHtml,
|
|
545
|
+
timestamp: new Date().toISOString()
|
|
546
|
+
});
|
|
547
|
+
if (this._divergences.length > MAX_DIVERGENCES) {
|
|
548
|
+
this._divergences = this._divergences.slice(this._divergences.length - MAX_DIVERGENCES);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
clear() {
|
|
552
|
+
this._components.clear();
|
|
553
|
+
this._divergences = [];
|
|
554
|
+
}
|
|
555
|
+
clearComponents() {
|
|
556
|
+
this._components.clear();
|
|
557
|
+
}
|
|
558
|
+
getClassificationLog() {
|
|
559
|
+
const lines = [];
|
|
560
|
+
for (const [name, comp] of this._components) {
|
|
561
|
+
let line = `${name}: ${comp.tier}`;
|
|
562
|
+
if (comp.holes.length > 0) {
|
|
563
|
+
line += `, ${comp.holes.length} hole${comp.holes.length > 1 ? "s" : ""} (${comp.holes.join(", ")})`;
|
|
564
|
+
}
|
|
565
|
+
lines.push(line);
|
|
566
|
+
}
|
|
567
|
+
const snapshot = this.getSnapshot();
|
|
568
|
+
const { total, aot, percentage } = snapshot.coverage;
|
|
569
|
+
if (total > 0) {
|
|
570
|
+
lines.push(`Coverage: ${aot}/${total} components (${percentage}%)`);
|
|
571
|
+
}
|
|
572
|
+
return lines;
|
|
573
|
+
}
|
|
574
|
+
getSnapshot() {
|
|
575
|
+
let aot = 0;
|
|
576
|
+
let runtime = 0;
|
|
577
|
+
for (const comp of this._components.values()) {
|
|
578
|
+
if (comp.tier === "runtime-fallback") {
|
|
579
|
+
runtime++;
|
|
580
|
+
} else {
|
|
581
|
+
aot++;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
const total = aot + runtime;
|
|
585
|
+
return {
|
|
586
|
+
components: Object.fromEntries(this._components),
|
|
587
|
+
coverage: {
|
|
588
|
+
total,
|
|
589
|
+
aot,
|
|
590
|
+
runtime,
|
|
591
|
+
percentage: total === 0 ? 0 : Math.round(aot / total * 100)
|
|
592
|
+
},
|
|
593
|
+
divergences: [...this._divergences]
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
// src/ssr-aot-manifest-dev.ts
|
|
598
|
+
import { compileForSSRAot as compileForSSRAot2 } from "@vertz/ui-compiler";
|
|
599
|
+
function createAotManifestManager(options) {
|
|
600
|
+
const { readFile: readFile2, listFiles } = options;
|
|
601
|
+
let currentManifest = null;
|
|
602
|
+
const diagnostics = new AotDiagnostics;
|
|
603
|
+
let rebuildCount = 0;
|
|
604
|
+
let lastRebuildMs = null;
|
|
605
|
+
let lastRebuildAt = null;
|
|
606
|
+
function compileFile(filePath, source) {
|
|
607
|
+
try {
|
|
608
|
+
const result = compileForSSRAot2(source, { filename: filePath });
|
|
609
|
+
return result.components.map((comp) => ({
|
|
610
|
+
name: comp.name,
|
|
611
|
+
entry: {
|
|
612
|
+
tier: comp.tier,
|
|
613
|
+
holes: comp.holes,
|
|
614
|
+
file: filePath
|
|
615
|
+
}
|
|
616
|
+
}));
|
|
617
|
+
} catch {
|
|
618
|
+
return [];
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
function updateDiagnostics(manifest, isFullBuild) {
|
|
622
|
+
if (isFullBuild) {
|
|
623
|
+
diagnostics.clear();
|
|
624
|
+
} else {
|
|
625
|
+
diagnostics.clearComponents();
|
|
626
|
+
}
|
|
627
|
+
const entries = Object.entries(manifest.components).map(([name, entry]) => ({
|
|
628
|
+
name,
|
|
629
|
+
tier: entry.tier,
|
|
630
|
+
holes: entry.holes
|
|
631
|
+
}));
|
|
632
|
+
diagnostics.recordCompilation(entries);
|
|
633
|
+
}
|
|
634
|
+
function fullBuild() {
|
|
635
|
+
const start = performance.now();
|
|
636
|
+
const files = listFiles();
|
|
637
|
+
const components = {};
|
|
638
|
+
for (const filePath of files) {
|
|
639
|
+
if (!filePath.endsWith(".tsx"))
|
|
640
|
+
continue;
|
|
641
|
+
const source = readFile2(filePath);
|
|
642
|
+
if (!source)
|
|
643
|
+
continue;
|
|
644
|
+
const entries = compileFile(filePath, source);
|
|
645
|
+
for (const { name, entry } of entries) {
|
|
646
|
+
components[name] = entry;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
currentManifest = { components };
|
|
650
|
+
updateDiagnostics(currentManifest, true);
|
|
651
|
+
rebuildCount++;
|
|
652
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
653
|
+
lastRebuildAt = new Date().toISOString();
|
|
654
|
+
}
|
|
655
|
+
function incrementalUpdate(filePath, sourceText) {
|
|
656
|
+
if (!currentManifest)
|
|
657
|
+
return;
|
|
658
|
+
const start = performance.now();
|
|
659
|
+
const newComponents = { ...currentManifest.components };
|
|
660
|
+
for (const [name, entry] of Object.entries(newComponents)) {
|
|
661
|
+
if (entry.file === filePath) {
|
|
662
|
+
delete newComponents[name];
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (sourceText.trim()) {
|
|
666
|
+
const entries = compileFile(filePath, sourceText);
|
|
667
|
+
for (const { name, entry } of entries) {
|
|
668
|
+
newComponents[name] = entry;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
currentManifest = { components: newComponents };
|
|
672
|
+
updateDiagnostics(currentManifest, false);
|
|
673
|
+
rebuildCount++;
|
|
674
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
675
|
+
lastRebuildAt = new Date().toISOString();
|
|
676
|
+
}
|
|
677
|
+
return {
|
|
678
|
+
build() {
|
|
679
|
+
fullBuild();
|
|
680
|
+
},
|
|
681
|
+
onFileChange(filePath, sourceText) {
|
|
682
|
+
if (!filePath.endsWith(".tsx"))
|
|
683
|
+
return;
|
|
684
|
+
incrementalUpdate(filePath, sourceText);
|
|
685
|
+
},
|
|
686
|
+
getManifest() {
|
|
687
|
+
return currentManifest;
|
|
688
|
+
},
|
|
689
|
+
getSnapshot() {
|
|
690
|
+
return {
|
|
691
|
+
manifest: currentManifest,
|
|
692
|
+
rebuildCount,
|
|
693
|
+
lastRebuildMs,
|
|
694
|
+
lastRebuildAt
|
|
695
|
+
};
|
|
696
|
+
},
|
|
697
|
+
getDiagnostics() {
|
|
698
|
+
return diagnostics;
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
// src/ssr-aot-pipeline.ts
|
|
703
|
+
import { compileTheme as compileTheme3 } from "@vertz/ui";
|
|
704
|
+
|
|
705
|
+
// src/ssr-route-matcher.ts
|
|
706
|
+
function matchUrlToPatterns(url, patterns) {
|
|
707
|
+
const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
|
|
708
|
+
const matches = [];
|
|
709
|
+
for (const pattern of patterns) {
|
|
710
|
+
const result = matchPattern(path, pattern);
|
|
711
|
+
if (result) {
|
|
712
|
+
matches.push(result);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
matches.sort((a, b) => {
|
|
716
|
+
const aSegments = a.pattern.split("/").length;
|
|
717
|
+
const bSegments = b.pattern.split("/").length;
|
|
718
|
+
return aSegments - bSegments;
|
|
719
|
+
});
|
|
720
|
+
return matches;
|
|
721
|
+
}
|
|
722
|
+
function matchPattern(path, pattern) {
|
|
723
|
+
const pathSegments = path.split("/").filter(Boolean);
|
|
724
|
+
const patternSegments = pattern.split("/").filter(Boolean);
|
|
725
|
+
if (patternSegments.length > pathSegments.length)
|
|
726
|
+
return;
|
|
727
|
+
const params = {};
|
|
728
|
+
for (let i = 0;i < patternSegments.length; i++) {
|
|
729
|
+
const seg = patternSegments[i];
|
|
730
|
+
const val = pathSegments[i];
|
|
731
|
+
if (seg.startsWith(":")) {
|
|
732
|
+
params[seg.slice(1)] = val;
|
|
733
|
+
} else if (seg !== val) {
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return { pattern, params };
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// src/ssr-single-pass.ts
|
|
741
|
+
import { compileTheme as compileTheme2 } from "@vertz/ui";
|
|
742
|
+
|
|
743
|
+
// src/ssr-manifest-prefetch.ts
|
|
744
|
+
function reconstructDescriptors(queries, routeParams, apiClient) {
|
|
745
|
+
if (!apiClient)
|
|
746
|
+
return [];
|
|
747
|
+
const result = [];
|
|
748
|
+
for (const query of queries) {
|
|
749
|
+
const descriptor = reconstructSingle(query, routeParams, apiClient);
|
|
750
|
+
if (descriptor) {
|
|
751
|
+
result.push(descriptor);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return result;
|
|
755
|
+
}
|
|
756
|
+
function reconstructSingle(query, routeParams, apiClient) {
|
|
757
|
+
const { entity, operation } = query;
|
|
758
|
+
if (!entity || !operation)
|
|
759
|
+
return;
|
|
760
|
+
const entitySdk = apiClient[entity];
|
|
761
|
+
if (!entitySdk)
|
|
762
|
+
return;
|
|
763
|
+
const method = entitySdk[operation];
|
|
764
|
+
if (typeof method !== "function")
|
|
765
|
+
return;
|
|
766
|
+
const args = buildFactoryArgs(query, routeParams);
|
|
767
|
+
if (args === undefined)
|
|
768
|
+
return;
|
|
769
|
+
try {
|
|
770
|
+
const descriptor = method(...args);
|
|
771
|
+
if (!descriptor || typeof descriptor._key !== "string" || typeof descriptor._fetch !== "function") {
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
return { key: descriptor._key, fetch: descriptor._fetch };
|
|
775
|
+
} catch {
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
function buildFactoryArgs(query, routeParams) {
|
|
780
|
+
const { operation, idParam, queryBindings } = query;
|
|
781
|
+
if (operation === "get") {
|
|
782
|
+
if (idParam) {
|
|
783
|
+
const id = routeParams[idParam];
|
|
784
|
+
if (!id)
|
|
785
|
+
return;
|
|
786
|
+
const options = resolveQueryBindings(queryBindings, routeParams);
|
|
787
|
+
if (options === undefined && queryBindings)
|
|
788
|
+
return;
|
|
789
|
+
return options ? [id, options] : [id];
|
|
790
|
+
}
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
if (!queryBindings)
|
|
794
|
+
return [];
|
|
795
|
+
const resolved = resolveQueryBindings(queryBindings, routeParams);
|
|
796
|
+
if (resolved === undefined)
|
|
797
|
+
return;
|
|
798
|
+
return [resolved];
|
|
799
|
+
}
|
|
800
|
+
function resolveQueryBindings(bindings, routeParams) {
|
|
801
|
+
if (!bindings)
|
|
802
|
+
return;
|
|
803
|
+
const resolved = {};
|
|
804
|
+
if (bindings.where) {
|
|
805
|
+
const where = {};
|
|
806
|
+
for (const [key, value] of Object.entries(bindings.where)) {
|
|
807
|
+
if (value === null)
|
|
808
|
+
return;
|
|
809
|
+
if (typeof value === "string" && value.startsWith("$")) {
|
|
810
|
+
const paramName = value.slice(1);
|
|
811
|
+
const paramValue = routeParams[paramName];
|
|
812
|
+
if (!paramValue)
|
|
813
|
+
return;
|
|
814
|
+
where[key] = paramValue;
|
|
815
|
+
} else {
|
|
816
|
+
where[key] = value;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
resolved.where = where;
|
|
820
|
+
}
|
|
821
|
+
if (bindings.select)
|
|
822
|
+
resolved.select = bindings.select;
|
|
823
|
+
if (bindings.include)
|
|
824
|
+
resolved.include = bindings.include;
|
|
825
|
+
if (bindings.orderBy)
|
|
826
|
+
resolved.orderBy = bindings.orderBy;
|
|
827
|
+
if (bindings.limit !== undefined)
|
|
828
|
+
resolved.limit = bindings.limit;
|
|
829
|
+
return resolved;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// src/ssr-single-pass.ts
|
|
833
|
+
async function ssrRenderSinglePass(module, url, options) {
|
|
834
|
+
if (options?.prefetch === false) {
|
|
835
|
+
return ssrRenderToString(module, url, options);
|
|
836
|
+
}
|
|
837
|
+
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
838
|
+
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
839
|
+
ensureDomShim();
|
|
840
|
+
const zeroDiscoveryData = attemptZeroDiscovery(normalizedUrl, module, options, ssrTimeout);
|
|
841
|
+
if (zeroDiscoveryData) {
|
|
842
|
+
return renderWithPrefetchedData(module, normalizedUrl, zeroDiscoveryData, options);
|
|
843
|
+
}
|
|
844
|
+
const discoveryCtx = createRequestContext(normalizedUrl);
|
|
845
|
+
if (options?.ssrAuth) {
|
|
846
|
+
discoveryCtx.ssrAuth = options.ssrAuth;
|
|
847
|
+
}
|
|
848
|
+
const discoveredData = await ssrStorage.run(discoveryCtx, async () => {
|
|
849
|
+
try {
|
|
850
|
+
setGlobalSSRTimeout(ssrTimeout);
|
|
851
|
+
const createApp = resolveAppFactory(module);
|
|
852
|
+
createApp();
|
|
853
|
+
if (discoveryCtx.ssrRedirect) {
|
|
854
|
+
return { redirect: discoveryCtx.ssrRedirect };
|
|
855
|
+
}
|
|
856
|
+
if (discoveryCtx.pendingRouteComponents?.size) {
|
|
857
|
+
const entries = Array.from(discoveryCtx.pendingRouteComponents.entries());
|
|
858
|
+
const results = await Promise.allSettled(entries.map(([route, promise]) => Promise.race([
|
|
859
|
+
promise.then((mod) => ({ route, factory: mod.default })),
|
|
860
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("lazy route timeout")), ssrTimeout))
|
|
861
|
+
])));
|
|
862
|
+
discoveryCtx.resolvedComponents = new Map;
|
|
863
|
+
for (const result of results) {
|
|
864
|
+
if (result.status === "fulfilled") {
|
|
865
|
+
const { route, factory } = result.value;
|
|
866
|
+
discoveryCtx.resolvedComponents.set(route, factory);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
discoveryCtx.pendingRouteComponents = undefined;
|
|
870
|
+
}
|
|
871
|
+
const queries = getSSRQueries();
|
|
872
|
+
const eligibleQueries = filterByEntityAccess(queries, options?.manifest?.entityAccess, options?.prefetchSession);
|
|
873
|
+
const resolvedQueries = [];
|
|
874
|
+
if (eligibleQueries.length > 0) {
|
|
875
|
+
await Promise.allSettled(eligibleQueries.map(({ promise, timeout, resolve, key }) => Promise.race([
|
|
876
|
+
promise.then((data) => {
|
|
877
|
+
resolve(data);
|
|
878
|
+
resolvedQueries.push({ key, data });
|
|
879
|
+
return "resolved";
|
|
880
|
+
}),
|
|
881
|
+
new Promise((r) => setTimeout(r, timeout || ssrTimeout)).then(() => "timeout")
|
|
882
|
+
])));
|
|
883
|
+
}
|
|
884
|
+
return {
|
|
885
|
+
resolvedQueries,
|
|
886
|
+
resolvedComponents: discoveryCtx.resolvedComponents
|
|
887
|
+
};
|
|
888
|
+
} finally {
|
|
889
|
+
clearGlobalSSRTimeout();
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
if ("redirect" in discoveredData) {
|
|
893
|
+
return {
|
|
894
|
+
html: "",
|
|
895
|
+
css: "",
|
|
896
|
+
ssrData: [],
|
|
897
|
+
headTags: "",
|
|
898
|
+
redirect: discoveredData.redirect
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
const renderCtx = createRequestContext(normalizedUrl);
|
|
902
|
+
if (options?.ssrAuth) {
|
|
903
|
+
renderCtx.ssrAuth = options.ssrAuth;
|
|
904
|
+
}
|
|
905
|
+
for (const { key, data } of discoveredData.resolvedQueries) {
|
|
906
|
+
renderCtx.queryCache.set(key, data);
|
|
907
|
+
}
|
|
908
|
+
renderCtx.resolvedComponents = discoveredData.resolvedComponents ?? new Map;
|
|
909
|
+
return ssrStorage.run(renderCtx, async () => {
|
|
910
|
+
try {
|
|
911
|
+
setGlobalSSRTimeout(ssrTimeout);
|
|
912
|
+
const createApp = resolveAppFactory(module);
|
|
913
|
+
let themeCss = "";
|
|
914
|
+
let themePreloadTags = "";
|
|
915
|
+
if (module.theme) {
|
|
916
|
+
try {
|
|
917
|
+
const compiled = compileTheme2(module.theme, {
|
|
918
|
+
fallbackMetrics: options?.fallbackMetrics
|
|
919
|
+
});
|
|
920
|
+
themeCss = compiled.css;
|
|
921
|
+
themePreloadTags = compiled.preloadTags;
|
|
922
|
+
} catch (e) {
|
|
923
|
+
console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
const app = createApp();
|
|
927
|
+
const vnode = toVNode(app);
|
|
928
|
+
const stream = renderToStream(vnode);
|
|
929
|
+
const html = await streamToString(stream);
|
|
930
|
+
const css = collectCSS(themeCss, module);
|
|
931
|
+
const ssrData = discoveredData.resolvedQueries.map(({ key, data }) => ({
|
|
932
|
+
key,
|
|
933
|
+
data: JSON.parse(JSON.stringify(data))
|
|
934
|
+
}));
|
|
935
|
+
return {
|
|
936
|
+
html,
|
|
937
|
+
css,
|
|
938
|
+
ssrData,
|
|
939
|
+
headTags: themePreloadTags,
|
|
940
|
+
discoveredRoutes: renderCtx.discoveredRoutes,
|
|
941
|
+
matchedRoutePatterns: renderCtx.matchedRoutePatterns
|
|
942
|
+
};
|
|
943
|
+
} finally {
|
|
944
|
+
clearGlobalSSRTimeout();
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
function attemptZeroDiscovery(url, module, options, ssrTimeout) {
|
|
949
|
+
const manifest = options?.manifest;
|
|
950
|
+
if (!manifest?.routeEntries || !module.api)
|
|
951
|
+
return null;
|
|
952
|
+
const matches = matchUrlToPatterns(url, manifest.routePatterns);
|
|
953
|
+
if (matches.length === 0)
|
|
954
|
+
return null;
|
|
955
|
+
const allQueries = [];
|
|
956
|
+
let mergedParams = {};
|
|
957
|
+
for (const match of matches) {
|
|
958
|
+
const entry = manifest.routeEntries[match.pattern];
|
|
959
|
+
if (entry) {
|
|
960
|
+
allQueries.push(...entry.queries);
|
|
961
|
+
}
|
|
962
|
+
mergedParams = { ...mergedParams, ...match.params };
|
|
963
|
+
}
|
|
964
|
+
if (allQueries.length === 0)
|
|
965
|
+
return null;
|
|
966
|
+
const descriptors = reconstructDescriptors(allQueries, mergedParams, module.api);
|
|
967
|
+
if (descriptors.length === 0)
|
|
968
|
+
return null;
|
|
969
|
+
return prefetchFromDescriptors(descriptors, ssrTimeout);
|
|
970
|
+
}
|
|
971
|
+
async function prefetchFromDescriptors(descriptors, ssrTimeout) {
|
|
972
|
+
const resolvedQueries = [];
|
|
973
|
+
await Promise.allSettled(descriptors.map(({ key, fetch: fetchFn }) => Promise.race([
|
|
974
|
+
fetchFn().then((result) => {
|
|
975
|
+
const data = unwrapResult(result);
|
|
976
|
+
resolvedQueries.push({ key, data });
|
|
977
|
+
return "resolved";
|
|
978
|
+
}),
|
|
979
|
+
new Promise((r) => setTimeout(r, ssrTimeout)).then(() => "timeout")
|
|
980
|
+
])));
|
|
981
|
+
return { resolvedQueries };
|
|
982
|
+
}
|
|
983
|
+
function unwrapResult(result) {
|
|
984
|
+
if (result && typeof result === "object" && "ok" in result && "data" in result) {
|
|
985
|
+
const r = result;
|
|
986
|
+
if (r.ok)
|
|
987
|
+
return r.data;
|
|
988
|
+
}
|
|
989
|
+
return result;
|
|
990
|
+
}
|
|
991
|
+
async function renderWithPrefetchedData(module, normalizedUrl, prefetchedData, options) {
|
|
992
|
+
const data = await prefetchedData;
|
|
993
|
+
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
994
|
+
const renderCtx = createRequestContext(normalizedUrl);
|
|
995
|
+
if (options?.ssrAuth) {
|
|
996
|
+
renderCtx.ssrAuth = options.ssrAuth;
|
|
997
|
+
}
|
|
998
|
+
for (const { key, data: queryData } of data.resolvedQueries) {
|
|
999
|
+
renderCtx.queryCache.set(key, queryData);
|
|
1000
|
+
}
|
|
1001
|
+
renderCtx.resolvedComponents = new Map;
|
|
1002
|
+
return ssrStorage.run(renderCtx, async () => {
|
|
1003
|
+
try {
|
|
1004
|
+
setGlobalSSRTimeout(ssrTimeout);
|
|
1005
|
+
const createApp = resolveAppFactory(module);
|
|
1006
|
+
let themeCss = "";
|
|
1007
|
+
let themePreloadTags = "";
|
|
1008
|
+
if (module.theme) {
|
|
1009
|
+
try {
|
|
1010
|
+
const compiled = compileTheme2(module.theme, {
|
|
1011
|
+
fallbackMetrics: options?.fallbackMetrics
|
|
1012
|
+
});
|
|
1013
|
+
themeCss = compiled.css;
|
|
1014
|
+
themePreloadTags = compiled.preloadTags;
|
|
1015
|
+
} catch (e) {
|
|
1016
|
+
console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
const app = createApp();
|
|
1020
|
+
const vnode = toVNode(app);
|
|
1021
|
+
const stream = renderToStream(vnode);
|
|
1022
|
+
const html = await streamToString(stream);
|
|
1023
|
+
if (renderCtx.ssrRedirect) {
|
|
1024
|
+
return {
|
|
1025
|
+
html: "",
|
|
1026
|
+
css: "",
|
|
1027
|
+
ssrData: [],
|
|
1028
|
+
headTags: "",
|
|
1029
|
+
redirect: renderCtx.ssrRedirect,
|
|
1030
|
+
discoveredRoutes: renderCtx.discoveredRoutes,
|
|
1031
|
+
matchedRoutePatterns: renderCtx.matchedRoutePatterns
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
const css = collectCSS(themeCss, module);
|
|
1035
|
+
const ssrData = data.resolvedQueries.map(({ key, data: d }) => ({
|
|
1036
|
+
key,
|
|
1037
|
+
data: JSON.parse(JSON.stringify(d))
|
|
1038
|
+
}));
|
|
1039
|
+
return {
|
|
1040
|
+
html,
|
|
1041
|
+
css,
|
|
1042
|
+
ssrData,
|
|
1043
|
+
headTags: themePreloadTags,
|
|
1044
|
+
discoveredRoutes: renderCtx.discoveredRoutes,
|
|
1045
|
+
matchedRoutePatterns: renderCtx.matchedRoutePatterns
|
|
1046
|
+
};
|
|
1047
|
+
} finally {
|
|
1048
|
+
clearGlobalSSRTimeout();
|
|
1049
|
+
}
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
var domShimInstalled = false;
|
|
1053
|
+
function ensureDomShim() {
|
|
1054
|
+
if (domShimInstalled && typeof document !== "undefined")
|
|
1055
|
+
return;
|
|
1056
|
+
domShimInstalled = true;
|
|
1057
|
+
installDomShim();
|
|
1058
|
+
}
|
|
1059
|
+
function resolveAppFactory(module) {
|
|
1060
|
+
const createApp = module.default || module.App;
|
|
1061
|
+
if (typeof createApp !== "function") {
|
|
1062
|
+
throw new Error("App entry must export a default function or named App function");
|
|
1063
|
+
}
|
|
1064
|
+
return createApp;
|
|
1065
|
+
}
|
|
1066
|
+
function filterByEntityAccess(queries, entityAccess, session) {
|
|
1067
|
+
if (!entityAccess || !session)
|
|
1068
|
+
return queries;
|
|
1069
|
+
return queries.filter(({ key }) => {
|
|
1070
|
+
const entity = extractEntityFromKey(key);
|
|
1071
|
+
const method = extractMethodFromKey(key);
|
|
1072
|
+
if (!entity)
|
|
1073
|
+
return true;
|
|
1074
|
+
const entityRules = entityAccess[entity];
|
|
1075
|
+
if (!entityRules)
|
|
1076
|
+
return true;
|
|
1077
|
+
const rule = entityRules[method];
|
|
1078
|
+
if (!rule)
|
|
1079
|
+
return true;
|
|
1080
|
+
return evaluateAccessRule(rule, session);
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
function extractEntityFromKey(key) {
|
|
1084
|
+
const pathStart = key.indexOf(":/");
|
|
1085
|
+
if (pathStart === -1)
|
|
1086
|
+
return;
|
|
1087
|
+
const path = key.slice(pathStart + 2);
|
|
1088
|
+
const firstSlash = path.indexOf("/");
|
|
1089
|
+
const questionMark = path.indexOf("?");
|
|
1090
|
+
if (firstSlash === -1 && questionMark === -1)
|
|
1091
|
+
return path;
|
|
1092
|
+
if (firstSlash === -1)
|
|
1093
|
+
return path.slice(0, questionMark);
|
|
1094
|
+
if (questionMark === -1)
|
|
1095
|
+
return path.slice(0, firstSlash);
|
|
1096
|
+
return path.slice(0, Math.min(firstSlash, questionMark));
|
|
1097
|
+
}
|
|
1098
|
+
function extractMethodFromKey(key) {
|
|
1099
|
+
const pathStart = key.indexOf(":/");
|
|
1100
|
+
if (pathStart === -1)
|
|
1101
|
+
return "list";
|
|
1102
|
+
const path = key.slice(pathStart + 2);
|
|
1103
|
+
const cleanPath = path.split("?")[0] ?? "";
|
|
1104
|
+
const segments = cleanPath.split("/").filter(Boolean);
|
|
1105
|
+
return segments.length > 1 ? "get" : "list";
|
|
1106
|
+
}
|
|
1107
|
+
function collectCSS(themeCss, module) {
|
|
1108
|
+
const alreadyIncluded = new Set;
|
|
1109
|
+
if (themeCss)
|
|
1110
|
+
alreadyIncluded.add(themeCss);
|
|
1111
|
+
if (module.styles) {
|
|
1112
|
+
for (const s of module.styles)
|
|
1113
|
+
alreadyIncluded.add(s);
|
|
1114
|
+
}
|
|
1115
|
+
const componentCss = module.getInjectedCSS ? module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s)) : [];
|
|
1116
|
+
const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
|
|
1117
|
+
const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
|
|
1118
|
+
`)}</style>` : "";
|
|
1119
|
+
const componentTag = componentCss.length > 0 ? `<style data-vertz-css>${componentCss.join(`
|
|
1120
|
+
`)}</style>` : "";
|
|
1121
|
+
return [themeTag, globalTag, componentTag].filter(Boolean).join(`
|
|
1122
|
+
`);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// src/ssr-aot-pipeline.ts
|
|
1126
|
+
function createHoles(holeNames, module, url, queryCache, ssrAuth) {
|
|
1127
|
+
if (holeNames.length === 0)
|
|
1128
|
+
return {};
|
|
1129
|
+
const holes = {};
|
|
1130
|
+
for (const name of holeNames) {
|
|
1131
|
+
holes[name] = () => {
|
|
1132
|
+
const holeCtx = createRequestContext(url);
|
|
1133
|
+
for (const [key, data] of queryCache) {
|
|
1134
|
+
holeCtx.queryCache.set(key, data);
|
|
1135
|
+
}
|
|
1136
|
+
if (ssrAuth) {
|
|
1137
|
+
holeCtx.ssrAuth = ssrAuth;
|
|
1138
|
+
}
|
|
1139
|
+
holeCtx.resolvedComponents = new Map;
|
|
1140
|
+
return ssrStorage.run(holeCtx, () => {
|
|
1141
|
+
ensureDomShim2();
|
|
1142
|
+
const factory = resolveHoleComponent(module, name);
|
|
1143
|
+
if (!factory) {
|
|
1144
|
+
return `<!-- AOT hole: ${name} not found -->`;
|
|
1145
|
+
}
|
|
1146
|
+
const node = factory();
|
|
1147
|
+
const vnode = toVNode(node);
|
|
1148
|
+
return serializeToHtml(vnode);
|
|
1149
|
+
});
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
return holes;
|
|
1153
|
+
}
|
|
1154
|
+
function resolveHoleComponent(module, name) {
|
|
1155
|
+
const moduleRecord = module;
|
|
1156
|
+
const exported = moduleRecord[name];
|
|
1157
|
+
if (typeof exported === "function") {
|
|
1158
|
+
return exported;
|
|
1159
|
+
}
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
async function ssrRenderAot(module, url, options) {
|
|
1163
|
+
const { aotManifest, manifest } = options;
|
|
1164
|
+
const ssrTimeout = options.ssrTimeout ?? 300;
|
|
1165
|
+
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
1166
|
+
const fallbackOptions = {
|
|
1167
|
+
ssrTimeout,
|
|
1168
|
+
fallbackMetrics: options.fallbackMetrics,
|
|
1169
|
+
ssrAuth: options.ssrAuth,
|
|
1170
|
+
manifest,
|
|
1171
|
+
prefetchSession: options.prefetchSession
|
|
1172
|
+
};
|
|
1173
|
+
const aotPatterns = Object.keys(aotManifest.routes);
|
|
1174
|
+
const matches = matchUrlToPatterns(normalizedUrl, aotPatterns);
|
|
1175
|
+
if (matches.length === 0) {
|
|
1176
|
+
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1177
|
+
}
|
|
1178
|
+
const match = matches[matches.length - 1];
|
|
1179
|
+
if (!match) {
|
|
1180
|
+
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1181
|
+
}
|
|
1182
|
+
const aotEntry = aotManifest.routes[match.pattern];
|
|
1183
|
+
if (!aotEntry) {
|
|
1184
|
+
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1185
|
+
}
|
|
1186
|
+
const queryCache = new Map;
|
|
1187
|
+
try {
|
|
1188
|
+
setGlobalSSRTimeout(ssrTimeout);
|
|
1189
|
+
const holes = createHoles(aotEntry.holes, module, normalizedUrl, queryCache, options.ssrAuth);
|
|
1190
|
+
const ctx = {
|
|
1191
|
+
holes,
|
|
1192
|
+
getData: (key) => queryCache.get(key),
|
|
1193
|
+
session: options.prefetchSession,
|
|
1194
|
+
params: match.params
|
|
1195
|
+
};
|
|
1196
|
+
const data = {};
|
|
1197
|
+
for (const [key, value] of queryCache) {
|
|
1198
|
+
data[key] = value;
|
|
1199
|
+
}
|
|
1200
|
+
const html = aotEntry.render(data, ctx);
|
|
1201
|
+
if (options.diagnostics && isAotDebugEnabled()) {
|
|
1202
|
+
try {
|
|
1203
|
+
const domResult = await ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1204
|
+
if (domResult.html !== html) {
|
|
1205
|
+
options.diagnostics.recordDivergence(match.pattern, html, domResult.html);
|
|
1206
|
+
}
|
|
1207
|
+
} catch {}
|
|
1208
|
+
}
|
|
1209
|
+
const css = collectCSSFromModule(module, options.fallbackMetrics);
|
|
1210
|
+
const ssrData = [];
|
|
1211
|
+
for (const [key, data2] of queryCache) {
|
|
1212
|
+
ssrData.push({ key, data: JSON.parse(JSON.stringify(data2)) });
|
|
1213
|
+
}
|
|
1214
|
+
return {
|
|
1215
|
+
html,
|
|
1216
|
+
css: css.cssString,
|
|
1217
|
+
ssrData,
|
|
1218
|
+
headTags: css.preloadTags
|
|
1219
|
+
};
|
|
1220
|
+
} finally {
|
|
1221
|
+
clearGlobalSSRTimeout();
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
function isAotDebugEnabled() {
|
|
1225
|
+
const env = process.env.VERTZ_DEBUG;
|
|
1226
|
+
if (!env)
|
|
1227
|
+
return false;
|
|
1228
|
+
return env === "1" || env.split(",").includes("aot");
|
|
1229
|
+
}
|
|
1230
|
+
var domShimInstalled2 = false;
|
|
1231
|
+
function ensureDomShim2() {
|
|
1232
|
+
if (domShimInstalled2 && typeof document !== "undefined")
|
|
1233
|
+
return;
|
|
1234
|
+
domShimInstalled2 = true;
|
|
1235
|
+
installDomShim();
|
|
1236
|
+
}
|
|
1237
|
+
function collectCSSFromModule(module, fallbackMetrics) {
|
|
1238
|
+
let themeCss = "";
|
|
1239
|
+
let preloadTags = "";
|
|
1240
|
+
if (module.theme) {
|
|
1241
|
+
try {
|
|
1242
|
+
const compiled = compileTheme3(module.theme, { fallbackMetrics });
|
|
1243
|
+
themeCss = compiled.css;
|
|
1244
|
+
preloadTags = compiled.preloadTags;
|
|
1245
|
+
} catch (e) {
|
|
1246
|
+
console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
const alreadyIncluded = new Set;
|
|
1250
|
+
if (themeCss)
|
|
1251
|
+
alreadyIncluded.add(themeCss);
|
|
1252
|
+
if (module.styles) {
|
|
1253
|
+
for (const s of module.styles)
|
|
1254
|
+
alreadyIncluded.add(s);
|
|
1255
|
+
}
|
|
1256
|
+
const componentCss = module.getInjectedCSS ? module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s)) : [];
|
|
1257
|
+
const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
|
|
1258
|
+
const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
|
|
1259
|
+
`)}</style>` : "";
|
|
1260
|
+
const componentTag = componentCss.length > 0 ? `<style data-vertz-css>${componentCss.join(`
|
|
1261
|
+
`)}</style>` : "";
|
|
1262
|
+
const cssString = [themeTag, globalTag, componentTag].filter(Boolean).join(`
|
|
1263
|
+
`);
|
|
1264
|
+
return { cssString, preloadTags };
|
|
1265
|
+
}
|
|
1266
|
+
// src/ssr-aot-runtime.ts
|
|
1267
|
+
var UNITLESS_PROPERTIES = new Set([
|
|
1268
|
+
"animationIterationCount",
|
|
1269
|
+
"aspectRatio",
|
|
1270
|
+
"borderImageOutset",
|
|
1271
|
+
"borderImageSlice",
|
|
1272
|
+
"borderImageWidth",
|
|
1273
|
+
"boxFlex",
|
|
1274
|
+
"boxFlexGroup",
|
|
1275
|
+
"boxOrdinalGroup",
|
|
1276
|
+
"columnCount",
|
|
1277
|
+
"columns",
|
|
1278
|
+
"flex",
|
|
1279
|
+
"flexGrow",
|
|
1280
|
+
"flexPositive",
|
|
1281
|
+
"flexShrink",
|
|
1282
|
+
"flexNegative",
|
|
1283
|
+
"flexOrder",
|
|
1284
|
+
"fontWeight",
|
|
1285
|
+
"gridArea",
|
|
1286
|
+
"gridColumn",
|
|
1287
|
+
"gridColumnEnd",
|
|
1288
|
+
"gridColumnSpan",
|
|
1289
|
+
"gridColumnStart",
|
|
1290
|
+
"gridRow",
|
|
1291
|
+
"gridRowEnd",
|
|
1292
|
+
"gridRowSpan",
|
|
1293
|
+
"gridRowStart",
|
|
1294
|
+
"lineClamp",
|
|
1295
|
+
"lineHeight",
|
|
1296
|
+
"opacity",
|
|
1297
|
+
"order",
|
|
1298
|
+
"orphans",
|
|
1299
|
+
"scale",
|
|
1300
|
+
"tabSize",
|
|
1301
|
+
"widows",
|
|
1302
|
+
"zIndex",
|
|
1303
|
+
"zoom"
|
|
1304
|
+
]);
|
|
1305
|
+
function __esc(value) {
|
|
1306
|
+
if (value == null || value === false)
|
|
1307
|
+
return "";
|
|
1308
|
+
if (value === true)
|
|
1309
|
+
return "true";
|
|
1310
|
+
if (typeof value === "number")
|
|
1311
|
+
return String(value);
|
|
1312
|
+
if (Array.isArray(value))
|
|
1313
|
+
return value.map(__esc).join("");
|
|
1314
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1315
|
+
}
|
|
1316
|
+
function __esc_attr(value) {
|
|
1317
|
+
const str = typeof value === "string" ? value : String(value);
|
|
1318
|
+
return str.replace(/&/g, "&").replace(/"/g, """);
|
|
1319
|
+
}
|
|
1320
|
+
function __ssr_spread(props) {
|
|
1321
|
+
const parts = [];
|
|
1322
|
+
for (const key in props) {
|
|
1323
|
+
const value = props[key];
|
|
1324
|
+
if (value == null || value === false)
|
|
1325
|
+
continue;
|
|
1326
|
+
const third = key.charAt(2);
|
|
1327
|
+
if (key.length > 2 && key.charAt(0) === "o" && key.charAt(1) === "n" && third >= "A" && third <= "Z") {
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1330
|
+
if (typeof value === "function")
|
|
1331
|
+
continue;
|
|
1332
|
+
if (key === "key" || key === "ref" || key === "children")
|
|
1333
|
+
continue;
|
|
1334
|
+
const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
|
|
1335
|
+
if (key === "style" && typeof value === "object") {
|
|
1336
|
+
const css = __ssr_style_object(value);
|
|
1337
|
+
if (css) {
|
|
1338
|
+
parts.push(` style="${__esc_attr(css)}"`);
|
|
1339
|
+
}
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
if (value === true) {
|
|
1343
|
+
parts.push(` ${attrName}`);
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
parts.push(` ${attrName}="${__esc_attr(value)}"`);
|
|
1347
|
+
}
|
|
1348
|
+
return parts.join("");
|
|
1349
|
+
}
|
|
1350
|
+
function camelToKebab(prop) {
|
|
1351
|
+
if (prop.startsWith("--"))
|
|
1352
|
+
return prop;
|
|
1353
|
+
if (prop.startsWith("ms")) {
|
|
1354
|
+
return `-${prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}`;
|
|
1355
|
+
}
|
|
1356
|
+
const first = prop.charAt(0);
|
|
1357
|
+
if (first >= "A" && first <= "Z") {
|
|
1358
|
+
return `-${first.toLowerCase()}${prop.slice(1).replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}`;
|
|
1359
|
+
}
|
|
1360
|
+
return prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
1361
|
+
}
|
|
1362
|
+
function __ssr_style_object(style) {
|
|
1363
|
+
const parts = [];
|
|
1364
|
+
for (const prop in style) {
|
|
1365
|
+
const value = style[prop];
|
|
1366
|
+
if (value == null || value === "")
|
|
1367
|
+
continue;
|
|
1368
|
+
const cssProp = camelToKebab(prop);
|
|
1369
|
+
if (typeof value === "number" && value !== 0 && !UNITLESS_PROPERTIES.has(prop)) {
|
|
1370
|
+
parts.push(`${cssProp}: ${value}px`);
|
|
1371
|
+
} else {
|
|
1372
|
+
parts.push(`${cssProp}: ${value}`);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
return parts.join("; ");
|
|
1376
|
+
}
|
|
428
1377
|
// src/ssr-html.ts
|
|
429
1378
|
function generateSSRHtml(options) {
|
|
430
1379
|
const {
|
|
@@ -457,11 +1406,131 @@ function generateSSRHtml(options) {
|
|
|
457
1406
|
</body>
|
|
458
1407
|
</html>`;
|
|
459
1408
|
}
|
|
1409
|
+
// src/ssr-prefetch-dev.ts
|
|
1410
|
+
import {
|
|
1411
|
+
analyzeComponentQueries,
|
|
1412
|
+
generatePrefetchManifest
|
|
1413
|
+
} from "@vertz/ui-compiler";
|
|
1414
|
+
function createPrefetchManifestManager(options) {
|
|
1415
|
+
const { routerPath, readFile: readFile2, resolveImport } = options;
|
|
1416
|
+
let currentManifest = null;
|
|
1417
|
+
let currentSSRManifest;
|
|
1418
|
+
let rebuildCount = 0;
|
|
1419
|
+
let lastRebuildMs = null;
|
|
1420
|
+
let lastRebuildAt = null;
|
|
1421
|
+
let fileToRouteIndices = new Map;
|
|
1422
|
+
function buildFileIndex(routes) {
|
|
1423
|
+
const index = new Map;
|
|
1424
|
+
for (let i = 0;i < routes.length; i++) {
|
|
1425
|
+
const file = routes[i]?.file;
|
|
1426
|
+
if (file) {
|
|
1427
|
+
const existing = index.get(file) ?? [];
|
|
1428
|
+
existing.push(i);
|
|
1429
|
+
index.set(file, existing);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return index;
|
|
1433
|
+
}
|
|
1434
|
+
function toSSRManifest(manifest) {
|
|
1435
|
+
const routeEntries = {};
|
|
1436
|
+
for (const route of manifest.routes) {
|
|
1437
|
+
const existing = routeEntries[route.pattern];
|
|
1438
|
+
if (existing) {
|
|
1439
|
+
existing.queries.push(...route.queries);
|
|
1440
|
+
} else {
|
|
1441
|
+
routeEntries[route.pattern] = { queries: [...route.queries] };
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
return {
|
|
1445
|
+
routePatterns: [...new Set(manifest.routes.map((r) => r.pattern))],
|
|
1446
|
+
routeEntries
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
function fullBuild(routerSourceOverride) {
|
|
1450
|
+
const start = performance.now();
|
|
1451
|
+
const routerSource = routerSourceOverride ?? readFile2(routerPath);
|
|
1452
|
+
if (!routerSource) {
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
try {
|
|
1456
|
+
const manifest = generatePrefetchManifest({
|
|
1457
|
+
routerSource,
|
|
1458
|
+
routerPath,
|
|
1459
|
+
readFile: readFile2,
|
|
1460
|
+
resolveImport
|
|
1461
|
+
});
|
|
1462
|
+
currentManifest = manifest;
|
|
1463
|
+
currentSSRManifest = toSSRManifest(manifest);
|
|
1464
|
+
fileToRouteIndices = buildFileIndex(manifest.routes);
|
|
1465
|
+
rebuildCount++;
|
|
1466
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
1467
|
+
lastRebuildAt = new Date().toISOString();
|
|
1468
|
+
} catch {}
|
|
1469
|
+
}
|
|
1470
|
+
function incrementalUpdate(filePath, sourceText) {
|
|
1471
|
+
if (!currentManifest)
|
|
1472
|
+
return;
|
|
1473
|
+
const indices = fileToRouteIndices.get(filePath);
|
|
1474
|
+
if (!indices || indices.length === 0)
|
|
1475
|
+
return;
|
|
1476
|
+
const start = performance.now();
|
|
1477
|
+
try {
|
|
1478
|
+
const analysis = analyzeComponentQueries(sourceText, filePath);
|
|
1479
|
+
const newRoutes = [...currentManifest.routes];
|
|
1480
|
+
for (const idx of indices) {
|
|
1481
|
+
const existing = newRoutes[idx];
|
|
1482
|
+
if (existing) {
|
|
1483
|
+
newRoutes[idx] = {
|
|
1484
|
+
...existing,
|
|
1485
|
+
queries: analysis.queries,
|
|
1486
|
+
params: analysis.params
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
const newManifest = {
|
|
1491
|
+
...currentManifest,
|
|
1492
|
+
routes: newRoutes,
|
|
1493
|
+
generatedAt: new Date().toISOString()
|
|
1494
|
+
};
|
|
1495
|
+
currentManifest = newManifest;
|
|
1496
|
+
currentSSRManifest = toSSRManifest(newManifest);
|
|
1497
|
+
rebuildCount++;
|
|
1498
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
1499
|
+
lastRebuildAt = new Date().toISOString();
|
|
1500
|
+
} catch {}
|
|
1501
|
+
}
|
|
1502
|
+
return {
|
|
1503
|
+
build() {
|
|
1504
|
+
fullBuild();
|
|
1505
|
+
},
|
|
1506
|
+
onFileChange(filePath, sourceText) {
|
|
1507
|
+
if (filePath === routerPath) {
|
|
1508
|
+
fullBuild(sourceText);
|
|
1509
|
+
} else {
|
|
1510
|
+
incrementalUpdate(filePath, sourceText);
|
|
1511
|
+
}
|
|
1512
|
+
},
|
|
1513
|
+
getSSRManifest() {
|
|
1514
|
+
return currentSSRManifest;
|
|
1515
|
+
},
|
|
1516
|
+
getSnapshot() {
|
|
1517
|
+
return {
|
|
1518
|
+
manifest: currentManifest,
|
|
1519
|
+
rebuildCount,
|
|
1520
|
+
lastRebuildMs,
|
|
1521
|
+
lastRebuildAt
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
460
1526
|
export {
|
|
461
1527
|
wrapWithHydrationMarkers,
|
|
1528
|
+
toPrefetchSession,
|
|
462
1529
|
streamToString,
|
|
463
1530
|
ssrStorage,
|
|
464
1531
|
ssrRenderToString,
|
|
1532
|
+
ssrRenderSinglePass,
|
|
1533
|
+
ssrRenderAot,
|
|
465
1534
|
ssrDiscoverQueries,
|
|
466
1535
|
setGlobalSSRTimeout,
|
|
467
1536
|
serializeToHtml,
|
|
@@ -474,8 +1543,11 @@ export {
|
|
|
474
1543
|
renderHeadToHtml,
|
|
475
1544
|
renderAssetTags,
|
|
476
1545
|
registerSSRQuery,
|
|
1546
|
+
reconstructDescriptors,
|
|
477
1547
|
rawHtml,
|
|
1548
|
+
matchUrlToPatterns,
|
|
478
1549
|
isInSSR,
|
|
1550
|
+
isAotDebugEnabled,
|
|
479
1551
|
inlineCriticalCss,
|
|
480
1552
|
getStreamingRuntimeScript,
|
|
481
1553
|
getSSRUrl,
|
|
@@ -483,7 +1555,9 @@ export {
|
|
|
483
1555
|
getGlobalSSRTimeout,
|
|
484
1556
|
getAccessSetForSSR,
|
|
485
1557
|
generateSSRHtml,
|
|
1558
|
+
generateAotBuildManifest,
|
|
486
1559
|
extractFontMetrics,
|
|
1560
|
+
evaluateAccessRule,
|
|
487
1561
|
encodeChunk,
|
|
488
1562
|
detectFallbackFont,
|
|
489
1563
|
createTemplateChunk,
|
|
@@ -492,8 +1566,16 @@ export {
|
|
|
492
1566
|
createSSRHandler,
|
|
493
1567
|
createSSRDataChunk,
|
|
494
1568
|
createSSRAdapter,
|
|
1569
|
+
createPrefetchManifestManager,
|
|
1570
|
+
createHoles,
|
|
1571
|
+
createAotManifestManager,
|
|
495
1572
|
createAccessSetScript,
|
|
496
1573
|
collectStreamChunks,
|
|
497
1574
|
clearGlobalSSRTimeout,
|
|
498
|
-
|
|
1575
|
+
__ssr_style_object,
|
|
1576
|
+
__ssr_spread,
|
|
1577
|
+
__esc_attr,
|
|
1578
|
+
__esc,
|
|
1579
|
+
HeadCollector,
|
|
1580
|
+
AotDiagnostics
|
|
499
1581
|
};
|