@jay-framework/dev-server 0.17.3 → 0.18.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +12 -2
  2. package/dist/index.js +117 -102
  3. package/package.json +14 -14
package/dist/index.d.ts CHANGED
@@ -141,6 +141,7 @@ interface RouteInfo {
141
141
  jayHtmlPath: string;
142
142
  compPath: string;
143
143
  }
144
+ type DevServerRouteRegistrar = (routes: DevServerRoute[]) => void;
144
145
  declare class DevServerService {
145
146
  private routes;
146
147
  private vite;
@@ -148,8 +149,17 @@ declare class DevServerService {
148
149
  private projectBase;
149
150
  private jayRollupConfig;
150
151
  private _freezeStore?;
151
- constructor(routes: DevServerRoute[], vite: ViteDevServer, pagesBase: string, projectBase: string, jayRollupConfig: JayRollupConfig, _freezeStore?: FreezeStore);
152
+ private rescanRoutes?;
153
+ constructor(routes: DevServerRoute[], vite: ViteDevServer, pagesBase: string, projectBase: string, jayRollupConfig: JayRollupConfig, _freezeStore?: FreezeStore, rescanRoutes?: () => Promise<DevServerRoute[]>);
154
+ private routeRegistrar?;
152
155
  get freezeStore(): FreezeStore | undefined;
156
+ /** Register new route handlers with Express (or another HTTP layer). */
157
+ attachRouteRegistrar(registrar: DevServerRouteRegistrar): void;
158
+ /**
159
+ * Rescan the pages directory for new routes and register any that were
160
+ * added since dev-server startup (e.g. after AIditor Add Page).
161
+ */
162
+ refreshRoutes(): Promise<RouteInfo[]>;
153
163
  /** List all page routes in the project. */
154
164
  listRoutes(): RouteInfo[];
155
165
  /**
@@ -277,4 +287,4 @@ declare function createViteForCli(options: {
277
287
  tsConfigFilePath?: string;
278
288
  }): Promise<ViteDevServer>;
279
289
 
280
- export { ACTION_ENDPOINT_BASE, type ActionBodyParserOptions, type ActionRouterOptions, type CreateViteServerOptions, DEV_SERVER_SERVICE, type DevServer, type DevServerOptions, type DevServerRoute, DevServerService, type FreezeEntry, FreezeStore, type RouteInfo, actionBodyParser, createActionRouter, createViteForCli, createViteServer, mkDevServer };
290
+ export { ACTION_ENDPOINT_BASE, type ActionBodyParserOptions, type ActionRouterOptions, type CreateViteServerOptions, DEV_SERVER_SERVICE, type DevServer, type DevServerOptions, type DevServerRoute, type DevServerRouteRegistrar, DevServerService, type FreezeEntry, FreezeStore, type RouteInfo, actionBodyParser, createActionRouter, createViteForCli, createViteServer, mkDevServer };
package/dist/index.js CHANGED
@@ -10,13 +10,13 @@ import { createRequire } from "module";
10
10
  import "@jay-framework/compiler-shared";
11
11
  import * as path from "node:path";
12
12
  import path__default from "node:path";
13
- import { JAY_IMPORT_RESOLVER, injectHeadfullFSTemplates, parseContract, slowRenderTransform, discoverHeadlessInstances, assignCoordinatesToJayHtml, resolveHeadlessInstances } from "@jay-framework/compiler-jay-html";
13
+ import { JAY_IMPORT_RESOLVER, injectHeadfullFSTemplates, parseContract, slowRenderTransform, assignCoordinatesToJayHtml, discoverHeadlessInstances } from "@jay-framework/compiler-jay-html";
14
14
  import { getLogger, getDevLogger } from "@jay-framework/logger";
15
15
  import { createRequire as createRequire$1 } from "node:module";
16
16
  import * as fsSync from "node:fs";
17
17
  import fsSync__default from "node:fs";
18
18
  import { scanRoutes, createRoute, routeToExpressRoute } from "@jay-framework/stack-route-scanner";
19
- import { discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, actionRegistry, discoverAndRegisterActions, discoverAllPluginActions, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, loadPageParts, runLoadParams, DevSlowlyChangingPhase, SlowRenderCache, preparePluginClientInits, registerService, clearServerElementCache, scanPlugins, getServiceRegistry, materializeContracts, renderFastChangingData, mergeHeadTags, generateClientScript, getClientInitData, generateSSRPageHtml, generateFrozenPageHtml, validateForEachInstances, slowRenderInstances } from "@jay-framework/stack-server-runtime";
19
+ import { discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, actionRegistry, discoverAndRegisterActions, discoverAllPluginActions, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, loadPageParts, runLoadParams, DevSlowlyChangingPhase, SlowRenderCache, preparePluginClientInits, registerService, clearServerElementCache, scanPlugins, getServiceRegistry, materializeContracts, parseCookies, renderFastChangingData, mergeHeadTags, generateClientScript, getClientInitData, generateSSRPageHtml, generateFrozenPageHtml, slowRenderInstances } from "@jay-framework/stack-server-runtime";
20
20
  import * as fs from "node:fs/promises";
21
21
  import fs__default from "node:fs/promises";
22
22
  import { pathToFileURL } from "node:url";
@@ -1949,8 +1949,6 @@ function getStatusCodeForError(code, isActionError) {
1949
1949
  return 500;
1950
1950
  }
1951
1951
  }
1952
- const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
1953
- const DEFAULT_MAX_FILES = 10;
1954
1952
  function mergeDottedMultipartKeys(body) {
1955
1953
  const keys = Object.keys(body);
1956
1954
  for (const key of keys) {
@@ -1971,7 +1969,7 @@ function mergeDottedMultipartKeys(body) {
1971
1969
  cur[parts[parts.length - 1]] = val;
1972
1970
  }
1973
1971
  }
1974
- function parseMultipart(req, tempDir, maxFileSize, maxFiles) {
1972
+ function parseMultipart(req, tempDir) {
1975
1973
  return new Promise((resolve, reject) => {
1976
1974
  fs$1.mkdirSync(tempDir, { recursive: true });
1977
1975
  const files = {};
@@ -1980,11 +1978,7 @@ function parseMultipart(req, tempDir, maxFileSize, maxFiles) {
1980
1978
  let errored = false;
1981
1979
  const pendingWrites = [];
1982
1980
  const bb = Busboy({
1983
- headers: req.headers,
1984
- limits: {
1985
- fileSize: maxFileSize,
1986
- files: maxFiles
1987
- }
1981
+ headers: req.headers
1988
1982
  });
1989
1983
  bb.on("file", (fieldname, stream, info) => {
1990
1984
  if (errored)
@@ -1993,26 +1987,14 @@ function parseMultipart(req, tempDir, maxFileSize, maxFiles) {
1993
1987
  const filename = info.filename || `upload-${fileCount}`;
1994
1988
  const tempPath = path$1.join(tempDir, `${fileCount}-${filename}`);
1995
1989
  let size = 0;
1996
- let truncated = false;
1997
1990
  const writeStream = fs$1.createWriteStream(tempPath);
1998
1991
  stream.pipe(writeStream);
1999
1992
  stream.on("data", (data) => {
2000
1993
  size += data.length;
2001
1994
  });
2002
- stream.on("limit", () => {
2003
- truncated = true;
2004
- });
2005
1995
  pendingWrites.push(
2006
1996
  new Promise((resolveWrite, rejectWrite) => {
2007
1997
  writeStream.on("close", () => {
2008
- if (truncated) {
2009
- rejectWrite(
2010
- new Error(
2011
- `File "${filename}" exceeds maximum size of ${maxFileSize} bytes`
2012
- )
2013
- );
2014
- return;
2015
- }
2016
1998
  const jayFile = {
2017
1999
  name: filename,
2018
2000
  type: info.mimeType,
@@ -2099,9 +2081,7 @@ function actionBodyParser(options) {
2099
2081
  }
2100
2082
  const requestId = crypto.randomUUID();
2101
2083
  const tempDir = path$1.join(buildFolder, ".tmp", "actions", requestId);
2102
- const maxFileSize = action.fileOptions?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
2103
- const maxFiles = action.fileOptions?.maxFiles ?? DEFAULT_MAX_FILES;
2104
- parseMultipart(req, tempDir, maxFileSize, maxFiles).then(({ body: body2, tempDir: td }) => {
2084
+ parseMultipart(req, tempDir).then(({ body: body2, tempDir: td }) => {
2105
2085
  req.body = body2;
2106
2086
  req._jayTempDir = td;
2107
2087
  next();
@@ -2215,17 +2195,40 @@ class FreezeStore {
2215
2195
  }
2216
2196
  const DEV_SERVER_SERVICE = createJayService("DevServerService");
2217
2197
  class DevServerService {
2218
- constructor(routes, vite, pagesBase, projectBase, jayRollupConfig, _freezeStore) {
2198
+ constructor(routes, vite, pagesBase, projectBase, jayRollupConfig, _freezeStore, rescanRoutes) {
2199
+ __publicField(this, "routeRegistrar");
2219
2200
  this.routes = routes;
2220
2201
  this.vite = vite;
2221
2202
  this.pagesBase = pagesBase;
2222
2203
  this.projectBase = projectBase;
2223
2204
  this.jayRollupConfig = jayRollupConfig;
2224
2205
  this._freezeStore = _freezeStore;
2206
+ this.rescanRoutes = rescanRoutes;
2225
2207
  }
2226
2208
  get freezeStore() {
2227
2209
  return this._freezeStore;
2228
2210
  }
2211
+ /** Register new route handlers with Express (or another HTTP layer). */
2212
+ attachRouteRegistrar(registrar) {
2213
+ this.routeRegistrar = registrar;
2214
+ }
2215
+ /**
2216
+ * Rescan the pages directory for new routes and register any that were
2217
+ * added since dev-server startup (e.g. after AIditor Add Page).
2218
+ */
2219
+ async refreshRoutes() {
2220
+ if (!this.rescanRoutes) {
2221
+ return this.listRoutes();
2222
+ }
2223
+ const added = await this.rescanRoutes();
2224
+ if (added.length > 0 && this.routeRegistrar) {
2225
+ this.routeRegistrar(added);
2226
+ getLogger().info(
2227
+ `[Routes] Registered ${added.length} new route(s): ${added.map((r) => r.fsRoute.rawRoute).join(", ")}`
2228
+ );
2229
+ }
2230
+ return this.listRoutes();
2231
+ }
2229
2232
  /** List all page routes in the project. */
2230
2233
  listRoutes() {
2231
2234
  return this.routes.map((r) => ({
@@ -2435,6 +2438,7 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, freezeStore
2435
2438
  for (const [key, value] of urlObj.searchParams) {
2436
2439
  query[key] = value;
2437
2440
  }
2441
+ const cookies = parseCookies(req.headers.cookie);
2438
2442
  const freezeId = query["_jay_freeze"];
2439
2443
  if (freezeId && freezeStore) {
2440
2444
  timing?.annotate("[FROZEN]");
@@ -2466,7 +2470,8 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, freezeStore
2466
2470
  res,
2467
2471
  url,
2468
2472
  timing,
2469
- query
2473
+ query,
2474
+ cookies
2470
2475
  );
2471
2476
  } else {
2472
2477
  const cachedEntry = await slowRenderCache.get(
@@ -2488,7 +2493,8 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, freezeStore
2488
2493
  res,
2489
2494
  url,
2490
2495
  timing,
2491
- query
2496
+ query,
2497
+ cookies
2492
2498
  );
2493
2499
  } else {
2494
2500
  await handlePreRenderRequest(
@@ -2505,7 +2511,8 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, freezeStore
2505
2511
  res,
2506
2512
  url,
2507
2513
  timing,
2508
- query
2514
+ query,
2515
+ cookies
2509
2516
  );
2510
2517
  }
2511
2518
  }
@@ -2518,7 +2525,7 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, freezeStore
2518
2525
  };
2519
2526
  return { path: routePath, handler, fsRoute: route };
2520
2527
  }
2521
- async function handleCachedRequest(vite, route, options, cachedEntry, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing, query = {}) {
2528
+ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing, query = {}, cookies = {}) {
2522
2529
  const loadStart = Date.now();
2523
2530
  const pagePartsResult = await loadPageParts(
2524
2531
  vite,
@@ -2564,7 +2571,8 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
2564
2571
  forEachInstances,
2565
2572
  headlessComps,
2566
2573
  cachedEntry.slowViewState,
2567
- query
2574
+ query,
2575
+ cookies
2568
2576
  );
2569
2577
  timing?.recordFastRender(Date.now() - fastStart);
2570
2578
  if (renderedFast.kind !== "PhaseOutput") {
@@ -2572,17 +2580,27 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
2572
2580
  timing?.end();
2573
2581
  return;
2574
2582
  }
2583
+ if (renderedFast.responseHeaders) {
2584
+ for (const [key, value] of Object.entries(renderedFast.responseHeaders)) {
2585
+ res.setHeader(key, value);
2586
+ }
2587
+ }
2575
2588
  const fastViewState = renderedFast.rendered;
2576
2589
  const fastCarryForward = renderedFast.carryForward;
2577
2590
  const headTags = renderedFast.headTags ?? mergeHeadTags(cachedEntry.carryForward?.__slowHeadTags ?? []);
2591
+ const fullViewState = deepMergeViewStates(
2592
+ cachedEntry.slowViewState,
2593
+ fastViewState,
2594
+ clientTrackByMap || {}
2595
+ );
2578
2596
  await sendResponse(
2579
2597
  vite,
2580
2598
  res,
2581
2599
  url,
2582
- cachedEntry.preRenderedPath,
2600
+ route.jayHtmlPath,
2583
2601
  route.jayHtmlPath,
2584
2602
  pageParts,
2585
- fastViewState,
2603
+ fullViewState,
2586
2604
  fastCarryForward,
2587
2605
  clientTrackByMap,
2588
2606
  projectInit,
@@ -2592,11 +2610,11 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
2592
2610
  getRouteDir(route),
2593
2611
  cachedEntry.slowViewState,
2594
2612
  timing,
2595
- cachedEntry.preRenderedContent,
2613
+ void 0,
2596
2614
  headTags
2597
2615
  );
2598
2616
  }
2599
- async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRenderCache, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing, query = {}) {
2617
+ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRenderCache, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing, query = {}, cookies = {}) {
2600
2618
  const loadStart = Date.now();
2601
2619
  const initialPartsResult = await loadPageParts(
2602
2620
  vite,
@@ -2681,10 +2699,11 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
2681
2699
  res,
2682
2700
  url,
2683
2701
  timing,
2684
- query
2702
+ query,
2703
+ cookies
2685
2704
  );
2686
2705
  }
2687
- async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing, query = {}) {
2706
+ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing, query = {}, cookies = {}) {
2688
2707
  const loadStart = Date.now();
2689
2708
  const pagePartsResult = await loadPageParts(
2690
2709
  vite,
@@ -2746,7 +2765,8 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
2746
2765
  forEachInstances,
2747
2766
  headlessInstanceComponents,
2748
2767
  renderedSlowly.rendered,
2749
- query
2768
+ query,
2769
+ cookies
2750
2770
  );
2751
2771
  timing?.recordFastRender(Date.now() - fastStart);
2752
2772
  if (renderedFast.kind !== "PhaseOutput") {
@@ -2754,6 +2774,11 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
2754
2774
  timing?.end();
2755
2775
  return;
2756
2776
  }
2777
+ if (renderedFast.responseHeaders) {
2778
+ for (const [key, value] of Object.entries(renderedFast.responseHeaders)) {
2779
+ res.setHeader(key, value);
2780
+ }
2781
+ }
2757
2782
  const viewState = deepMergeViewStates(
2758
2783
  renderedSlowly.rendered,
2759
2784
  renderedFast.rendered,
@@ -2789,15 +2814,13 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
2789
2814
  async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, routePattern, routeDir, slowViewState, timing, preLoadedContent, headTags) {
2790
2815
  let pageHtml;
2791
2816
  try {
2792
- let jayHtmlContent = preLoadedContent ?? await fs__default.readFile(jayHtmlPath, "utf-8");
2793
- const jayHtmlFilename = path__default.basename(jayHtmlPath);
2794
2817
  const jayHtmlDir = path__default.dirname(jayHtmlPath);
2795
- const sourceDir = path__default.dirname(sourceJayHtmlPath);
2796
- jayHtmlContent = injectHeadfullFSTemplates(jayHtmlContent, sourceDir, JAY_IMPORT_RESOLVER);
2818
+ let jayHtmlContent = preLoadedContent ?? await fs__default.readFile(jayHtmlPath, "utf-8");
2819
+ jayHtmlContent = injectHeadfullFSTemplates(jayHtmlContent, jayHtmlDir, JAY_IMPORT_RESOLVER);
2797
2820
  pageHtml = await generateSSRPageHtml(
2798
2821
  vite,
2799
2822
  jayHtmlContent,
2800
- jayHtmlFilename,
2823
+ path__default.basename(jayHtmlPath),
2801
2824
  jayHtmlDir,
2802
2825
  viewState,
2803
2826
  jayHtmlPath,
@@ -2816,8 +2839,7 @@ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, page
2816
2839
  slowViewState,
2817
2840
  routePattern
2818
2841
  },
2819
- // Pass source directory for headfull FS file resolution when using pre-rendered path
2820
- jayHtmlDir !== sourceDir ? sourceDir : void 0,
2842
+ void 0,
2821
2843
  headTags
2822
2844
  );
2823
2845
  } catch (err) {
@@ -2902,37 +2924,21 @@ async function handleFrozenRequest(vite, route, options, freezeStore, slowRender
2902
2924
  }
2903
2925
  async function preRenderJayHtml(route, slowViewState, headlessContracts, headlessInstanceComponents, partKeys = []) {
2904
2926
  const jayHtmlContent = await fs__default.readFile(route.jayHtmlPath, "utf-8");
2905
- const contractPath = route.jayHtmlPath.replace(".jay-html", ".jay-contract");
2906
- let contract;
2907
- try {
2908
- const contractContent = await fs__default.readFile(contractPath, "utf-8");
2909
- const parseResult = parseContract(contractContent, path__default.basename(contractPath));
2910
- if (parseResult.val) {
2911
- contract = parseResult.val;
2912
- } else if (parseResult.validations.length > 0) {
2913
- getLogger().error(
2914
- `[SlowRender] Contract parse error for ${contractPath}: ${parseResult.validations.join(", ")}`
2915
- );
2916
- return void 0;
2917
- }
2918
- } catch (error) {
2919
- if (error.code !== "ENOENT") {
2920
- getLogger().error(`[SlowRender] Error reading contract ${contractPath}: ${error}`);
2921
- return void 0;
2922
- }
2923
- }
2924
- const hasPageLevelSlowData = slowViewState && Object.keys(slowViewState).some((k) => !partKeys.includes(k));
2925
- if (!contract && hasPageLevelSlowData) {
2926
- getLogger().warn(
2927
- `[SlowRender] Page ${route.jayHtmlPath} has slow ViewState but no contract. Without a contract, slow bindings cannot be resolved. Move data to withFastRender or add a .jay-contract file with phase annotations.`
2928
- );
2929
- }
2930
2927
  const sourceDir = path__default.dirname(route.jayHtmlPath);
2931
2928
  const jayHtmlWithTemplates = injectHeadfullFSTemplates(
2932
2929
  jayHtmlContent,
2933
2930
  sourceDir,
2934
2931
  JAY_IMPORT_RESOLVER
2935
2932
  );
2933
+ let contract;
2934
+ const contractPath = route.jayHtmlPath.replace(".jay-html", ".jay-contract");
2935
+ try {
2936
+ const contractContent = await fs__default.readFile(contractPath, "utf-8");
2937
+ const parseResult = parseContract(contractContent, path__default.basename(contractPath));
2938
+ if (parseResult.val)
2939
+ contract = parseResult.val;
2940
+ } catch {
2941
+ }
2936
2942
  const result = slowRenderTransform({
2937
2943
  jayHtmlContent: jayHtmlWithTemplates,
2938
2944
  slowViewState,
@@ -2949,28 +2955,19 @@ async function preRenderJayHtml(route, slowViewState, headlessContracts, headles
2949
2955
  }
2950
2956
  return void 0;
2951
2957
  }
2952
- let preRenderedJayHtml = result.val.preRenderedJayHtml;
2958
+ const preRenderedJayHtml = result.val.preRenderedJayHtml;
2953
2959
  let instancePhaseData;
2954
2960
  let forEachInstances;
2955
2961
  if (headlessInstanceComponents.length > 0) {
2956
- const discoveryResult = discoverHeadlessInstances(preRenderedJayHtml);
2957
- const htmlWithRefs = discoveryResult.preRenderedJayHtml;
2958
2962
  const headlessContractNameSet = new Set(
2959
2963
  headlessInstanceComponents.map((c2) => c2.contractName)
2960
2964
  );
2961
- preRenderedJayHtml = assignCoordinatesToJayHtml(htmlWithRefs, headlessContractNameSet);
2962
- const finalDiscovery = discoverHeadlessInstances(preRenderedJayHtml);
2965
+ const withCoords = assignCoordinatesToJayHtml(
2966
+ jayHtmlWithTemplates,
2967
+ headlessContractNameSet
2968
+ );
2969
+ const finalDiscovery = discoverHeadlessInstances(withCoords);
2963
2970
  if (finalDiscovery.forEachInstances.length > 0) {
2964
- const validationErrors = validateForEachInstances(
2965
- finalDiscovery.forEachInstances,
2966
- headlessInstanceComponents
2967
- );
2968
- if (validationErrors.length > 0) {
2969
- getLogger().error(
2970
- `[SlowRender] ForEach instance validation failed: ${validationErrors.join(", ")}`
2971
- );
2972
- return void 0;
2973
- }
2974
2971
  forEachInstances = finalDiscovery.forEachInstances;
2975
2972
  }
2976
2973
  if (finalDiscovery.instances.length > 0) {
@@ -2980,19 +2977,6 @@ async function preRenderJayHtml(route, slowViewState, headlessContracts, headles
2980
2977
  );
2981
2978
  if (slowResult) {
2982
2979
  instancePhaseData = slowResult.instancePhaseData;
2983
- const pass2Result = resolveHeadlessInstances(
2984
- preRenderedJayHtml,
2985
- slowResult.resolvedData,
2986
- JAY_IMPORT_RESOLVER
2987
- );
2988
- if (pass2Result.val) {
2989
- preRenderedJayHtml = pass2Result.val;
2990
- }
2991
- if (pass2Result.validations.length > 0) {
2992
- getLogger().error(
2993
- `[SlowRender] Instance resolution warnings for ${route.jayHtmlPath}: ${pass2Result.validations.join(", ")}`
2994
- );
2995
- }
2996
2980
  }
2997
2981
  if (!instancePhaseData) {
2998
2982
  const componentByContractName = /* @__PURE__ */ new Map();
@@ -3000,7 +2984,7 @@ async function preRenderJayHtml(route, slowViewState, headlessContracts, headles
3000
2984
  componentByContractName.set(comp.contractName, comp);
3001
2985
  }
3002
2986
  instancePhaseData = {
3003
- discovered: discoveryResult.instances.filter((i) => componentByContractName.has(i.contractName)).map((i) => {
2987
+ discovered: finalDiscovery.instances.filter((i) => componentByContractName.has(i.contractName)).map((i) => {
3004
2988
  const comp = componentByContractName.get(i.contractName);
3005
2989
  const contractProps = comp.contract?.props ?? [];
3006
2990
  const normalizedProps = {};
@@ -3105,13 +3089,44 @@ async function mkDevServer(rawOptions) {
3105
3089
  pluginClientInits
3106
3090
  )
3107
3091
  );
3092
+ async function rescanAndMergeNewRoutes() {
3093
+ const projectRoutes2 = await initRoutes(options.pagesRootFolder);
3094
+ const filteredProjectRoutes2 = options.buildFolder ? projectRoutes2.filter((route) => !route.jayHtmlPath.startsWith(options.buildFolder)) : projectRoutes2;
3095
+ const pluginRoutes2 = await scanPluginRoutes(
3096
+ options.projectRootFolder,
3097
+ filteredProjectRoutes2
3098
+ );
3099
+ const scannedJayRoutes = [...filteredProjectRoutes2, ...pluginRoutes2];
3100
+ const existingRawRoutes = new Set(devServerRoutes.map((r) => r.fsRoute.rawRoute));
3101
+ const added = [];
3102
+ for (const jayRoute of scannedJayRoutes) {
3103
+ if (existingRawRoutes.has(jayRoute.rawRoute))
3104
+ continue;
3105
+ const devRoute = mkRoute(
3106
+ jayRoute,
3107
+ vite,
3108
+ slowlyPhase,
3109
+ options,
3110
+ slowRenderCache,
3111
+ freezeStore,
3112
+ projectInit,
3113
+ pluginsWithInit,
3114
+ pluginClientInits
3115
+ );
3116
+ devServerRoutes.push(devRoute);
3117
+ existingRawRoutes.add(jayRoute.rawRoute);
3118
+ added.push(devRoute);
3119
+ }
3120
+ return added;
3121
+ }
3108
3122
  const service = new DevServerService(
3109
3123
  devServerRoutes,
3110
3124
  vite,
3111
3125
  options.pagesRootFolder,
3112
3126
  options.projectRootFolder,
3113
3127
  options.jayRollupConfig,
3114
- freezeStore
3128
+ freezeStore,
3129
+ rescanAndMergeNewRoutes
3115
3130
  );
3116
3131
  registerService(DEV_SERVER_SERVICE, service);
3117
3132
  return {
@@ -3155,8 +3170,8 @@ function setupServiceHotReload(vite, lifecycleManager) {
3155
3170
  });
3156
3171
  }
3157
3172
  function setupActionRouter(vite, buildFolder) {
3158
- vite.middlewares.use(actionBodyParser({ buildFolder }));
3159
- vite.middlewares.use(ACTION_ENDPOINT_BASE, createActionRouter());
3173
+ vite.middlewares.use(actionBodyParser({ buildFolder, registry: actionRegistry }));
3174
+ vite.middlewares.use(ACTION_ENDPOINT_BASE, createActionRouter({ registry: actionRegistry }));
3160
3175
  getLogger().info(`[Actions] Action router mounted at ${ACTION_ENDPOINT_BASE}`);
3161
3176
  }
3162
3177
  function setupFreezeEndpoint(vite, freezeStore) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/dev-server",
3
- "version": "0.17.3",
3
+ "version": "0.18.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
@@ -23,23 +23,23 @@
23
23
  "test:watch": "vitest"
24
24
  },
25
25
  "dependencies": {
26
- "@jay-framework/compiler-jay-stack": "^0.17.3",
27
- "@jay-framework/compiler-shared": "^0.17.3",
28
- "@jay-framework/component": "^0.17.3",
29
- "@jay-framework/fullstack-component": "^0.17.3",
30
- "@jay-framework/logger": "^0.17.3",
31
- "@jay-framework/runtime": "^0.17.3",
32
- "@jay-framework/stack-client-runtime": "^0.17.3",
33
- "@jay-framework/stack-route-scanner": "^0.17.3",
34
- "@jay-framework/stack-server-runtime": "^0.17.3",
35
- "@jay-framework/view-state-merge": "^0.17.3",
26
+ "@jay-framework/compiler-jay-stack": "^0.18.0",
27
+ "@jay-framework/compiler-shared": "^0.18.0",
28
+ "@jay-framework/component": "^0.18.0",
29
+ "@jay-framework/fullstack-component": "^0.18.0",
30
+ "@jay-framework/logger": "^0.18.0",
31
+ "@jay-framework/runtime": "^0.18.0",
32
+ "@jay-framework/stack-client-runtime": "^0.18.0",
33
+ "@jay-framework/stack-route-scanner": "^0.18.0",
34
+ "@jay-framework/stack-server-runtime": "^0.18.0",
35
+ "@jay-framework/view-state-merge": "^0.18.0",
36
36
  "busboy": "^1.6.0",
37
37
  "vite": "^5.0.11"
38
38
  },
39
39
  "devDependencies": {
40
- "@jay-framework/dev-environment": "^0.17.3",
41
- "@jay-framework/jay-cli": "^0.17.3",
42
- "@jay-framework/stack-client-runtime": "^0.17.3",
40
+ "@jay-framework/dev-environment": "^0.18.0",
41
+ "@jay-framework/jay-cli": "^0.18.0",
42
+ "@jay-framework/stack-client-runtime": "^0.18.0",
43
43
  "@playwright/test": "^1.58.2",
44
44
  "@types/busboy": "^1.5.4",
45
45
  "@types/express": "^5.0.2",