@jay-framework/dev-server 0.11.0 → 0.12.0
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/index.d.ts +49 -2
- package/dist/index.js +379 -99
- package/package.json +14 -13
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,10 @@ import { ViteDevServer, Connect } from 'vite';
|
|
|
2
2
|
import { JayRoute } from '@jay-framework/stack-route-scanner';
|
|
3
3
|
import { RequestHandler } from 'express-serve-static-core';
|
|
4
4
|
import { JayRollupConfig } from '@jay-framework/rollup-plugin';
|
|
5
|
+
import { LogLevel } from '@jay-framework/logger';
|
|
5
6
|
import { ProjectClientInitInfo, PluginWithInit, ActionRegistry } from '@jay-framework/stack-server-runtime';
|
|
6
7
|
import { RequestHandler as RequestHandler$1 } from 'express';
|
|
8
|
+
import { JayRollupConfig as JayRollupConfig$1 } from '@jay-framework/compiler-jay-stack';
|
|
7
9
|
|
|
8
10
|
interface DevServerOptions {
|
|
9
11
|
publicBaseUrlPath?: string;
|
|
@@ -23,6 +25,11 @@ interface DevServerOptions {
|
|
|
23
25
|
* The automation API is available at `window.__jay.automation` and via `AUTOMATION_CONTEXT`.
|
|
24
26
|
*/
|
|
25
27
|
disableAutomation?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Log level for dev server output.
|
|
30
|
+
* Controls both Jay logging and Vite logging.
|
|
31
|
+
*/
|
|
32
|
+
logLevel?: LogLevel;
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
/**
|
|
@@ -100,7 +107,7 @@ interface DevServer {
|
|
|
100
107
|
routes: DevServerRoute[];
|
|
101
108
|
lifecycleManager: ServiceLifecycleManager;
|
|
102
109
|
}
|
|
103
|
-
declare function mkDevServer(
|
|
110
|
+
declare function mkDevServer(rawOptions: DevServerOptions): Promise<DevServer>;
|
|
104
111
|
|
|
105
112
|
/**
|
|
106
113
|
* Action Router for Jay Stack dev server.
|
|
@@ -151,4 +158,44 @@ declare function createActionRouter(options?: ActionRouterOptions): RequestHandl
|
|
|
151
158
|
*/
|
|
152
159
|
declare function actionBodyParser(): RequestHandler$1;
|
|
153
160
|
|
|
154
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Vite Factory
|
|
163
|
+
*
|
|
164
|
+
* Single source of truth for creating Vite servers.
|
|
165
|
+
* Used by both the dev-server and CLI commands.
|
|
166
|
+
*/
|
|
167
|
+
|
|
168
|
+
interface CreateViteServerOptions {
|
|
169
|
+
/** Project root directory */
|
|
170
|
+
projectRoot: string;
|
|
171
|
+
/** Root directory for pages (defaults to projectRoot) */
|
|
172
|
+
pagesRoot?: string;
|
|
173
|
+
/** Base URL path for public assets */
|
|
174
|
+
base?: string;
|
|
175
|
+
/** Jay Stack compiler config (optional, will use defaults if not provided) */
|
|
176
|
+
jayRollupConfig?: JayRollupConfig$1;
|
|
177
|
+
/** Log level (defaults to 'info' for dev-server, 'warn' for CLI) */
|
|
178
|
+
logLevel?: 'info' | 'warn' | 'error' | 'silent';
|
|
179
|
+
/** Whether to clear screen on rebuild */
|
|
180
|
+
clearScreen?: boolean;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Creates a Vite server configured for Jay Stack.
|
|
184
|
+
*
|
|
185
|
+
* This is the single source of truth for Vite configuration.
|
|
186
|
+
* Both dev-server and CLI use this function.
|
|
187
|
+
*/
|
|
188
|
+
declare function createViteServer(options: CreateViteServerOptions): Promise<ViteDevServer>;
|
|
189
|
+
/**
|
|
190
|
+
* Creates a minimal Vite server for CLI usage.
|
|
191
|
+
*
|
|
192
|
+
* This is a convenience wrapper around createViteServer with CLI-appropriate defaults.
|
|
193
|
+
* Disables dependency optimization and ignores the build/ folder to avoid errors
|
|
194
|
+
* from stale build artifacts (e.g., build/client-scripts/ referencing build/slow-render-cache/).
|
|
195
|
+
*/
|
|
196
|
+
declare function createViteForCli(options: {
|
|
197
|
+
projectRoot: string;
|
|
198
|
+
tsConfigFilePath?: string;
|
|
199
|
+
}): Promise<ViteDevServer>;
|
|
200
|
+
|
|
201
|
+
export { ACTION_ENDPOINT_BASE, type ActionRouterOptions, type CreateViteServerOptions, type DevServer, type DevServerOptions, type DevServerRoute, actionBodyParser, createActionRouter, createViteForCli, createViteServer, mkDevServer };
|
package/dist/index.js
CHANGED
|
@@ -5,16 +5,17 @@ var __publicField = (obj, key, value) => {
|
|
|
5
5
|
return value;
|
|
6
6
|
};
|
|
7
7
|
import { createServer } from "vite";
|
|
8
|
-
import { scanRoutes, routeToExpressRoute } from "@jay-framework/stack-route-scanner";
|
|
9
|
-
import { discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, actionRegistry, discoverAndRegisterActions, discoverAllPluginActions, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, DevSlowlyChangingPhase, SlowRenderCache, preparePluginClientInits, loadPageParts, renderFastChangingData, generateClientScript, getClientInitData } from "@jay-framework/stack-server-runtime";
|
|
10
8
|
import { jayRuntime } from "@jay-framework/vite-plugin";
|
|
11
9
|
import { createRequire } from "module";
|
|
12
10
|
import "@jay-framework/compiler-shared";
|
|
13
11
|
import * as path from "node:path";
|
|
14
12
|
import path__default from "node:path";
|
|
15
|
-
import { parseContract, slowRenderTransform } from "@jay-framework/compiler-jay-html";
|
|
13
|
+
import { discoverHeadlessInstances, parseContract, slowRenderTransform, JAY_IMPORT_RESOLVER, resolveHeadlessInstances } from "@jay-framework/compiler-jay-html";
|
|
14
|
+
import { getLogger, getDevLogger } from "@jay-framework/logger";
|
|
16
15
|
import { createRequire as createRequire$1 } from "node:module";
|
|
17
16
|
import * as fs from "node:fs";
|
|
17
|
+
import { scanRoutes, routeToExpressRoute } from "@jay-framework/stack-route-scanner";
|
|
18
|
+
import { discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, actionRegistry, discoverAndRegisterActions, discoverAllPluginActions, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, DevSlowlyChangingPhase, SlowRenderCache, preparePluginClientInits, getServiceRegistry, materializeContracts, loadPageParts, renderFastChangingData, slowRenderInstances, generateClientScript, getClientInitData, resolveServices } from "@jay-framework/stack-server-runtime";
|
|
18
19
|
import fs$1 from "node:fs/promises";
|
|
19
20
|
import { pathToFileURL } from "node:url";
|
|
20
21
|
const s$1 = createRequire(import.meta.url), e$1 = s$1("typescript"), c$1 = new Proxy(e$1, {
|
|
@@ -1021,7 +1022,7 @@ function createImportChainTracker(options = {}) {
|
|
|
1021
1022
|
importChain.clear();
|
|
1022
1023
|
detectedServerModules.clear();
|
|
1023
1024
|
if (verbose) {
|
|
1024
|
-
|
|
1025
|
+
getLogger().info("[import-chain-tracker] Build started, tracking imports...");
|
|
1025
1026
|
}
|
|
1026
1027
|
},
|
|
1027
1028
|
resolveId(source, importer, options2) {
|
|
@@ -1033,7 +1034,7 @@ function createImportChainTracker(options = {}) {
|
|
|
1033
1034
|
}
|
|
1034
1035
|
if (importer) {
|
|
1035
1036
|
if (verbose) {
|
|
1036
|
-
|
|
1037
|
+
getLogger().info(
|
|
1037
1038
|
`[import-chain-tracker] ${shortenPath(importer)} imports ${source}`
|
|
1038
1039
|
);
|
|
1039
1040
|
}
|
|
@@ -1050,14 +1051,14 @@ function createImportChainTracker(options = {}) {
|
|
|
1050
1051
|
if (isServerOnlyModule(id)) {
|
|
1051
1052
|
detectedServerModules.add(id);
|
|
1052
1053
|
const chain = buildImportChain(id);
|
|
1053
|
-
|
|
1054
|
+
getLogger().error(
|
|
1054
1055
|
`
|
|
1055
1056
|
[import-chain-tracker] ⚠️ Server-only module detected in client build!`
|
|
1056
1057
|
);
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1058
|
+
getLogger().error(`Module: ${shortenPath(id)}`);
|
|
1059
|
+
getLogger().error(`Import chain:`);
|
|
1060
|
+
getLogger().error(formatChain(chain));
|
|
1061
|
+
getLogger().error("");
|
|
1061
1062
|
}
|
|
1062
1063
|
const importRegex = /import\s+(?:(?:\{[^}]*\}|[^{}\s,]+)\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
1063
1064
|
let match;
|
|
@@ -1067,16 +1068,18 @@ function createImportChainTracker(options = {}) {
|
|
|
1067
1068
|
if (isServerOnlyModule(importedModule)) {
|
|
1068
1069
|
if (!detectedServerModules.has(importedModule)) {
|
|
1069
1070
|
detectedServerModules.add(importedModule);
|
|
1070
|
-
|
|
1071
|
+
getLogger().error(
|
|
1071
1072
|
`
|
|
1072
1073
|
[import-chain-tracker] ⚠️ Server-only import detected in client build!`
|
|
1073
1074
|
);
|
|
1074
|
-
|
|
1075
|
+
getLogger().error(
|
|
1076
|
+
`Module "${importedModule}" imported by: ${shortenPath(id)}`
|
|
1077
|
+
);
|
|
1075
1078
|
const chain = buildImportChain(id);
|
|
1076
1079
|
chain.push(importedModule);
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
+
getLogger().error(`Import chain:`);
|
|
1081
|
+
getLogger().error(formatChain(chain));
|
|
1082
|
+
getLogger().error("");
|
|
1080
1083
|
}
|
|
1081
1084
|
}
|
|
1082
1085
|
}
|
|
@@ -1084,21 +1087,21 @@ function createImportChainTracker(options = {}) {
|
|
|
1084
1087
|
},
|
|
1085
1088
|
buildEnd() {
|
|
1086
1089
|
if (detectedServerModules.size > 0) {
|
|
1087
|
-
|
|
1090
|
+
getLogger().warn(
|
|
1088
1091
|
`
|
|
1089
1092
|
[import-chain-tracker] ⚠️ ${detectedServerModules.size} server-only module(s) detected during transform:`
|
|
1090
1093
|
);
|
|
1091
1094
|
for (const mod of detectedServerModules) {
|
|
1092
|
-
|
|
1095
|
+
getLogger().warn(` - ${mod}`);
|
|
1093
1096
|
}
|
|
1094
|
-
|
|
1097
|
+
getLogger().warn(
|
|
1095
1098
|
"\nNote: These may be stripped by the code-split transform if only used in .withServer()."
|
|
1096
1099
|
);
|
|
1097
|
-
|
|
1100
|
+
getLogger().warn(
|
|
1098
1101
|
'If build fails with "not exported" errors, check the import chains above.\n'
|
|
1099
1102
|
);
|
|
1100
1103
|
} else if (verbose) {
|
|
1101
|
-
|
|
1104
|
+
getLogger().info(
|
|
1102
1105
|
"[import-chain-tracker] ✅ No server-only modules detected in client build"
|
|
1103
1106
|
);
|
|
1104
1107
|
}
|
|
@@ -1165,7 +1168,7 @@ function transformImports(options) {
|
|
|
1165
1168
|
hasChanges = true;
|
|
1166
1169
|
const newSource = `${packageName}/client`;
|
|
1167
1170
|
if (verbose) {
|
|
1168
|
-
|
|
1171
|
+
getLogger().info(
|
|
1169
1172
|
`[plugin-client-import] Rewriting import ${source} -> ${newSource} (in ${path.basename(filePath)})`
|
|
1170
1173
|
);
|
|
1171
1174
|
}
|
|
@@ -1182,7 +1185,7 @@ function transformImports(options) {
|
|
|
1182
1185
|
hasChanges = true;
|
|
1183
1186
|
const newSource = `${packageName}/client`;
|
|
1184
1187
|
if (verbose) {
|
|
1185
|
-
|
|
1188
|
+
getLogger().info(
|
|
1186
1189
|
`[plugin-client-import] Rewriting export ${source} -> ${newSource} (in ${path.basename(filePath)})`
|
|
1187
1190
|
);
|
|
1188
1191
|
}
|
|
@@ -1258,7 +1261,7 @@ function jayStackCompiler(options = {}) {
|
|
|
1258
1261
|
try {
|
|
1259
1262
|
return transformJayStackBuilder(code, id, environment);
|
|
1260
1263
|
} catch (error) {
|
|
1261
|
-
|
|
1264
|
+
getLogger().error(`[jay-stack:code-split] Error transforming ${id}: ${error}`);
|
|
1262
1265
|
return null;
|
|
1263
1266
|
}
|
|
1264
1267
|
}
|
|
@@ -1318,12 +1321,14 @@ function jayStackCompiler(options = {}) {
|
|
|
1318
1321
|
try {
|
|
1319
1322
|
code = await fs.promises.readFile(actualPath, "utf-8");
|
|
1320
1323
|
} catch (err) {
|
|
1321
|
-
|
|
1324
|
+
getLogger().error(
|
|
1325
|
+
`[action-transform] Could not read ${actualPath}: ${err}`
|
|
1326
|
+
);
|
|
1322
1327
|
return null;
|
|
1323
1328
|
}
|
|
1324
1329
|
const actions = extractActionsFromSource(code, actualPath);
|
|
1325
1330
|
if (actions.length === 0) {
|
|
1326
|
-
|
|
1331
|
+
getLogger().warn(`[action-transform] No actions found in ${actualPath}`);
|
|
1327
1332
|
return null;
|
|
1328
1333
|
}
|
|
1329
1334
|
const lines = [
|
|
@@ -1350,6 +1355,69 @@ function jayStackCompiler(options = {}) {
|
|
|
1350
1355
|
);
|
|
1351
1356
|
return plugins;
|
|
1352
1357
|
}
|
|
1358
|
+
async function createViteServer(options) {
|
|
1359
|
+
const {
|
|
1360
|
+
projectRoot,
|
|
1361
|
+
pagesRoot = projectRoot,
|
|
1362
|
+
base,
|
|
1363
|
+
jayRollupConfig = { tsConfigFilePath: path__default.join(projectRoot, "tsconfig.json") },
|
|
1364
|
+
logLevel = "info",
|
|
1365
|
+
clearScreen = true
|
|
1366
|
+
} = options;
|
|
1367
|
+
const vite = await createServer({
|
|
1368
|
+
// Don't start HTTP server - we use middleware mode
|
|
1369
|
+
server: { middlewareMode: true },
|
|
1370
|
+
// Use Jay Stack compiler for .jay-html and other custom transforms
|
|
1371
|
+
plugins: [...jayStackCompiler(jayRollupConfig)],
|
|
1372
|
+
// Custom app type (no default middleware)
|
|
1373
|
+
appType: "custom",
|
|
1374
|
+
// Base URL path
|
|
1375
|
+
base,
|
|
1376
|
+
// Root directory for module resolution
|
|
1377
|
+
root: pagesRoot,
|
|
1378
|
+
// SSR configuration
|
|
1379
|
+
ssr: {
|
|
1380
|
+
// Mark jay-framework packages as external so Vite uses Node's require
|
|
1381
|
+
// This ensures all packages share the same module instances (Symbol identity)
|
|
1382
|
+
external: ["@jay-framework/stack-server-runtime", "@jay-framework/fullstack-component"]
|
|
1383
|
+
},
|
|
1384
|
+
// Disable automatic entry point discovery for pre-bundling —
|
|
1385
|
+
// we run in middleware mode with no HTML files, so Vite can't auto-detect entries
|
|
1386
|
+
optimizeDeps: {
|
|
1387
|
+
entries: []
|
|
1388
|
+
},
|
|
1389
|
+
// Logging
|
|
1390
|
+
logLevel,
|
|
1391
|
+
clearScreen
|
|
1392
|
+
});
|
|
1393
|
+
return vite;
|
|
1394
|
+
}
|
|
1395
|
+
async function createViteForCli(options) {
|
|
1396
|
+
const { projectRoot, tsConfigFilePath = path__default.join(projectRoot, "tsconfig.json") } = options;
|
|
1397
|
+
const vite = await createServer({
|
|
1398
|
+
server: {
|
|
1399
|
+
middlewareMode: true,
|
|
1400
|
+
watch: {
|
|
1401
|
+
ignored: ["**/build/**"]
|
|
1402
|
+
}
|
|
1403
|
+
},
|
|
1404
|
+
plugins: [
|
|
1405
|
+
...jayStackCompiler({ tsConfigFilePath })
|
|
1406
|
+
],
|
|
1407
|
+
appType: "custom",
|
|
1408
|
+
root: projectRoot,
|
|
1409
|
+
ssr: {
|
|
1410
|
+
external: ["@jay-framework/stack-server-runtime", "@jay-framework/fullstack-component"]
|
|
1411
|
+
},
|
|
1412
|
+
// Disable dependency optimization — CLI only uses SSR, no browser bundles
|
|
1413
|
+
optimizeDeps: {
|
|
1414
|
+
entries: []
|
|
1415
|
+
},
|
|
1416
|
+
logLevel: "warn",
|
|
1417
|
+
clearScreen: false
|
|
1418
|
+
});
|
|
1419
|
+
return vite;
|
|
1420
|
+
}
|
|
1353
1421
|
class ServiceLifecycleManager {
|
|
1354
1422
|
constructor(projectRoot, sourceBase = "src") {
|
|
1355
1423
|
/** Path to project's lib/init.ts (makeJayInit pattern) */
|
|
@@ -1388,8 +1456,9 @@ class ServiceLifecycleManager {
|
|
|
1388
1456
|
* 4. Auto-discovering and registering actions
|
|
1389
1457
|
*/
|
|
1390
1458
|
async initialize() {
|
|
1459
|
+
const log = getLogger();
|
|
1391
1460
|
if (this.isInitialized) {
|
|
1392
|
-
|
|
1461
|
+
log.warn("[Services] Already initialized, skipping...");
|
|
1393
1462
|
return;
|
|
1394
1463
|
}
|
|
1395
1464
|
this.projectInitFilePath = this.findProjectInitFile();
|
|
@@ -1399,18 +1468,18 @@ class ServiceLifecycleManager {
|
|
|
1399
1468
|
});
|
|
1400
1469
|
this.pluginsWithInit = sortPluginsByDependencies(discoveredPlugins);
|
|
1401
1470
|
if (this.pluginsWithInit.length > 0) {
|
|
1402
|
-
|
|
1471
|
+
log.info(
|
|
1403
1472
|
`[Services] Found ${this.pluginsWithInit.length} plugin(s) with init: ${this.pluginsWithInit.map((p) => p.name).join(", ")}`
|
|
1404
1473
|
);
|
|
1405
1474
|
}
|
|
1406
1475
|
await executePluginServerInits(this.pluginsWithInit, this.viteServer ?? void 0, true);
|
|
1407
1476
|
if (this.projectInitFilePath) {
|
|
1408
|
-
|
|
1477
|
+
log.info("[DevServer] Loading project init: src/init.ts");
|
|
1409
1478
|
try {
|
|
1410
1479
|
if (this.viteServer) {
|
|
1411
1480
|
const module = await this.viteServer.ssrLoadModule(this.projectInitFilePath);
|
|
1412
1481
|
if (module.init?._serverInit) {
|
|
1413
|
-
|
|
1482
|
+
log.info("[DevServer] Running server init: project");
|
|
1414
1483
|
const { setClientInitData } = await import("@jay-framework/stack-server-runtime");
|
|
1415
1484
|
const clientData = await module.init._serverInit();
|
|
1416
1485
|
if (clientData !== void 0 && clientData !== null) {
|
|
@@ -1422,14 +1491,14 @@ class ServiceLifecycleManager {
|
|
|
1422
1491
|
await import(fileUrl);
|
|
1423
1492
|
}
|
|
1424
1493
|
} catch (error) {
|
|
1425
|
-
|
|
1494
|
+
log.error(`[Services] Failed to load project init: ${error}`);
|
|
1426
1495
|
throw error;
|
|
1427
1496
|
}
|
|
1428
1497
|
} else {
|
|
1429
|
-
|
|
1498
|
+
log.info("[Services] No init.ts found, skipping project initialization");
|
|
1430
1499
|
}
|
|
1431
1500
|
await runInitCallbacks();
|
|
1432
|
-
|
|
1501
|
+
log.info("[Services] Initialization complete");
|
|
1433
1502
|
await this.discoverActions();
|
|
1434
1503
|
this.isInitialized = true;
|
|
1435
1504
|
}
|
|
@@ -1437,6 +1506,7 @@ class ServiceLifecycleManager {
|
|
|
1437
1506
|
* Auto-discovers and registers actions from project and plugins.
|
|
1438
1507
|
*/
|
|
1439
1508
|
async discoverActions() {
|
|
1509
|
+
const log = getLogger();
|
|
1440
1510
|
let totalActions = 0;
|
|
1441
1511
|
try {
|
|
1442
1512
|
const result = await discoverAndRegisterActions({
|
|
@@ -1448,7 +1518,7 @@ class ServiceLifecycleManager {
|
|
|
1448
1518
|
});
|
|
1449
1519
|
totalActions += result.actionCount;
|
|
1450
1520
|
} catch (error) {
|
|
1451
|
-
|
|
1521
|
+
log.error(`[Actions] Failed to auto-discover project actions: ${error}`);
|
|
1452
1522
|
}
|
|
1453
1523
|
try {
|
|
1454
1524
|
const pluginActions = await discoverAllPluginActions({
|
|
@@ -1459,20 +1529,21 @@ class ServiceLifecycleManager {
|
|
|
1459
1529
|
});
|
|
1460
1530
|
totalActions += pluginActions.length;
|
|
1461
1531
|
} catch (error) {
|
|
1462
|
-
|
|
1532
|
+
log.error(`[Actions] Failed to auto-discover plugin actions: ${error}`);
|
|
1463
1533
|
}
|
|
1464
1534
|
if (totalActions > 0) {
|
|
1465
|
-
|
|
1535
|
+
log.info(`[Actions] Auto-registered ${totalActions} action(s) total`);
|
|
1466
1536
|
}
|
|
1467
1537
|
}
|
|
1468
1538
|
/**
|
|
1469
1539
|
* Shuts down services gracefully with timeout
|
|
1470
1540
|
*/
|
|
1471
1541
|
async shutdown(timeoutMs = 5e3) {
|
|
1542
|
+
const log = getLogger();
|
|
1472
1543
|
if (!this.isInitialized) {
|
|
1473
1544
|
return;
|
|
1474
1545
|
}
|
|
1475
|
-
|
|
1546
|
+
log.info("[Services] Shutting down...");
|
|
1476
1547
|
try {
|
|
1477
1548
|
await Promise.race([
|
|
1478
1549
|
runShutdownCallbacks(),
|
|
@@ -1480,12 +1551,12 @@ class ServiceLifecycleManager {
|
|
|
1480
1551
|
(_, reject) => setTimeout(() => reject(new Error("Shutdown timeout")), timeoutMs)
|
|
1481
1552
|
)
|
|
1482
1553
|
]);
|
|
1483
|
-
|
|
1554
|
+
log.info("[Services] Shutdown complete");
|
|
1484
1555
|
} catch (error) {
|
|
1485
1556
|
if (error.message === "Shutdown timeout") {
|
|
1486
|
-
|
|
1557
|
+
log.warn(`[Services] Shutdown timed out after ${timeoutMs}ms`);
|
|
1487
1558
|
} else {
|
|
1488
|
-
|
|
1559
|
+
log.error(`[Services] Shutdown error: ${error}`);
|
|
1489
1560
|
}
|
|
1490
1561
|
} finally {
|
|
1491
1562
|
this.isInitialized = false;
|
|
@@ -1495,7 +1566,8 @@ class ServiceLifecycleManager {
|
|
|
1495
1566
|
* Hot reload: shutdown, clear caches, re-import, and re-initialize
|
|
1496
1567
|
*/
|
|
1497
1568
|
async reload() {
|
|
1498
|
-
|
|
1569
|
+
const log = getLogger();
|
|
1570
|
+
log.info("[Services] Reloading services...");
|
|
1499
1571
|
await this.shutdown();
|
|
1500
1572
|
clearLifecycleCallbacks();
|
|
1501
1573
|
clearServiceRegistry();
|
|
@@ -1511,7 +1583,7 @@ class ServiceLifecycleManager {
|
|
|
1511
1583
|
}
|
|
1512
1584
|
this.isInitialized = false;
|
|
1513
1585
|
await this.initialize();
|
|
1514
|
-
|
|
1586
|
+
log.info("[Services] Reload complete");
|
|
1515
1587
|
}
|
|
1516
1588
|
/**
|
|
1517
1589
|
* Returns the path to the init file if found.
|
|
@@ -1813,6 +1885,7 @@ function filterPluginsForPage(allPluginClientInits, allPluginsWithInit, usedPack
|
|
|
1813
1885
|
function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, projectInit, allPluginsWithInit = [], allPluginClientInits = []) {
|
|
1814
1886
|
const routePath = routeToExpressRoute(route);
|
|
1815
1887
|
const handler = async (req, res) => {
|
|
1888
|
+
const timing = getDevLogger()?.startRequest(req.method, req.path);
|
|
1816
1889
|
try {
|
|
1817
1890
|
const url = req.originalUrl.replace(options.publicBaseUrlPath, "");
|
|
1818
1891
|
const pageParams = { ...route.inferredParams, ...req.params };
|
|
@@ -1826,7 +1899,7 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, projectInit
|
|
|
1826
1899
|
try {
|
|
1827
1900
|
await fs$1.access(cachedEntry.preRenderedPath);
|
|
1828
1901
|
} catch {
|
|
1829
|
-
|
|
1902
|
+
getLogger().info(
|
|
1830
1903
|
`[SlowRender] Cached file missing, rebuilding: ${cachedEntry.preRenderedPath}`
|
|
1831
1904
|
);
|
|
1832
1905
|
await slowRenderCache.invalidate(route.jayHtmlPath);
|
|
@@ -1845,7 +1918,8 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, projectInit
|
|
|
1845
1918
|
allPluginsWithInit,
|
|
1846
1919
|
projectInit,
|
|
1847
1920
|
res,
|
|
1848
|
-
url
|
|
1921
|
+
url,
|
|
1922
|
+
timing
|
|
1849
1923
|
);
|
|
1850
1924
|
} else if (useSlowRenderCache) {
|
|
1851
1925
|
await handlePreRenderRequest(
|
|
@@ -1860,7 +1934,8 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, projectInit
|
|
|
1860
1934
|
allPluginsWithInit,
|
|
1861
1935
|
projectInit,
|
|
1862
1936
|
res,
|
|
1863
|
-
url
|
|
1937
|
+
url,
|
|
1938
|
+
timing
|
|
1864
1939
|
);
|
|
1865
1940
|
} else {
|
|
1866
1941
|
await handleDirectRequest(
|
|
@@ -1874,18 +1949,21 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, projectInit
|
|
|
1874
1949
|
allPluginsWithInit,
|
|
1875
1950
|
projectInit,
|
|
1876
1951
|
res,
|
|
1877
|
-
url
|
|
1952
|
+
url,
|
|
1953
|
+
timing
|
|
1878
1954
|
);
|
|
1879
1955
|
}
|
|
1880
1956
|
} catch (e2) {
|
|
1881
1957
|
vite?.ssrFixStacktrace(e2);
|
|
1882
|
-
|
|
1958
|
+
getLogger().error(e2.stack);
|
|
1883
1959
|
res.status(500).end(e2.stack);
|
|
1960
|
+
timing?.end();
|
|
1884
1961
|
}
|
|
1885
1962
|
};
|
|
1886
1963
|
return { path: routePath, handler, fsRoute: route };
|
|
1887
1964
|
}
|
|
1888
|
-
async function handleCachedRequest(vite, route, options, cachedEntry, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url) {
|
|
1965
|
+
async function handleCachedRequest(vite, route, options, cachedEntry, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing) {
|
|
1966
|
+
const loadStart = Date.now();
|
|
1889
1967
|
const pagePartsResult = await loadPageParts(
|
|
1890
1968
|
vite,
|
|
1891
1969
|
route,
|
|
@@ -1894,9 +1972,11 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
|
|
|
1894
1972
|
options.jayRollupConfig,
|
|
1895
1973
|
{ preRenderedPath: cachedEntry.preRenderedPath }
|
|
1896
1974
|
);
|
|
1975
|
+
timing?.recordViteSsr(Date.now() - loadStart);
|
|
1897
1976
|
if (!pagePartsResult.val) {
|
|
1898
|
-
|
|
1977
|
+
getLogger().info(pagePartsResult.validations.join("\n"));
|
|
1899
1978
|
res.status(500).end(pagePartsResult.validations.join("\n"));
|
|
1979
|
+
timing?.end();
|
|
1900
1980
|
return;
|
|
1901
1981
|
}
|
|
1902
1982
|
const { parts: pageParts, clientTrackByMap, usedPackages } = pagePartsResult.val;
|
|
@@ -1905,32 +1985,56 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
|
|
|
1905
1985
|
allPluginsWithInit,
|
|
1906
1986
|
usedPackages
|
|
1907
1987
|
);
|
|
1988
|
+
const fastStart = Date.now();
|
|
1908
1989
|
const renderedFast = await renderFastChangingData(
|
|
1909
1990
|
pageParams,
|
|
1910
1991
|
pageProps,
|
|
1911
1992
|
cachedEntry.carryForward,
|
|
1912
1993
|
pageParts
|
|
1913
1994
|
);
|
|
1995
|
+
timing?.recordFastRender(Date.now() - fastStart);
|
|
1914
1996
|
if (renderedFast.kind !== "PhaseOutput") {
|
|
1915
1997
|
handleOtherResponseCodes(res, renderedFast);
|
|
1998
|
+
timing?.end();
|
|
1916
1999
|
return;
|
|
1917
2000
|
}
|
|
2001
|
+
let fastViewState = renderedFast.rendered;
|
|
2002
|
+
let fastCarryForward = renderedFast.carryForward;
|
|
2003
|
+
const instancePhaseData = cachedEntry.carryForward?.__instances;
|
|
2004
|
+
if (instancePhaseData && pagePartsResult.val.headlessInstanceComponents.length > 0) {
|
|
2005
|
+
const instanceFastResult = await renderFastChangingDataForInstances(
|
|
2006
|
+
instancePhaseData,
|
|
2007
|
+
pagePartsResult.val.headlessInstanceComponents
|
|
2008
|
+
);
|
|
2009
|
+
if (instanceFastResult) {
|
|
2010
|
+
fastViewState = {
|
|
2011
|
+
...fastViewState,
|
|
2012
|
+
__headlessInstances: instanceFastResult.viewStates
|
|
2013
|
+
};
|
|
2014
|
+
fastCarryForward = {
|
|
2015
|
+
...fastCarryForward,
|
|
2016
|
+
__headlessInstances: instanceFastResult.carryForwards
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
1918
2020
|
await sendResponse(
|
|
1919
2021
|
vite,
|
|
1920
2022
|
res,
|
|
1921
2023
|
url,
|
|
1922
2024
|
cachedEntry.preRenderedPath,
|
|
1923
2025
|
pageParts,
|
|
1924
|
-
|
|
1925
|
-
|
|
2026
|
+
fastViewState,
|
|
2027
|
+
fastCarryForward,
|
|
1926
2028
|
clientTrackByMap,
|
|
1927
2029
|
projectInit,
|
|
1928
2030
|
pluginsForPage,
|
|
1929
2031
|
options,
|
|
1930
|
-
cachedEntry.slowViewState
|
|
2032
|
+
cachedEntry.slowViewState,
|
|
2033
|
+
timing
|
|
1931
2034
|
);
|
|
1932
2035
|
}
|
|
1933
|
-
async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRenderCache, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url) {
|
|
2036
|
+
async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRenderCache, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing) {
|
|
2037
|
+
const loadStart = Date.now();
|
|
1934
2038
|
const initialPartsResult = await loadPageParts(
|
|
1935
2039
|
vite,
|
|
1936
2040
|
route,
|
|
@@ -1938,35 +2042,48 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
|
|
|
1938
2042
|
options.projectRootFolder,
|
|
1939
2043
|
options.jayRollupConfig
|
|
1940
2044
|
);
|
|
2045
|
+
timing?.recordViteSsr(Date.now() - loadStart);
|
|
1941
2046
|
if (!initialPartsResult.val) {
|
|
1942
|
-
|
|
2047
|
+
getLogger().info(initialPartsResult.validations.join("\n"));
|
|
1943
2048
|
res.status(500).end(initialPartsResult.validations.join("\n"));
|
|
2049
|
+
timing?.end();
|
|
1944
2050
|
return;
|
|
1945
2051
|
}
|
|
2052
|
+
const slowStart = Date.now();
|
|
1946
2053
|
const renderedSlowly = await slowlyPhase.runSlowlyForPage(
|
|
1947
2054
|
pageParams,
|
|
1948
2055
|
pageProps,
|
|
1949
2056
|
initialPartsResult.val.parts
|
|
1950
2057
|
);
|
|
1951
2058
|
if (renderedSlowly.kind !== "PhaseOutput") {
|
|
2059
|
+
timing?.recordSlowRender(Date.now() - slowStart);
|
|
1952
2060
|
if (renderedSlowly.kind === "ClientError") {
|
|
1953
2061
|
handleOtherResponseCodes(res, renderedSlowly);
|
|
1954
2062
|
}
|
|
2063
|
+
timing?.end();
|
|
1955
2064
|
return;
|
|
1956
2065
|
}
|
|
1957
|
-
const
|
|
1958
|
-
|
|
2066
|
+
const preRenderResult = await preRenderJayHtml(
|
|
2067
|
+
route,
|
|
2068
|
+
renderedSlowly.rendered,
|
|
2069
|
+
initialPartsResult.val.headlessContracts,
|
|
2070
|
+
initialPartsResult.val.headlessInstanceComponents
|
|
2071
|
+
);
|
|
2072
|
+
timing?.recordSlowRender(Date.now() - slowStart);
|
|
2073
|
+
if (!preRenderResult) {
|
|
1959
2074
|
res.status(500).end("Failed to pre-render jay-html");
|
|
2075
|
+
timing?.end();
|
|
1960
2076
|
return;
|
|
1961
2077
|
}
|
|
2078
|
+
const carryForward = preRenderResult.instancePhaseData ? { ...renderedSlowly.carryForward, __instances: preRenderResult.instancePhaseData } : renderedSlowly.carryForward;
|
|
1962
2079
|
const preRenderedPath = await slowRenderCache.set(
|
|
1963
2080
|
route.jayHtmlPath,
|
|
1964
2081
|
pageParams,
|
|
1965
|
-
|
|
2082
|
+
preRenderResult.preRenderedJayHtml,
|
|
1966
2083
|
renderedSlowly.rendered,
|
|
1967
|
-
|
|
2084
|
+
carryForward
|
|
1968
2085
|
);
|
|
1969
|
-
|
|
2086
|
+
getLogger().info(`[SlowRender] Cached pre-rendered jay-html at ${preRenderedPath}`);
|
|
1970
2087
|
const pagePartsResult = await loadPageParts(
|
|
1971
2088
|
vite,
|
|
1972
2089
|
route,
|
|
@@ -1976,8 +2093,9 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
|
|
|
1976
2093
|
{ preRenderedPath }
|
|
1977
2094
|
);
|
|
1978
2095
|
if (!pagePartsResult.val) {
|
|
1979
|
-
|
|
2096
|
+
getLogger().info(pagePartsResult.validations.join("\n"));
|
|
1980
2097
|
res.status(500).end(pagePartsResult.validations.join("\n"));
|
|
2098
|
+
timing?.end();
|
|
1981
2099
|
return;
|
|
1982
2100
|
}
|
|
1983
2101
|
const { parts: pageParts, clientTrackByMap, usedPackages } = pagePartsResult.val;
|
|
@@ -1986,32 +2104,56 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
|
|
|
1986
2104
|
allPluginsWithInit,
|
|
1987
2105
|
usedPackages
|
|
1988
2106
|
);
|
|
2107
|
+
const fastStart = Date.now();
|
|
1989
2108
|
const renderedFast = await renderFastChangingData(
|
|
1990
2109
|
pageParams,
|
|
1991
2110
|
pageProps,
|
|
1992
|
-
|
|
2111
|
+
carryForward,
|
|
1993
2112
|
pageParts
|
|
1994
2113
|
);
|
|
2114
|
+
timing?.recordFastRender(Date.now() - fastStart);
|
|
1995
2115
|
if (renderedFast.kind !== "PhaseOutput") {
|
|
1996
2116
|
handleOtherResponseCodes(res, renderedFast);
|
|
2117
|
+
timing?.end();
|
|
1997
2118
|
return;
|
|
1998
2119
|
}
|
|
2120
|
+
let fastViewState = renderedFast.rendered;
|
|
2121
|
+
let fastCarryForward = renderedFast.carryForward;
|
|
2122
|
+
const instancePhaseData = carryForward?.__instances;
|
|
2123
|
+
if (instancePhaseData && pagePartsResult.val.headlessInstanceComponents.length > 0) {
|
|
2124
|
+
const instanceFastResult = await renderFastChangingDataForInstances(
|
|
2125
|
+
instancePhaseData,
|
|
2126
|
+
pagePartsResult.val.headlessInstanceComponents
|
|
2127
|
+
);
|
|
2128
|
+
if (instanceFastResult) {
|
|
2129
|
+
fastViewState = {
|
|
2130
|
+
...fastViewState,
|
|
2131
|
+
__headlessInstances: instanceFastResult.viewStates
|
|
2132
|
+
};
|
|
2133
|
+
fastCarryForward = {
|
|
2134
|
+
...fastCarryForward,
|
|
2135
|
+
__headlessInstances: instanceFastResult.carryForwards
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
1999
2139
|
await sendResponse(
|
|
2000
2140
|
vite,
|
|
2001
2141
|
res,
|
|
2002
2142
|
url,
|
|
2003
2143
|
preRenderedPath,
|
|
2004
2144
|
pageParts,
|
|
2005
|
-
|
|
2006
|
-
|
|
2145
|
+
fastViewState,
|
|
2146
|
+
fastCarryForward,
|
|
2007
2147
|
clientTrackByMap,
|
|
2008
2148
|
projectInit,
|
|
2009
2149
|
pluginsForPage,
|
|
2010
2150
|
options,
|
|
2011
|
-
renderedSlowly.rendered
|
|
2151
|
+
renderedSlowly.rendered,
|
|
2152
|
+
timing
|
|
2012
2153
|
);
|
|
2013
2154
|
}
|
|
2014
|
-
async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url) {
|
|
2155
|
+
async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing) {
|
|
2156
|
+
const loadStart = Date.now();
|
|
2015
2157
|
const pagePartsResult = await loadPageParts(
|
|
2016
2158
|
vite,
|
|
2017
2159
|
route,
|
|
@@ -2019,9 +2161,11 @@ async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams
|
|
|
2019
2161
|
options.projectRootFolder,
|
|
2020
2162
|
options.jayRollupConfig
|
|
2021
2163
|
);
|
|
2164
|
+
timing?.recordViteSsr(Date.now() - loadStart);
|
|
2022
2165
|
if (!pagePartsResult.val) {
|
|
2023
|
-
|
|
2166
|
+
getLogger().info(pagePartsResult.validations.join("\n"));
|
|
2024
2167
|
res.status(500).end(pagePartsResult.validations.join("\n"));
|
|
2168
|
+
timing?.end();
|
|
2025
2169
|
return;
|
|
2026
2170
|
}
|
|
2027
2171
|
const {
|
|
@@ -2035,21 +2179,59 @@ async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams
|
|
|
2035
2179
|
allPluginsWithInit,
|
|
2036
2180
|
usedPackages
|
|
2037
2181
|
);
|
|
2182
|
+
const slowStart = Date.now();
|
|
2038
2183
|
const renderedSlowly = await slowlyPhase.runSlowlyForPage(pageParams, pageProps, pageParts);
|
|
2039
2184
|
if (renderedSlowly.kind !== "PhaseOutput") {
|
|
2185
|
+
timing?.recordSlowRender(Date.now() - slowStart);
|
|
2040
2186
|
if (renderedSlowly.kind === "ClientError") {
|
|
2041
2187
|
handleOtherResponseCodes(res, renderedSlowly);
|
|
2042
2188
|
}
|
|
2189
|
+
timing?.end();
|
|
2043
2190
|
return;
|
|
2044
2191
|
}
|
|
2192
|
+
let instanceViewStates;
|
|
2193
|
+
let instancePhaseDataForFast;
|
|
2194
|
+
const headlessInstanceComponents = pagePartsResult.val.headlessInstanceComponents ?? [];
|
|
2195
|
+
if (headlessInstanceComponents.length > 0) {
|
|
2196
|
+
const jayHtmlContent = await fs$1.readFile(route.jayHtmlPath, "utf-8");
|
|
2197
|
+
const discoveryResult = discoverHeadlessInstances(jayHtmlContent);
|
|
2198
|
+
if (discoveryResult.instances.length > 0) {
|
|
2199
|
+
const slowResult = await slowRenderInstances(
|
|
2200
|
+
discoveryResult.instances,
|
|
2201
|
+
headlessInstanceComponents
|
|
2202
|
+
);
|
|
2203
|
+
if (slowResult) {
|
|
2204
|
+
instanceViewStates = { ...slowResult.slowViewStates };
|
|
2205
|
+
instancePhaseDataForFast = slowResult.instancePhaseData;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
timing?.recordSlowRender(Date.now() - slowStart);
|
|
2210
|
+
const fastStart = Date.now();
|
|
2045
2211
|
const renderedFast = await renderFastChangingData(
|
|
2046
2212
|
pageParams,
|
|
2047
2213
|
pageProps,
|
|
2048
2214
|
renderedSlowly.carryForward,
|
|
2049
2215
|
pageParts
|
|
2050
2216
|
);
|
|
2217
|
+
if (instancePhaseDataForFast && instanceViewStates) {
|
|
2218
|
+
const instanceFastResult = await renderFastChangingDataForInstances(
|
|
2219
|
+
instancePhaseDataForFast,
|
|
2220
|
+
headlessInstanceComponents
|
|
2221
|
+
);
|
|
2222
|
+
if (instanceFastResult) {
|
|
2223
|
+
for (const [coordKey, fastVS] of Object.entries(instanceFastResult.viewStates)) {
|
|
2224
|
+
instanceViewStates[coordKey] = {
|
|
2225
|
+
...instanceViewStates[coordKey] || {},
|
|
2226
|
+
...fastVS
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
timing?.recordFastRender(Date.now() - fastStart);
|
|
2051
2232
|
if (renderedFast.kind !== "PhaseOutput") {
|
|
2052
2233
|
handleOtherResponseCodes(res, renderedFast);
|
|
2234
|
+
timing?.end();
|
|
2053
2235
|
return;
|
|
2054
2236
|
}
|
|
2055
2237
|
let viewState;
|
|
@@ -2062,6 +2244,12 @@ async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams
|
|
|
2062
2244
|
} else {
|
|
2063
2245
|
viewState = { ...renderedSlowly.rendered, ...renderedFast.rendered };
|
|
2064
2246
|
}
|
|
2247
|
+
if (instanceViewStates && Object.keys(instanceViewStates).length > 0) {
|
|
2248
|
+
viewState = {
|
|
2249
|
+
...viewState,
|
|
2250
|
+
__headlessInstances: instanceViewStates
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2065
2253
|
await sendResponse(
|
|
2066
2254
|
vite,
|
|
2067
2255
|
res,
|
|
@@ -2073,10 +2261,12 @@ async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams
|
|
|
2073
2261
|
clientTrackByMap,
|
|
2074
2262
|
projectInit,
|
|
2075
2263
|
pluginsForPage,
|
|
2076
|
-
options
|
|
2264
|
+
options,
|
|
2265
|
+
void 0,
|
|
2266
|
+
timing
|
|
2077
2267
|
);
|
|
2078
2268
|
}
|
|
2079
|
-
async function sendResponse(vite, res, url, jayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, slowViewState) {
|
|
2269
|
+
async function sendResponse(vite, res, url, jayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, slowViewState, timing) {
|
|
2080
2270
|
const pageHtml = generateClientScript(
|
|
2081
2271
|
viewState,
|
|
2082
2272
|
carryForward,
|
|
@@ -2091,10 +2281,19 @@ async function sendResponse(vite, res, url, jayHtmlPath, pageParts, viewState, c
|
|
|
2091
2281
|
slowViewState
|
|
2092
2282
|
}
|
|
2093
2283
|
);
|
|
2284
|
+
if (options.buildFolder) {
|
|
2285
|
+
const pageName = !url || url === "/" ? "index" : url.replace(/^\//, "").replace(/\//g, "-");
|
|
2286
|
+
const clientScriptDir = path__default.join(options.buildFolder, "client-scripts");
|
|
2287
|
+
await fs$1.mkdir(clientScriptDir, { recursive: true });
|
|
2288
|
+
await fs$1.writeFile(path__default.join(clientScriptDir, `${pageName}.html`), pageHtml, "utf-8");
|
|
2289
|
+
}
|
|
2290
|
+
const viteStart = Date.now();
|
|
2094
2291
|
const compiledPageHtml = await vite.transformIndexHtml(!!url ? url : "/", pageHtml);
|
|
2292
|
+
timing?.recordViteClient(Date.now() - viteStart);
|
|
2095
2293
|
res.status(200).set({ "Content-Type": "text/html" }).send(compiledPageHtml);
|
|
2294
|
+
timing?.end();
|
|
2096
2295
|
}
|
|
2097
|
-
async function preRenderJayHtml(route, slowViewState) {
|
|
2296
|
+
async function preRenderJayHtml(route, slowViewState, headlessContracts, headlessInstanceComponents) {
|
|
2098
2297
|
const jayHtmlContent = await fs$1.readFile(route.jayHtmlPath, "utf-8");
|
|
2099
2298
|
const contractPath = route.jayHtmlPath.replace(".jay-html", ".jay-contract");
|
|
2100
2299
|
let contract;
|
|
@@ -2104,15 +2303,14 @@ async function preRenderJayHtml(route, slowViewState) {
|
|
|
2104
2303
|
if (parseResult.val) {
|
|
2105
2304
|
contract = parseResult.val;
|
|
2106
2305
|
} else if (parseResult.validations.length > 0) {
|
|
2107
|
-
|
|
2108
|
-
`[SlowRender] Contract parse error for ${contractPath}
|
|
2109
|
-
parseResult.validations
|
|
2306
|
+
getLogger().error(
|
|
2307
|
+
`[SlowRender] Contract parse error for ${contractPath}: ${parseResult.validations.join(", ")}`
|
|
2110
2308
|
);
|
|
2111
2309
|
return void 0;
|
|
2112
2310
|
}
|
|
2113
2311
|
} catch (error) {
|
|
2114
2312
|
if (error.code !== "ENOENT") {
|
|
2115
|
-
|
|
2313
|
+
getLogger().error(`[SlowRender] Error reading contract ${contractPath}: ${error}`);
|
|
2116
2314
|
return void 0;
|
|
2117
2315
|
}
|
|
2118
2316
|
}
|
|
@@ -2120,20 +2318,99 @@ async function preRenderJayHtml(route, slowViewState) {
|
|
|
2120
2318
|
jayHtmlContent,
|
|
2121
2319
|
slowViewState,
|
|
2122
2320
|
contract,
|
|
2123
|
-
|
|
2321
|
+
headlessContracts,
|
|
2322
|
+
sourceDir: path__default.dirname(route.jayHtmlPath),
|
|
2323
|
+
importResolver: JAY_IMPORT_RESOLVER
|
|
2124
2324
|
});
|
|
2125
|
-
if (result.val) {
|
|
2126
|
-
|
|
2325
|
+
if (!result.val) {
|
|
2326
|
+
if (result.validations.length > 0) {
|
|
2327
|
+
getLogger().error(
|
|
2328
|
+
`[SlowRender] Transform failed for ${route.jayHtmlPath}: ${result.validations.join(", ")}`
|
|
2329
|
+
);
|
|
2330
|
+
}
|
|
2331
|
+
return void 0;
|
|
2332
|
+
}
|
|
2333
|
+
let preRenderedJayHtml = result.val.preRenderedJayHtml;
|
|
2334
|
+
let instancePhaseData;
|
|
2335
|
+
if (headlessInstanceComponents.length > 0) {
|
|
2336
|
+
const discoveryResult = discoverHeadlessInstances(preRenderedJayHtml);
|
|
2337
|
+
preRenderedJayHtml = discoveryResult.preRenderedJayHtml;
|
|
2338
|
+
if (discoveryResult.instances.length > 0) {
|
|
2339
|
+
const slowResult = await slowRenderInstances(
|
|
2340
|
+
discoveryResult.instances,
|
|
2341
|
+
headlessInstanceComponents
|
|
2342
|
+
);
|
|
2343
|
+
if (slowResult) {
|
|
2344
|
+
instancePhaseData = slowResult.instancePhaseData;
|
|
2345
|
+
const pass2Result = resolveHeadlessInstances(
|
|
2346
|
+
preRenderedJayHtml,
|
|
2347
|
+
slowResult.resolvedData,
|
|
2348
|
+
JAY_IMPORT_RESOLVER
|
|
2349
|
+
);
|
|
2350
|
+
if (pass2Result.val) {
|
|
2351
|
+
preRenderedJayHtml = pass2Result.val;
|
|
2352
|
+
}
|
|
2353
|
+
if (pass2Result.validations.length > 0) {
|
|
2354
|
+
getLogger().error(
|
|
2355
|
+
`[SlowRender] Instance resolution warnings for ${route.jayHtmlPath}: ${pass2Result.validations.join(", ")}`
|
|
2356
|
+
);
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2127
2360
|
}
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2361
|
+
return { preRenderedJayHtml, instancePhaseData };
|
|
2362
|
+
}
|
|
2363
|
+
async function renderFastChangingDataForInstances(instancePhaseData, headlessInstanceComponents) {
|
|
2364
|
+
const componentByContractName = /* @__PURE__ */ new Map();
|
|
2365
|
+
for (const comp of headlessInstanceComponents) {
|
|
2366
|
+
componentByContractName.set(comp.contractName, comp);
|
|
2367
|
+
}
|
|
2368
|
+
const viewStates = {};
|
|
2369
|
+
const carryForwards = {};
|
|
2370
|
+
let hasResults = false;
|
|
2371
|
+
for (const instance of instancePhaseData.discovered) {
|
|
2372
|
+
const coordKey = instance.coordinate.join("/");
|
|
2373
|
+
const comp = componentByContractName.get(instance.contractName);
|
|
2374
|
+
if (!comp || !comp.compDefinition.fastRender) {
|
|
2375
|
+
continue;
|
|
2376
|
+
}
|
|
2377
|
+
const instanceCarryForward = instancePhaseData.carryForwards[coordKey] || {};
|
|
2378
|
+
const services = resolveServices(comp.compDefinition.services);
|
|
2379
|
+
const fastResult = await comp.compDefinition.fastRender(
|
|
2380
|
+
instance.props,
|
|
2381
|
+
instanceCarryForward,
|
|
2382
|
+
...services
|
|
2132
2383
|
);
|
|
2384
|
+
if (fastResult.kind === "PhaseOutput") {
|
|
2385
|
+
viewStates[coordKey] = fastResult.rendered;
|
|
2386
|
+
carryForwards[coordKey] = fastResult.carryForward;
|
|
2387
|
+
hasResults = true;
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
return hasResults ? { viewStates, carryForwards } : void 0;
|
|
2391
|
+
}
|
|
2392
|
+
async function materializeDynamicContracts(projectRootFolder, buildFolder, viteServer) {
|
|
2393
|
+
try {
|
|
2394
|
+
const services = getServiceRegistry();
|
|
2395
|
+
const result = await materializeContracts(
|
|
2396
|
+
{
|
|
2397
|
+
projectRoot: projectRootFolder,
|
|
2398
|
+
outputDir: path__default.join(buildFolder, "materialized-contracts"),
|
|
2399
|
+
verbose: false,
|
|
2400
|
+
viteServer
|
|
2401
|
+
},
|
|
2402
|
+
services
|
|
2403
|
+
);
|
|
2404
|
+
const dynamicCount = result.dynamicCount;
|
|
2405
|
+
if (dynamicCount > 0) {
|
|
2406
|
+
getLogger().info(`[Contracts] Materialized ${dynamicCount} dynamic contract(s)`);
|
|
2407
|
+
}
|
|
2408
|
+
} catch (error) {
|
|
2409
|
+
getLogger().warn(`[Contracts] Failed to materialize dynamic contracts: ${error.message}`);
|
|
2133
2410
|
}
|
|
2134
|
-
return void 0;
|
|
2135
2411
|
}
|
|
2136
|
-
async function mkDevServer(
|
|
2412
|
+
async function mkDevServer(rawOptions) {
|
|
2413
|
+
const options = defaults(rawOptions);
|
|
2137
2414
|
const {
|
|
2138
2415
|
publicBaseUrlPath,
|
|
2139
2416
|
pagesRootFolder,
|
|
@@ -2141,23 +2418,20 @@ async function mkDevServer(options) {
|
|
|
2141
2418
|
buildFolder,
|
|
2142
2419
|
jayRollupConfig,
|
|
2143
2420
|
dontCacheSlowly
|
|
2144
|
-
} =
|
|
2421
|
+
} = options;
|
|
2422
|
+
const viteLogLevel = options.logLevel === "silent" ? "silent" : options.logLevel === "verbose" ? "info" : "warn";
|
|
2145
2423
|
const lifecycleManager = new ServiceLifecycleManager(projectRootFolder);
|
|
2146
2424
|
setupGracefulShutdown(lifecycleManager);
|
|
2147
|
-
const vite = await
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
appType: "custom",
|
|
2425
|
+
const vite = await createViteServer({
|
|
2426
|
+
projectRoot: projectRootFolder,
|
|
2427
|
+
pagesRoot: pagesRootFolder,
|
|
2151
2428
|
base: publicBaseUrlPath,
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
// Mark stack-server-runtime as external so Vite uses Node's require
|
|
2155
|
-
// This ensures lib/init.ts and dev-server share the same module instance
|
|
2156
|
-
external: ["@jay-framework/stack-server-runtime"]
|
|
2157
|
-
}
|
|
2429
|
+
jayRollupConfig,
|
|
2430
|
+
logLevel: viteLogLevel
|
|
2158
2431
|
});
|
|
2159
2432
|
lifecycleManager.setViteServer(vite);
|
|
2160
2433
|
await lifecycleManager.initialize();
|
|
2434
|
+
await materializeDynamicContracts(projectRootFolder, buildFolder, vite);
|
|
2161
2435
|
setupServiceHotReload(vite, lifecycleManager);
|
|
2162
2436
|
setupActionRouter(vite);
|
|
2163
2437
|
const routes = await initRoutes(pagesRootFolder);
|
|
@@ -2189,7 +2463,7 @@ async function mkDevServer(options) {
|
|
|
2189
2463
|
}
|
|
2190
2464
|
function setupGracefulShutdown(lifecycleManager) {
|
|
2191
2465
|
const shutdown = async (signal) => {
|
|
2192
|
-
|
|
2466
|
+
getLogger().important(`
|
|
2193
2467
|
${signal} received, shutting down gracefully...`);
|
|
2194
2468
|
await lifecycleManager.shutdown();
|
|
2195
2469
|
process.exit(0);
|
|
@@ -2205,7 +2479,7 @@ function setupServiceHotReload(vite, lifecycleManager) {
|
|
|
2205
2479
|
vite.watcher.add(initFilePath);
|
|
2206
2480
|
vite.watcher.on("change", async (changedPath) => {
|
|
2207
2481
|
if (changedPath === initFilePath) {
|
|
2208
|
-
|
|
2482
|
+
getLogger().info("[Services] lib/init.ts changed, reloading services...");
|
|
2209
2483
|
try {
|
|
2210
2484
|
await lifecycleManager.reload();
|
|
2211
2485
|
vite.ws.send({
|
|
@@ -2213,7 +2487,7 @@ function setupServiceHotReload(vite, lifecycleManager) {
|
|
|
2213
2487
|
path: "*"
|
|
2214
2488
|
});
|
|
2215
2489
|
} catch (error) {
|
|
2216
|
-
|
|
2490
|
+
getLogger().error(`[Services] Failed to reload services: ${error}`);
|
|
2217
2491
|
}
|
|
2218
2492
|
}
|
|
2219
2493
|
});
|
|
@@ -2221,7 +2495,7 @@ function setupServiceHotReload(vite, lifecycleManager) {
|
|
|
2221
2495
|
function setupActionRouter(vite) {
|
|
2222
2496
|
vite.middlewares.use(actionBodyParser());
|
|
2223
2497
|
vite.middlewares.use(ACTION_ENDPOINT_BASE, createActionRouter());
|
|
2224
|
-
|
|
2498
|
+
getLogger().info(`[Actions] Action router mounted at ${ACTION_ENDPOINT_BASE}`);
|
|
2225
2499
|
}
|
|
2226
2500
|
function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder) {
|
|
2227
2501
|
vite.watcher.on("change", (changedPath) => {
|
|
@@ -2230,7 +2504,7 @@ function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder) {
|
|
|
2230
2504
|
}
|
|
2231
2505
|
if (changedPath.endsWith(".jay-html")) {
|
|
2232
2506
|
cache.invalidate(changedPath).then(() => {
|
|
2233
|
-
|
|
2507
|
+
getLogger().info(`[SlowRender] Cache invalidated for ${changedPath}`);
|
|
2234
2508
|
});
|
|
2235
2509
|
return;
|
|
2236
2510
|
}
|
|
@@ -2238,14 +2512,18 @@ function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder) {
|
|
|
2238
2512
|
const dir = path__default.dirname(changedPath);
|
|
2239
2513
|
const jayHtmlPath = path__default.join(dir, "page.jay-html");
|
|
2240
2514
|
cache.invalidate(jayHtmlPath).then(() => {
|
|
2241
|
-
|
|
2515
|
+
getLogger().info(
|
|
2516
|
+
`[SlowRender] Cache invalidated for ${jayHtmlPath} (page.ts changed)`
|
|
2517
|
+
);
|
|
2242
2518
|
});
|
|
2243
2519
|
return;
|
|
2244
2520
|
}
|
|
2245
2521
|
if (changedPath.endsWith(".jay-contract")) {
|
|
2246
2522
|
const jayHtmlPath = changedPath.replace(".jay-contract", ".jay-html");
|
|
2247
2523
|
cache.invalidate(jayHtmlPath).then(() => {
|
|
2248
|
-
|
|
2524
|
+
getLogger().info(
|
|
2525
|
+
`[SlowRender] Cache invalidated for ${jayHtmlPath} (contract changed)`
|
|
2526
|
+
);
|
|
2249
2527
|
});
|
|
2250
2528
|
return;
|
|
2251
2529
|
}
|
|
@@ -2255,5 +2533,7 @@ export {
|
|
|
2255
2533
|
ACTION_ENDPOINT_BASE,
|
|
2256
2534
|
actionBodyParser,
|
|
2257
2535
|
createActionRouter,
|
|
2536
|
+
createViteForCli,
|
|
2537
|
+
createViteServer,
|
|
2258
2538
|
mkDevServer
|
|
2259
2539
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/dev-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,21 +22,22 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@jay-framework/compiler-jay-stack": "^0.
|
|
26
|
-
"@jay-framework/compiler-shared": "^0.
|
|
27
|
-
"@jay-framework/component": "^0.
|
|
28
|
-
"@jay-framework/fullstack-component": "^0.
|
|
29
|
-
"@jay-framework/
|
|
30
|
-
"@jay-framework/
|
|
31
|
-
"@jay-framework/stack-
|
|
32
|
-
"@jay-framework/stack-
|
|
33
|
-
"@jay-framework/
|
|
25
|
+
"@jay-framework/compiler-jay-stack": "^0.12.0",
|
|
26
|
+
"@jay-framework/compiler-shared": "^0.12.0",
|
|
27
|
+
"@jay-framework/component": "^0.12.0",
|
|
28
|
+
"@jay-framework/fullstack-component": "^0.12.0",
|
|
29
|
+
"@jay-framework/logger": "^0.12.0",
|
|
30
|
+
"@jay-framework/runtime": "^0.12.0",
|
|
31
|
+
"@jay-framework/stack-client-runtime": "^0.12.0",
|
|
32
|
+
"@jay-framework/stack-route-scanner": "^0.12.0",
|
|
33
|
+
"@jay-framework/stack-server-runtime": "^0.12.0",
|
|
34
|
+
"@jay-framework/view-state-merge": "^0.12.0",
|
|
34
35
|
"vite": "^5.0.11"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
|
-
"@jay-framework/dev-environment": "^0.
|
|
38
|
-
"@jay-framework/jay-cli": "^0.
|
|
39
|
-
"@jay-framework/stack-client-runtime": "^0.
|
|
38
|
+
"@jay-framework/dev-environment": "^0.12.0",
|
|
39
|
+
"@jay-framework/jay-cli": "^0.12.0",
|
|
40
|
+
"@jay-framework/stack-client-runtime": "^0.12.0",
|
|
40
41
|
"@types/express": "^5.0.2",
|
|
41
42
|
"@types/node": "^22.15.21",
|
|
42
43
|
"nodemon": "^3.0.3",
|