@jay-framework/dev-server 0.25.4 → 0.25.5

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 +28 -1
  2. package/dist/index.js +185 -17
  3. package/package.json +15 -15
package/dist/index.d.ts CHANGED
@@ -39,6 +39,19 @@ interface DevServerOptions {
39
39
  logLevel?: LogLevel;
40
40
  /** HTTP server for HMR WebSocket attachment (avoids default port 24678 collision) */
41
41
  httpServer?: Server;
42
+ /**
43
+ * Hosts the dev server's Vite host-check accepts, beyond localhost (DL#220). A list of hosts
44
+ * (Vite host-suffix syntax, e.g. `.example.dev`), or `true` to disable the check entirely — for
45
+ * cloud preview proxies. Merged with the `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS` env var.
46
+ */
47
+ allowedHosts?: string[] | true;
48
+ /**
49
+ * Enable source-editing dev markers (DL#219). When true, the pre-render pipeline injects
50
+ * `jay-dev-src` / `jay-dev-item` attributes so the source-editing inspector can map a rendered
51
+ * pixel back to its jay-html source. Off by default — the markers change rendered output, so
52
+ * they are only emitted for AIditor / source-editing sessions.
53
+ */
54
+ sourceEditing?: boolean;
42
55
  }
43
56
 
44
57
  /**
@@ -277,7 +290,21 @@ interface CreateViteServerOptions {
277
290
  clearScreen?: boolean;
278
291
  /** HTTP server for HMR WebSocket (avoids default port 24678 collision) */
279
292
  httpServer?: Server;
293
+ /**
294
+ * Hosts Vite's dev-server host-check accepts, beyond localhost (DL#220). A list of hosts (Vite
295
+ * host-suffix syntax, e.g. `.example.dev`), or `true` to disable the check entirely. Merged with
296
+ * the `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS` env var. `undefined`/empty → Vite's default.
297
+ */
298
+ allowedHosts?: string[] | true;
280
299
  }
