@jay-framework/dev-server 0.17.4 → 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.
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, parseCookies, 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) => ({
@@ -2585,14 +2588,19 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
2585
2588
  const fastViewState = renderedFast.rendered;
2586
2589
  const fastCarryForward = renderedFast.carryForward;
2587
2590
  const headTags = renderedFast.headTags ?? mergeHeadTags(cachedEntry.carryForward?.__slowHeadTags ?? []);
2591
+ const fullViewState = deepMergeViewStates(
2592
+ cachedEntry.slowViewState,
2593
+ fastViewState,
2594
+ clientTrackByMap || {}
2595
+ );
2588
2596
  await sendResponse(
2589
2597
  vite,
2590
2598
  res,
2591
2599
  url,
2592
- cachedEntry.preRenderedPath,
2600
+ route.jayHtmlPath,
2593
2601
  route.jayHtmlPath,
2594
2602
  pageParts,
2595
- fastViewState,
2603
+ fullViewState,
2596
2604
  fastCarryForward,
2597
2605
  clientTrackByMap,
2598
2606
  projectInit,
@@ -2602,7 +2610,7 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
2602
2610
  getRouteDir(route),
2603
2611
  cachedEntry.slowViewState,
2604
2612
  timing,
2605
- cachedEntry.preRenderedContent,
2613
+ void 0,
2606
2614
  headTags
2607
2615
  );
2608
2616
  }
@@ -2806,15 +2814,13 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
2806
2814
  async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, routePattern, routeDir, slowViewState, timing, preLoadedContent, headTags) {
2807
2815
  let pageHtml;
2808
2816
  try {
2809
- let jayHtmlContent = preLoadedContent ?? await fs__default.readFile(jayHtmlPath, "utf-8");
2810
- const jayHtmlFilename = path__default.basename(jayHtmlPath);
2811
2817
  const jayHtmlDir = path__default.dirname(jayHtmlPath);
2812
- const sourceDir = path__default.dirname(sourceJayHtmlPath);
2813
- 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);
2814
2820
  pageHtml = await generateSSRPageHtml(
2815
2821
  vite,
2816
2822
  jayHtmlContent,
2817
- jayHtmlFilename,
2823
+ path__default.basename(jayHtmlPath),
2818
2824
  jayHtmlDir,
2819
2825
  viewState,
2820
2826
  jayHtmlPath,
@@ -2833,8 +2839,7 @@ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, page
2833
2839
  slowViewState,
2834
2840
  routePattern
2835
2841
  },
2836
- // Pass source directory for headfull FS file resolution when using pre-rendered path
2837
- jayHtmlDir !== sourceDir ? sourceDir : void 0,
2842
+ void 0,
2838
2843
  headTags
2839
2844
  );
