@vertz/ui-server 0.2.34 → 0.2.36
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.d.ts +2 -1
- package/dist/bun-dev-server.js +32 -6
- package/dist/bun-plugin/index.js +73 -14
- package/dist/index.d.ts +32 -2
- package/dist/index.js +9 -5
- package/dist/node-handler.d.ts +20 -0
- package/dist/node-handler.js +2 -2
- package/dist/shared/{chunk-xdb8qn68.js → chunk-9s2dppdh.js} +1 -1
- package/dist/shared/{chunk-2kx402c1.js → chunk-hjsbx25c.js} +7 -5
- package/dist/shared/{chunk-hx7drzm3.js → chunk-wq2ke61r.js} +31 -6
- package/dist/ssr/index.d.ts +21 -1
- package/dist/ssr/index.js +2 -2
- package/package.json +5 -5
package/dist/bun-dev-server.d.ts
CHANGED
|
@@ -126,6 +126,7 @@ interface ErrorDetail {
|
|
|
126
126
|
}
|
|
127
127
|
type ErrorCategory = "build" | "resolve" | "runtime" | "ssr";
|
|
128
128
|
declare function isStaleGraphError(message: string): boolean;
|
|
129
|
+
declare function parsePluginError(text: string): ErrorDetail | null;
|
|
129
130
|
/**
|
|
130
131
|
* Detect Bun's reload stub — the response served when the dev bundler
|
|
131
132
|
* fails to compile a client module. The stub is literally:
|
|
@@ -257,4 +258,4 @@ declare function clearSSRRequireCache(): number;
|
|
|
257
258
|
* SSR is always on. HMR always works. No mode toggle needed.
|
|
258
259
|
*/
|
|
259
260
|
declare function createBunDevServer(options: BunDevServerOptions): BunDevServer;
|
|
260
|
-
export { shouldCheckStaleBundler, parseHMRAssets, isStaleGraphError, isReloadStub, generateSSRPageHtml, formatTerminalRuntimeError, detectFaviconTag, createRuntimeErrorDeduplicator, createFetchInterceptor, createBunDevServer, clearSSRRequireCache, buildScriptTag, SSRPageHtmlOptions, HMRAssets, FetchInterceptorOptions, ErrorDetail, ErrorCategory, BunDevServerOptions, BunDevServer };
|
|
261
|
+
export { shouldCheckStaleBundler, parsePluginError, parseHMRAssets, isStaleGraphError, isReloadStub, generateSSRPageHtml, formatTerminalRuntimeError, detectFaviconTag, createRuntimeErrorDeduplicator, createFetchInterceptor, createBunDevServer, clearSSRRequireCache, buildScriptTag, SSRPageHtmlOptions, HMRAssets, FetchInterceptorOptions, ErrorDetail, ErrorCategory, BunDevServerOptions, BunDevServer };
|
package/dist/bun-dev-server.js
CHANGED
|
@@ -2134,11 +2134,12 @@ function resolveQueryBindings(bindings, routeParams) {
|
|
|
2134
2134
|
}
|
|
2135
2135
|
|
|
2136
2136
|
// src/ssr-route-matcher.ts
|
|
2137
|
-
function matchUrlToPatterns(url, patterns) {
|
|
2137
|
+
function matchUrlToPatterns(url, patterns, options) {
|
|
2138
2138
|
const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
|
|
2139
|
+
const exact = options?.exact ?? false;
|
|
2139
2140
|
const matches = [];
|
|
2140
2141
|
for (const pattern of patterns) {
|
|
2141
|
-
const result = matchPattern(path, pattern);
|
|
2142
|
+
const result = matchPattern(path, pattern, exact);
|
|
2142
2143
|
if (result) {
|
|
2143
2144
|
matches.push(result);
|
|
2144
2145
|
}
|
|
@@ -2150,11 +2151,16 @@ function matchUrlToPatterns(url, patterns) {
|
|
|
2150
2151
|
});
|
|
2151
2152
|
return matches;
|
|
2152
2153
|
}
|
|
2153
|
-
function matchPattern(path, pattern) {
|
|
2154
|
+
function matchPattern(path, pattern, exact) {
|
|
2154
2155
|
const pathSegments = path.split("/").filter(Boolean);
|
|
2155
2156
|
const patternSegments = pattern.split("/").filter(Boolean);
|
|
2156
|
-
if (
|
|
2157
|
-
|
|
2157
|
+
if (exact) {
|
|
2158
|
+
if (patternSegments.length !== pathSegments.length)
|
|
2159
|
+
return;
|
|
2160
|
+
} else {
|
|
2161
|
+
if (patternSegments.length > pathSegments.length)
|
|
2162
|
+
return;
|
|
2163
|
+
}
|
|
2158
2164
|
const params = {};
|
|
2159
2165
|
for (let i = 0;i < patternSegments.length; i++) {
|
|
2160
2166
|
const seg = patternSegments[i];
|
|
@@ -2553,6 +2559,19 @@ var STALE_GRAPH_PATTERNS = [
|
|
|
2553
2559
|
function isStaleGraphError(message) {
|
|
2554
2560
|
return STALE_GRAPH_PATTERNS.some((pattern) => pattern.test(message));
|
|
2555
2561
|
}
|
|
2562
|
+
var PLUGIN_ERROR_PATTERN = /\[vertz-bun-plugin\] Failed to process (.+?): ([\s\S]*)/;
|
|
2563
|
+
var LINE_COL_PATTERN = /\((\d+):(\d+)\)/;
|
|
2564
|
+
function parsePluginError(text) {
|
|
2565
|
+
const match = text.match(PLUGIN_ERROR_PATTERN);
|
|
2566
|
+
if (!match)
|
|
2567
|
+
return null;
|
|
2568
|
+
const file = match[1];
|
|
2569
|
+
const message = match[2]?.trim() || "Compilation failed";
|
|
2570
|
+
const locMatch = message.match(LINE_COL_PATTERN);
|
|
2571
|
+
const line = locMatch ? Number(locMatch[1]) : undefined;
|
|
2572
|
+
const column = locMatch ? Number(locMatch[2]) : undefined;
|
|
2573
|
+
return { message, file, line, column };
|
|
2574
|
+
}
|
|
2556
2575
|
function isReloadStub(text) {
|
|
2557
2576
|
return text.trimStart().startsWith("try{location.reload()}");
|
|
2558
2577
|
}
|
|
@@ -3119,7 +3138,13 @@ function createBunDevServer(options) {
|
|
|
3119
3138
|
const text = args.map((a) => typeof a === "string" ? a : String(a)).join(" ");
|
|
3120
3139
|
if (!text.startsWith("[Server]")) {
|
|
3121
3140
|
lastBuildError = text;
|
|
3122
|
-
|
|
3141
|
+
const pluginErr = parsePluginError(text);
|
|
3142
|
+
if (pluginErr && text !== lastBroadcastedError) {
|
|
3143
|
+
lastBroadcastedError = text;
|
|
3144
|
+
const absFile = pluginErr.file ? resolve(projectRoot, pluginErr.file) : undefined;
|
|
3145
|
+
const lineText = pluginErr.line && absFile ? readLineText(absFile, pluginErr.line) : undefined;
|
|
3146
|
+
broadcastError("build", [{ ...pluginErr, absFile, lineText }]);
|
|
3147
|
+
} else if (resolvePatterns.some((p) => text.includes(p)) && text !== lastBroadcastedError) {
|
|
3123
3148
|
lastBroadcastedError = text;
|
|
3124
3149
|
broadcastError("resolve", [{ message: text }]);
|
|
3125
3150
|
} else {
|
|
@@ -4190,6 +4215,7 @@ data: {}
|
|
|
4190
4215
|
}
|
|
4191
4216
|
export {
|
|
4192
4217
|
shouldCheckStaleBundler,
|
|
4218
|
+
parsePluginError,
|
|
4193
4219
|
parseHMRAssets,
|
|
4194
4220
|
isStaleGraphError,
|
|
4195
4221
|
isReloadStub,
|
package/dist/bun-plugin/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
// src/bun-plugin/plugin.ts
|
|
11
11
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
12
|
-
import { dirname, relative, resolve as
|
|
12
|
+
import { dirname, relative, resolve as resolve3 } from "path";
|
|
13
13
|
import remapping from "@ampproject/remapping";
|
|
14
14
|
import {
|
|
15
15
|
ComponentAnalyzer,
|
|
@@ -55,6 +55,39 @@ function injectContextStableIds(source, sourceFile, relFilePath) {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// src/bun-plugin/native-compiler-loader.ts
|
|
59
|
+
import { existsSync } from "fs";
|
|
60
|
+
import { join, resolve } from "path";
|
|
61
|
+
var __dirname = "/home/runner/work/vertz/vertz/packages/ui-server/src/bun-plugin";
|
|
62
|
+
function tryLoadNativeCompiler() {
|
|
63
|
+
if (process.env.VERTZ_NATIVE_COMPILER !== "1") {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const platform = process.platform === "darwin" ? "darwin" : "linux";
|
|
67
|
+
const arch = process.arch === "arm64" ? "arm64" : "x64";
|
|
68
|
+
const binaryName = `vertz-compiler.${platform}-${arch}.node`;
|
|
69
|
+
try {
|
|
70
|
+
const modulePath = __require.resolve(`@vertz/native-compiler/${binaryName}`);
|
|
71
|
+
return __require(modulePath);
|
|
72
|
+
} catch {}
|
|
73
|
+
let dir = resolve(__dirname);
|
|
74
|
+
for (let i = 0;i < 10; i++) {
|
|
75
|
+
const candidate = join(dir, "native", "vertz-compiler", binaryName);
|
|
76
|
+
if (existsSync(candidate)) {
|
|
77
|
+
try {
|
|
78
|
+
return __require(candidate);
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const parent = resolve(dir, "..");
|
|
84
|
+
if (parent === dir)
|
|
85
|
+
break;
|
|
86
|
+
dir = parent;
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
58
91
|
// src/bun-plugin/entity-schema-loader.ts
|
|
59
92
|
import { readFileSync } from "fs";
|
|
60
93
|
function loadEntitySchema(schemaPath) {
|
|
@@ -491,8 +524,8 @@ function filePathHash(filePath) {
|
|
|
491
524
|
|
|
492
525
|
// src/bun-plugin/image-processor.ts
|
|
493
526
|
import { createHash } from "crypto";
|
|
494
|
-
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
495
|
-
import { basename, extname, resolve } from "path";
|
|
527
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
528
|
+
import { basename, extname, resolve as resolve2 } from "path";
|
|
496
529
|
import sharp from "sharp";
|
|
497
530
|
var FORMAT_MAP = {
|
|
498
531
|
jpeg: { ext: ".jpg", mime: "image/jpeg" },
|
|
@@ -505,7 +538,7 @@ var FORMAT_MAP = {
|
|
|
505
538
|
};
|
|
506
539
|
async function processImage(opts) {
|
|
507
540
|
const { sourcePath, width, height, quality, fit, outputDir } = opts;
|
|
508
|
-
if (!
|
|
541
|
+
if (!existsSync2(sourcePath)) {
|
|
509
542
|
return { ok: false, error: `Image not found: ${sourcePath}` };
|
|
510
543
|
}
|
|
511
544
|
const sourceBuffer = readFileSync2(sourcePath);
|
|
@@ -518,10 +551,10 @@ async function processImage(opts) {
|
|
|
518
551
|
const webp1xName = `${name}-${hash}-${width}w.webp`;
|
|
519
552
|
const webp2xName = `${name}-${hash}-${width * 2}w.webp`;
|
|
520
553
|
const fallbackName = `${name}-${hash}-${width * 2}w${formatInfo.ext}`;
|
|
521
|
-
const webp1xPath =
|
|
522
|
-
const webp2xPath =
|
|
523
|
-
const fallbackPath =
|
|
524
|
-
if (
|
|
554
|
+
const webp1xPath = resolve2(outputDir, webp1xName);
|
|
555
|
+
const webp2xPath = resolve2(outputDir, webp2xName);
|
|
556
|
+
const fallbackPath = resolve2(outputDir, fallbackName);
|
|
557
|
+
if (existsSync2(webp1xPath) && existsSync2(webp2xPath) && existsSync2(fallbackPath)) {
|
|
525
558
|
return {
|
|
526
559
|
ok: true,
|
|
527
560
|
webp1x: { path: webp1xPath, url: `/__vertz_img/${webp1xName}` },
|
|
@@ -935,14 +968,14 @@ function createVertzBunPlugin(options) {
|
|
|
935
968
|
const fastRefresh = options?.fastRefresh ?? hmr;
|
|
936
969
|
const routeSplitting = options?.routeSplitting ?? false;
|
|
937
970
|
const projectRoot = options?.projectRoot ?? process.cwd();
|
|
938
|
-
const cssOutDir = options?.cssOutDir ??
|
|
971
|
+
const cssOutDir = options?.cssOutDir ?? resolve3(projectRoot, ".vertz", "css");
|
|
939
972
|
const cssExtractor = new CSSExtractor;
|
|
940
973
|
const componentAnalyzer = new ComponentAnalyzer;
|
|
941
974
|
const logger = options?.logger;
|
|
942
975
|
const diagnostics = options?.diagnostics;
|
|
943
976
|
const fileExtractions = new Map;
|
|
944
977
|
const cssSidecarMap = new Map;
|
|
945
|
-
const srcDir = options?.srcDir ??
|
|
978
|
+
const srcDir = options?.srcDir ?? resolve3(projectRoot, "src");
|
|
946
979
|
const frameworkManifestJson = __require(__require.resolve("@vertz/ui/reactivity.json"));
|
|
947
980
|
const manifestResult = generateAllManifests({
|
|
948
981
|
srcDir,
|
|
@@ -997,7 +1030,7 @@ function createVertzBunPlugin(options) {
|
|
|
997
1030
|
}
|
|
998
1031
|
}
|
|
999
1032
|
diagnostics?.recordFieldSelectionManifest(fieldSelectionFileCount);
|
|
1000
|
-
const entitySchemaPath = options?.entitySchemaPath ??
|
|
1033
|
+
const entitySchemaPath = options?.entitySchemaPath ?? resolve3(projectRoot, ".vertz", "generated", "entity-schema.json");
|
|
1001
1034
|
let entitySchema = loadEntitySchema(entitySchemaPath);
|
|
1002
1035
|
if (logger?.isEnabled("fields") && entitySchema) {
|
|
1003
1036
|
logger.log("fields", "entity-schema-loaded", {
|
|
@@ -1005,6 +1038,8 @@ function createVertzBunPlugin(options) {
|
|
|
1005
1038
|
entities: Object.keys(entitySchema).length
|
|
1006
1039
|
});
|
|
1007
1040
|
}
|
|
1041
|
+
const nativeCompiler = tryLoadNativeCompiler();
|
|
1042
|
+
let nativeManifestWarningLogged = false;
|
|
1008
1043
|
mkdirSync2(cssOutDir, { recursive: true });
|
|
1009
1044
|
const plugin = {
|
|
1010
1045
|
name: "vertz-bun-plugin",
|
|
@@ -1094,7 +1129,7 @@ function createVertzBunPlugin(options) {
|
|
|
1094
1129
|
}))
|
|
1095
1130
|
});
|
|
1096
1131
|
}
|
|
1097
|
-
const imageOutputDir =
|
|
1132
|
+
const imageOutputDir = resolve3(projectRoot, ".vertz", "images");
|
|
1098
1133
|
const imageQueue = [];
|
|
1099
1134
|
const imageResult = transformImages(codeForCompile, args.path, {
|
|
1100
1135
|
projectRoot,
|
|
@@ -1124,7 +1159,31 @@ function createVertzBunPlugin(options) {
|
|
|
1124
1159
|
if (imageQueue.length > 0) {
|
|
1125
1160
|
await Promise.all(imageQueue.map((opts) => processImage({ ...opts, fit: opts.fit })));
|
|
1126
1161
|
}
|
|
1127
|
-
const compileResult =
|
|
1162
|
+
const compileResult = nativeCompiler ? (() => {
|
|
1163
|
+
if (!nativeManifestWarningLogged && manifests.size > 0) {
|
|
1164
|
+
nativeManifestWarningLogged = true;
|
|
1165
|
+
const userManifestCount = [...manifests.keys()].filter((k) => !k.includes("node_modules")).length;
|
|
1166
|
+
if (userManifestCount > 0) {
|
|
1167
|
+
console.warn(`[vertz-bun-plugin] Native compiler does not support cross-file reactivity manifests. ` + `${userManifestCount} user module(s) with exported signal APIs will not have ` + `.value insertion for their signal properties. ` + `Set VERTZ_NATIVE_COMPILER=0 if cross-file reactivity is needed.`);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
const nativeResult = nativeCompiler.compile(codeAfterImageTransform, {
|
|
1171
|
+
filename: args.path,
|
|
1172
|
+
target: options?.target,
|
|
1173
|
+
fastRefresh: false
|
|
1174
|
+
});
|
|
1175
|
+
return {
|
|
1176
|
+
code: nativeResult.code,
|
|
1177
|
+
map: nativeResult.map ? JSON.parse(nativeResult.map) : { version: 3, sources: [], mappings: "", names: [] },
|
|
1178
|
+
diagnostics: (nativeResult.diagnostics ?? []).map((d) => ({
|
|
1179
|
+
code: "native-diagnostic",
|
|
1180
|
+
message: d.message,
|
|
1181
|
+
severity: "warning",
|
|
1182
|
+
line: d.line ?? 1,
|
|
1183
|
+
column: d.column ?? 0
|
|
1184
|
+
}))
|
|
1185
|
+
};
|
|
1186
|
+
})() : compile(codeAfterImageTransform, {
|
|
1128
1187
|
filename: args.path,
|
|
1129
1188
|
target: options?.target,
|
|
1130
1189
|
manifests: getManifestsRecord()
|
|
@@ -1145,7 +1204,7 @@ function createVertzBunPlugin(options) {
|
|
|
1145
1204
|
if (hmr) {
|
|
1146
1205
|
const hash = filePathHash(args.path);
|
|
1147
1206
|
const cssFileName = `${hash}.css`;
|
|
1148
|
-
const cssFilePath =
|
|
1207
|
+
const cssFilePath = resolve3(cssOutDir, cssFileName);
|
|
1149
1208
|
writeFileSync2(cssFilePath, extraction.css);
|
|
1150
1209
|
cssSidecarMap.set(args.path, cssFilePath);
|
|
1151
1210
|
const relPath2 = relative(dirname(args.path), cssFilePath);
|
package/dist/index.d.ts
CHANGED
|
@@ -302,6 +302,18 @@ interface AotManifest {
|
|
|
302
302
|
/** Route pattern → AOT entry. */
|
|
303
303
|
routes: Record<string, AotRouteEntry>;
|
|
304
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Resolves custom data for AOT-rendered routes.
|
|
307
|
+
*
|
|
308
|
+
* Called after the entity prefetch pipeline, before the `allKeysResolved` bail check.
|
|
309
|
+
* Only called when there are unresolved query keys remaining.
|
|
310
|
+
*
|
|
311
|
+
* @param pattern - Matched route pattern (e.g., '/products/:id')
|
|
312
|
+
* @param params - Route params (e.g., \{ id: 'abc-123' \})
|
|
313
|
+
* @param unresolvedKeys - Query keys not yet populated by entity prefetch
|
|
314
|
+
* @returns Map of cache key → data, or empty Map to skip
|
|
315
|
+
*/
|
|
316
|
+
type AotDataResolver = (pattern: string, params: Record<string, string>, unresolvedKeys: string[]) => Promise<Map<string, unknown>> | Map<string, unknown>;
|
|
305
317
|
/** Options for `ssrRenderAot()`. */
|
|
306
318
|
interface SSRRenderAotOptions {
|
|
307
319
|
/** AOT manifest with pre-compiled render functions. */
|
|
@@ -318,6 +330,8 @@ interface SSRRenderAotOptions {
|
|
|
318
330
|
prefetchSession?: PrefetchSession;
|
|
319
331
|
/** AOT diagnostics collector (dev mode). When provided with VERTZ_DEBUG=aot, enables dual rendering and divergence detection. */
|
|
320
332
|
diagnostics?: AotDiagnostics;
|
|
333
|
+
/** Custom data resolver for non-entity AOT routes. */
|
|
334
|
+
aotDataResolver?: AotDataResolver;
|
|
321
335
|
}
|
|
322
336
|
/**
|
|
323
337
|
* Create closure-based runtime fallback renderers for components
|
|
@@ -465,6 +479,14 @@ interface SSRHandlerOptions {
|
|
|
465
479
|
* Load via `loadAotManifest(serverDir)` at startup.
|
|
466
480
|
*/
|
|
467
481
|
aotManifest?: AotManifest;
|
|
482
|
+
/**
|
|
483
|
+
* Custom data resolver for AOT routes with non-entity data sources.
|
|
484
|
+
*
|
|
485
|
+
* Called after entity prefetch, with only the unresolved query keys.
|
|
486
|
+
* Enables AOT rendering for routes that use custom data layers
|
|
487
|
+
* (JSON files, third-party APIs, custom DB clients).
|
|
488
|
+
*/
|
|
489
|
+
aotDataResolver?: AotDataResolver;
|
|
468
490
|
}
|
|
469
491
|
declare function createSSRHandler(options: SSRHandlerOptions): (request: Request) => Promise<Response>;
|
|
470
492
|
type NodeHandlerOptions = SSRHandlerOptions;
|
|
@@ -1069,13 +1091,21 @@ interface MatchedRoute {
|
|
|
1069
1091
|
/** Extracted route parameter values (e.g., { projectId: 'abc123' }) */
|
|
1070
1092
|
params: Record<string, string>;
|
|
1071
1093
|
}
|
|
1094
|
+
interface MatchOptions {
|
|
1095
|
+
/**
|
|
1096
|
+
* When true, require exact segment count match (no prefix matching).
|
|
1097
|
+
* Use for AOT routes (page-level). When false (default), prefix matching
|
|
1098
|
+
* is used for layout matching where `/` matches all nested routes.
|
|
1099
|
+
*/
|
|
1100
|
+
exact?: boolean;
|
|
1101
|
+
}
|
|
1072
1102
|
/**
|
|
1073
1103
|
* Match a URL path against a list of route patterns.
|
|
1074
1104
|
* Returns all matching patterns (layouts + page) ordered from most general to most specific.
|
|
1075
1105
|
*
|
|
1076
1106
|
* Patterns use Express-style `:param` syntax.
|
|
1077
1107
|
*/
|
|
1078
|
-
declare function matchUrlToPatterns(url: string, patterns: string[]): MatchedRoute[];
|
|
1108
|
+
declare function matchUrlToPatterns(url: string, patterns: string[], options?: MatchOptions): MatchedRoute[];
|
|
1079
1109
|
/**
|
|
1080
1110
|
* Serialize data to JSON with `<` escaped as `\u003c`.
|
|
1081
1111
|
* Prevents `<\/script>` breakout and `<!--` injection in inline scripts.
|
|
@@ -1126,4 +1156,4 @@ declare function collectStreamChunks(stream: ReadableStream<Uint8Array>): Promis
|
|
|
1126
1156
|
* @param nonce - Optional CSP nonce to add to the inline script tag.
|
|
1127
1157
|
*/
|
|
1128
1158
|
declare function createTemplateChunk(slotId: number, resolvedHtml: string, nonce?: string): string;
|
|
1129
|
-
export { wrapWithHydrationMarkers, toPrefetchSession, streamToString, ssrStorage, ssrRenderToString, ssrRenderSinglePass, ssrRenderAot, ssrDiscoverQueries, setGlobalSSRTimeout, serializeToHtml, safeSerialize, resetSlotCounter, renderToStream, renderToHTMLStream, renderToHTML, renderPage, renderHeadToHtml, renderAssetTags, registerSSRQuery, reconstructDescriptors, rawHtml, matchUrlToPatterns, loadAotManifest, isInSSR, isAotDebugEnabled, inlineCriticalCss, getStreamingRuntimeScript, getSSRUrl, getSSRQueries, getGlobalSSRTimeout, getAccessSetForSSR, generateSSRHtml, generateAotBuildManifest, generateAotBarrel, extractRoutes, extractFontMetrics, evaluateAccessRule, encodeChunk, detectFallbackFont, createTemplateChunk, createSlotPlaceholder, createSessionScript, createSSRHandler, createSSRDataChunk, createSSRAdapter, createPrefetchManifestManager, createNodeHandler, createHoles, createAotManifestManager, createAccessSetScript, collectStreamChunks, clearGlobalSSRTimeout, buildAotRouteMap, __ssr_style_object, __ssr_spread, __esc_attr, __esc, VNode, SessionResolver, SessionData, SerializedAccessRule, SSRSinglePassOptions, SSRSessionInfo, SSRRenderResult, SSRRenderAotOptions, SSRQueryEntry2 as SSRQueryEntry, SSRPrefetchManifest, SSRModule, SSRHandlerOptions, SSRDiscoverResult, SSRAotContext, RenderToStreamOptions, RenderToHTMLStreamOptions, RenderToHTMLOptions, ReconstructedDescriptor, RawHtml, PrefetchSession, PrefetchManifestSnapshot, PrefetchManifestManagerOptions, PrefetchManifestManager, PageOptions, NodeHandlerOptions, MatchedRoute, HydrationOptions, HeadEntry, HeadCollector, GenerateSSRHtmlOptions, FontFallbackMetrics7 as FontFallbackMetrics, FallbackFontName2 as FallbackFontName, ExtractedRoute, EntityAccessMap, AssetDescriptor, AotTier, AotRouteMapEntry, AotRouteEntry, AotRenderFn, AotManifestSnapshot, AotManifestManagerOptions, AotManifestManager, AotManifest, AotDivergenceEntry, AotDiagnosticsSnapshot, AotDiagnostics, AotDevManifest, AotDevComponentEntry, AotComponentDiagnostic, AotCompiledFile, AotBuildManifest, AotBuildComponentEntry, AotBarrelResult };
|
|
1159
|
+
export { wrapWithHydrationMarkers, toPrefetchSession, streamToString, ssrStorage, ssrRenderToString, ssrRenderSinglePass, ssrRenderAot, ssrDiscoverQueries, setGlobalSSRTimeout, serializeToHtml, safeSerialize, resetSlotCounter, renderToStream, renderToHTMLStream, renderToHTML, renderPage, renderHeadToHtml, renderAssetTags, registerSSRQuery, reconstructDescriptors, rawHtml, matchUrlToPatterns, loadAotManifest, isInSSR, isAotDebugEnabled, inlineCriticalCss, getStreamingRuntimeScript, getSSRUrl, getSSRQueries, getGlobalSSRTimeout, getAccessSetForSSR, generateSSRHtml, generateAotBuildManifest, generateAotBarrel, extractRoutes, extractFontMetrics, evaluateAccessRule, encodeChunk, detectFallbackFont, createTemplateChunk, createSlotPlaceholder, createSessionScript, createSSRHandler, createSSRDataChunk, createSSRAdapter, createPrefetchManifestManager, createNodeHandler, createHoles, createAotManifestManager, createAccessSetScript, collectStreamChunks, clearGlobalSSRTimeout, buildAotRouteMap, __ssr_style_object, __ssr_spread, __esc_attr, __esc, VNode, SessionResolver, SessionData, SerializedAccessRule, SSRSinglePassOptions, SSRSessionInfo, SSRRenderResult, SSRRenderAotOptions, SSRQueryEntry2 as SSRQueryEntry, SSRPrefetchManifest, SSRModule, SSRHandlerOptions, SSRDiscoverResult, SSRAotContext, RenderToStreamOptions, RenderToHTMLStreamOptions, RenderToHTMLOptions, ReconstructedDescriptor, RawHtml, PrefetchSession, PrefetchManifestSnapshot, PrefetchManifestManagerOptions, PrefetchManifestManager, PageOptions, NodeHandlerOptions, MatchedRoute, MatchOptions, HydrationOptions, HeadEntry, HeadCollector, GenerateSSRHtmlOptions, FontFallbackMetrics7 as FontFallbackMetrics, FallbackFontName2 as FallbackFontName, ExtractedRoute, EntityAccessMap, AssetDescriptor, AotTier, AotRouteMapEntry, AotRouteEntry, AotRenderFn, AotManifestSnapshot, AotManifestManagerOptions, AotManifestManager, AotManifest, AotDivergenceEntry, AotDiagnosticsSnapshot, AotDiagnostics, AotDevManifest, AotDevComponentEntry, AotDataResolver, AotComponentDiagnostic, AotCompiledFile, AotBuildManifest, AotBuildComponentEntry, AotBarrelResult };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSSRHandler,
|
|
3
3
|
loadAotManifest
|
|
4
|
-
} from "./shared/chunk-
|
|
4
|
+
} from "./shared/chunk-hjsbx25c.js";
|
|
5
5
|
import {
|
|
6
6
|
createNodeHandler
|
|
7
|
-
} from "./shared/chunk-
|
|
7
|
+
} from "./shared/chunk-9s2dppdh.js";
|
|
8
8
|
import {
|
|
9
9
|
collectStreamChunks,
|
|
10
10
|
compileThemeCached,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
ssrRenderToString,
|
|
35
35
|
streamToString,
|
|
36
36
|
toPrefetchSession
|
|
37
|
-
} from "./shared/chunk-
|
|
37
|
+
} from "./shared/chunk-wq2ke61r.js";
|
|
38
38
|
import {
|
|
39
39
|
clearGlobalSSRTimeout,
|
|
40
40
|
createSSRAdapter,
|
|
@@ -137,7 +137,9 @@ function generateAotBarrel(compiledFiles, routeMap) {
|
|
|
137
137
|
existing.push(fnName);
|
|
138
138
|
fileToFns.set(filePath, existing);
|
|
139
139
|
}
|
|
140
|
-
const lines = [
|
|
140
|
+
const lines = [
|
|
141
|
+
"import { __esc, __esc_attr, __ssr_spread, __ssr_style_object } from '@vertz/ui-server';"
|
|
142
|
+
];
|
|
141
143
|
const files = {};
|
|
142
144
|
let fileIndex = 0;
|
|
143
145
|
for (const [filePath, fns] of fileToFns) {
|
|
@@ -147,7 +149,9 @@ function generateAotBarrel(compiledFiles, routeMap) {
|
|
|
147
149
|
lines.push(`export { ${fns.sort().join(", ")} } from '${moduleRef}';`);
|
|
148
150
|
const compiled = compiledFiles[filePath];
|
|
149
151
|
if (compiled) {
|
|
150
|
-
|
|
152
|
+
const helperImport = `import { __esc, __esc_attr, __ssr_spread, __ssr_style_object } from '@vertz/ui-server';
|
|
153
|
+
`;
|
|
154
|
+
files[`${tempFileName}.tsx`] = helperImport + compiled.code;
|
|
151
155
|
}
|
|
152
156
|
fileIndex++;
|
|
153
157
|
}
|
package/dist/node-handler.d.ts
CHANGED
|
@@ -115,6 +115,18 @@ interface AotManifest {
|
|
|
115
115
|
/** Route pattern → AOT entry. */
|
|
116
116
|
routes: Record<string, AotRouteEntry>;
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Resolves custom data for AOT-rendered routes.
|
|
120
|
+
*
|
|
121
|
+
* Called after the entity prefetch pipeline, before the `allKeysResolved` bail check.
|
|
122
|
+
* Only called when there are unresolved query keys remaining.
|
|
123
|
+
*
|
|
124
|
+
* @param pattern - Matched route pattern (e.g., '/products/:id')
|
|
125
|
+
* @param params - Route params (e.g., \{ id: 'abc-123' \})
|
|
126
|
+
* @param unresolvedKeys - Query keys not yet populated by entity prefetch
|
|
127
|
+
* @returns Map of cache key → data, or empty Map to skip
|
|
128
|
+
*/
|
|
129
|
+
type AotDataResolver = (pattern: string, params: Record<string, string>, unresolvedKeys: string[]) => Promise<Map<string, unknown>> | Map<string, unknown>;
|
|
118
130
|
import { AccessSet } from "@vertz/ui/auth";
|
|
119
131
|
interface SessionData {
|
|
120
132
|
user: {
|
|
@@ -217,6 +229,14 @@ interface SSRHandlerOptions {
|
|
|
217
229
|
* Load via `loadAotManifest(serverDir)` at startup.
|
|
218
230
|
*/
|
|
219
231
|
aotManifest?: AotManifest;
|
|
232
|
+
/**
|
|
233
|
+
* Custom data resolver for AOT routes with non-entity data sources.
|
|
234
|
+
*
|
|
235
|
+
* Called after entity prefetch, with only the unresolved query keys.
|
|
236
|
+
* Enables AOT rendering for routes that use custom data layers
|
|
237
|
+
* (JSON files, third-party APIs, custom DB clients).
|
|
238
|
+
*/
|
|
239
|
+
aotDataResolver?: AotDataResolver;
|
|
220
240
|
}
|
|
221
241
|
type NodeHandlerOptions = SSRHandlerOptions;
|
|
222
242
|
declare function createNodeHandler(options: NodeHandlerOptions): (req: IncomingMessage, res: ServerResponse) => void;
|
package/dist/node-handler.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
ssrRenderSinglePass,
|
|
12
12
|
ssrStreamNavQueries,
|
|
13
13
|
toPrefetchSession
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-wq2ke61r.js";
|
|
15
15
|
|
|
16
16
|
// src/aot-manifest-loader.ts
|
|
17
17
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -108,7 +108,8 @@ function createSSRHandler(options) {
|
|
|
108
108
|
sessionResolver,
|
|
109
109
|
manifest,
|
|
110
110
|
progressiveHTML,
|
|
111
|
-
aotManifest
|
|
111
|
+
aotManifest,
|
|
112
|
+
aotDataResolver
|
|
112
113
|
} = options;
|
|
113
114
|
const { template, linkHeader, modulepreloadTags, splitResult } = precomputeHandlerState(options);
|
|
114
115
|
return async (request) => {
|
|
@@ -128,7 +129,7 @@ function createSSRHandler(options) {
|
|
|
128
129
|
if (useProgressive) {
|
|
129
130
|
return handleProgressiveHTMLRequest(module, splitResult, pathname + url.search, ssrTimeout, nonce, fallbackMetrics, linkHeader, modulepreloadTags, routeChunkManifest, cacheControl, sessionScript, ssrAuth, manifest);
|
|
130
131
|
}
|
|
131
|
-
return handleHTMLRequest(module, template, pathname + url.search, ssrTimeout, nonce, fallbackMetrics, linkHeader, modulepreloadTags, routeChunkManifest, cacheControl, sessionScript, ssrAuth, manifest, aotManifest);
|
|
132
|
+
return handleHTMLRequest(module, template, pathname + url.search, ssrTimeout, nonce, fallbackMetrics, linkHeader, modulepreloadTags, routeChunkManifest, cacheControl, sessionScript, ssrAuth, manifest, aotManifest, aotDataResolver);
|
|
132
133
|
};
|
|
133
134
|
}
|
|
134
135
|
async function handleNavRequest(module, url, ssrTimeout) {
|
|
@@ -217,7 +218,7 @@ async function handleProgressiveHTMLRequest(module, split, url, ssrTimeout, nonc
|
|
|
217
218
|
});
|
|
218
219
|
}
|
|
219
220
|
}
|
|
220
|
-
async function handleHTMLRequest(module, template, url, ssrTimeout, nonce, fallbackMetrics, linkHeader, staticModulepreloadTags, routeChunkManifest, cacheControl, sessionScript, ssrAuth, manifest, aotManifest) {
|
|
221
|
+
async function handleHTMLRequest(module, template, url, ssrTimeout, nonce, fallbackMetrics, linkHeader, staticModulepreloadTags, routeChunkManifest, cacheControl, sessionScript, ssrAuth, manifest, aotManifest, aotDataResolver) {
|
|
221
222
|
try {
|
|
222
223
|
const prefetchSession = ssrAuth ? toPrefetchSession(ssrAuth) : undefined;
|
|
223
224
|
const result = aotManifest ? await ssrRenderAot(module, url, {
|
|
@@ -226,7 +227,8 @@ async function handleHTMLRequest(module, template, url, ssrTimeout, nonce, fallb
|
|
|
226
227
|
ssrTimeout,
|
|
227
228
|
fallbackMetrics,
|
|
228
229
|
ssrAuth,
|
|
229
|
-
prefetchSession
|
|
230
|
+
prefetchSession,
|
|
231
|
+
aotDataResolver
|
|
230
232
|
}) : await ssrRenderSinglePass(module, url, {
|
|
231
233
|
ssrTimeout,
|
|
232
234
|
fallbackMetrics,
|
|
@@ -626,11 +626,12 @@ data: ${safeSerialize(entry)}
|
|
|
626
626
|
}
|
|
627
627
|
|
|
628
628
|
// src/ssr-route-matcher.ts
|
|
629
|
-
function matchUrlToPatterns(url, patterns) {
|
|
629
|
+
function matchUrlToPatterns(url, patterns, options) {
|
|
630
630
|
const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
|
|
631
|
+
const exact = options?.exact ?? false;
|
|
631
632
|
const matches = [];
|
|
632
633
|
for (const pattern of patterns) {
|
|
633
|
-
const result = matchPattern(path, pattern);
|
|
634
|
+
const result = matchPattern(path, pattern, exact);
|
|
634
635
|
if (result) {
|
|
635
636
|
matches.push(result);
|
|
636
637
|
}
|
|
@@ -642,11 +643,16 @@ function matchUrlToPatterns(url, patterns) {
|
|
|
642
643
|
});
|
|
643
644
|
return matches;
|
|
644
645
|
}
|
|
645
|
-
function matchPattern(path, pattern) {
|
|
646
|
+
function matchPattern(path, pattern, exact) {
|
|
646
647
|
const pathSegments = path.split("/").filter(Boolean);
|
|
647
648
|
const patternSegments = pattern.split("/").filter(Boolean);
|
|
648
|
-
if (
|
|
649
|
-
|
|
649
|
+
if (exact) {
|
|
650
|
+
if (patternSegments.length !== pathSegments.length)
|
|
651
|
+
return;
|
|
652
|
+
} else {
|
|
653
|
+
if (patternSegments.length > pathSegments.length)
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
650
656
|
const params = {};
|
|
651
657
|
for (let i = 0;i < patternSegments.length; i++) {
|
|
652
658
|
const seg = patternSegments[i];
|
|
@@ -1057,7 +1063,7 @@ async function ssrRenderAot(module, url, options) {
|
|
|
1057
1063
|
prefetchSession: options.prefetchSession
|
|
1058
1064
|
};
|
|
1059
1065
|
const aotPatterns = Object.keys(aotManifest.routes);
|
|
1060
|
-
const matches = matchUrlToPatterns(normalizedUrl, aotPatterns);
|
|
1066
|
+
const matches = matchUrlToPatterns(normalizedUrl, aotPatterns, { exact: true });
|
|
1061
1067
|
if (matches.length === 0) {
|
|
1062
1068
|
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1063
1069
|
}
|
|
@@ -1076,6 +1082,25 @@ async function ssrRenderAot(module, url, options) {
|
|
|
1076
1082
|
await prefetchForAot(aotEntry.queryKeys, manifest.routeEntries, match, apiClient, ssrTimeout, queryCache);
|
|
1077
1083
|
}
|
|
1078
1084
|
}
|
|
1085
|
+
if (aotEntry.queryKeys && aotEntry.queryKeys.length > 0 && options.aotDataResolver) {
|
|
1086
|
+
const unresolvedKeys = aotEntry.queryKeys.filter((k) => !queryCache.has(k));
|
|
1087
|
+
if (unresolvedKeys.length > 0) {
|
|
1088
|
+
try {
|
|
1089
|
+
const resolved = await options.aotDataResolver(match.pattern, match.params, unresolvedKeys);
|
|
1090
|
+
for (const [key, value] of resolved) {
|
|
1091
|
+
queryCache.set(key, value);
|
|
1092
|
+
}
|
|
1093
|
+
} catch {
|
|
1094
|
+
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
if (aotEntry.queryKeys && aotEntry.queryKeys.length > 0) {
|
|
1099
|
+
const allKeysResolved = aotEntry.queryKeys.every((k) => queryCache.has(k));
|
|
1100
|
+
if (!allKeysResolved) {
|
|
1101
|
+
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1079
1104
|
try {
|
|
1080
1105
|
setGlobalSSRTimeout(ssrTimeout);
|
|
1081
1106
|
const holes = createHoles(aotEntry.holes, module, normalizedUrl, queryCache, options.ssrAuth);
|
package/dist/ssr/index.d.ts
CHANGED
|
@@ -162,6 +162,18 @@ interface AotManifest {
|
|
|
162
162
|
routes: Record<string, AotRouteEntry>;
|
|
163
163
|
}
|
|
164
164
|
/**
|
|
165
|
+
* Resolves custom data for AOT-rendered routes.
|
|
166
|
+
*
|
|
167
|
+
* Called after the entity prefetch pipeline, before the `allKeysResolved` bail check.
|
|
168
|
+
* Only called when there are unresolved query keys remaining.
|
|
169
|
+
*
|
|
170
|
+
* @param pattern - Matched route pattern (e.g., '/products/:id')
|
|
171
|
+
* @param params - Route params (e.g., \{ id: 'abc-123' \})
|
|
172
|
+
* @param unresolvedKeys - Query keys not yet populated by entity prefetch
|
|
173
|
+
* @returns Map of cache key → data, or empty Map to skip
|
|
174
|
+
*/
|
|
175
|
+
type AotDataResolver = (pattern: string, params: Record<string, string>, unresolvedKeys: string[]) => Promise<Map<string, unknown>> | Map<string, unknown>;
|
|
176
|
+
/**
|
|
165
177
|
* Load AOT manifest and routes module from a server build directory.
|
|
166
178
|
*
|
|
167
179
|
* Returns `null` if either `aot-manifest.json` or `aot-routes.js` is missing,
|
|
@@ -335,6 +347,14 @@ interface SSRHandlerOptions {
|
|
|
335
347
|
* Load via `loadAotManifest(serverDir)` at startup.
|
|
336
348
|
*/
|
|
337
349
|
aotManifest?: AotManifest;
|
|
350
|
+
/**
|
|
351
|
+
* Custom data resolver for AOT routes with non-entity data sources.
|
|
352
|
+
*
|
|
353
|
+
* Called after entity prefetch, with only the unresolved query keys.
|
|
354
|
+
* Enables AOT rendering for routes that use custom data layers
|
|
355
|
+
* (JSON files, third-party APIs, custom DB clients).
|
|
356
|
+
*/
|
|
357
|
+
aotDataResolver?: AotDataResolver;
|
|
338
358
|
}
|
|
339
359
|
declare function createSSRHandler(options: SSRHandlerOptions): (request: Request) => Promise<Response>;
|
|
340
360
|
interface InjectIntoTemplateOptions {
|
|
@@ -357,4 +377,4 @@ interface InjectIntoTemplateOptions {
|
|
|
357
377
|
* injects CSS before </head>, and ssrData before </body>.
|
|
358
378
|
*/
|
|
359
379
|
declare function injectIntoTemplate(options: InjectIntoTemplateOptions): string;
|
|
360
|
-
export { stripScriptsFromStaticHTML, ssrRenderToString, ssrDiscoverQueries, prerenderRoutes, loadAotManifest, injectIntoTemplate, filterPrerenderableRoutes, discoverRoutes, createSSRHandler, collectPrerenderPaths, SSRRenderResult, SSRModule, SSRHandlerOptions, SSRDiscoverResult, PrerenderResult, PrerenderOptions, AotManifest };
|
|
380
|
+
export { stripScriptsFromStaticHTML, ssrRenderToString, ssrDiscoverQueries, prerenderRoutes, loadAotManifest, injectIntoTemplate, filterPrerenderableRoutes, discoverRoutes, createSSRHandler, collectPrerenderPaths, SSRRenderResult, SSRModule, SSRHandlerOptions, SSRDiscoverResult, PrerenderResult, PrerenderOptions, AotManifest, AotDataResolver };
|
package/dist/ssr/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSSRHandler,
|
|
3
3
|
loadAotManifest
|
|
4
|
-
} from "../shared/chunk-
|
|
4
|
+
} from "../shared/chunk-hjsbx25c.js";
|
|
5
5
|
import {
|
|
6
6
|
injectIntoTemplate,
|
|
7
7
|
ssrDiscoverQueries,
|
|
8
8
|
ssrRenderToString
|
|
9
|
-
} from "../shared/chunk-
|
|
9
|
+
} from "../shared/chunk-wq2ke61r.js";
|
|
10
10
|
import"../shared/chunk-ybftdw1r.js";
|
|
11
11
|
// src/prerender.ts
|
|
12
12
|
async function discoverRoutes(module) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertz/ui-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.36",
|
|
4
4
|
"description": "Vertz UI server-side rendering runtime",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -67,9 +67,9 @@
|
|
|
67
67
|
"@ampproject/remapping": "^2.3.0",
|
|
68
68
|
"@capsizecss/unpack": "^4.0.0",
|
|
69
69
|
"@jridgewell/trace-mapping": "^0.3.31",
|
|
70
|
-
"@vertz/core": "^0.2.
|
|
71
|
-
"@vertz/ui": "^0.2.
|
|
72
|
-
"@vertz/ui-compiler": "^0.2.
|
|
70
|
+
"@vertz/core": "^0.2.35",
|
|
71
|
+
"@vertz/ui": "^0.2.35",
|
|
72
|
+
"@vertz/ui-compiler": "^0.2.35",
|
|
73
73
|
"magic-string": "^0.30.0",
|
|
74
74
|
"sharp": "^0.34.5",
|
|
75
75
|
"ts-morph": "^27.0.2"
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@happy-dom/global-registrator": "^20.8.3",
|
|
79
79
|
"@playwright/test": "^1.58.2",
|
|
80
|
-
"@vertz/codegen": "^0.2.
|
|
80
|
+
"@vertz/codegen": "^0.2.35",
|
|
81
81
|
"@vertz/ui-auth": "^0.2.19",
|
|
82
82
|
"bun-types": "^1.3.10",
|
|
83
83
|
"bunup": "^0.16.31",
|