@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/bun-dev-server.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
// src/bun-dev-server.ts
|
|
11
11
|
import { execSync } from "child_process";
|
|
12
|
-
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, watch as watch2, writeFileSync as writeFileSync2 } from "fs";
|
|
12
|
+
import { existsSync as existsSync2, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync2, watch as watch2, writeFileSync as writeFileSync2 } from "fs";
|
|
13
13
|
import { dirname, normalize, resolve } from "path";
|
|
14
14
|
|
|
15
15
|
// src/debug-logger.ts
|
|
@@ -592,6 +592,47 @@ function createSourceMapResolver(projectRoot) {
|
|
|
592
592
|
};
|
|
593
593
|
}
|
|
594
594
|
|
|
595
|
+
// src/ssr-access-evaluator.ts
|
|
596
|
+
function toPrefetchSession(ssrAuth) {
|
|
597
|
+
if (!ssrAuth || ssrAuth.status !== "authenticated" || !ssrAuth.user) {
|
|
598
|
+
return { status: "unauthenticated" };
|
|
599
|
+
}
|
|
600
|
+
const roles = ssrAuth.user.role ? [ssrAuth.user.role] : undefined;
|
|
601
|
+
return {
|
|
602
|
+
status: "authenticated",
|
|
603
|
+
roles,
|
|
604
|
+
tenantId: ssrAuth.user.tenantId
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
function evaluateAccessRule(rule, session) {
|
|
608
|
+
switch (rule.type) {
|
|
609
|
+
case "public":
|
|
610
|
+
return true;
|
|
611
|
+
case "authenticated":
|
|
612
|
+
return session.status === "authenticated";
|
|
613
|
+
case "role":
|
|
614
|
+
if (session.status !== "authenticated")
|
|
615
|
+
return false;
|
|
616
|
+
return session.roles?.some((r) => rule.roles.includes(r)) === true;
|
|
617
|
+
case "entitlement":
|
|
618
|
+
if (session.status !== "authenticated")
|
|
619
|
+
return false;
|
|
620
|
+
return session.entitlements?.[rule.value] === true;
|
|
621
|
+
case "where":
|
|
622
|
+
return true;
|
|
623
|
+
case "fva":
|
|
624
|
+
return session.status === "authenticated";
|
|
625
|
+
case "deny":
|
|
626
|
+
return false;
|
|
627
|
+
case "all":
|
|
628
|
+
return rule.rules.every((r) => evaluateAccessRule(r, session));
|
|
629
|
+
case "any":
|
|
630
|
+
return rule.rules.some((r) => evaluateAccessRule(r, session));
|
|
631
|
+
default:
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
595
636
|
// src/ssr-access-set.ts
|
|
596
637
|
function createAccessSetScript(accessSet, nonce) {
|
|
597
638
|
const json = JSON.stringify(accessSet);
|
|
@@ -603,6 +644,304 @@ function escapeAttr(s) {
|
|
|
603
644
|
return s.replace(/[&"'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
604
645
|
}
|
|
605
646
|
|
|
647
|
+
// src/ssr-aot-manifest-dev.ts
|
|
648
|
+
import { compileForSSRAot } from "@vertz/ui-compiler";
|
|
649
|
+
|
|
650
|
+
// src/ssr-aot-diagnostics.ts
|
|
651
|
+
var MAX_DIVERGENCES = 20;
|
|
652
|
+
|
|
653
|
+
class AotDiagnostics {
|
|
654
|
+
_components = new Map;
|
|
655
|
+
_divergences = [];
|
|
656
|
+
recordCompilation(components) {
|
|
657
|
+
for (const comp of components) {
|
|
658
|
+
this._components.set(comp.name, {
|
|
659
|
+
tier: comp.tier,
|
|
660
|
+
holes: comp.holes
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
recordDivergence(component, aotHtml, domHtml) {
|
|
665
|
+
this._divergences.push({
|
|
666
|
+
component,
|
|
667
|
+
aotHtml,
|
|
668
|
+
domHtml,
|
|
669
|
+
timestamp: new Date().toISOString()
|
|
670
|
+
});
|
|
671
|
+
if (this._divergences.length > MAX_DIVERGENCES) {
|
|
672
|
+
this._divergences = this._divergences.slice(this._divergences.length - MAX_DIVERGENCES);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
clear() {
|
|
676
|
+
this._components.clear();
|
|
677
|
+
this._divergences = [];
|
|
678
|
+
}
|
|
679
|
+
clearComponents() {
|
|
680
|
+
this._components.clear();
|
|
681
|
+
}
|
|
682
|
+
getClassificationLog() {
|
|
683
|
+
const lines = [];
|
|
684
|
+
for (const [name, comp] of this._components) {
|
|
685
|
+
let line = `${name}: ${comp.tier}`;
|
|
686
|
+
if (comp.holes.length > 0) {
|
|
687
|
+
line += `, ${comp.holes.length} hole${comp.holes.length > 1 ? "s" : ""} (${comp.holes.join(", ")})`;
|
|
688
|
+
}
|
|
689
|
+
lines.push(line);
|
|
690
|
+
}
|
|
691
|
+
const snapshot = this.getSnapshot();
|
|
692
|
+
const { total, aot, percentage } = snapshot.coverage;
|
|
693
|
+
if (total > 0) {
|
|
694
|
+
lines.push(`Coverage: ${aot}/${total} components (${percentage}%)`);
|
|
695
|
+
}
|
|
696
|
+
return lines;
|
|
697
|
+
}
|
|
698
|
+
getSnapshot() {
|
|
699
|
+
let aot = 0;
|
|
700
|
+
let runtime = 0;
|
|
701
|
+
for (const comp of this._components.values()) {
|
|
702
|
+
if (comp.tier === "runtime-fallback") {
|
|
703
|
+
runtime++;
|
|
704
|
+
} else {
|
|
705
|
+
aot++;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
const total = aot + runtime;
|
|
709
|
+
return {
|
|
710
|
+
components: Object.fromEntries(this._components),
|
|
711
|
+
coverage: {
|
|
712
|
+
total,
|
|
713
|
+
aot,
|
|
714
|
+
runtime,
|
|
715
|
+
percentage: total === 0 ? 0 : Math.round(aot / total * 100)
|
|
716
|
+
},
|
|
717
|
+
divergences: [...this._divergences]
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// src/ssr-aot-manifest-dev.ts
|
|
723
|
+
function createAotManifestManager(options) {
|
|
724
|
+
const { readFile: readFile2, listFiles } = options;
|
|
725
|
+
let currentManifest = null;
|
|
726
|
+
const diagnostics = new AotDiagnostics;
|
|
727
|
+
let rebuildCount = 0;
|
|
728
|
+
let lastRebuildMs = null;
|
|
729
|
+
let lastRebuildAt = null;
|
|
730
|
+
function compileFile(filePath, source) {
|
|
731
|
+
try {
|
|
732
|
+
const result = compileForSSRAot(source, { filename: filePath });
|
|
733
|
+
return result.components.map((comp) => ({
|
|
734
|
+
name: comp.name,
|
|
735
|
+
entry: {
|
|
736
|
+
tier: comp.tier,
|
|
737
|
+
holes: comp.holes,
|
|
738
|
+
file: filePath
|
|
739
|
+
}
|
|
740
|
+
}));
|
|
741
|
+
} catch {
|
|
742
|
+
return [];
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function updateDiagnostics(manifest, isFullBuild) {
|
|
746
|
+
if (isFullBuild) {
|
|
747
|
+
diagnostics.clear();
|
|
748
|
+
} else {
|
|
749
|
+
diagnostics.clearComponents();
|
|
750
|
+
}
|
|
751
|
+
const entries = Object.entries(manifest.components).map(([name, entry]) => ({
|
|
752
|
+
name,
|
|
753
|
+
tier: entry.tier,
|
|
754
|
+
holes: entry.holes
|
|
755
|
+
}));
|
|
756
|
+
diagnostics.recordCompilation(entries);
|
|
757
|
+
}
|
|
758
|
+
function fullBuild() {
|
|
759
|
+
const start = performance.now();
|
|
760
|
+
const files = listFiles();
|
|
761
|
+
const components = {};
|
|
762
|
+
for (const filePath of files) {
|
|
763
|
+
if (!filePath.endsWith(".tsx"))
|
|
764
|
+
continue;
|
|
765
|
+
const source = readFile2(filePath);
|
|
766
|
+
if (!source)
|
|
767
|
+
continue;
|
|
768
|
+
const entries = compileFile(filePath, source);
|
|
769
|
+
for (const { name, entry } of entries) {
|
|
770
|
+
components[name] = entry;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
currentManifest = { components };
|
|
774
|
+
updateDiagnostics(currentManifest, true);
|
|
775
|
+
rebuildCount++;
|
|
776
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
777
|
+
lastRebuildAt = new Date().toISOString();
|
|
778
|
+
}
|
|
779
|
+
function incrementalUpdate(filePath, sourceText) {
|
|
780
|
+
if (!currentManifest)
|
|
781
|
+
return;
|
|
782
|
+
const start = performance.now();
|
|
783
|
+
const newComponents = { ...currentManifest.components };
|
|
784
|
+
for (const [name, entry] of Object.entries(newComponents)) {
|
|
785
|
+
if (entry.file === filePath) {
|
|
786
|
+
delete newComponents[name];
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (sourceText.trim()) {
|
|
790
|
+
const entries = compileFile(filePath, sourceText);
|
|
791
|
+
for (const { name, entry } of entries) {
|
|
792
|
+
newComponents[name] = entry;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
currentManifest = { components: newComponents };
|
|
796
|
+
updateDiagnostics(currentManifest, false);
|
|
797
|
+
rebuildCount++;
|
|
798
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
799
|
+
lastRebuildAt = new Date().toISOString();
|
|
800
|
+
}
|
|
801
|
+
return {
|
|
802
|
+
build() {
|
|
803
|
+
fullBuild();
|
|
804
|
+
},
|
|
805
|
+
onFileChange(filePath, sourceText) {
|
|
806
|
+
if (!filePath.endsWith(".tsx"))
|
|
807
|
+
return;
|
|
808
|
+
incrementalUpdate(filePath, sourceText);
|
|
809
|
+
},
|
|
810
|
+
getManifest() {
|
|
811
|
+
return currentManifest;
|
|
812
|
+
},
|
|
813
|
+
getSnapshot() {
|
|
814
|
+
return {
|
|
815
|
+
manifest: currentManifest,
|
|
816
|
+
rebuildCount,
|
|
817
|
+
lastRebuildMs,
|
|
818
|
+
lastRebuildAt
|
|
819
|
+
};
|
|
820
|
+
},
|
|
821
|
+
getDiagnostics() {
|
|
822
|
+
return diagnostics;
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// src/ssr-prefetch-dev.ts
|
|
828
|
+
import {
|
|
829
|
+
analyzeComponentQueries,
|
|
830
|
+
generatePrefetchManifest
|
|
831
|
+
} from "@vertz/ui-compiler";
|
|
832
|
+
function createPrefetchManifestManager(options) {
|
|
833
|
+
const { routerPath, readFile: readFile2, resolveImport } = options;
|
|
834
|
+
let currentManifest = null;
|
|
835
|
+
let currentSSRManifest;
|
|
836
|
+
let rebuildCount = 0;
|
|
837
|
+
let lastRebuildMs = null;
|
|
838
|
+
let lastRebuildAt = null;
|
|
839
|
+
let fileToRouteIndices = new Map;
|
|
840
|
+
function buildFileIndex(routes) {
|
|
841
|
+
const index = new Map;
|
|
842
|
+
for (let i = 0;i < routes.length; i++) {
|
|
843
|
+
const file = routes[i]?.file;
|
|
844
|
+
if (file) {
|
|
845
|
+
const existing = index.get(file) ?? [];
|
|
846
|
+
existing.push(i);
|
|
847
|
+
index.set(file, existing);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
return index;
|
|
851
|
+
}
|
|
852
|
+
function toSSRManifest(manifest) {
|
|
853
|
+
const routeEntries = {};
|
|
854
|
+
for (const route of manifest.routes) {
|
|
855
|
+
const existing = routeEntries[route.pattern];
|
|
856
|
+
if (existing) {
|
|
857
|
+
existing.queries.push(...route.queries);
|
|
858
|
+
} else {
|
|
859
|
+
routeEntries[route.pattern] = { queries: [...route.queries] };
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
return {
|
|
863
|
+
routePatterns: [...new Set(manifest.routes.map((r) => r.pattern))],
|
|
864
|
+
routeEntries
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
function fullBuild(routerSourceOverride) {
|
|
868
|
+
const start = performance.now();
|
|
869
|
+
const routerSource = routerSourceOverride ?? readFile2(routerPath);
|
|
870
|
+
if (!routerSource) {
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
try {
|
|
874
|
+
const manifest = generatePrefetchManifest({
|
|
875
|
+
routerSource,
|
|
876
|
+
routerPath,
|
|
877
|
+
readFile: readFile2,
|
|
878
|
+
resolveImport
|
|
879
|
+
});
|
|
880
|
+
currentManifest = manifest;
|
|
881
|
+
currentSSRManifest = toSSRManifest(manifest);
|
|
882
|
+
fileToRouteIndices = buildFileIndex(manifest.routes);
|
|
883
|
+
rebuildCount++;
|
|
884
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
885
|
+
lastRebuildAt = new Date().toISOString();
|
|
886
|
+
} catch {}
|
|
887
|
+
}
|
|
888
|
+
function incrementalUpdate(filePath, sourceText) {
|
|
889
|
+
if (!currentManifest)
|
|
890
|
+
return;
|
|
891
|
+
const indices = fileToRouteIndices.get(filePath);
|
|
892
|
+
if (!indices || indices.length === 0)
|
|
893
|
+
return;
|
|
894
|
+
const start = performance.now();
|
|
895
|
+
try {
|
|
896
|
+
const analysis = analyzeComponentQueries(sourceText, filePath);
|
|
897
|
+
const newRoutes = [...currentManifest.routes];
|
|
898
|
+
for (const idx of indices) {
|
|
899
|
+
const existing = newRoutes[idx];
|
|
900
|
+
if (existing) {
|
|
901
|
+
newRoutes[idx] = {
|
|
902
|
+
...existing,
|
|
903
|
+
queries: analysis.queries,
|
|
904
|
+
params: analysis.params
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
const newManifest = {
|
|
909
|
+
...currentManifest,
|
|
910
|
+
routes: newRoutes,
|
|
911
|
+
generatedAt: new Date().toISOString()
|
|
912
|
+
};
|
|
913
|
+
currentManifest = newManifest;
|
|
914
|
+
currentSSRManifest = toSSRManifest(newManifest);
|
|
915
|
+
rebuildCount++;
|
|
916
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
917
|
+
lastRebuildAt = new Date().toISOString();
|
|
918
|
+
} catch {}
|
|
919
|
+
}
|
|
920
|
+
return {
|
|
921
|
+
build() {
|
|
922
|
+
fullBuild();
|
|
923
|
+
},
|
|
924
|
+
onFileChange(filePath, sourceText) {
|
|
925
|
+
if (filePath === routerPath) {
|
|
926
|
+
fullBuild(sourceText);
|
|
927
|
+
} else {
|
|
928
|
+
incrementalUpdate(filePath, sourceText);
|
|
929
|
+
}
|
|
930
|
+
},
|
|
931
|
+
getSSRManifest() {
|
|
932
|
+
return currentSSRManifest;
|
|
933
|
+
},
|
|
934
|
+
getSnapshot() {
|
|
935
|
+
return {
|
|
936
|
+
manifest: currentManifest,
|
|
937
|
+
rebuildCount,
|
|
938
|
+
lastRebuildMs,
|
|
939
|
+
lastRebuildAt
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
|
|
606
945
|
// src/ssr-render.ts
|
|
607
946
|
import { compileTheme } from "@vertz/ui";
|
|
608
947
|
import { EntityStore, MemoryCache, QueryEnvelopeStore } from "@vertz/ui/internals";
|
|
@@ -612,6 +951,7 @@ import { setAdapter } from "@vertz/ui/internals";
|
|
|
612
951
|
|
|
613
952
|
// src/dom-shim/ssr-node.ts
|
|
614
953
|
class SSRNode {
|
|
954
|
+
nodeType = 1;
|
|
615
955
|
childNodes = [];
|
|
616
956
|
parentNode = null;
|
|
617
957
|
get firstChild() {
|
|
@@ -657,6 +997,7 @@ class SSRNode {
|
|
|
657
997
|
|
|
658
998
|
// src/dom-shim/ssr-comment.ts
|
|
659
999
|
class SSRComment extends SSRNode {
|
|
1000
|
+
nodeType = 8;
|
|
660
1001
|
text;
|
|
661
1002
|
constructor(text) {
|
|
662
1003
|
super();
|
|
@@ -680,6 +1021,7 @@ function rawHtml(html) {
|
|
|
680
1021
|
|
|
681
1022
|
// src/dom-shim/ssr-text-node.ts
|
|
682
1023
|
class SSRTextNode extends SSRNode {
|
|
1024
|
+
nodeType = 3;
|
|
683
1025
|
text;
|
|
684
1026
|
constructor(text) {
|
|
685
1027
|
super();
|
|
@@ -695,6 +1037,7 @@ class SSRTextNode extends SSRNode {
|
|
|
695
1037
|
|
|
696
1038
|
// src/dom-shim/ssr-fragment.ts
|
|
697
1039
|
class SSRDocumentFragment extends SSRNode {
|
|
1040
|
+
nodeType = 11;
|
|
698
1041
|
children = [];
|
|
699
1042
|
appendChild(child) {
|
|
700
1043
|
if (child instanceof SSRTextNode) {
|
|
@@ -1695,6 +2038,426 @@ function escapeAttr3(s) {
|
|
|
1695
2038
|
return s.replace(/[&"'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
1696
2039
|
}
|
|
1697
2040
|
|
|
2041
|
+
// src/ssr-single-pass.ts
|
|
2042
|
+
import { compileTheme as compileTheme2 } from "@vertz/ui";
|
|
2043
|
+
|
|
2044
|
+
// src/ssr-manifest-prefetch.ts
|
|
2045
|
+
function reconstructDescriptors(queries, routeParams, apiClient) {
|
|
2046
|
+
if (!apiClient)
|
|
2047
|
+
return [];
|
|
2048
|
+
const result = [];
|
|
2049
|
+
for (const query of queries) {
|
|
2050
|
+
const descriptor = reconstructSingle(query, routeParams, apiClient);
|
|
2051
|
+
if (descriptor) {
|
|
2052
|
+
result.push(descriptor);
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
return result;
|
|
2056
|
+
}
|
|
2057
|
+
function reconstructSingle(query, routeParams, apiClient) {
|
|
2058
|
+
const { entity, operation } = query;
|
|
2059
|
+
if (!entity || !operation)
|
|
2060
|
+
return;
|
|
2061
|
+
const entitySdk = apiClient[entity];
|
|
2062
|
+
if (!entitySdk)
|
|
2063
|
+
return;
|
|
2064
|
+
const method = entitySdk[operation];
|
|
2065
|
+
if (typeof method !== "function")
|
|
2066
|
+
return;
|
|
2067
|
+
const args = buildFactoryArgs(query, routeParams);
|
|
2068
|
+
if (args === undefined)
|
|
2069
|
+
return;
|
|
2070
|
+
try {
|
|
2071
|
+
const descriptor = method(...args);
|
|
2072
|
+
if (!descriptor || typeof descriptor._key !== "string" || typeof descriptor._fetch !== "function") {
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
return { key: descriptor._key, fetch: descriptor._fetch };
|
|
2076
|
+
} catch {
|
|
2077
|
+
return;
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
function buildFactoryArgs(query, routeParams) {
|
|
2081
|
+
const { operation, idParam, queryBindings } = query;
|
|
2082
|
+
if (operation === "get") {
|
|
2083
|
+
if (idParam) {
|
|
2084
|
+
const id = routeParams[idParam];
|
|
2085
|
+
if (!id)
|
|
2086
|
+
return;
|
|
2087
|
+
const options = resolveQueryBindings(queryBindings, routeParams);
|
|
2088
|
+
if (options === undefined && queryBindings)
|
|
2089
|
+
return;
|
|
2090
|
+
return options ? [id, options] : [id];
|
|
2091
|
+
}
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
2094
|
+
if (!queryBindings)
|
|
2095
|
+
return [];
|
|
2096
|
+
const resolved = resolveQueryBindings(queryBindings, routeParams);
|
|
2097
|
+
if (resolved === undefined)
|
|
2098
|
+
return;
|
|
2099
|
+
return [resolved];
|
|
2100
|
+
}
|
|
2101
|
+
function resolveQueryBindings(bindings, routeParams) {
|
|
2102
|
+
if (!bindings)
|
|
2103
|
+
return;
|
|
2104
|
+
const resolved = {};
|
|
2105
|
+
if (bindings.where) {
|
|
2106
|
+
const where = {};
|
|
2107
|
+
for (const [key, value] of Object.entries(bindings.where)) {
|
|
2108
|
+
if (value === null)
|
|
2109
|
+
return;
|
|
2110
|
+
if (typeof value === "string" && value.startsWith("$")) {
|
|
2111
|
+
const paramName = value.slice(1);
|
|
2112
|
+
const paramValue = routeParams[paramName];
|
|
2113
|
+
if (!paramValue)
|
|
2114
|
+
return;
|
|
2115
|
+
where[key] = paramValue;
|
|
2116
|
+
} else {
|
|
2117
|
+
where[key] = value;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
resolved.where = where;
|
|
2121
|
+
}
|
|
2122
|
+
if (bindings.select)
|
|
2123
|
+
resolved.select = bindings.select;
|
|
2124
|
+
if (bindings.include)
|
|
2125
|
+
resolved.include = bindings.include;
|
|
2126
|
+
if (bindings.orderBy)
|
|
2127
|
+
resolved.orderBy = bindings.orderBy;
|
|
2128
|
+
if (bindings.limit !== undefined)
|
|
2129
|
+
resolved.limit = bindings.limit;
|
|
2130
|
+
return resolved;
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
// src/ssr-route-matcher.ts
|
|
2134
|
+
function matchUrlToPatterns(url, patterns) {
|
|
2135
|
+
const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
|
|
2136
|
+
const matches = [];
|
|
2137
|
+
for (const pattern of patterns) {
|
|
2138
|
+
const result = matchPattern(path, pattern);
|
|
2139
|
+
if (result) {
|
|
2140
|
+
matches.push(result);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
matches.sort((a, b) => {
|
|
2144
|
+
const aSegments = a.pattern.split("/").length;
|
|
2145
|
+
const bSegments = b.pattern.split("/").length;
|
|
2146
|
+
return aSegments - bSegments;
|
|
2147
|
+
});
|
|
2148
|
+
return matches;
|
|
2149
|
+
}
|
|
2150
|
+
function matchPattern(path, pattern) {
|
|
2151
|
+
const pathSegments = path.split("/").filter(Boolean);
|
|
2152
|
+
const patternSegments = pattern.split("/").filter(Boolean);
|
|
2153
|
+
if (patternSegments.length > pathSegments.length)
|
|
2154
|
+
return;
|
|
2155
|
+
const params = {};
|
|
2156
|
+
for (let i = 0;i < patternSegments.length; i++) {
|
|
2157
|
+
const seg = patternSegments[i];
|
|
2158
|
+
const val = pathSegments[i];
|
|
2159
|
+
if (seg.startsWith(":")) {
|
|
2160
|
+
params[seg.slice(1)] = val;
|
|
2161
|
+
} else if (seg !== val) {
|
|
2162
|
+
return;
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
return { pattern, params };
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
// src/ssr-single-pass.ts
|
|
2169
|
+
async function ssrRenderSinglePass(module, url, options) {
|
|
2170
|
+
if (options?.prefetch === false) {
|
|
2171
|
+
return ssrRenderToString(module, url, options);
|
|
2172
|
+
}
|
|
2173
|
+
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
2174
|
+
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
2175
|
+
ensureDomShim2();
|
|
2176
|
+
const zeroDiscoveryData = attemptZeroDiscovery(normalizedUrl, module, options, ssrTimeout);
|
|
2177
|
+
if (zeroDiscoveryData) {
|
|
2178
|
+
return renderWithPrefetchedData(module, normalizedUrl, zeroDiscoveryData, options);
|
|
2179
|
+
}
|
|
2180
|
+
const discoveryCtx = createRequestContext(normalizedUrl);
|
|
2181
|
+
if (options?.ssrAuth) {
|
|
2182
|
+
discoveryCtx.ssrAuth = options.ssrAuth;
|
|
2183
|
+
}
|
|
2184
|
+
const discoveredData = await ssrStorage.run(discoveryCtx, async () => {
|
|
2185
|
+
try {
|
|
2186
|
+
setGlobalSSRTimeout(ssrTimeout);
|
|
2187
|
+
const createApp = resolveAppFactory2(module);
|
|
2188
|
+
createApp();
|
|
2189
|
+
if (discoveryCtx.ssrRedirect) {
|
|
2190
|
+
return { redirect: discoveryCtx.ssrRedirect };
|
|
2191
|
+
}
|
|
2192
|
+
if (discoveryCtx.pendingRouteComponents?.size) {
|
|
2193
|
+
const entries = Array.from(discoveryCtx.pendingRouteComponents.entries());
|
|
2194
|
+
const results = await Promise.allSettled(entries.map(([route, promise]) => Promise.race([
|
|
2195
|
+
promise.then((mod) => ({ route, factory: mod.default })),
|
|
2196
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("lazy route timeout")), ssrTimeout))
|
|
2197
|
+
])));
|
|
2198
|
+
discoveryCtx.resolvedComponents = new Map;
|
|
2199
|
+
for (const result of results) {
|
|
2200
|
+
if (result.status === "fulfilled") {
|
|
2201
|
+
const { route, factory } = result.value;
|
|
2202
|
+
discoveryCtx.resolvedComponents.set(route, factory);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
discoveryCtx.pendingRouteComponents = undefined;
|
|
2206
|
+
}
|
|
2207
|
+
const queries = getSSRQueries();
|
|
2208
|
+
const eligibleQueries = filterByEntityAccess(queries, options?.manifest?.entityAccess, options?.prefetchSession);
|
|
2209
|
+
const resolvedQueries = [];
|
|
2210
|
+
if (eligibleQueries.length > 0) {
|
|
2211
|
+
await Promise.allSettled(eligibleQueries.map(({ promise, timeout, resolve, key }) => Promise.race([
|
|
2212
|
+
promise.then((data) => {
|
|
2213
|
+
resolve(data);
|
|
2214
|
+
resolvedQueries.push({ key, data });
|
|
2215
|
+
return "resolved";
|
|
2216
|
+
}),
|
|
2217
|
+
new Promise((r) => setTimeout(r, timeout || ssrTimeout)).then(() => "timeout")
|
|
2218
|
+
])));
|
|
2219
|
+
}
|
|
2220
|
+
return {
|
|
2221
|
+
resolvedQueries,
|
|
2222
|
+
resolvedComponents: discoveryCtx.resolvedComponents
|
|
2223
|
+
};
|
|
2224
|
+
} finally {
|
|
2225
|
+
clearGlobalSSRTimeout();
|
|
2226
|
+
}
|
|
2227
|
+
});
|
|
2228
|
+
if ("redirect" in discoveredData) {
|
|
2229
|
+
return {
|
|
2230
|
+
html: "",
|
|
2231
|
+
css: "",
|
|
2232
|
+
ssrData: [],
|
|
2233
|
+
headTags: "",
|
|
2234
|
+
redirect: discoveredData.redirect
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
const renderCtx = createRequestContext(normalizedUrl);
|
|
2238
|
+
if (options?.ssrAuth) {
|
|
2239
|
+
renderCtx.ssrAuth = options.ssrAuth;
|
|
2240
|
+
}
|
|
2241
|
+
for (const { key, data } of discoveredData.resolvedQueries) {
|
|
2242
|
+
renderCtx.queryCache.set(key, data);
|
|
2243
|
+
}
|
|
2244
|
+
renderCtx.resolvedComponents = discoveredData.resolvedComponents ?? new Map;
|
|
2245
|
+
return ssrStorage.run(renderCtx, async () => {
|
|
2246
|
+
try {
|
|
2247
|
+
setGlobalSSRTimeout(ssrTimeout);
|
|
2248
|
+
const createApp = resolveAppFactory2(module);
|
|
2249
|
+
let themeCss = "";
|
|
2250
|
+
let themePreloadTags = "";
|
|
2251
|
+
if (module.theme) {
|
|
2252
|
+
try {
|
|
2253
|
+
const compiled = compileTheme2(module.theme, {
|
|
2254
|
+
fallbackMetrics: options?.fallbackMetrics
|
|
2255
|
+
});
|
|
2256
|
+
themeCss = compiled.css;
|
|
2257
|
+
themePreloadTags = compiled.preloadTags;
|
|
2258
|
+
} catch (e) {
|
|
2259
|
+
console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
const app = createApp();
|
|
2263
|
+
const vnode = toVNode(app);
|
|
2264
|
+
const stream = renderToStream(vnode);
|
|
2265
|
+
const html = await streamToString(stream);
|
|
2266
|
+
const css = collectCSS2(themeCss, module);
|
|
2267
|
+
const ssrData = discoveredData.resolvedQueries.map(({ key, data }) => ({
|
|
2268
|
+
key,
|
|
2269
|
+
data: JSON.parse(JSON.stringify(data))
|
|
2270
|
+
}));
|
|
2271
|
+
return {
|
|
2272
|
+
html,
|
|
2273
|
+
css,
|
|
2274
|
+
ssrData,
|
|
2275
|
+
headTags: themePreloadTags,
|
|
2276
|
+
discoveredRoutes: renderCtx.discoveredRoutes,
|
|
2277
|
+
matchedRoutePatterns: renderCtx.matchedRoutePatterns
|
|
2278
|
+
};
|
|
2279
|
+
} finally {
|
|
2280
|
+
clearGlobalSSRTimeout();
|
|
2281
|
+
}
|
|
2282
|
+
});
|
|
2283
|
+
}
|
|
2284
|
+
function attemptZeroDiscovery(url, module, options, ssrTimeout) {
|
|
2285
|
+
const manifest = options?.manifest;
|
|
2286
|
+
if (!manifest?.routeEntries || !module.api)
|
|
2287
|
+
return null;
|
|
2288
|
+
const matches = matchUrlToPatterns(url, manifest.routePatterns);
|
|
2289
|
+
if (matches.length === 0)
|
|
2290
|
+
return null;
|
|
2291
|
+
const allQueries = [];
|
|
2292
|
+
let mergedParams = {};
|
|
2293
|
+
for (const match of matches) {
|
|
2294
|
+
const entry = manifest.routeEntries[match.pattern];
|
|
2295
|
+
if (entry) {
|
|
2296
|
+
allQueries.push(...entry.queries);
|
|
2297
|
+
}
|
|
2298
|
+
mergedParams = { ...mergedParams, ...match.params };
|
|
2299
|
+
}
|
|
2300
|
+
if (allQueries.length === 0)
|
|
2301
|
+
return null;
|
|
2302
|
+
const descriptors = reconstructDescriptors(allQueries, mergedParams, module.api);
|
|
2303
|
+
if (descriptors.length === 0)
|
|
2304
|
+
return null;
|
|
2305
|
+
return prefetchFromDescriptors(descriptors, ssrTimeout);
|
|
2306
|
+
}
|
|
2307
|
+
async function prefetchFromDescriptors(descriptors, ssrTimeout) {
|
|
2308
|
+
const resolvedQueries = [];
|
|
2309
|
+
await Promise.allSettled(descriptors.map(({ key, fetch: fetchFn }) => Promise.race([
|
|
2310
|
+
fetchFn().then((result) => {
|
|
2311
|
+
const data = unwrapResult(result);
|
|
2312
|
+
resolvedQueries.push({ key, data });
|
|
2313
|
+
return "resolved";
|
|
2314
|
+
}),
|
|
2315
|
+
new Promise((r) => setTimeout(r, ssrTimeout)).then(() => "timeout")
|
|
2316
|
+
])));
|
|
2317
|
+
return { resolvedQueries };
|
|
2318
|
+
}
|
|
2319
|
+
function unwrapResult(result) {
|
|
2320
|
+
if (result && typeof result === "object" && "ok" in result && "data" in result) {
|
|
2321
|
+
const r = result;
|
|
2322
|
+
if (r.ok)
|
|
2323
|
+
return r.data;
|
|
2324
|
+
}
|
|
2325
|
+
return result;
|
|
2326
|
+
}
|
|
2327
|
+
async function renderWithPrefetchedData(module, normalizedUrl, prefetchedData, options) {
|
|
2328
|
+
const data = await prefetchedData;
|
|
2329
|
+
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
2330
|
+
const renderCtx = createRequestContext(normalizedUrl);
|
|
2331
|
+
if (options?.ssrAuth) {
|
|
2332
|
+
renderCtx.ssrAuth = options.ssrAuth;
|
|
2333
|
+
}
|
|
2334
|
+
for (const { key, data: queryData } of data.resolvedQueries) {
|
|
2335
|
+
renderCtx.queryCache.set(key, queryData);
|
|
2336
|
+
}
|
|
2337
|
+
renderCtx.resolvedComponents = new Map;
|
|
2338
|
+
return ssrStorage.run(renderCtx, async () => {
|
|
2339
|
+
try {
|
|
2340
|
+
setGlobalSSRTimeout(ssrTimeout);
|
|
2341
|
+
const createApp = resolveAppFactory2(module);
|
|
2342
|
+
let themeCss = "";
|
|
2343
|
+
let themePreloadTags = "";
|
|
2344
|
+
if (module.theme) {
|
|
2345
|
+
try {
|
|
2346
|
+
const compiled = compileTheme2(module.theme, {
|
|
2347
|
+
fallbackMetrics: options?.fallbackMetrics
|
|
2348
|
+
});
|
|
2349
|
+
themeCss = compiled.css;
|
|
2350
|
+
themePreloadTags = compiled.preloadTags;
|
|
2351
|
+
} catch (e) {
|
|
2352
|
+
console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
const app = createApp();
|
|
2356
|
+
const vnode = toVNode(app);
|
|
2357
|
+
const stream = renderToStream(vnode);
|
|
2358
|
+
const html = await streamToString(stream);
|
|
2359
|
+
if (renderCtx.ssrRedirect) {
|
|
2360
|
+
return {
|
|
2361
|
+
html: "",
|
|
2362
|
+
css: "",
|
|
2363
|
+
ssrData: [],
|
|
2364
|
+
headTags: "",
|
|
2365
|
+
redirect: renderCtx.ssrRedirect,
|
|
2366
|
+
discoveredRoutes: renderCtx.discoveredRoutes,
|
|
2367
|
+
matchedRoutePatterns: renderCtx.matchedRoutePatterns
|
|
2368
|
+
};
|
|
2369
|
+
}
|
|
2370
|
+
const css = collectCSS2(themeCss, module);
|
|
2371
|
+
const ssrData = data.resolvedQueries.map(({ key, data: d }) => ({
|
|
2372
|
+
key,
|
|
2373
|
+
data: JSON.parse(JSON.stringify(d))
|
|
2374
|
+
}));
|
|
2375
|
+
return {
|
|
2376
|
+
html,
|
|
2377
|
+
css,
|
|
2378
|
+
ssrData,
|
|
2379
|
+
headTags: themePreloadTags,
|
|
2380
|
+
discoveredRoutes: renderCtx.discoveredRoutes,
|
|
2381
|
+
matchedRoutePatterns: renderCtx.matchedRoutePatterns
|
|
2382
|
+
};
|
|
2383
|
+
} finally {
|
|
2384
|
+
clearGlobalSSRTimeout();
|
|
2385
|
+
}
|
|
2386
|
+
});
|
|
2387
|
+
}
|
|
2388
|
+
var domShimInstalled2 = false;
|
|
2389
|
+
function ensureDomShim2() {
|
|
2390
|
+
if (domShimInstalled2 && typeof document !== "undefined")
|
|
2391
|
+
return;
|
|
2392
|
+
domShimInstalled2 = true;
|
|
2393
|
+
installDomShim();
|
|
2394
|
+
}
|
|
2395
|
+
function resolveAppFactory2(module) {
|
|
2396
|
+
const createApp = module.default || module.App;
|
|
2397
|
+
if (typeof createApp !== "function") {
|
|
2398
|
+
throw new Error("App entry must export a default function or named App function");
|
|
2399
|
+
}
|
|
2400
|
+
return createApp;
|
|
2401
|
+
}
|
|
2402
|
+
function filterByEntityAccess(queries, entityAccess, session) {
|
|
2403
|
+
if (!entityAccess || !session)
|
|
2404
|
+
return queries;
|
|
2405
|
+
return queries.filter(({ key }) => {
|
|
2406
|
+
const entity = extractEntityFromKey(key);
|
|
2407
|
+
const method = extractMethodFromKey(key);
|
|
2408
|
+
if (!entity)
|
|
2409
|
+
return true;
|
|
2410
|
+
const entityRules = entityAccess[entity];
|
|
2411
|
+
if (!entityRules)
|
|
2412
|
+
return true;
|
|
2413
|
+
const rule = entityRules[method];
|
|
2414
|
+
if (!rule)
|
|
2415
|
+
return true;
|
|
2416
|
+
return evaluateAccessRule(rule, session);
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
function extractEntityFromKey(key) {
|
|
2420
|
+
const pathStart = key.indexOf(":/");
|
|
2421
|
+
if (pathStart === -1)
|
|
2422
|
+
return;
|
|
2423
|
+
const path = key.slice(pathStart + 2);
|
|
2424
|
+
const firstSlash = path.indexOf("/");
|
|
2425
|
+
const questionMark = path.indexOf("?");
|
|
2426
|
+
if (firstSlash === -1 && questionMark === -1)
|
|
2427
|
+
return path;
|
|
2428
|
+
if (firstSlash === -1)
|
|
2429
|
+
return path.slice(0, questionMark);
|
|
2430
|
+
if (questionMark === -1)
|
|
2431
|
+
return path.slice(0, firstSlash);
|
|
2432
|
+
return path.slice(0, Math.min(firstSlash, questionMark));
|
|
2433
|
+
}
|
|
2434
|
+
function extractMethodFromKey(key) {
|
|
2435
|
+
const pathStart = key.indexOf(":/");
|
|
2436
|
+
if (pathStart === -1)
|
|
2437
|
+
return "list";
|
|
2438
|
+
const path = key.slice(pathStart + 2);
|
|
2439
|
+
const cleanPath = path.split("?")[0] ?? "";
|
|
2440
|
+
const segments = cleanPath.split("/").filter(Boolean);
|
|
2441
|
+
return segments.length > 1 ? "get" : "list";
|
|
2442
|
+
}
|
|
2443
|
+
function collectCSS2(themeCss, module) {
|
|
2444
|
+
const alreadyIncluded = new Set;
|
|
2445
|
+
if (themeCss)
|
|
2446
|
+
alreadyIncluded.add(themeCss);
|
|
2447
|
+
if (module.styles) {
|
|
2448
|
+
for (const s of module.styles)
|
|
2449
|
+
alreadyIncluded.add(s);
|
|
2450
|
+
}
|
|
2451
|
+
const componentCss = module.getInjectedCSS ? module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s)) : [];
|
|
2452
|
+
const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
|
|
2453
|
+
const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
|
|
2454
|
+
`)}</style>` : "";
|
|
2455
|
+
const componentTag = componentCss.length > 0 ? `<style data-vertz-css>${componentCss.join(`
|
|
2456
|
+
`)}</style>` : "";
|
|
2457
|
+
return [themeTag, globalTag, componentTag].filter(Boolean).join(`
|
|
2458
|
+
`);
|
|
2459
|
+
}
|
|
2460
|
+
|
|
1698
2461
|
// src/upstream-watcher.ts
|
|
1699
2462
|
import { existsSync, lstatSync, readdirSync, realpathSync, watch } from "fs";
|
|
1700
2463
|
import { join as join3 } from "path";
|
|
@@ -2143,6 +2906,18 @@ function clearSSRRequireCache() {
|
|
|
2143
2906
|
}
|
|
2144
2907
|
return keys.length;
|
|
2145
2908
|
}
|
|
2909
|
+
function collectFiles(dir) {
|
|
2910
|
+
const files = [];
|
|
2911
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
2912
|
+
const fullPath = resolve(dir, entry.name);
|
|
2913
|
+
if (entry.isDirectory()) {
|
|
2914
|
+
files.push(...collectFiles(fullPath));
|
|
2915
|
+
} else {
|
|
2916
|
+
files.push(fullPath);
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
return files;
|
|
2920
|
+
}
|
|
2146
2921
|
function createBunDevServer(options) {
|
|
2147
2922
|
const {
|
|
2148
2923
|
entry,
|
|
@@ -2173,6 +2948,7 @@ function createBunDevServer(options) {
|
|
|
2173
2948
|
mkdirSync(devDir, { recursive: true });
|
|
2174
2949
|
const logger = createDebugLogger(devDir);
|
|
2175
2950
|
const diagnostics = new DiagnosticsCollector;
|
|
2951
|
+
let aotManifestManager = null;
|
|
2176
2952
|
let server = null;
|
|
2177
2953
|
let srcWatcherRef = null;
|
|
2178
2954
|
let refreshTimeout = null;
|
|
@@ -2540,6 +3316,83 @@ function createBunDevServer(options) {
|
|
|
2540
3316
|
console.warn("[Server] Failed to extract font metrics:", e);
|
|
2541
3317
|
}
|
|
2542
3318
|
}
|
|
3319
|
+
let prefetchManager = null;
|
|
3320
|
+
const srcDir = resolve(projectRoot, "src");
|
|
3321
|
+
const routerCandidates = [resolve(srcDir, "router.tsx"), resolve(srcDir, "router.ts")];
|
|
3322
|
+
const routerPath = routerCandidates.find((p) => existsSync2(p));
|
|
3323
|
+
if (routerPath) {
|
|
3324
|
+
prefetchManager = createPrefetchManifestManager({
|
|
3325
|
+
routerPath,
|
|
3326
|
+
readFile: (path) => {
|
|
3327
|
+
try {
|
|
3328
|
+
return readFileSync2(path, "utf-8");
|
|
3329
|
+
} catch {
|
|
3330
|
+
return;
|
|
3331
|
+
}
|
|
3332
|
+
},
|
|
3333
|
+
resolveImport: (specifier, fromFile) => {
|
|
3334
|
+
if (!specifier.startsWith("."))
|
|
3335
|
+
return;
|
|
3336
|
+
const dir = dirname(fromFile);
|
|
3337
|
+
const base = resolve(dir, specifier);
|
|
3338
|
+
for (const ext of [".tsx", ".ts", ".jsx", ".js"]) {
|
|
3339
|
+
const candidate = `${base}${ext}`;
|
|
3340
|
+
if (existsSync2(candidate))
|
|
3341
|
+
return candidate;
|
|
3342
|
+
}
|
|
3343
|
+
for (const ext of [".tsx", ".ts"]) {
|
|
3344
|
+
const candidate = resolve(base, `index${ext}`);
|
|
3345
|
+
if (existsSync2(candidate))
|
|
3346
|
+
return candidate;
|
|
3347
|
+
}
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
});
|
|
3351
|
+
try {
|
|
3352
|
+
const buildStart = performance.now();
|
|
3353
|
+
prefetchManager.build();
|
|
3354
|
+
const buildMs = Math.round(performance.now() - buildStart);
|
|
3355
|
+
logger.log("prefetch", "initial-build", { routerPath, durationMs: buildMs });
|
|
3356
|
+
if (logRequests) {
|
|
3357
|
+
const manifest = prefetchManager.getSSRManifest();
|
|
3358
|
+
const routeCount = manifest?.routePatterns.length ?? 0;
|
|
3359
|
+
console.log(`[Server] Prefetch manifest built (${routeCount} routes, ${buildMs}ms)`);
|
|
3360
|
+
}
|
|
3361
|
+
} catch (e) {
|
|
3362
|
+
console.warn("[Server] Failed to build prefetch manifest:", e instanceof Error ? e.message : e);
|
|
3363
|
+
prefetchManager = null;
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
aotManifestManager = createAotManifestManager({
|
|
3367
|
+
readFile: (path) => {
|
|
3368
|
+
try {
|
|
3369
|
+
return readFileSync2(path, "utf-8");
|
|
3370
|
+
} catch {
|
|
3371
|
+
return;
|
|
3372
|
+
}
|
|
3373
|
+
},
|
|
3374
|
+
listFiles: () => {
|
|
3375
|
+
try {
|
|
3376
|
+
return collectFiles(srcDir);
|
|
3377
|
+
} catch {
|
|
3378
|
+
return [];
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
});
|
|
3382
|
+
try {
|
|
3383
|
+
const aotStart = performance.now();
|
|
3384
|
+
aotManifestManager.build();
|
|
3385
|
+
const aotMs = Math.round(performance.now() - aotStart);
|
|
3386
|
+
logger.log("aot", "initial-build", { durationMs: aotMs });
|
|
3387
|
+
if (logRequests) {
|
|
3388
|
+
const manifest = aotManifestManager.getManifest();
|
|
3389
|
+
const count = manifest ? Object.keys(manifest.components).length : 0;
|
|
3390
|
+
console.log(`[Server] AOT manifest built (${count} components, ${aotMs}ms)`);
|
|
3391
|
+
}
|
|
3392
|
+
} catch (e) {
|
|
3393
|
+
console.warn("[Server] Failed to build AOT manifest:", e instanceof Error ? e.message : e);
|
|
3394
|
+
aotManifestManager = null;
|
|
3395
|
+
}
|
|
2543
3396
|
mkdirSync(devDir, { recursive: true });
|
|
2544
3397
|
const frInitPath = resolve(devDir, "fast-refresh-init.ts");
|
|
2545
3398
|
writeFileSync2(frInitPath, `import '@vertz/ui-server/fast-refresh-runtime';
|
|
@@ -2630,6 +3483,18 @@ if (import.meta.hot) import.meta.hot.accept();
|
|
|
2630
3483
|
if (pathname === "/__vertz_diagnostics") {
|
|
2631
3484
|
return Response.json(diagnostics.getSnapshot());
|
|
2632
3485
|
}
|
|
3486
|
+
if (pathname === "/__vertz_ssr_aot") {
|
|
3487
|
+
if (!aotManifestManager) {
|
|
3488
|
+
return Response.json({ error: "AOT manifest manager not available" }, { status: 404 });
|
|
3489
|
+
}
|
|
3490
|
+
return Response.json(aotManifestManager.getDiagnostics().getSnapshot());
|
|
3491
|
+
}
|
|
3492
|
+
if (pathname === "/__vertz_prefetch_manifest") {
|
|
3493
|
+
if (!prefetchManager) {
|
|
3494
|
+
return Response.json({ error: "No prefetch manifest available (router file not found)" }, { status: 404 });
|
|
3495
|
+
}
|
|
3496
|
+
return Response.json(prefetchManager.getSnapshot());
|
|
3497
|
+
}
|
|
2633
3498
|
if (pathname === "/_vertz/image") {
|
|
2634
3499
|
return handleDevImageProxy(request);
|
|
2635
3500
|
}
|
|
@@ -2756,10 +3621,12 @@ data: {}
|
|
|
2756
3621
|
const doRender = async () => {
|
|
2757
3622
|
logger.log("ssr", "render-start", { url: pathname });
|
|
2758
3623
|
const ssrStart = performance.now();
|
|
2759
|
-
const result = await
|
|
3624
|
+
const result = await ssrRenderSinglePass(ssrMod, pathname + url.search, {
|
|
2760
3625
|
ssrTimeout: 300,
|
|
2761
3626
|
fallbackMetrics: fontFallbackMetrics,
|
|
2762
|
-
ssrAuth
|
|
3627
|
+
ssrAuth,
|
|
3628
|
+
manifest: prefetchManager?.getSSRManifest(),
|
|
3629
|
+
prefetchSession: toPrefetchSession(ssrAuth)
|
|
2763
3630
|
});
|
|
2764
3631
|
logger.log("ssr", "render-done", {
|
|
2765
3632
|
url: pathname,
|
|
@@ -2969,7 +3836,6 @@ data: {}
|
|
|
2969
3836
|
console.warn("[Server] Could not discover HMR bundled URL:", e);
|
|
2970
3837
|
}
|
|
2971
3838
|
}
|
|
2972
|
-
const srcDir = resolve(projectRoot, "src");
|
|
2973
3839
|
stopped = false;
|
|
2974
3840
|
if (existsSync2(srcDir)) {
|
|
2975
3841
|
srcWatcherRef = watch2(srcDir, { recursive: true }, (_event, filename) => {
|
|
@@ -3040,7 +3906,30 @@ data: {}
|
|
|
3040
3906
|
const { changed } = updateServerManifest(changedFilePath, source);
|
|
3041
3907
|
const manifestDurationMs = Math.round(performance.now() - manifestStartMs);
|
|
3042
3908
|
diagnostics.recordManifestUpdate(lastChangedFile, changed, manifestDurationMs);
|
|
3043
|
-
|
|
3909
|
+
if (prefetchManager) {
|
|
3910
|
+
const prefetchStart = performance.now();
|
|
3911
|
+
prefetchManager.onFileChange(changedFilePath, source);
|
|
3912
|
+
const prefetchMs = Math.round(performance.now() - prefetchStart);
|
|
3913
|
+
logger.log("prefetch", "rebuild", {
|
|
3914
|
+
file: lastChangedFile,
|
|
3915
|
+
durationMs: prefetchMs,
|
|
3916
|
+
isRouter: changedFilePath === routerPath
|
|
3917
|
+
});
|
|
3918
|
+
}
|
|
3919
|
+
if (aotManifestManager) {
|
|
3920
|
+
const aotStart = performance.now();
|
|
3921
|
+
aotManifestManager.onFileChange(changedFilePath, source);
|
|
3922
|
+
const aotMs = Math.round(performance.now() - aotStart);
|
|
3923
|
+
logger.log("aot", "rebuild", {
|
|
3924
|
+
file: lastChangedFile,
|
|
3925
|
+
durationMs: aotMs
|
|
3926
|
+
});
|
|
3927
|
+
}
|
|
3928
|
+
} catch {
|
|
3929
|
+
if (aotManifestManager) {
|
|
3930
|
+
aotManifestManager.onFileChange(changedFilePath, "");
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3044
3933
|
}
|
|
3045
3934
|
if (stopped)
|
|
3046
3935
|
return;
|