2840
2845
  } catch (err) {
@@ -2919,37 +2924,21 @@ async function handleFrozenRequest(vite, route, options, freezeStore, slowRender
2919
2924
  }
2920
2925
  async function preRenderJayHtml(route, slowViewState, headlessContracts, headlessInstanceComponents, partKeys = []) {
2921
2926
  const jayHtmlContent = await fs__default.readFile(route.jayHtmlPath, "utf-8");
2922
- const contractPath = route.jayHtmlPath.replace(".jay-html", ".jay-contract");
2923
- let contract;
2924
- try {
2925
- const contractContent = await fs__default.readFile(contractPath, "utf-8");
2926
- const parseResult = parseContract(contractContent, path__default.basename(contractPath));
2927
- if (parseResult.val) {
2928
- contract = parseResult.val;
2929
- } else if (parseResult.validations.length > 0) {
2930
- getLogger().error(
2931
- `[SlowRender] Contract parse error for ${contractPath}: ${parseResult.validations.join(", ")}`
2932
- );
2933
- return void 0;
2934
- }
2935
- } catch (error) {
2936
- if (error.code !== "ENOENT") {
2937
- getLogger().error(`[SlowRender] Error reading contract ${contractPath}: ${error}`);
2938
- return void 0;
2939
- }
2940
- }
2941
- const hasPageLevelSlowData = slowViewState && Object.keys(slowViewState).some((k) => !partKeys.includes(k));
2942
- if (!contract && hasPageLevelSlowData) {
2943
- getLogger().warn(
2944
- `[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.`
2945
- );
2946
- }
2947
2927
  const sourceDir = path__default.dirname(route.jayHtmlPath);
2948
2928
  const jayHtmlWithTemplates = injectHeadfullFSTemplates(
2949
2929
  jayHtmlContent,
2950
2930
  sourceDir,
2951
2931
  JAY_IMPORT_RESOLVER
2952
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
+ }
2953
2942
  const result = slowRenderTransform({
2954
2943
  jayHtmlContent: jayHtmlWithTemplates,
2955
2944
  slowViewState,
@@ -2966,28 +2955,19 @@ async function preRenderJayHtml(route, slowViewState, headlessContracts, headles
2966
2955
  }
2967
2956
  return void 0;
2968
2957
  }
2969
- let preRenderedJayHtml = result.val.preRenderedJayHtml;
2958
+ const preRenderedJayHtml = result.val.preRenderedJayHtml;
2970
2959
  let instancePhaseData;
2971
2960
  let forEachInstances;
2972
2961
  if (headlessInstanceComponents.length > 0) {
2973
- const discoveryResult = discoverHeadlessInstances(preRenderedJayHtml);
2974
- const htmlWithRefs = discoveryResult.preRenderedJayHtml;
2975
2962
  const headlessContractNameSet = new Set(
2976
2963
  headlessInstanceComponents.map((c2) => c2.contractName)
2977
2964
  );
2978
- preRenderedJayHtml = assignCoordinatesToJayHtml(htmlWithRefs, headlessContractNameSet);
2979
- const finalDiscovery = discoverHeadlessInstances(preRenderedJayHtml);
2965
+ const withCoords = assignCoordinatesToJayHtml(
2966
+ jayHtmlWithTemplates,
2967
+ headlessContractNameSet
2968
+ );
2969
+ const finalDiscovery = discoverHeadlessInstances(withCoords);
2980
2970
  if (finalDiscovery.forEachInstances.length > 0) {
2981
- const validationErrors = validateForEachInstances(
2982
- finalDiscovery.forEachInstances,
2983
- headlessInstanceComponents
2984
- );
2985
- if (validationErrors.length > 0) {
2986
- getLogger().error(
2987
- `[SlowRender] ForEach instance validation failed: ${validationErrors.join(", ")}`
2988
- );
2989
- return void 0;
2990
- }
2991
2971
  forEachInstances = finalDiscovery.forEachInstances;
2992
2972
  }
2993
2973
  if (finalDiscovery.instances.length > 0) {
@@ -2997,19 +2977,6 @@ async function preRenderJayHtml(route, slowViewState, headlessContracts, headles
2997
2977
  );
2998
2978
  if (slowResult) {
2999
2979
  instancePhaseData = slowResult.instancePhaseData;
3000
- const pass2Result = resolveHeadlessInstances(
3001
- preRenderedJayHtml,
3002
- slowResult.resolvedData,
3003
- JAY_IMPORT_RESOLVER
3004
- );
3005
- if (pass2Result.val) {
3006
- preRenderedJayHtml = pass2Result.val;
3007
- }
3008
- if (pass2Result.validations.length > 0) {
3009
- getLogger().error(
3010
- `[SlowRender] Instance resolution warnings for ${route.jayHtmlPath}: ${pass2Result.validations.join(", ")}`
3011
- );
3012
- }
3013
2980
  }
3014
2981
  if (!instancePhaseData) {
3015
2982
  const componentByContractName = /* @__PURE__ */ new Map();
@@ -3017,7 +2984,7 @@ async function preRenderJayHtml(route, slowViewState, headlessContracts, headles
3017
2984
  componentByContractName.set(comp.contractName, comp);
3018
2985
  }
3019
2986
  instancePhaseData = {
3020
- discovered: discoveryResult.instances.filter((i) => componentByContractName.has(i.contractName)).map((i) => {
2987
+ discovered: finalDiscovery.instances.filter((i) => componentByContractName.has(i.contractName)).map((i) => {
3021
2988
  const comp = componentByContractName.get(i.contractName);
3022
2989
  const contractProps = comp.contract?.props ?? [];
3023
2990
  const normalizedProps = {};
@@ -3122,13 +3089,44 @@ async function mkDevServer(rawOptions) {
3122
3089
  pluginClientInits
3123
3090
  )
3124
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
+ }
3125
3122
  const service = new DevServerService(
3126
3123
  devServerRoutes,
3127
3124
  vite,
3128
3125
  options.pagesRootFolder,
3129
3126
  options.projectRootFolder,
3130
3127
  options.jayRollupConfig,
3131
- freezeStore
3128
+ freezeStore,
3129
+ rescanAndMergeNewRoutes
3132
3130
  );
3133
3131
  registerService(DEV_SERVER_SERVICE, service);
3134
3132
  return {
@@ -3172,8 +3170,8 @@ function setupServiceHotReload(vite, lifecycleManager) {
3172
3170
  });
3173
3171
  }
3174
3172
  function setupActionRouter(vite, buildFolder) {
3175
- vite.middlewares.use(actionBodyParser({ buildFolder }));
3176
- 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 }));
3177
3175
  getLogger().info(`[Actions] Action router mounted at ${ACTION_ENDPOINT_BASE}`);
3178
3176
  }
3179
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.4",
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.4",
27
- "@jay-framework/compiler-shared": "^0.17.4",
28
- "@jay-framework/component": "^0.17.4",
29
- "@jay-framework/fullstack-component": "^0.17.4",
30
- "@jay-framework/logger": "^0.17.4",
31
- "@jay-framework/runtime": "^0.17.4",
32
- "@jay-framework/stack-client-runtime": "^0.17.4",
33
- "@jay-framework/stack-route-scanner": "^0.17.4",
34
- "@jay-framework/stack-server-runtime": "^0.17.4",
35
- "@jay-framework/view-state-merge": "^0.17.4",
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.4",
41
- "@jay-framework/jay-cli": "^0.17.4",
42
- "@jay-framework/stack-client-runtime": "^0.17.4",
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",