300
+ /**
301
+ * Resolves Vite's `server.allowedHosts` from the `.jay` config value and the
302
+ * `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS` env var (Vite's own convention — honored natively only in
303
+ * Vite 6, so the nested Vite 5.4 needs us to read it). Returns `true` (disable check) if either source
304
+ * says so, a de-duplicated merged list otherwise, or `undefined` when neither is set (Vite keeps its
305
+ * localhost-only default). (DL#220)
306
+ */
307
+ declare function resolveAllowedHosts(fromConfig: string[] | true | undefined, env: string | undefined): string[] | true | undefined;
281
308
  /**
282
309
  * Creates a Vite server configured for Jay Stack.
283
310
  *
@@ -297,4 +324,4 @@ declare function createViteForCli(options: {
297
324
  tsConfigFilePath?: string;
298
325
  }): Promise<ViteDevServer>;
299
326
 
300
- 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 };
327
+ 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, resolveAllowedHosts };
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { createRequire } from "module";
7
7
  import "@jay-framework/compiler-shared";
8
8
  import * as path from "node:path";
9
9
  import path__default from "node:path";
10
- import { parseJayFile, JAY_IMPORT_RESOLVER, discoverHeadlessInstances, assignCoordinatesToJayHtml, parseAction, generateServerElementFile, parseContract, slowRenderTransform } from "@jay-framework/compiler-jay-html";
10
+ import { parseJayFile, JAY_IMPORT_RESOLVER, discoverHeadlessInstances, assignCoordinatesToJayHtml, parseAction, generateServerElementFile, noteExportedTypesFileChanged, injectDevMarkers, parseContract, slowRenderTransform } from "@jay-framework/compiler-jay-html";
11
11
  import { getLogger, getDevLogger } from "@jay-framework/logger";
12
12
  import { createRequire as createRequire$1 } from "node:module";
13
13
  import * as fs from "node:fs";
@@ -1518,6 +1518,13 @@ function jayStackCompiler(options = {}) {
1518
1518
  function stripInlineActionBuilders(code, builderNames) {
1519
1519
  return transformJayStackBuilder(code, "action-strip.ts", "client", new Set(builderNames));
1520
1520
  }
1521
+ function resolveAllowedHosts(fromConfig, env) {
1522
+ if (fromConfig === true) return true;
1523
+ const envHosts = (env ?? "").split(",").map((host) => host.trim()).filter(Boolean);
1524
+ if (envHosts.includes("true")) return true;
1525
+ const merged = [...fromConfig ?? [], ...envHosts];
1526
+ return merged.length ? Array.from(new Set(merged)) : void 0;
1527
+ }
1521
1528
  async function createViteServer(options) {
1522
1529
  const {
1523
1530
  projectRoot,
@@ -1526,14 +1533,21 @@ async function createViteServer(options) {
1526
1533
  jayRollupConfig = { tsConfigFilePath: path__default.join(projectRoot, "tsconfig.json") },
1527
1534
  logLevel = "info",
1528
1535
  clearScreen = true,
1529
- httpServer
1536
+ httpServer,
1537
+ allowedHosts
1530
1538
  } = options;
1539
+ const resolvedAllowedHosts = resolveAllowedHosts(
1540
+ allowedHosts,
1541
+ process.env.__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS
1542
+ );
1531
1543
  const vite = await createServer({
1532
1544
  // Don't start HTTP server - we use middleware mode
1533
1545
  server: {
1534
1546
  middlewareMode: true,
1535
1547
  hmr: httpServer ? { server: httpServer } : void 0,
1536
- watch: { ignored: ["**/build/**"] }
1548
+ watch: { ignored: ["**/build/**"] },
1549
+ // Omit the key entirely when unset so Vite keeps its localhost-only default (DL#220).
1550
+ ...resolvedAllowedHosts !== void 0 && { allowedHosts: resolvedAllowedHosts }
1537
1551
  },
1538
1552
  // Use Jay Stack compiler for .jay-html and other custom transforms
1539
1553
  plugins: [
@@ -2018,8 +2032,9 @@ function buildPageReloadHmrScript() {
2018
2032
  });
2019
2033
  }`;
2020
2034
  }
2021
- function buildFreezeScript(routePattern) {
2035
+ function buildFreezeScript(routePattern, sourceFile) {
2022
2036
  const routePatternLiteral = routePattern ? `'${routePattern}'` : "undefined";
2037
+ const sourceFileLiteral = sourceFile ? JSON.stringify(sourceFile) : "undefined";
2023
2038
  return `
2024
2039
  // Page Freeze (DL#127, DL#128 iframe addendum)
2025
2040
  // Sticky embed mode: URL param sets a session cookie so in-iframe navigation preserves it
@@ -2074,13 +2089,152 @@ function buildFreezeScript(routePattern) {
2074
2089
  }
2075
2090
  }
2076
2091
 
2092
+ // Source-editing inspector (DL#219). Resolves a viewport point/rect to the jay-dev-* source
2093
+ // markers emitted when the dev server runs with sourceEditing on. Harmless without markers —
2094
+ // it simply finds no jay-dev-src and replies node:null. Reply carries source LOCATORS (line
2095
+ // ranges + route) plus runtime identities (forEach record id, region name + static props);
2096
+ // the agent reads bound expressions / collection / if-conditions from the jay-html itself.
2097
+ function __jayParseLines(s) {
2098
+ if (!s) return null;
2099
+ const parts = s.split(':');
2100
+ return { from: parseInt(parts[0], 10), to: parseInt(parts[1], 10) };
2101
+ }
2102
+ function __jayLines(el) {
2103
+ return __jayParseLines(el.getAttribute('jay-dev-src'));
2104
+ }
2105
+ // A significant enclosing frame: a forEach item root or a region boundary. Returns null for
2106
+ // plain wrapper elements (the agent reconstructs those from source via node.lines).
2107
+ function __jayFrame(el) {
2108
+ const item = el.getAttribute('jay-dev-item');
2109
+ if (item) return { kind: 'forEach', lines: __jayLines(el), itemId: item };
2110
+ const region = el.getAttribute('jay-dev-region');
2111
+ if (region) {
2112
+ // Region lines live in jay-dev-region-src (the <jay:…> tag's range), distinct from the
2113
+ // root element's own jay-dev-src.
2114
+ const frame = { kind: 'region', lines: __jayParseLines(el.getAttribute('jay-dev-region-src')), name: region };
2115
+ const rawProps = el.getAttribute('jay-dev-region-props');
2116
+ if (rawProps) { try { frame.props = JSON.parse(decodeURIComponent(rawProps)); } catch (e) {} }
2117
+ return frame;
2118
+ }
2119
+ return null;
2120
+ }
2121
+ // One-level HTML of an element: its open tag (authored attrs only, jay-* stripped), its direct
2122
+ // children shown as empty tags (grandchildren elided with …), and its text — so the agent sees
2123
+ // exactly what was clicked inside generated content without the whole subtree.
2124
+ function __jayOneLevelHtml(el) {
2125
+ if (!el || !el.tagName) return '';
2126
+ const tag = el.tagName.toLowerCase();
2127
+ let attrs = '';
2128
+ for (let i = 0; i < el.attributes.length; i++) {
2129
+ const a = el.attributes[i];
2130
+ if (a.name.indexOf('jay-') === 0) continue; // strip jay-dev-* / jay-coordinate
2131
+ attrs += ' ' + a.name + '="' + a.value + '"';
2132
+ }
2133
+ let inner = '';
2134
+ const kids = el.childNodes || [];
2135
+ for (let i = 0; i < kids.length; i++) {
2136
+ const c = kids[i];
2137
+ if (c.nodeType === 3) inner += (c.textContent || '');
2138
+ else if (c.nodeType === 1) {
2139
+ const ct = c.tagName.toLowerCase();
2140
+ inner += '<' + ct + '>' + (c.childNodes && c.childNodes.length ? '…' : '') + '</' + ct + '>';
2141
+ }
2142
+ }
2143
+ if (inner.length > 300) inner = inner.slice(0, 300) + '…';
2144
+ return '<' + tag + attrs + '>' + inner + '</' + tag + '>';
2145
+ }
2146
+ function __jayResolveElement(el) {
2147
+ if (!el) return null;
2148
+ // Walk el up to (but excluding) <body>.
2149
+ const entries = [];
2150
+ let cur = el;
2151
+ while (cur && cur.nodeType === 1 && cur.tagName && cur.tagName.toLowerCase() !== 'body') {
2152
+ entries.push(cur);
2153
+ cur = cur.parentElement;
2154
+ }
2155
+ // Source anchor = nearest element at/above el carrying a source marker (its line range).
2156
+ let sourceEl = null;
2157
+ for (let i = 0; i < entries.length; i++) {
2158
+ if (entries[i].getAttribute('jay-dev-src')) { sourceEl = entries[i]; break; }
2159
+ }
2160
+ if (!sourceEl) return null;
2161
+ // Stack = only the significant enclosing frames (forEach + region), innermost → outermost.
2162
+ const stack = [];
2163
+ for (let i = 0; i < entries.length; i++) {
2164
+ const f = __jayFrame(entries[i]);
2165
+ if (f) stack.push(f);
2166
+ }
2167
+ // node describes the ACTUAL element el (what the pointer hit / the rect chose). html — a
2168
+ // one-level rendering (open tag with authored attrs, direct children as empty tags, inline
2169
+ // text; grandchildren + long text elided) — is the single content field: it carries the tag,
2170
+ // attributes (src/href/value), and value, bounded in size. lines come from the source anchor;
2171
+ // for bound/generated content (an html-string value) el has no jay-dev-src of its own, so
2172
+ // lines point to the ENCLOSING binding (the editable source) and contentFromBinding flags it.
2173
+ const r = el.getBoundingClientRect();
2174
+ const node = {
2175
+ lines: __jayLines(sourceEl),
2176
+ tag: el.tagName ? el.tagName.toLowerCase() : '',
2177
+ html: __jayOneLevelHtml(el),
2178
+ rect: { x: r.x, y: r.y, width: r.width, height: r.height },
2179
+ };
2180
+ const childEls = Array.prototype.slice.call(el.children || []);
2181
+ const childrenGenerated =
2182
+ childEls.length > 0 && childEls.every((c) => !c.getAttribute('jay-dev-src'));
2183
+ if (!el.getAttribute('jay-dev-src') || childrenGenerated) {
2184
+ node.contentFromBinding = true;
2185
+ }
2186
+ return { node, stack };
2187
+ }
2188
+ function __jayInspectPoint(x, y) {
2189
+ return __jayResolveElement(document.elementFromPoint(x, y));
2190
+ }
2191
+ function __jayInspectRect(x, y, w, h) {
2192
+ const sel = { left: x, top: y, right: x + w, bottom: y + h };
2193
+ const marked = Array.prototype.slice.call(document.querySelectorAll('[jay-dev-src]'));
2194
+ let best = null, bestArea = -1;
2195
+ for (let i = 0; i < marked.length; i++) {
2196
+ const el = marked[i];
2197
+ const r = el.getBoundingClientRect();
2198
+ const elArea = r.width * r.height;
2199
+ if (elArea <= 0) continue;
2200
+ const ix = Math.max(0, Math.min(sel.right, r.right) - Math.max(sel.left, r.left));
2201
+ const iy = Math.max(0, Math.min(sel.bottom, r.bottom) - Math.max(sel.top, r.top));
2202
+ // Rect rule (DL#219): keep elements the box mostly encloses (overlap over the ELEMENT's
2203
+ // own area >= 0.8), then pick the LARGEST — the element the box was drawn around. A tight
2204
+ // box only clears the threshold for the small element; a big box clears it for the
2205
+ // container too, and largest-area returns the container (not its first child).
2206
+ if ((ix * iy) / elArea >= 0.8 && elArea > bestArea) {
2207
+ bestArea = elArea;
2208
+ best = el;
2209
+ }
2210
+ }
2211
+ if (best) return __jayResolveElement(best);
2212
+ return __jayInspectPoint(x + w / 2, y + h / 2);
2213
+ }
2214
+
2077
2215
  if (__jayEmbedMode) {
2078
2216
  // Notify parent of the current route on load (DL#128 route addendum)
2079
2217
  window.parent.postMessage({ type: 'jay:route', route: window.location.pathname, routePattern: ${routePatternLiteral} }, '*');
2080
2218
 
2081
- // Embed mode: parent triggers freeze via postMessage
2219
+ // Embed mode: parent triggers freeze / inspect via postMessage
2082
2220
  window.addEventListener('message', (e) => {
2083
- if (e.data?.type === 'jay:requestFreeze') __jayDoFreeze();
2221
+ const d = e.data;
2222
+ if (!d) return;
2223
+ if (d.type === 'jay:requestFreeze') { __jayDoFreeze(); return; }
2224
+ if (d.type === 'jay:inspectPoint' || d.type === 'jay:inspectRect') {
2225
+ const result = d.type === 'jay:inspectPoint'
2226
+ ? __jayInspectPoint(d.x, d.y)
2227
+ : __jayInspectRect(d.x, d.y, d.width, d.height);
2228
+ window.parent.postMessage({
2229
+ type: 'jay:inspectResult',
2230
+ requestId: d.requestId,
2231
+ file: ${sourceFileLiteral},
2232
+ route: window.location.pathname,
2233
+ routePattern: ${routePatternLiteral},
2234
+ node: result ? result.node : null, // node.rect is the matched element's on-screen box
2235
+ stack: result ? result.stack : [],
2236
+ }, '*');
2237
+ }
2084
2238
  });
2085
2239
  } else {
2086
2240
  // Standalone: Alt+S / Option+S keyboard shortcut
@@ -2105,7 +2259,7 @@ function buildAutomationWrap(options, mode) {
2105
2259
  }
2106
2260
  const appendLine = appendDom ? `
2107
2261
  target.appendChild(wrapped.element.dom);` : "";
2108
- const freezeScript = buildFreezeScript(options.routePattern);
2262
+ const freezeScript = buildFreezeScript(options.routePattern, options.sourceFile);
2109
2263
  if (hasSlowViewState) {
2110
2264
  return `
2111
2265
  // Wrap with automation for dev tooling
@@ -4547,7 +4701,9 @@ function defaults(options) {
4547
4701
  pagesRoot: pagesRootFolder,
4548
4702
  buildFolder
4549
4703
  },
4550
- httpServer: options.httpServer
4704
+ httpServer: options.httpServer,
4705
+ allowedHosts: options.allowedHosts,
4706
+ sourceEditing: options.sourceEditing
4551
4707
  };
4552
4708
  }
4553
4709
  let _renderDevErrorPage = async (res, info) => {
@@ -4908,7 +5064,8 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
4908
5064
  initialPartsResult.val.headlessInstanceComponents,
4909
5065
  partKeys,
4910
5066
  pageParams,
4911
- pageProps
5067
+ pageProps,
5068
+ options.sourceEditing
4912
5069
  );
4913
5070
  timing?.recordSlowRender(Date.now() - slowStart);
4914
5071
  if (!preRenderResult) {
@@ -5056,7 +5213,8 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
5056
5213
  pluginsForPage,
5057
5214
  {
5058
5215
  enableAutomation: !options.disableAutomation,
5059
- routePattern: routeToExpressRoute(route)
5216
+ routePattern: routeToExpressRoute(route),
5217
+ sourceFile: path__default.relative(options.projectRootFolder, route.jayHtmlPath)
5060
5218
  }
5061
5219
  );
5062
5220
  if (options.buildFolder) {
@@ -5073,9 +5231,11 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
5073
5231
  }
5074
5232
  async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, routePattern, routeDir, slowViewState, timing, preLoadedContent, headTags, scripts, statusCode = 200) {
5075
5233
  let pageHtml;
5234
+ const sourceFile = path__default.relative(options.projectRootFolder, sourceJayHtmlPath);
5076
5235
  try {
5077
5236
  const jayHtmlDir = path__default.dirname(jayHtmlPath);
5078
- const jayHtmlContent = preLoadedContent ?? await fs__default.readFile(jayHtmlPath, "utf-8");
5237
+ const rawContent = preLoadedContent ?? await fs__default.readFile(jayHtmlPath, "utf-8");
5238
+ const jayHtmlContent = options.sourceEditing ? injectDevMarkers(rawContent) : rawContent;
5079
5239
  pageHtml = await generateSSRPageHtml(
5080
5240
  vite,
5081
5241
  jayHtmlContent,
@@ -5096,7 +5256,8 @@ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, page
5096
5256
  {
5097
5257
  enableAutomation: !options.disableAutomation,
5098
5258
  slowViewState,
5099
- routePattern
5259
+ routePattern,
5260
+ sourceFile
5100
5261
  },
5101
5262
  void 0,
5102
5263
  headTags,
@@ -5116,7 +5277,8 @@ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, page
5116
5277
  {
5117
5278
  enableAutomation: !options.disableAutomation,
5118
5279
  slowViewState,
5119
- routePattern
5280
+ routePattern,
5281
+ sourceFile
5120
5282
  }
5121
5283
  );
5122
5284
  }
@@ -5187,8 +5349,9 @@ async function handleFrozenRequest(vite, route, options, freezeStore, slowRender
5187
5349
  }
5188
5350
  timing?.end();
5189
5351
  }
5190
- async function preRenderJayHtml(route, slowViewState, headlessContracts, headlessInstanceComponents, partKeys = [], pageParams = {}, pageProps = { language: "en", url: "" }) {
5191
- const jayHtmlWithTemplates = await fs__default.readFile(route.jayHtmlPath, "utf-8");
5352
+ async function preRenderJayHtml(route, slowViewState, headlessContracts, headlessInstanceComponents, partKeys = [], pageParams = {}, pageProps = { language: "en", url: "" }, sourceEditing = false) {
5353
+ const rawJayHtml = await fs__default.readFile(route.jayHtmlPath, "utf-8");
5354
+ const jayHtmlWithTemplates = sourceEditing ? injectDevMarkers(rawJayHtml) : rawJayHtml;
5192
5355
  const sourceDir = path__default.dirname(route.jayHtmlPath);
5193
5356
  let contract;
5194
5357
  const contractPath = route.jayHtmlPath.replace(".jay-html", ".jay-contract");
@@ -5307,7 +5470,8 @@ async function mkDevServer(rawOptions) {
5307
5470
  base: publicBaseUrlPath,
5308
5471
  jayRollupConfig,
5309
5472
  logLevel: viteLogLevel,
5310
- httpServer: options.httpServer
5473
+ httpServer: options.httpServer,
5474
+ allowedHosts: options.allowedHosts
5311
5475
  });
5312
5476
  lifecycleManager.setViteServer(vite);
5313
5477
  await lifecycleManager.initialize();
@@ -5529,6 +5693,9 @@ function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder, projectR
5529
5693
  }
5530
5694
  };
5531
5695
  vite.watcher.on("change", (changedPath) => {
5696
+ if (/\.(ts|tsx|jay-contract)$/.test(changedPath)) {
5697
+ noteExportedTypesFileChanged(changedPath);
5698
+ }
5532
5699
  if (watchedFiles.has(changedPath)) {
5533
5700
  clearServerElementCache();
5534
5701
  cache.clear().then(() => {
@@ -5596,5 +5763,6 @@ export {
5596
5763
  createActionRouter,
5597
5764
  createViteForCli,
5598
5765
  createViteServer,
5599
- mkDevServer
5766
+ mkDevServer,
5767
+ resolveAllowedHosts
5600
5768
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/dev-server",
3
- "version": "0.25.4",
3
+ "version": "0.25.5",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
@@ -23,24 +23,24 @@
23
23
  "test:watch": "vitest"
24
24
  },
25
25
  "dependencies": {
26
- "@jay-framework/compiler-jay-stack": "^0.25.4",
27
- "@jay-framework/compiler-shared": "^0.25.4",
28
- "@jay-framework/component": "^0.25.4",
29
- "@jay-framework/fullstack-component": "^0.25.4",
30
- "@jay-framework/logger": "^0.25.4",
31
- "@jay-framework/runtime": "^0.25.4",
32
- "@jay-framework/stack-client-runtime": "^0.25.4",
33
- "@jay-framework/stack-route-scanner": "^0.25.4",
34
- "@jay-framework/stack-server-build": "^0.25.4",
35
- "@jay-framework/stack-server-runtime": "^0.25.4",
36
- "@jay-framework/view-state-merge": "^0.25.4",
26
+ "@jay-framework/compiler-jay-stack": "^0.25.5",
27
+ "@jay-framework/compiler-shared": "^0.25.5",
28
+ "@jay-framework/component": "^0.25.5",
29
+ "@jay-framework/fullstack-component": "^0.25.5",
30
+ "@jay-framework/logger": "^0.25.5",
31
+ "@jay-framework/runtime": "^0.25.5",
32
+ "@jay-framework/stack-client-runtime": "^0.25.5",
33
+ "@jay-framework/stack-route-scanner": "^0.25.5",
34
+ "@jay-framework/stack-server-build": "^0.25.5",
35
+ "@jay-framework/stack-server-runtime": "^0.25.5",
36
+ "@jay-framework/view-state-merge": "^0.25.5",
37
37
  "busboy": "^1.6.0",
38
38
  "vite": "^5.0.11"
39
39
  },
40
40
  "devDependencies": {
41
- "@jay-framework/dev-environment": "^0.25.4",
42
- "@jay-framework/jay-cli": "^0.25.4",
43
- "@jay-framework/stack-client-runtime": "^0.25.4",
41
+ "@jay-framework/dev-environment": "^0.25.5",
42
+ "@jay-framework/jay-cli": "^0.25.5",
43
+ "@jay-framework/stack-client-runtime": "^0.25.5",
44
44
  "@playwright/test": "^1.58.2",
45
45
  "@types/busboy": "^1.5.4",
46
46
  "@types/express": "^5.0.2",