@jay-framework/dev-server 0.11.0 → 0.13.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 +513 -100
- 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, validateForEachInstances, 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,67 @@ 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: [...jayStackCompiler({ tsConfigFilePath })],
|
|
1405
|
+
appType: "custom",
|
|
1406
|
+
root: projectRoot,
|
|
1407
|
+
ssr: {
|
|
1408
|
+
external: ["@jay-framework/stack-server-runtime", "@jay-framework/fullstack-component"]
|
|
1409
|
+
},
|
|
1410
|
+
// Disable dependency optimization — CLI only uses SSR, no browser bundles
|
|
1411
|
+
optimizeDeps: {
|
|
1412
|
+
entries: []
|
|
1413
|
+
},
|
|
1414
|
+
logLevel: "warn",
|
|
1415
|
+
clearScreen: false
|
|
1416
|
+
});
|
|
1417
|
+
return vite;
|
|
1418
|
+
}
|
|
1353
1419
|
class ServiceLifecycleManager {
|
|
1354
1420
|
constructor(projectRoot, sourceBase = "src") {
|
|
1355
1421
|
/** Path to project's lib/init.ts (makeJayInit pattern) */
|
|
@@ -1388,8 +1454,9 @@ class ServiceLifecycleManager {
|
|
|
1388
1454
|
* 4. Auto-discovering and registering actions
|
|
1389
1455
|
*/
|
|
1390
1456
|
async initialize() {
|
|
1457
|
+
const log = getLogger();
|
|
1391
1458
|
if (this.isInitialized) {
|
|
1392
|
-
|
|
1459
|
+
log.warn("[Services] Already initialized, skipping...");
|
|
1393
1460
|
return;
|
|
1394
1461
|
}
|
|
1395
1462
|
this.projectInitFilePath = this.findProjectInitFile();
|
|
@@ -1399,18 +1466,18 @@ class ServiceLifecycleManager {
|
|
|
1399
1466
|
});
|
|
1400
1467
|
this.pluginsWithInit = sortPluginsByDependencies(discoveredPlugins);
|
|
1401
1468
|
if (this.pluginsWithInit.length > 0) {
|
|
1402
|
-
|
|
1469
|
+
log.info(
|
|
1403
1470
|
`[Services] Found ${this.pluginsWithInit.length} plugin(s) with init: ${this.pluginsWithInit.map((p) => p.name).join(", ")}`
|
|
1404
1471
|
);
|
|
1405
1472
|
}
|
|
1406
1473
|
await executePluginServerInits(this.pluginsWithInit, this.viteServer ?? void 0, true);
|
|
1407
1474
|
if (this.projectInitFilePath) {
|
|
1408
|
-
|
|
1475
|
+
log.info("[DevServer] Loading project init: src/init.ts");
|
|
1409
1476
|
try {
|
|
1410
1477
|
if (this.viteServer) {
|
|
1411
1478
|
const module = await this.viteServer.ssrLoadModule(this.projectInitFilePath);
|
|
1412
1479
|
if (module.init?._serverInit) {
|
|
1413
|
-
|
|
1480
|
+
log.info("[DevServer] Running server init: project");
|
|
1414
1481
|
const { setClientInitData } = await import("@jay-framework/stack-server-runtime");
|
|
1415
1482
|
const clientData = await module.init._serverInit();
|
|
1416
1483
|
if (clientData !== void 0 && clientData !== null) {
|
|
@@ -1422,14 +1489,14 @@ class ServiceLifecycleManager {
|
|
|
1422
1489
|
await import(fileUrl);
|
|
1423
1490
|
}
|
|
1424
1491
|
} catch (error) {
|
|
1425
|
-
|
|
1492
|
+
log.error(`[Services] Failed to load project init: ${error}`);
|
|
1426
1493
|
throw error;
|
|
1427
1494
|
}
|
|
1428
1495
|
} else {
|
|
1429
|
-
|
|
1496
|
+
log.info("[Services] No init.ts found, skipping project initialization");
|
|
1430
1497
|
}
|
|
1431
1498
|
await runInitCallbacks();
|
|
1432
|
-
|
|
1499
|
+
log.info("[Services] Initialization complete");
|
|
1433
1500
|
await this.discoverActions();
|
|
1434
1501
|
this.isInitialized = true;
|
|
1435
1502
|
}
|
|
@@ -1437,6 +1504,7 @@ class ServiceLifecycleManager {
|
|
|
1437
1504
|
* Auto-discovers and registers actions from project and plugins.
|
|
1438
1505
|
*/
|
|
1439
1506
|
async discoverActions() {
|
|
1507
|
+
const log = getLogger();
|
|
1440
1508
|
let totalActions = 0;
|
|
1441
1509
|
try {
|
|
1442
1510
|
const result = await discoverAndRegisterActions({
|
|
@@ -1448,7 +1516,7 @@ class ServiceLifecycleManager {
|
|
|
1448
1516
|
});
|
|
1449
1517
|
totalActions += result.actionCount;
|
|
1450
1518
|
} catch (error) {
|
|
1451
|
-
|
|
1519
|
+
log.error(`[Actions] Failed to auto-discover project actions: ${error}`);
|
|
1452
1520
|
}
|
|
1453
1521
|
try {
|
|
1454
1522
|
const pluginActions = await discoverAllPluginActions({
|
|
@@ -1459,20 +1527,21 @@ class ServiceLifecycleManager {
|
|
|
1459
1527
|
});
|
|
1460
1528
|
totalActions += pluginActions.length;
|
|
1461
1529
|
} catch (error) {
|
|
1462
|
-
|
|
1530
|
+
log.error(`[Actions] Failed to auto-discover plugin actions: ${error}`);
|
|
1463
1531
|
}
|
|
1464
1532
|
if (totalActions > 0) {
|
|
1465
|
-
|
|
1533
|
+
log.info(`[Actions] Auto-registered ${totalActions} action(s) total`);
|
|
1466
1534
|
}
|
|
1467
1535
|
}
|
|
1468
1536
|
/**
|
|
1469
1537
|
* Shuts down services gracefully with timeout
|
|
1470
1538
|
*/
|
|
1471
1539
|
async shutdown(timeoutMs = 5e3) {
|
|
1540
|
+
const log = getLogger();
|
|
1472
1541
|
if (!this.isInitialized) {
|
|
1473
1542
|
return;
|
|
1474
1543
|
}
|
|
1475
|
-
|
|
1544
|
+
log.info("[Services] Shutting down...");
|
|
1476
1545
|
try {
|
|
1477
1546
|
await Promise.race([
|
|
1478
1547
|
runShutdownCallbacks(),
|
|
@@ -1480,12 +1549,12 @@ class ServiceLifecycleManager {
|
|
|
1480
1549
|
(_, reject) => setTimeout(() => reject(new Error("Shutdown timeout")), timeoutMs)
|
|
1481
1550
|
)
|
|
1482
1551
|
]);
|
|
1483
|
-
|
|
1552
|
+
log.info("[Services] Shutdown complete");
|
|
1484
1553
|
} catch (error) {
|
|
1485
1554
|
if (error.message === "Shutdown timeout") {
|
|
1486
|
-
|
|
1555
|
+
log.warn(`[Services] Shutdown timed out after ${timeoutMs}ms`);
|
|
1487
1556
|
} else {
|
|
1488
|
-
|
|
1557
|
+
log.error(`[Services] Shutdown error: ${error}`);
|
|
1489
1558
|
}
|
|
1490
1559
|
} finally {
|
|
1491
1560
|
this.isInitialized = false;
|
|
@@ -1495,7 +1564,8 @@ class ServiceLifecycleManager {
|
|
|
1495
1564
|
* Hot reload: shutdown, clear caches, re-import, and re-initialize
|
|
1496
1565
|
*/
|
|
1497
1566
|
async reload() {
|
|
1498
|
-
|
|
1567
|
+
const log = getLogger();
|
|
1568
|
+
log.info("[Services] Reloading services...");
|
|
1499
1569
|
await this.shutdown();
|
|
1500
1570
|
clearLifecycleCallbacks();
|
|
1501
1571
|
clearServiceRegistry();
|
|
@@ -1511,7 +1581,7 @@ class ServiceLifecycleManager {
|
|
|
1511
1581
|
}
|
|
1512
1582
|
this.isInitialized = false;
|
|
1513
1583
|
await this.initialize();
|
|
1514
|
-
|
|
1584
|
+
log.info("[Services] Reload complete");
|
|
1515
1585
|
}
|
|
1516
1586
|
/**
|
|
1517
1587
|
* Returns the path to the init file if found.
|
|
@@ -1792,7 +1862,12 @@ function filterPluginsForPage(allPluginClientInits, allPluginsWithInit, usedPack
|
|
|
1792
1862
|
pluginsByPackage.set(plugin.packageName, plugin);
|
|
1793
1863
|
}
|
|
1794
1864
|
const expandedPackages = new Set(usedPackages);
|
|
1795
|
-
const
|
|
1865
|
+
for (const plugin of allPluginsWithInit) {
|
|
1866
|
+
if (plugin.global) {
|
|
1867
|
+
expandedPackages.add(plugin.packageName);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
const toProcess = [...expandedPackages];
|
|
1796
1871
|
while (toProcess.length > 0) {
|
|
1797
1872
|
const packageName = toProcess.pop();
|
|
1798
1873
|
const plugin = pluginsByPackage.get(packageName);
|
|
@@ -1813,6 +1888,7 @@ function filterPluginsForPage(allPluginClientInits, allPluginsWithInit, usedPack
|
|
|
1813
1888
|
function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, projectInit, allPluginsWithInit = [], allPluginClientInits = []) {
|
|
1814
1889
|
const routePath = routeToExpressRoute(route);
|
|
1815
1890
|
const handler = async (req, res) => {
|
|
1891
|
+
const timing = getDevLogger()?.startRequest(req.method, req.path);
|
|
1816
1892
|
try {
|
|
1817
1893
|
const url = req.originalUrl.replace(options.publicBaseUrlPath, "");
|
|
1818
1894
|
const pageParams = { ...route.inferredParams, ...req.params };
|
|
@@ -1826,7 +1902,7 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, projectInit
|
|
|
1826
1902
|
try {
|
|
1827
1903
|
await fs$1.access(cachedEntry.preRenderedPath);
|
|
1828
1904
|
} catch {
|
|
1829
|
-
|
|
1905
|
+
getLogger().info(
|
|
1830
1906
|
`[SlowRender] Cached file missing, rebuilding: ${cachedEntry.preRenderedPath}`
|
|
1831
1907
|
);
|
|
1832
1908
|
await slowRenderCache.invalidate(route.jayHtmlPath);
|
|
@@ -1845,7 +1921,8 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, projectInit
|
|
|
1845
1921
|
allPluginsWithInit,
|
|
1846
1922
|
projectInit,
|
|
1847
1923
|
res,
|
|
1848
|
-
url
|
|
1924
|
+
url,
|
|
1925
|
+
timing
|
|
1849
1926
|
);
|
|
1850
1927
|
} else if (useSlowRenderCache) {
|
|
1851
1928
|
await handlePreRenderRequest(
|
|
@@ -1860,7 +1937,8 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, projectInit
|
|
|
1860
1937
|
allPluginsWithInit,
|
|
1861
1938
|
projectInit,
|
|
1862
1939
|
res,
|
|
1863
|
-
url
|
|
1940
|
+
url,
|
|
1941
|
+
timing
|
|
1864
1942
|
);
|
|
1865
1943
|
} else {
|
|
1866
1944
|
await handleDirectRequest(
|
|
@@ -1874,18 +1952,21 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, projectInit
|
|
|
1874
1952
|
allPluginsWithInit,
|
|
1875
1953
|
projectInit,
|
|
1876
1954
|
res,
|
|
1877
|
-
url
|
|
1955
|
+
url,
|
|
1956
|
+
timing
|
|
1878
1957
|
);
|
|
1879
1958
|
}
|
|
1880
1959
|
} catch (e2) {
|
|
1881
1960
|
vite?.ssrFixStacktrace(e2);
|
|
1882
|
-
|
|
1961
|
+
getLogger().error(e2.stack);
|
|
1883
1962
|
res.status(500).end(e2.stack);
|
|
1963
|
+
timing?.end();
|
|
1884
1964
|
}
|
|
1885
1965
|
};
|
|
1886
1966
|
return { path: routePath, handler, fsRoute: route };
|
|
1887
1967
|
}
|
|
1888
|
-
async function handleCachedRequest(vite, route, options, cachedEntry, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url) {
|
|
1968
|
+
async function handleCachedRequest(vite, route, options, cachedEntry, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing) {
|
|
1969
|
+
const loadStart = Date.now();
|
|
1889
1970
|
const pagePartsResult = await loadPageParts(
|
|
1890
1971
|
vite,
|
|
1891
1972
|
route,
|
|
@@ -1894,9 +1975,11 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
|
|
|
1894
1975
|
options.jayRollupConfig,
|
|
1895
1976
|
{ preRenderedPath: cachedEntry.preRenderedPath }
|
|
1896
1977
|
);
|
|
1978
|
+
timing?.recordViteSsr(Date.now() - loadStart);
|
|
1897
1979
|
if (!pagePartsResult.val) {
|
|
1898
|
-
|
|
1980
|
+
getLogger().info(pagePartsResult.validations.join("\n"));
|
|
1899
1981
|
res.status(500).end(pagePartsResult.validations.join("\n"));
|
|
1982
|
+
timing?.end();
|
|
1900
1983
|
return;
|
|
1901
1984
|
}
|
|
1902
1985
|
const { parts: pageParts, clientTrackByMap, usedPackages } = pagePartsResult.val;
|
|
@@ -1905,32 +1988,71 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
|
|
|
1905
1988
|
allPluginsWithInit,
|
|
1906
1989
|
usedPackages
|
|
1907
1990
|
);
|
|
1991
|
+
const fastStart = Date.now();
|
|
1908
1992
|
const renderedFast = await renderFastChangingData(
|
|
1909
1993
|
pageParams,
|
|
1910
1994
|
pageProps,
|
|
1911
1995
|
cachedEntry.carryForward,
|
|
1912
1996
|
pageParts
|
|
1913
1997
|
);
|
|
1998
|
+
timing?.recordFastRender(Date.now() - fastStart);
|
|
1914
1999
|
if (renderedFast.kind !== "PhaseOutput") {
|
|
1915
2000
|
handleOtherResponseCodes(res, renderedFast);
|
|
2001
|
+
timing?.end();
|
|
1916
2002
|
return;
|
|
1917
2003
|
}
|
|
2004
|
+
let fastViewState = renderedFast.rendered;
|
|
2005
|
+
let fastCarryForward = renderedFast.carryForward;
|
|
2006
|
+
const instancePhaseData = cachedEntry.carryForward?.__instances;
|
|
2007
|
+
const headlessComps = pagePartsResult.val.headlessInstanceComponents;
|
|
2008
|
+
if (instancePhaseData && headlessComps.length > 0) {
|
|
2009
|
+
const instanceFastResult = await renderFastChangingDataForInstances(
|
|
2010
|
+
instancePhaseData,
|
|
2011
|
+
headlessComps
|
|
2012
|
+
);
|
|
2013
|
+
if (instanceFastResult) {
|
|
2014
|
+
fastViewState = {
|
|
2015
|
+
...fastViewState,
|
|
2016
|
+
__headlessInstances: instanceFastResult.viewStates
|
|
2017
|
+
};
|
|
2018
|
+
fastCarryForward = {
|
|
2019
|
+
...fastCarryForward,
|
|
2020
|
+
__headlessInstances: instanceFastResult.carryForwards
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
if (instancePhaseData.forEachInstances && instancePhaseData.forEachInstances.length > 0) {
|
|
2024
|
+
const forEachResult = await renderFastChangingDataForForEachInstances(
|
|
2025
|
+
instancePhaseData.forEachInstances,
|
|
2026
|
+
headlessComps,
|
|
2027
|
+
fastViewState
|
|
2028
|
+
);
|
|
2029
|
+
if (forEachResult) {
|
|
2030
|
+
const existingHeadless = fastViewState.__headlessInstances || {};
|
|
2031
|
+
fastViewState = {
|
|
2032
|
+
...fastViewState,
|
|
2033
|
+
__headlessInstances: { ...existingHeadless, ...forEachResult }
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
1918
2038
|
await sendResponse(
|
|
1919
2039
|
vite,
|
|
1920
2040
|
res,
|
|
1921
2041
|
url,
|
|
1922
2042
|
cachedEntry.preRenderedPath,
|
|
1923
2043
|
pageParts,
|
|
1924
|
-
|
|
1925
|
-
|
|
2044
|
+
fastViewState,
|
|
2045
|
+
fastCarryForward,
|
|
1926
2046
|
clientTrackByMap,
|
|
1927
2047
|
projectInit,
|
|
1928
2048
|
pluginsForPage,
|
|
1929
2049
|
options,
|
|
1930
|
-
cachedEntry.slowViewState
|
|
2050
|
+
cachedEntry.slowViewState,
|
|
2051
|
+
timing
|
|
1931
2052
|
);
|
|
1932
2053
|
}
|
|
1933
|
-
async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRenderCache, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url) {
|
|
2054
|
+
async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRenderCache, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing) {
|
|
2055
|
+
const loadStart = Date.now();
|
|
1934
2056
|
const initialPartsResult = await loadPageParts(
|
|
1935
2057
|
vite,
|
|
1936
2058
|
route,
|
|
@@ -1938,35 +2060,61 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
|
|
|
1938
2060
|
options.projectRootFolder,
|
|
1939
2061
|
options.jayRollupConfig
|
|
1940
2062
|
);
|
|
2063
|
+
timing?.recordViteSsr(Date.now() - loadStart);
|
|
1941
2064
|
if (!initialPartsResult.val) {
|
|
1942
|
-
|
|
2065
|
+
getLogger().info(initialPartsResult.validations.join("\n"));
|
|
1943
2066
|
res.status(500).end(initialPartsResult.validations.join("\n"));
|
|
2067
|
+
timing?.end();
|
|
1944
2068
|
return;
|
|
1945
2069
|
}
|
|
2070
|
+
const slowStart = Date.now();
|
|
1946
2071
|
const renderedSlowly = await slowlyPhase.runSlowlyForPage(
|
|
1947
2072
|
pageParams,
|
|
1948
2073
|
pageProps,
|
|
1949
2074
|
initialPartsResult.val.parts
|
|
1950
2075
|
);
|
|
1951
2076
|
if (renderedSlowly.kind !== "PhaseOutput") {
|
|
2077
|
+
timing?.recordSlowRender(Date.now() - slowStart);
|
|
1952
2078
|
if (renderedSlowly.kind === "ClientError") {
|
|
1953
2079
|
handleOtherResponseCodes(res, renderedSlowly);
|
|
1954
2080
|
}
|
|
2081
|
+
timing?.end();
|
|
1955
2082
|
return;
|
|
1956
2083
|
}
|
|
1957
|
-
const
|
|
1958
|
-
|
|
2084
|
+
const preRenderResult = await preRenderJayHtml(
|
|
2085
|
+
route,
|
|
2086
|
+
renderedSlowly.rendered,
|
|
2087
|
+
initialPartsResult.val.headlessContracts,
|
|
2088
|
+
initialPartsResult.val.headlessInstanceComponents
|
|
2089
|
+
);
|
|
2090
|
+
timing?.recordSlowRender(Date.now() - slowStart);
|
|
2091
|
+
if (!preRenderResult) {
|
|
1959
2092
|
res.status(500).end("Failed to pre-render jay-html");
|
|
2093
|
+
timing?.end();
|
|
1960
2094
|
return;
|
|
1961
2095
|
}
|
|
2096
|
+
let instancePhaseDataForCache = preRenderResult.instancePhaseData;
|
|
2097
|
+
if (instancePhaseDataForCache && preRenderResult.forEachInstances) {
|
|
2098
|
+
instancePhaseDataForCache = {
|
|
2099
|
+
...instancePhaseDataForCache,
|
|
2100
|
+
forEachInstances: preRenderResult.forEachInstances
|
|
2101
|
+
};
|
|
2102
|
+
} else if (preRenderResult.forEachInstances) {
|
|
2103
|
+
instancePhaseDataForCache = {
|
|
2104
|
+
discovered: [],
|
|
2105
|
+
carryForwards: {},
|
|
2106
|
+
forEachInstances: preRenderResult.forEachInstances
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
const carryForward = instancePhaseDataForCache ? { ...renderedSlowly.carryForward, __instances: instancePhaseDataForCache } : renderedSlowly.carryForward;
|
|
1962
2110
|
const preRenderedPath = await slowRenderCache.set(
|
|
1963
2111
|
route.jayHtmlPath,
|
|
1964
2112
|
pageParams,
|
|
1965
|
-
|
|
2113
|
+
preRenderResult.preRenderedJayHtml,
|
|
1966
2114
|
renderedSlowly.rendered,
|
|
1967
|
-
|
|
2115
|
+
carryForward
|
|
1968
2116
|
);
|
|
1969
|
-
|
|
2117
|
+
getLogger().info(`[SlowRender] Cached pre-rendered jay-html at ${preRenderedPath}`);
|
|
1970
2118
|
const pagePartsResult = await loadPageParts(
|
|
1971
2119
|
vite,
|
|
1972
2120
|
route,
|
|
@@ -1976,8 +2124,9 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
|
|
|
1976
2124
|
{ preRenderedPath }
|
|
1977
2125
|
);
|
|
1978
2126
|
if (!pagePartsResult.val) {
|
|
1979
|
-
|
|
2127
|
+
getLogger().info(pagePartsResult.validations.join("\n"));
|
|
1980
2128
|
res.status(500).end(pagePartsResult.validations.join("\n"));
|
|
2129
|
+
timing?.end();
|
|
1981
2130
|
return;
|
|
1982
2131
|
}
|
|
1983
2132
|
const { parts: pageParts, clientTrackByMap, usedPackages } = pagePartsResult.val;
|
|
@@ -1986,32 +2135,71 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
|
|
|
1986
2135
|
allPluginsWithInit,
|
|
1987
2136
|
usedPackages
|
|
1988
2137
|
);
|
|
2138
|
+
const fastStart = Date.now();
|
|
1989
2139
|
const renderedFast = await renderFastChangingData(
|
|
1990
2140
|
pageParams,
|
|
1991
2141
|
pageProps,
|
|
1992
|
-
|
|
2142
|
+
carryForward,
|
|
1993
2143
|
pageParts
|
|
1994
2144
|
);
|
|
2145
|
+
timing?.recordFastRender(Date.now() - fastStart);
|
|
1995
2146
|
if (renderedFast.kind !== "PhaseOutput") {
|
|
1996
2147
|
handleOtherResponseCodes(res, renderedFast);
|
|
2148
|
+
timing?.end();
|
|
1997
2149
|
return;
|
|
1998
2150
|
}
|
|
2151
|
+
let fastViewState = renderedFast.rendered;
|
|
2152
|
+
let fastCarryForward = renderedFast.carryForward;
|
|
2153
|
+
const instancePhaseData = carryForward?.__instances;
|
|
2154
|
+
const headlessComps = pagePartsResult.val.headlessInstanceComponents;
|
|
2155
|
+
if (instancePhaseData && headlessComps.length > 0) {
|
|
2156
|
+
const instanceFastResult = await renderFastChangingDataForInstances(
|
|
2157
|
+
instancePhaseData,
|
|
2158
|
+
headlessComps
|
|
2159
|
+
);
|
|
2160
|
+
if (instanceFastResult) {
|
|
2161
|
+
fastViewState = {
|
|
2162
|
+
...fastViewState,
|
|
2163
|
+
__headlessInstances: instanceFastResult.viewStates
|
|
2164
|
+
};
|
|
2165
|
+
fastCarryForward = {
|
|
2166
|
+
...fastCarryForward,
|
|
2167
|
+
__headlessInstances: instanceFastResult.carryForwards
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2170
|
+
if (instancePhaseData.forEachInstances && instancePhaseData.forEachInstances.length > 0) {
|
|
2171
|
+
const forEachResult = await renderFastChangingDataForForEachInstances(
|
|
2172
|
+
instancePhaseData.forEachInstances,
|
|
2173
|
+
headlessComps,
|
|
2174
|
+
fastViewState
|
|
2175
|
+
);
|
|
2176
|
+
if (forEachResult) {
|
|
2177
|
+
const existingHeadless = fastViewState.__headlessInstances || {};
|
|
2178
|
+
fastViewState = {
|
|
2179
|
+
...fastViewState,
|
|
2180
|
+
__headlessInstances: { ...existingHeadless, ...forEachResult }
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
1999
2185
|
await sendResponse(
|
|
2000
2186
|
vite,
|
|
2001
2187
|
res,
|
|
2002
2188
|
url,
|
|
2003
2189
|
preRenderedPath,
|
|
2004
2190
|
pageParts,
|
|
2005
|
-
|
|
2006
|
-
|
|
2191
|
+
fastViewState,
|
|
2192
|
+
fastCarryForward,
|
|
2007
2193
|
clientTrackByMap,
|
|
2008
2194
|
projectInit,
|
|
2009
2195
|
pluginsForPage,
|
|
2010
2196
|
options,
|
|
2011
|
-
renderedSlowly.rendered
|
|
2197
|
+
renderedSlowly.rendered,
|
|
2198
|
+
timing
|
|
2012
2199
|
);
|
|
2013
2200
|
}
|
|
2014
|
-
async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url) {
|
|
2201
|
+
async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing) {
|
|
2202
|
+
const loadStart = Date.now();
|
|
2015
2203
|
const pagePartsResult = await loadPageParts(
|
|
2016
2204
|
vite,
|
|
2017
2205
|
route,
|
|
@@ -2019,9 +2207,11 @@ async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams
|
|
|
2019
2207
|
options.projectRootFolder,
|
|
2020
2208
|
options.jayRollupConfig
|
|
2021
2209
|
);
|
|
2210
|
+
timing?.recordViteSsr(Date.now() - loadStart);
|
|
2022
2211
|
if (!pagePartsResult.val) {
|
|
2023
|
-
|
|
2212
|
+
getLogger().info(pagePartsResult.validations.join("\n"));
|
|
2024
2213
|
res.status(500).end(pagePartsResult.validations.join("\n"));
|
|
2214
|
+
timing?.end();
|
|
2025
2215
|
return;
|
|
2026
2216
|
}
|
|
2027
2217
|
const {
|
|
@@ -2035,21 +2225,89 @@ async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams
|
|
|
2035
2225
|
allPluginsWithInit,
|
|
2036
2226
|
usedPackages
|
|
2037
2227
|
);
|
|
2228
|
+
const slowStart = Date.now();
|
|
2038
2229
|
const renderedSlowly = await slowlyPhase.runSlowlyForPage(pageParams, pageProps, pageParts);
|
|
2039
2230
|
if (renderedSlowly.kind !== "PhaseOutput") {
|
|
2231
|
+
timing?.recordSlowRender(Date.now() - slowStart);
|
|
2040
2232
|
if (renderedSlowly.kind === "ClientError") {
|
|
2041
2233
|
handleOtherResponseCodes(res, renderedSlowly);
|
|
2042
2234
|
}
|
|
2235
|
+
timing?.end();
|
|
2043
2236
|
return;
|
|
2044
2237
|
}
|
|
2238
|
+
let instanceViewStates;
|
|
2239
|
+
let instancePhaseDataForFast;
|
|
2240
|
+
let forEachInstancesForFast;
|
|
2241
|
+
const headlessInstanceComponents = pagePartsResult.val.headlessInstanceComponents ?? [];
|
|
2242
|
+
if (headlessInstanceComponents.length > 0) {
|
|
2243
|
+
const jayHtmlContent = await fs$1.readFile(route.jayHtmlPath, "utf-8");
|
|
2244
|
+
const discoveryResult = discoverHeadlessInstances(jayHtmlContent);
|
|
2245
|
+
if (discoveryResult.forEachInstances.length > 0) {
|
|
2246
|
+
const validationErrors = validateForEachInstances(
|
|
2247
|
+
discoveryResult.forEachInstances,
|
|
2248
|
+
headlessInstanceComponents
|
|
2249
|
+
);
|
|
2250
|
+
if (validationErrors.length > 0) {
|
|
2251
|
+
getLogger().error(
|
|
2252
|
+
`[SlowRender] ForEach instance validation failed: ${validationErrors.join(", ")}`
|
|
2253
|
+
);
|
|
2254
|
+
res.status(500).end(validationErrors.join("\n"));
|
|
2255
|
+
timing?.end();
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
forEachInstancesForFast = discoveryResult.forEachInstances;
|
|
2259
|
+
}
|
|
2260
|
+
if (discoveryResult.instances.length > 0) {
|
|
2261
|
+
const slowResult = await slowRenderInstances(
|
|
2262
|
+
discoveryResult.instances,
|
|
2263
|
+
headlessInstanceComponents
|
|
2264
|
+
);
|
|
2265
|
+
if (slowResult) {
|
|
2266
|
+
instanceViewStates = { ...slowResult.slowViewStates };
|
|
2267
|
+
instancePhaseDataForFast = slowResult.instancePhaseData;
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
timing?.recordSlowRender(Date.now() - slowStart);
|
|
2272
|
+
const fastStart = Date.now();
|
|
2045
2273
|
const renderedFast = await renderFastChangingData(
|
|
2046
2274
|
pageParams,
|
|
2047
2275
|
pageProps,
|
|
2048
2276
|
renderedSlowly.carryForward,
|
|
2049
2277
|
pageParts
|
|
2050
2278
|
);
|
|
2279
|
+
if (instancePhaseDataForFast && instanceViewStates) {
|
|
2280
|
+
const instanceFastResult = await renderFastChangingDataForInstances(
|
|
2281
|
+
instancePhaseDataForFast,
|
|
2282
|
+
headlessInstanceComponents
|
|
2283
|
+
);
|
|
2284
|
+
if (instanceFastResult) {
|
|
2285
|
+
for (const [coordKey, fastVS] of Object.entries(instanceFastResult.viewStates)) {
|
|
2286
|
+
instanceViewStates[coordKey] = {
|
|
2287
|
+
...instanceViewStates[coordKey] || {},
|
|
2288
|
+
...fastVS
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
if (forEachInstancesForFast && renderedFast.kind === "PhaseOutput") {
|
|
2294
|
+
const forEachResult = await renderFastChangingDataForForEachInstances(
|
|
2295
|
+
forEachInstancesForFast,
|
|
2296
|
+
headlessInstanceComponents,
|
|
2297
|
+
{ ...renderedSlowly.rendered, ...renderedFast.rendered }
|
|
2298
|
+
);
|
|
2299
|
+
if (forEachResult) {
|
|
2300
|
+
if (!instanceViewStates)
|
|
2301
|
+
instanceViewStates = {};
|
|
2302
|
+
for (const [coordKey, fastVS] of Object.entries(forEachResult)) {
|
|
2303
|
+
instanceViewStates[coordKey] = fastVS;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
timing?.recordFastRender(Date.now() - fastStart);
|
|
2051
2308
|
if (renderedFast.kind !== "PhaseOutput") {
|
|
2052
2309
|
handleOtherResponseCodes(res, renderedFast);
|
|
2310
|
+
timing?.end();
|
|
2053
2311
|
return;
|
|
2054
2312
|
}
|
|
2055
2313
|
let viewState;
|
|
@@ -2062,6 +2320,12 @@ async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams
|
|
|
2062
2320
|
} else {
|
|
2063
2321
|
viewState = { ...renderedSlowly.rendered, ...renderedFast.rendered };
|
|
2064
2322
|
}
|
|
2323
|
+
if (instanceViewStates && Object.keys(instanceViewStates).length > 0) {
|
|
2324
|
+
viewState = {
|
|
2325
|
+
...viewState,
|
|
2326
|
+
__headlessInstances: instanceViewStates
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2065
2329
|
await sendResponse(
|
|
2066
2330
|
vite,
|
|
2067
2331
|
res,
|
|
@@ -2073,10 +2337,12 @@ async function handleDirectRequest(vite, route, options, slowlyPhase, pageParams
|
|
|
2073
2337
|
clientTrackByMap,
|
|
2074
2338
|
projectInit,
|
|
2075
2339
|
pluginsForPage,
|
|
2076
|
-
options
|
|
2340
|
+
options,
|
|
2341
|
+
void 0,
|
|
2342
|
+
timing
|
|
2077
2343
|
);
|
|
2078
2344
|
}
|
|
2079
|
-
async function sendResponse(vite, res, url, jayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, slowViewState) {
|
|
2345
|
+
async function sendResponse(vite, res, url, jayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, slowViewState, timing) {
|
|
2080
2346
|
const pageHtml = generateClientScript(
|
|
2081
2347
|
viewState,
|
|
2082
2348
|
carryForward,
|
|
@@ -2091,10 +2357,19 @@ async function sendResponse(vite, res, url, jayHtmlPath, pageParts, viewState, c
|
|
|
2091
2357
|
slowViewState
|
|
2092
2358
|
}
|
|
2093
2359
|
);
|
|
2360
|
+
if (options.buildFolder) {
|
|
2361
|
+
const pageName = !url || url === "/" ? "index" : url.replace(/^\//, "").replace(/\//g, "-");
|
|
2362
|
+
const clientScriptDir = path__default.join(options.buildFolder, "client-scripts");
|
|
2363
|
+
await fs$1.mkdir(clientScriptDir, { recursive: true });
|
|
2364
|
+
await fs$1.writeFile(path__default.join(clientScriptDir, `${pageName}.html`), pageHtml, "utf-8");
|
|
2365
|
+
}
|
|
2366
|
+
const viteStart = Date.now();
|
|
2094
2367
|
const compiledPageHtml = await vite.transformIndexHtml(!!url ? url : "/", pageHtml);
|
|
2368
|
+
timing?.recordViteClient(Date.now() - viteStart);
|
|
2095
2369
|
res.status(200).set({ "Content-Type": "text/html" }).send(compiledPageHtml);
|
|
2370
|
+
timing?.end();
|
|
2096
2371
|
}
|
|
2097
|
-
async function preRenderJayHtml(route, slowViewState) {
|
|
2372
|
+
async function preRenderJayHtml(route, slowViewState, headlessContracts, headlessInstanceComponents) {
|
|
2098
2373
|
const jayHtmlContent = await fs$1.readFile(route.jayHtmlPath, "utf-8");
|
|
2099
2374
|
const contractPath = route.jayHtmlPath.replace(".jay-html", ".jay-contract");
|
|
2100
2375
|
let contract;
|
|
@@ -2104,15 +2379,14 @@ async function preRenderJayHtml(route, slowViewState) {
|
|
|
2104
2379
|
if (parseResult.val) {
|
|
2105
2380
|
contract = parseResult.val;
|
|
2106
2381
|
} else if (parseResult.validations.length > 0) {
|
|
2107
|
-
|
|
2108
|
-
`[SlowRender] Contract parse error for ${contractPath}
|
|
2109
|
-
parseResult.validations
|
|
2382
|
+
getLogger().error(
|
|
2383
|
+
`[SlowRender] Contract parse error for ${contractPath}: ${parseResult.validations.join(", ")}`
|
|
2110
2384
|
);
|
|
2111
2385
|
return void 0;
|
|
2112
2386
|
}
|
|
2113
2387
|
} catch (error) {
|
|
2114
2388
|
if (error.code !== "ENOENT") {
|
|
2115
|
-
|
|
2389
|
+
getLogger().error(`[SlowRender] Error reading contract ${contractPath}: ${error}`);
|
|
2116
2390
|
return void 0;
|
|
2117
2391
|
}
|
|
2118
2392
|
}
|
|
@@ -2120,20 +2394,156 @@ async function preRenderJayHtml(route, slowViewState) {
|
|
|
2120
2394
|
jayHtmlContent,
|
|
2121
2395
|
slowViewState,
|
|
2122
2396
|
contract,
|
|
2123
|
-
|
|
2397
|
+
headlessContracts,
|
|
2398
|
+
sourceDir: path__default.dirname(route.jayHtmlPath),
|
|
2399
|
+
importResolver: JAY_IMPORT_RESOLVER
|
|
2124
2400
|
});
|
|
2125
|
-
if (result.val) {
|
|
2126
|
-
|
|
2401
|
+
if (!result.val) {
|
|
2402
|
+
if (result.validations.length > 0) {
|
|
2403
|
+
getLogger().error(
|
|
2404
|
+
`[SlowRender] Transform failed for ${route.jayHtmlPath}: ${result.validations.join(", ")}`
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
return void 0;
|
|
2408
|
+
}
|
|
2409
|
+
let preRenderedJayHtml = result.val.preRenderedJayHtml;
|
|
2410
|
+
let instancePhaseData;
|
|
2411
|
+
let forEachInstances;
|
|
2412
|
+
if (headlessInstanceComponents.length > 0) {
|
|
2413
|
+
const discoveryResult = discoverHeadlessInstances(preRenderedJayHtml);
|
|
2414
|
+
preRenderedJayHtml = discoveryResult.preRenderedJayHtml;
|
|
2415
|
+
if (discoveryResult.forEachInstances.length > 0) {
|
|
2416
|
+
const validationErrors = validateForEachInstances(
|
|
2417
|
+
discoveryResult.forEachInstances,
|
|
2418
|
+
headlessInstanceComponents
|
|
2419
|
+
);
|
|
2420
|
+
if (validationErrors.length > 0) {
|
|
2421
|
+
getLogger().error(
|
|
2422
|
+
`[SlowRender] ForEach instance validation failed: ${validationErrors.join(", ")}`
|
|
2423
|
+
);
|
|
2424
|
+
return void 0;
|
|
2425
|
+
}
|
|
2426
|
+
forEachInstances = discoveryResult.forEachInstances;
|
|
2427
|
+
}
|
|
2428
|
+
if (discoveryResult.instances.length > 0) {
|
|
2429
|
+
const slowResult = await slowRenderInstances(
|
|
2430
|
+
discoveryResult.instances,
|
|
2431
|
+
headlessInstanceComponents
|
|
2432
|
+
);
|
|
2433
|
+
if (slowResult) {
|
|
2434
|
+
instancePhaseData = slowResult.instancePhaseData;
|
|
2435
|
+
const pass2Result = resolveHeadlessInstances(
|
|
2436
|
+
preRenderedJayHtml,
|
|
2437
|
+
slowResult.resolvedData,
|
|
2438
|
+
JAY_IMPORT_RESOLVER
|
|
2439
|
+
);
|
|
2440
|
+
if (pass2Result.val) {
|
|
2441
|
+
preRenderedJayHtml = pass2Result.val;
|
|
2442
|
+
}
|
|
2443
|
+
if (pass2Result.validations.length > 0) {
|
|
2444
|
+
getLogger().error(
|
|
2445
|
+
`[SlowRender] Instance resolution warnings for ${route.jayHtmlPath}: ${pass2Result.validations.join(", ")}`
|
|
2446
|
+
);
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
return { preRenderedJayHtml, instancePhaseData, forEachInstances };
|
|
2452
|
+
}
|
|
2453
|
+
async function renderFastChangingDataForInstances(instancePhaseData, headlessInstanceComponents) {
|
|
2454
|
+
const componentByContractName = /* @__PURE__ */ new Map();
|
|
2455
|
+
for (const comp of headlessInstanceComponents) {
|
|
2456
|
+
componentByContractName.set(comp.contractName, comp);
|
|
2127
2457
|
}
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2458
|
+
const viewStates = {};
|
|
2459
|
+
const carryForwards = {};
|
|
2460
|
+
let hasResults = false;
|
|
2461
|
+
for (const instance of instancePhaseData.discovered) {
|
|
2462
|
+
const coordKey = instance.coordinate.join("/");
|
|
2463
|
+
const comp = componentByContractName.get(instance.contractName);
|
|
2464
|
+
if (!comp || !comp.compDefinition.fastRender) {
|
|
2465
|
+
continue;
|
|
2466
|
+
}
|
|
2467
|
+
const instanceCarryForward = instancePhaseData.carryForwards[coordKey] || {};
|
|
2468
|
+
const services = resolveServices(comp.compDefinition.services);
|
|
2469
|
+
const fastResult = await comp.compDefinition.fastRender(
|
|
2470
|
+
instance.props,
|
|
2471
|
+
instanceCarryForward,
|
|
2472
|
+
...services
|
|
2132
2473
|
);
|
|
2474
|
+
if (fastResult.kind === "PhaseOutput") {
|
|
2475
|
+
viewStates[coordKey] = fastResult.rendered;
|
|
2476
|
+
carryForwards[coordKey] = fastResult.carryForward;
|
|
2477
|
+
hasResults = true;
|
|
2478
|
+
}
|
|
2133
2479
|
}
|
|
2134
|
-
return void 0;
|
|
2480
|
+
return hasResults ? { viewStates, carryForwards } : void 0;
|
|
2481
|
+
}
|
|
2482
|
+
async function renderFastChangingDataForForEachInstances(forEachInstances, headlessInstanceComponents, mergedViewState) {
|
|
2483
|
+
const componentByContractName = /* @__PURE__ */ new Map();
|
|
2484
|
+
for (const comp of headlessInstanceComponents) {
|
|
2485
|
+
componentByContractName.set(comp.contractName, comp);
|
|
2486
|
+
}
|
|
2487
|
+
const viewStates = {};
|
|
2488
|
+
let hasResults = false;
|
|
2489
|
+
for (const instance of forEachInstances) {
|
|
2490
|
+
const comp = componentByContractName.get(instance.contractName);
|
|
2491
|
+
if (!comp)
|
|
2492
|
+
continue;
|
|
2493
|
+
const items = resolvePathValue(mergedViewState, instance.forEachPath);
|
|
2494
|
+
if (!Array.isArray(items))
|
|
2495
|
+
continue;
|
|
2496
|
+
for (const item of items) {
|
|
2497
|
+
const trackByValue = String(item[instance.trackBy]);
|
|
2498
|
+
const props = {};
|
|
2499
|
+
for (const [propName, binding] of Object.entries(instance.propBindings)) {
|
|
2500
|
+
props[propName] = resolveBinding(String(binding), item);
|
|
2501
|
+
}
|
|
2502
|
+
if (comp.compDefinition.fastRender) {
|
|
2503
|
+
const services = resolveServices(comp.compDefinition.services);
|
|
2504
|
+
const fastResult = await comp.compDefinition.fastRender(props, ...services);
|
|
2505
|
+
if (fastResult.kind === "PhaseOutput") {
|
|
2506
|
+
const coord = [trackByValue, instance.coordinateSuffix].toString();
|
|
2507
|
+
viewStates[coord] = fastResult.rendered;
|
|
2508
|
+
hasResults = true;
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
return hasResults ? viewStates : void 0;
|
|
2135
2514
|
}
|
|
2136
|
-
|
|
2515
|
+
function resolvePathValue(obj, path2) {
|
|
2516
|
+
return path2.split(".").reduce((current, segment) => current?.[segment], obj);
|
|
2517
|
+
}
|
|
2518
|
+
function resolveBinding(binding, item) {
|
|
2519
|
+
const match = binding.match(/^\{(.+)\}$/);
|
|
2520
|
+
if (match) {
|
|
2521
|
+
return String(item[match[1]] ?? "");
|
|
2522
|
+
}
|
|
2523
|
+
return binding;
|
|
2524
|
+
}
|
|
2525
|
+
async function materializeDynamicContracts(projectRootFolder, buildFolder, viteServer) {
|
|
2526
|
+
try {
|
|
2527
|
+
const services = getServiceRegistry();
|
|
2528
|
+
const result = await materializeContracts(
|
|
2529
|
+
{
|
|
2530
|
+
projectRoot: projectRootFolder,
|
|
2531
|
+
outputDir: path__default.join(buildFolder, "materialized-contracts"),
|
|
2532
|
+
verbose: false,
|
|
2533
|
+
viteServer
|
|
2534
|
+
},
|
|
2535
|
+
services
|
|
2536
|
+
);
|
|
2537
|
+
const dynamicCount = result.dynamicCount;
|
|
2538
|
+
if (dynamicCount > 0) {
|
|
2539
|
+
getLogger().info(`[Contracts] Materialized ${dynamicCount} dynamic contract(s)`);
|
|
2540
|
+
}
|
|
2541
|
+
} catch (error) {
|
|
2542
|
+
getLogger().warn(`[Contracts] Failed to materialize dynamic contracts: ${error.message}`);
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
async function mkDevServer(rawOptions) {
|
|
2546
|
+
const options = defaults(rawOptions);
|
|
2137
2547
|
const {
|
|
2138
2548
|
publicBaseUrlPath,
|
|
2139
2549
|
pagesRootFolder,
|
|
@@ -2141,23 +2551,20 @@ async function mkDevServer(options) {
|
|
|
2141
2551
|
buildFolder,
|
|
2142
2552
|
jayRollupConfig,
|
|
2143
2553
|
dontCacheSlowly
|
|
2144
|
-
} =
|
|
2554
|
+
} = options;
|
|
2555
|
+
const viteLogLevel = options.logLevel === "silent" ? "silent" : options.logLevel === "verbose" ? "info" : "warn";
|
|
2145
2556
|
const lifecycleManager = new ServiceLifecycleManager(projectRootFolder);
|
|
2146
2557
|
setupGracefulShutdown(lifecycleManager);
|
|
2147
|
-
const vite = await
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
appType: "custom",
|
|
2558
|
+
const vite = await createViteServer({
|
|
2559
|
+
projectRoot: projectRootFolder,
|
|
2560
|
+
pagesRoot: pagesRootFolder,
|
|
2151
2561
|
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
|
-
}
|
|
2562
|
+
jayRollupConfig,
|
|
2563
|
+
logLevel: viteLogLevel
|
|
2158
2564
|
});
|
|
2159
2565
|
lifecycleManager.setViteServer(vite);
|
|
2160
2566
|
await lifecycleManager.initialize();
|
|
2567
|
+
await materializeDynamicContracts(projectRootFolder, buildFolder, vite);
|
|
2161
2568
|
setupServiceHotReload(vite, lifecycleManager);
|
|
2162
2569
|
setupActionRouter(vite);
|
|
2163
2570
|
const routes = await initRoutes(pagesRootFolder);
|
|
@@ -2189,7 +2596,7 @@ async function mkDevServer(options) {
|
|
|
2189
2596
|
}
|
|
2190
2597
|
function setupGracefulShutdown(lifecycleManager) {
|
|
2191
2598
|
const shutdown = async (signal) => {
|
|
2192
|
-
|
|
2599
|
+
getLogger().important(`
|
|
2193
2600
|
${signal} received, shutting down gracefully...`);
|
|
2194
2601
|
await lifecycleManager.shutdown();
|
|
2195
2602
|
process.exit(0);
|
|
@@ -2205,7 +2612,7 @@ function setupServiceHotReload(vite, lifecycleManager) {
|
|
|
2205
2612
|
vite.watcher.add(initFilePath);
|
|
2206
2613
|
vite.watcher.on("change", async (changedPath) => {
|
|
2207
2614
|
if (changedPath === initFilePath) {
|
|
2208
|
-
|
|
2615
|
+
getLogger().info("[Services] lib/init.ts changed, reloading services...");
|
|
2209
2616
|
try {
|
|
2210
2617
|
await lifecycleManager.reload();
|
|
2211
2618
|
vite.ws.send({
|
|
@@ -2213,7 +2620,7 @@ function setupServiceHotReload(vite, lifecycleManager) {
|
|
|
2213
2620
|
path: "*"
|
|
2214
2621
|
});
|
|
2215
2622
|
} catch (error) {
|
|
2216
|
-
|
|
2623
|
+
getLogger().error(`[Services] Failed to reload services: ${error}`);
|
|
2217
2624
|
}
|
|
2218
2625
|
}
|
|
2219
2626
|
});
|
|
@@ -2221,7 +2628,7 @@ function setupServiceHotReload(vite, lifecycleManager) {
|
|
|
2221
2628
|
function setupActionRouter(vite) {
|
|
2222
2629
|
vite.middlewares.use(actionBodyParser());
|
|
2223
2630
|
vite.middlewares.use(ACTION_ENDPOINT_BASE, createActionRouter());
|
|
2224
|
-
|
|
2631
|
+
getLogger().info(`[Actions] Action router mounted at ${ACTION_ENDPOINT_BASE}`);
|
|
2225
2632
|
}
|
|
2226
2633
|
function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder) {
|
|
2227
2634
|
vite.watcher.on("change", (changedPath) => {
|
|
@@ -2230,7 +2637,7 @@ function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder) {
|
|
|
2230
2637
|
}
|
|
2231
2638
|
if (changedPath.endsWith(".jay-html")) {
|
|
2232
2639
|
cache.invalidate(changedPath).then(() => {
|
|
2233
|
-
|
|
2640
|
+
getLogger().info(`[SlowRender] Cache invalidated for ${changedPath}`);
|
|
2234
2641
|
});
|
|
2235
2642
|
return;
|
|
2236
2643
|
}
|
|
@@ -2238,14 +2645,18 @@ function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder) {
|
|
|
2238
2645
|
const dir = path__default.dirname(changedPath);
|
|
2239
2646
|
const jayHtmlPath = path__default.join(dir, "page.jay-html");
|
|
2240
2647
|
cache.invalidate(jayHtmlPath).then(() => {
|
|
2241
|
-
|
|
2648
|
+
getLogger().info(
|
|
2649
|
+
`[SlowRender] Cache invalidated for ${jayHtmlPath} (page.ts changed)`
|
|
2650
|
+
);
|
|
2242
2651
|
});
|
|
2243
2652
|
return;
|
|
2244
2653
|
}
|
|
2245
2654
|
if (changedPath.endsWith(".jay-contract")) {
|
|
2246
2655
|
const jayHtmlPath = changedPath.replace(".jay-contract", ".jay-html");
|
|
2247
2656
|
cache.invalidate(jayHtmlPath).then(() => {
|
|
2248
|
-
|
|
2657
|
+
getLogger().info(
|
|
2658
|
+
`[SlowRender] Cache invalidated for ${jayHtmlPath} (contract changed)`
|
|
2659
|
+
);
|
|
2249
2660
|
});
|
|
2250
2661
|
return;
|
|
2251
2662
|
}
|
|
@@ -2255,5 +2666,7 @@ export {
|
|
|
2255
2666
|
ACTION_ENDPOINT_BASE,
|
|
2256
2667
|
actionBodyParser,
|
|
2257
2668
|
createActionRouter,
|
|
2669
|
+
createViteForCli,
|
|
2670
|
+
createViteServer,
|
|
2258
2671
|
mkDevServer
|
|
2259
2672
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/dev-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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.13.0",
|
|
26
|
+
"@jay-framework/compiler-shared": "^0.13.0",
|
|
27
|
+
"@jay-framework/component": "^0.13.0",
|
|
28
|
+
"@jay-framework/fullstack-component": "^0.13.0",
|
|
29
|
+
"@jay-framework/logger": "^0.13.0",
|
|
30
|
+
"@jay-framework/runtime": "^0.13.0",
|
|
31
|
+
"@jay-framework/stack-client-runtime": "^0.13.0",
|
|
32
|
+
"@jay-framework/stack-route-scanner": "^0.13.0",
|
|
33
|
+
"@jay-framework/stack-server-runtime": "^0.13.0",
|
|
34
|
+
"@jay-framework/view-state-merge": "^0.13.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.13.0",
|
|
39
|
+
"@jay-framework/jay-cli": "^0.13.0",
|
|
40
|
+
"@jay-framework/stack-client-runtime": "^0.13.0",
|
|
40
41
|
"@types/express": "^5.0.2",
|
|
41
42
|
"@types/node": "^22.15.21",
|
|
42
43
|
"nodemon": "^3.0.3",
|