@jay-framework/dev-server 0.25.5 → 0.26.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 +30 -1
  2. package/dist/index.js +103 -50
  3. package/package.json +17 -17
package/dist/index.d.ts CHANGED
@@ -186,6 +186,35 @@ declare class DevServerService {
186
186
  loadRouteParams(routePath: string): AsyncGenerator<Record<string, string>[]>;
187
187
  }
188
188
 
189
+ /**
190
+ * Percent-encode `[`/`]` in the path of asset URLs (`href`/`src`) in served HTML.
191
+ *
192
+ * Dynamic routes produce asset paths with literal brackets (e.g.
193
+ * `.../pre-rendered/products/[slug]/page.css`), whether Vite serves them as
194
+ * `/@fs/…` or root-relative. Bare brackets in a URL are rejected by some
195
+ * dev-machine hosting proxies — the browser then sees an empty MIME type
196
+ * ("Refused to apply style … MIME type ('')"). This mirrors the production fix
197
+ * (DL#144/R7), which encodes brackets in *all* frontend asset paths; it is
198
+ * deliberately NOT `/@fs/`-specific (production has no `@fs`).
199
+ *
200
+ * Encoding only the emitted URL is sufficient and safe: the dev servers that
201
+ * handle these requests — Vite (`decodeURI`) and serve-static/express
202
+ * (`decodeURIComponent`) — decode `%5B` back to `[`, and CSS/module loading
203
+ * resolves via the plugin's `meta.originId` (not the id), so the encoded URL
204
+ * still maps to the literal on-disk file. Only the path (before `?`) is touched,
205
+ * so query strings (e.g. `?a[]=1`) are intact. In practice only asset URLs carry
206
+ * brackets — resolved navigation hrefs do not — so real links are unaffected.
207
+ * Exported so the dev-serving test guards the real behavior rather than a copy.
208
+ */
209
+ declare function encodeBracketsInAssetUrls(html: string): string;
210
+ /**
211
+ * Filesystem-safe name for the per-route debug artifact, derived from the route
212
+ * path only (DL#229). The query string is dropped — otherwise a long query
213
+ * (e.g. an ~800-char auth token passed by a hosting proxy) pushes the filename
214
+ * past `NAME_MAX` (255) and `fs.writeFile` throws `ENAMETOOLONG`. Also strips
215
+ * non-fs-safe characters and length-caps as defense-in-depth. Pure → unit-tested.
216
+ */
217
+ declare function debugPageName(url: string | undefined): string;
189
218
  interface DevServerRoute {
190
219
  path: string;
191
220
  handler: RequestHandler;
@@ -324,4 +353,4 @@ declare function createViteForCli(options: {
324
353
  tsConfigFilePath?: string;
325
354
  }): Promise<ViteDevServer>;
326
355
 
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 };
356
+ 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, debugPageName, encodeBracketsInAssetUrls, mkDevServer, resolveAllowedHosts };
package/dist/index.js CHANGED
@@ -11,11 +11,11 @@ import { parseJayFile, JAY_IMPORT_RESOLVER, discoverHeadlessInstances, assignCoo
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";
14
- import fs__default$1 from "node:fs";
14
+ import fs__default from "node:fs";
15
15
  import { scanRoutes, createRoute, routeToExpressRoute } from "@jay-framework/stack-route-scanner";
16
- import { makePassthroughInstanceComponent, actionRegistry, scanPlugins, serializeHeadTags, mergeHeadTags, serializeScripts, discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, runLoadParams, DevSlowlyChangingPhase, preparePluginClientInits, registerService, getServiceRegistry, parseCookies, renderFastChangingData, getClientInitData, slowRenderInstances, normalizeInstancePropNames, resolveErrorPageToken, buildErrorViewState } from "@jay-framework/stack-server-runtime";
16
+ import { makePassthroughInstanceComponent, actionRegistry, scanPlugins, serializeHeadTags, mergeHeadTags, serializeScripts, discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, crossOriginErrorResponse, needsSameOriginCheck, isCrossOriginRequest, isSecureRequest, parseCookies, runLoadParams, DevSlowlyChangingPhase, preparePluginClientInits, registerService, getServiceRegistry, renderFastChangingData, getClientInitData, slowRenderInstances, normalizeInstancePropNames, resolveErrorPageToken, buildErrorViewState } from "@jay-framework/stack-server-runtime";
17
17
  import * as fs$1 from "node:fs/promises";
18
- import fs__default from "node:fs/promises";
18
+ import fs$1__default from "node:fs/promises";
19
19
  import jsBeautify from "js-beautify";
20
20
  import fs$2 from "fs";
21
21
  import path$1 from "path";
@@ -1052,7 +1052,9 @@ function extractActionFromExpression(node) {
1052
1052
  current = expr.expression;
1053
1053
  continue;
1054
1054
  }
1055
- if (c$1.isPropertyAccessExpression(expr) && ["withServices", "withCaching", "withHandler", "withTimeout"].includes(expr.name.text)) {
1055
+ if (c$1.isPropertyAccessExpression(expr) && ["withServices", "withCaching", "withHandler", "withTimeout", "withRequest"].includes(
1056
+ expr.name.text
1057
+ )) {
1056
1058
  current = expr.expression;
1057
1059
  continue;
1058
1060
  }
@@ -1637,7 +1639,7 @@ function resolveClientImportPath(route) {
1637
1639
  return route.compPath;
1638
1640
  }
1639
1641
  async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfig, options) {
1640
- const exists = await fs__default.access(route.compPath, fs__default.constants.F_OK).then(() => true).catch(() => false);
1642
+ const exists = await fs$1__default.access(route.compPath, fs$1__default.constants.F_OK).then(() => true).catch(() => false);
1641
1643
  const parts = [];
1642
1644
  const validations = [];
1643
1645
  if (exists) {
@@ -1657,7 +1659,7 @@ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfi
1657
1659
  }
1658
1660
  }
1659
1661
  const jayHtmlFilePath = options?.preRenderedPath ?? route.jayHtmlPath;
1660
- const jayHtmlSource = options?.preRenderedContent ?? (await fs__default.readFile(jayHtmlFilePath)).toString();
1662
+ const jayHtmlSource = options?.preRenderedContent ?? (await fs$1__default.readFile(jayHtmlFilePath)).toString();
1661
1663
  const fileName = path__default.basename(route.jayHtmlPath);
1662
1664
  const dirName = path__default.dirname(route.jayHtmlPath);
1663
1665
  const jayHtmlWithValidations = await parseJayFile(
@@ -2512,7 +2514,7 @@ async function generateFrozenPageHtml(vite, jayHtmlContent, jayHtmlFilename, jay
2512
2514
  if (cached.cssHref) {
2513
2515
  try {
2514
2516
  const cssPath = cached.cssHref.replace(/^\/@fs/, "").replace(/\?.*$/, "");
2515
- const cssContent = await fs__default.readFile(cssPath, "utf-8");
2517
+ const cssContent = await fs$1__default.readFile(cssPath, "utf-8");
2516
2518
  inlineCss = `<style>${cssContent}</style>`;
2517
2519
  } catch {
2518
2520
  inlineCss = `<link rel="stylesheet" href="${cached.cssHref}" />`;
@@ -2591,11 +2593,11 @@ async function compileAndLoadServerElement(vite, jayHtmlContent, jayHtmlFilename
2591
2593
  generateServerElementFile(parsedJayFile, serverElementOptions)
2592
2594
  );
2593
2595
  const serverElementDir = path__default.join(buildFolder, "pre-rendered", routeDir);
2594
- await fs__default.mkdir(serverElementDir, { recursive: true });
2596
+ await fs$1__default.mkdir(serverElementDir, { recursive: true });
2595
2597
  const adjustedCode = rebaseRelativeImports(serverElementCode, jayHtmlDir, serverElementDir);
2596
2598
  const serverElementFilename = jayHtmlFilename.replace(".jay-html", ".server-element.ts");
2597
2599
  const serverElementPath = path__default.join(serverElementDir, serverElementFilename);
2598
- await fs__default.writeFile(serverElementPath, adjustedCode, "utf-8");
2600
+ await fs$1__default.writeFile(serverElementPath, adjustedCode, "utf-8");
2599
2601
  const existingModule = vite.moduleGraph.getModuleById(serverElementPath);
2600
2602
  if (existingModule) {
2601
2603
  vite.moduleGraph.invalidateModule(existingModule);
@@ -2607,7 +2609,7 @@ async function compileAndLoadServerElement(vite, jayHtmlContent, jayHtmlFilename
2607
2609
  if (parsedJayFile.css) {
2608
2610
  const cssFilename = jayHtmlFilename.replace(".jay-html", ".css");
2609
2611
  const cssPath = path__default.join(serverElementDir, cssFilename);
2610
- await fs__default.writeFile(cssPath, parsedJayFile.css, "utf-8");
2612
+ await fs$1__default.writeFile(cssPath, parsedJayFile.css, "utf-8");
2611
2613
  const cssModules = vite.moduleGraph.getModulesByFile(cssPath);
2612
2614
  if (cssModules) {
2613
2615
  for (const mod of cssModules) {
@@ -3684,7 +3686,7 @@ class SlowRenderCache {
3684
3686
  const preRenderedPath = this.computeCachePath(jayHtmlPath, params, routeDir);
3685
3687
  let fileContent;
3686
3688
  try {
3687
- fileContent = await fs__default.readFile(preRenderedPath, "utf-8");
3689
+ fileContent = await fs$1__default.readFile(preRenderedPath, "utf-8");
3688
3690
  } catch {
3689
3691
  return void 0;
3690
3692
  }
@@ -3712,8 +3714,8 @@ class SlowRenderCache {
3712
3714
  carryForward,
3713
3715
  jayHtmlPath
3714
3716
  );
3715
- await fs__default.mkdir(path__default.dirname(preRenderedPath), { recursive: true });
3716
- await fs__default.writeFile(preRenderedPath, fileContent, "utf-8");
3717
+ await fs$1__default.mkdir(path__default.dirname(preRenderedPath), { recursive: true });
3718
+ await fs$1__default.writeFile(preRenderedPath, fileContent, "utf-8");
3717
3719
  this.trackFile(jayHtmlPath, preRenderedPath);
3718
3720
  return {
3719
3721
  preRenderedPath,
@@ -3729,7 +3731,7 @@ class SlowRenderCache {
3729
3731
  async has(jayHtmlPath, params, routeDir) {
3730
3732
  const preRenderedPath = this.computeCachePath(jayHtmlPath, params, routeDir);
3731
3733
  try {
3732
- await fs__default.access(preRenderedPath);
3734
+ await fs$1__default.access(preRenderedPath);
3733
3735
  return true;
3734
3736
  } catch {
3735
3737
  return false;
@@ -3744,7 +3746,7 @@ class SlowRenderCache {
3744
3746
  if (files) {
3745
3747
  for (const filePath of files) {
3746
3748
  try {
3747
- await fs__default.unlink(filePath);
3749
+ await fs$1__default.unlink(filePath);
3748
3750
  } catch {
3749
3751
  }
3750
3752
  }
@@ -3781,14 +3783,14 @@ class SlowRenderCache {
3781
3783
  for (const files of this.pathToFiles.values()) {
3782
3784
  for (const filePath of files) {
3783
3785
  try {
3784
- await fs__default.unlink(filePath);
3786
+ await fs$1__default.unlink(filePath);
3785
3787
  } catch {
3786
3788
  }
3787
3789
  }
3788
3790
  }
3789
3791
  this.pathToFiles.clear();
3790
3792
  try {
3791
- await fs__default.rm(this.cacheDir, { recursive: true, force: true });
3793
+ await fs$1__default.rm(this.cacheDir, { recursive: true, force: true });
3792
3794
  } catch {
3793
3795
  }
3794
3796
  }
@@ -3839,11 +3841,11 @@ class SlowRenderCache {
3839
3841
  const basename = path__default.basename(jayHtmlPath, ".jay-html");
3840
3842
  const cacheSubDir = path__default.join(this.cacheDir, dir);
3841
3843
  try {
3842
- const files = await fs__default.readdir(cacheSubDir);
3844
+ const files = await fs$1__default.readdir(cacheSubDir);
3843
3845
  for (const file of files) {
3844
3846
  if (file.startsWith(basename)) {
3845
3847
  try {
3846
- await fs__default.unlink(path__default.join(cacheSubDir, file));
3848
+ await fs$1__default.unlink(path__default.join(cacheSubDir, file));
3847
3849
  } catch {
3848
3850
  }
3849
3851
  }
@@ -4159,6 +4161,10 @@ function createActionRouter(options) {
4159
4161
  });
4160
4162
  return;
4161
4163
  }
4164
+ if (isRejectedCrossOrigin(action, req)) {
4165
+ res.status(403).json(crossOriginErrorResponse(actionName));
4166
+ return;
4167
+ }
4162
4168
  let input;
4163
4169
  try {
4164
4170
  if (requestMethod === "GET") {
@@ -4190,11 +4196,15 @@ function createActionRouter(options) {
4190
4196
  const cleanup = () => {
4191
4197
  if (tempDir) cleanupTempDir(tempDir);
4192
4198
  };
4199
+ const incoming = action.needsRequest ? readIncomingRequest(req) : void 0;
4200
+ if (action.needsRequest) {
4201
+ res.set("Cache-Control", registry.getCacheHeaders(actionName));
4202
+ }
4193
4203
  if (registry.isStreaming(actionName)) {
4194
4204
  res.setHeader("Content-Type", "application/x-ndjson");
4195
4205
  res.setHeader("Transfer-Encoding", "chunked");
4196
4206
  try {
4197
- const generator = registry.executeStream(actionName, input);
4207
+ const generator = registry.executeStream(actionName, input, incoming);
4198
4208
  for await (const chunk of generator) {
4199
4209
  res.write(JSON.stringify({ chunk }) + "\n");
4200
4210
  }
@@ -4207,7 +4217,7 @@ function createActionRouter(options) {
4207
4217
  timing?.end();
4208
4218
  return;
4209
4219
  }
4210
- const result = await registry.execute(actionName, input);
4220
+ const result = await registry.execute(actionName, input, incoming);
4211
4221
  cleanup();
4212
4222
  if (requestMethod === "GET" && result.success) {
4213
4223
  const cacheHeaders = registry.getCacheHeaders(actionName);
@@ -4216,6 +4226,9 @@ function createActionRouter(options) {
4216
4226
  }
4217
4227
  }
4218
4228
  if (result.success) {
4229
+ for (const setCookie of result.setCookies ?? []) {
4230
+ res.append("Set-Cookie", setCookie);
4231
+ }
4219
4232
  res.status(200).json({
4220
4233
  success: true,
4221
4234
  data: result.data
@@ -4230,6 +4243,19 @@ function createActionRouter(options) {
4230
4243
  timing?.end();
4231
4244
  };
4232
4245
  }
4246
+ function isRejectedCrossOrigin(action, req) {
4247
+ return needsSameOriginCheck(action) && isCrossOriginRequest({
4248
+ secFetchSite: req.headers["sec-fetch-site"],
4249
+ origin: req.headers.origin,
4250
+ requestOrigin: `${req.protocol}://${req.headers.host}`
4251
+ });
4252
+ }
4253
+ function readIncomingRequest(req) {
4254
+ return {
4255
+ cookies: parseCookies(req.headers.cookie),
4256
+ secure: isSecureRequest(req.protocol, req.headers["x-forwarded-proto"])
4257
+ };
4258
+ }
4233
4259
  function getStatusCodeForError(code, isActionError) {
4234
4260
  if (isActionError) {
4235
4261
  return 422;
@@ -4360,6 +4386,11 @@ function actionBodyParser(options) {
4360
4386
  next();
4361
4387
  return;
4362
4388
  }
4389
+ const routedAction = registryToUse.get(req.path.slice(ACTION_ENDPOINT_BASE.length + 1));
4390
+ if (routedAction && isRejectedCrossOrigin(routedAction, req)) {
4391
+ next();
4392
+ return;
4393
+ }
4363
4394
  const contentType = req.headers["content-type"] || "";
4364
4395
  if (contentType.startsWith("multipart/form-data")) {
4365
4396
  const actionName = req.path.slice(ACTION_ENDPOINT_BASE.length + 1);
@@ -4556,6 +4587,30 @@ class DevServerService {
4556
4587
  yield* runLoadParams(loaded.val.parts);
4557
4588
  }
4558
4589
  }
4590
+ function encodeBracketsInAssetUrls(html) {
4591
+ return html.replace(/\b(href|src)="([^"]*)"/g, (whole, attr, value) => {
4592
+ const q = value.indexOf("?");
4593
+ const pathPart = q === -1 ? value : value.slice(0, q);
4594
+ if (!pathPart.includes("[") && !pathPart.includes("]")) return whole;
4595
+ const encodedPath = pathPart.replace(/\[/g, "%5B").replace(/\]/g, "%5D");
4596
+ return `${attr}="${encodedPath}${q === -1 ? "" : value.slice(q)}"`;
4597
+ });
4598
+ }
4599
+ function debugPageName(url) {
4600
+ const routePath = (url || "/").split("?")[0];
4601
+ if (routePath === "/" || routePath === "") return "index";
4602
+ return routePath.replace(/^\//, "").replace(/\//g, "-").replace(/[^\w.-]/g, "_").slice(0, 200) || "index";
4603
+ }
4604
+ async function writeDebugPage(options, url, html) {
4605
+ if (options.logLevel !== "verbose" || !options.buildFolder) return;
4606
+ try {
4607
+ const clientScriptDir = path__default.join(options.buildFolder, "debug", "client-entry");
4608
+ await fs$1__default.mkdir(clientScriptDir, { recursive: true });
4609
+ await fs$1__default.writeFile(path__default.join(clientScriptDir, `${debugPageName(url)}.html`), html, "utf-8");
4610
+ } catch (e2) {
4611
+ getLogger().info(`[dev] debug page write skipped: ${e2.message}`);
4612
+ }
4613
+ }
4559
4614
  let _watchLinkedFiles = () => {
4560
4615
  };
4561
4616
  async function initRoutes(pagesBaseFolder) {
@@ -4623,7 +4678,7 @@ function resolvePluginExport(pluginPath, exportSubpath) {
4623
4678
  const normalized = exportSubpath.replace(/^\.\//, "");
4624
4679
  const packageJsonPath = path__default.join(pluginPath, "package.json");
4625
4680
  try {
4626
- const packageJson = JSON.parse(fs__default$1.readFileSync(packageJsonPath, "utf-8"));
4681
+ const packageJson = JSON.parse(fs__default.readFileSync(packageJsonPath, "utf-8"));
4627
4682
  if (packageJson.exports) {
4628
4683
  const exportKey = "./" + normalized;
4629
4684
  const exportValue = packageJson.exports[exportKey];
@@ -4640,7 +4695,7 @@ function resolvePluginExport(pluginPath, exportSubpath) {
4640
4695
  for (const dir of ["dist", "lib", ""]) {
4641
4696
  const candidate = path__default.join(pluginPath, dir, normalized);
4642
4697
  try {
4643
- fs__default$1.accessSync(candidate);
4698
+ fs__default.accessSync(candidate);
4644
4699
  return candidate;
4645
4700
  } catch {
4646
4701
  }
@@ -4649,14 +4704,14 @@ function resolvePluginExport(pluginPath, exportSubpath) {
4649
4704
  }
4650
4705
  function resolvePluginModule(plugin, exportKey = ".") {
4651
4706
  const pkgJsonPath = path__default.join(plugin.pluginPath, "package.json");
4652
- if (fs__default$1.existsSync(pkgJsonPath)) {
4707
+ if (fs__default.existsSync(pkgJsonPath)) {
4653
4708
  try {
4654
- const pkg = JSON.parse(fs__default$1.readFileSync(pkgJsonPath, "utf-8"));
4709
+ const pkg = JSON.parse(fs__default.readFileSync(pkgJsonPath, "utf-8"));
4655
4710
  const mainExport = pkg.exports?.[exportKey];
4656
4711
  const mainPath = typeof mainExport === "string" ? mainExport : mainExport?.default || mainExport?.import || pkg.main;
4657
4712
  if (mainPath) {
4658
4713
  const resolved = path__default.join(plugin.pluginPath, mainPath);
4659
- if (fs__default$1.existsSync(resolved)) return resolved;
4714
+ if (fs__default.existsSync(resolved)) return resolved;
4660
4715
  }
4661
4716
  } catch {
4662
4717
  }
@@ -4665,7 +4720,7 @@ function resolvePluginModule(plugin, exportKey = ".") {
4665
4720
  for (const ext of [".ts", ".js", "/index.ts", "/index.js"]) {
4666
4721
  const candidate = path__default.join(plugin.pluginPath, modulePath + ext);
4667
4722
  try {
4668
- fs__default$1.accessSync(candidate);
4723
+ fs__default.accessSync(candidate);
4669
4724
  return candidate;
4670
4725
  } catch {
4671
4726
  }
@@ -4673,7 +4728,7 @@ function resolvePluginModule(plugin, exportKey = ".") {
4673
4728
  for (const ext of [".ts", ".js"]) {
4674
4729
  const candidate = path__default.join(plugin.pluginPath, "lib", path__default.basename(modulePath) + ext);
4675
4730
  try {
4676
- fs__default$1.accessSync(candidate);
4731
+ fs__default.accessSync(candidate);
4677
4732
  return candidate;
4678
4733
  } catch {
4679
4734
  }
@@ -5217,14 +5272,11 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
5217
5272
  sourceFile: path__default.relative(options.projectRootFolder, route.jayHtmlPath)
5218
5273
  }
5219
5274
  );
5220
- if (options.buildFolder) {
5221
- const pageName = !url || url === "/" ? "index" : url.replace(/^\//, "").replace(/\//g, "-");
5222
- const clientScriptDir = path__default.join(options.buildFolder, "debug", "client-entry");
5223
- await fs__default.mkdir(clientScriptDir, { recursive: true });
5224
- await fs__default.writeFile(path__default.join(clientScriptDir, `${pageName}.html`), pageHtml, "utf-8");
5225
- }
5275
+ await writeDebugPage(options, url, pageHtml);
5226
5276
  const viteStart = Date.now();
5227
- const compiledPageHtml = await vite.transformIndexHtml(!!url ? url : "/", pageHtml);
5277
+ const compiledPageHtml = encodeBracketsInAssetUrls(
5278
+ await vite.transformIndexHtml(url?.split("?")[0] || "/", pageHtml)
5279
+ );
5228
5280
  timing?.recordViteClient(Date.now() - viteStart);
5229
5281
  res.status(200).set({ "Content-Type": "text/html" }).send(compiledPageHtml);
5230
5282
  timing?.end();
@@ -5234,7 +5286,7 @@ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, page
5234
5286
  const sourceFile = path__default.relative(options.projectRootFolder, sourceJayHtmlPath);
5235
5287
  try {
5236
5288
  const jayHtmlDir = path__default.dirname(jayHtmlPath);
5237
- const rawContent = preLoadedContent ?? await fs__default.readFile(jayHtmlPath, "utf-8");
5289
+ const rawContent = preLoadedContent ?? await fs$1__default.readFile(jayHtmlPath, "utf-8");
5238
5290
  const jayHtmlContent = options.sourceEditing ? injectDevMarkers(rawContent) : rawContent;
5239
5291
  pageHtml = await generateSSRPageHtml(
5240
5292
  vite,
@@ -5282,19 +5334,16 @@ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, page
5282
5334
  }
5283
5335
  );
5284
5336
  }
5285
- if (options.buildFolder) {
5286
- const pageName = !url || url === "/" ? "index" : url.replace(/^\//, "").replace(/\//g, "-");
5287
- const clientScriptDir = path__default.join(options.buildFolder, "debug", "client-entry");
5288
- await fs__default.mkdir(clientScriptDir, { recursive: true });
5289
- await fs__default.writeFile(path__default.join(clientScriptDir, `${pageName}.html`), pageHtml, "utf-8");
5290
- }
5337
+ await writeDebugPage(options, url, pageHtml);
5291
5338
  for (const [id, mod] of vite.moduleGraph.idToModuleMap) {
5292
5339
  if (id.includes("html-proxy")) {
5293
5340
  vite.moduleGraph.invalidateModule(mod);
5294
5341
  }
5295
5342
  }
5296
5343
  const viteStart = Date.now();
5297
- const compiledPageHtml = await vite.transformIndexHtml(!!url ? url : "/", pageHtml);
5344
+ const compiledPageHtml = encodeBracketsInAssetUrls(
5345
+ await vite.transformIndexHtml(url?.split("?")[0] || "/", pageHtml)
5346
+ );
5298
5347
  timing?.recordViteClient(Date.now() - viteStart);
5299
5348
  res.status(statusCode).set({ "Content-Type": "text/html" }).send(compiledPageHtml);
5300
5349
  timing?.end();
@@ -5313,7 +5362,7 @@ async function handleFrozenRequest(vite, route, options, freezeStore, slowRender
5313
5362
  const routeDir = getRouteDir(route);
5314
5363
  const cachedEntry = await slowRenderCache.get(route.jayHtmlPath, pageParams, routeDir);
5315
5364
  const jayHtmlPath = cachedEntry?.preRenderedPath ?? route.jayHtmlPath;
5316
- const jayHtmlContent = cachedEntry?.preRenderedContent ?? await fs__default.readFile(jayHtmlPath, "utf-8");
5365
+ const jayHtmlContent = cachedEntry?.preRenderedContent ?? await fs$1__default.readFile(jayHtmlPath, "utf-8");
5317
5366
  const jayHtmlFilename = path__default.basename(jayHtmlPath);
5318
5367
  const jayHtmlDir = path__default.dirname(jayHtmlPath);
5319
5368
  const sourceDir = path__default.dirname(route.jayHtmlPath);
@@ -5338,7 +5387,9 @@ async function handleFrozenRequest(vite, route, options, freezeStore, slowRender
5338
5387
  headers["Access-Control-Allow-Origin"] = "*";
5339
5388
  }
5340
5389
  if (injectDevHmr) {
5341
- const compiledHtml = await vite.transformIndexHtml(url || "/", html);
5390
+ const compiledHtml = encodeBracketsInAssetUrls(
5391
+ await vite.transformIndexHtml(url?.split("?")[0] || "/", html)
5392
+ );
5342
5393
  res.status(200).set(headers).send(compiledHtml);
5343
5394
  } else {
5344
5395
  res.status(200).set(headers).send(html);
@@ -5350,13 +5401,13 @@ async function handleFrozenRequest(vite, route, options, freezeStore, slowRender
5350
5401
  timing?.end();
5351
5402
  }
5352
5403
  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");
5404
+ const rawJayHtml = await fs$1__default.readFile(route.jayHtmlPath, "utf-8");
5354
5405
  const jayHtmlWithTemplates = sourceEditing ? injectDevMarkers(rawJayHtml) : rawJayHtml;
5355
5406
  const sourceDir = path__default.dirname(route.jayHtmlPath);
5356
5407
  let contract;
5357
5408
  const contractPath = route.jayHtmlPath.replace(".jay-html", ".jay-contract");
5358
5409
  try {
5359
- const contractContent = await fs__default.readFile(contractPath, "utf-8");
5410
+ const contractContent = await fs$1__default.readFile(contractPath, "utf-8");
5360
5411
  const parseResult = parseContract(contractContent, path__default.basename(contractPath));
5361
5412
  if (parseResult.val) contract = parseResult.val;
5362
5413
  } catch {
@@ -5453,10 +5504,10 @@ async function mkDevServer(rawOptions) {
5453
5504
  const { publicBaseUrlPath, pagesRootFolder, projectRootFolder, buildFolder, jayRollupConfig } = options;
5454
5505
  if (buildFolder) {
5455
5506
  try {
5456
- const entries = await fs__default.readdir(buildFolder).catch(() => []);
5507
+ const entries = await fs$1__default.readdir(buildFolder).catch(() => []);
5457
5508
  for (const entry of entries) {
5458
5509
  if (entry === "freezes") continue;
5459
- await fs__default.rm(path__default.join(buildFolder, entry), { recursive: true, force: true });
5510
+ await fs$1__default.rm(path__default.join(buildFolder, entry), { recursive: true, force: true });
5460
5511
  }
5461
5512
  } catch {
5462
5513
  }
@@ -5533,7 +5584,7 @@ async function mkDevServer(rawOptions) {
5533
5584
  for (let i = devServerRoutes.length - 1; i >= 0; i--) {
5534
5585
  const route = devServerRoutes[i];
5535
5586
  const fsPath = route.fsRoute.jayHtmlPath;
5536
- const stillOnDisk = scannedRawRoutes.has(route.fsRoute.rawRoute) && fs__default$1.existsSync(fsPath);
5587
+ const stillOnDisk = scannedRawRoutes.has(route.fsRoute.rawRoute) && fs__default.existsSync(fsPath);
5537
5588
  if (!stillOnDisk) {
5538
5589
  devServerRoutes.splice(i, 1);
5539
5590
  }
@@ -5763,6 +5814,8 @@ export {
5763
5814
  createActionRouter,
5764
5815
  createViteForCli,
5765
5816
  createViteServer,
5817
+ debugPageName,
5818
+ encodeBracketsInAssetUrls,
5766
5819
  mkDevServer,
5767
5820
  resolveAllowedHosts
5768
5821
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/dev-server",
3
- "version": "0.25.5",
3
+ "version": "0.26.0",
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.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",
26
+ "@jay-framework/compiler-jay-stack": "^0.26.0",
27
+ "@jay-framework/compiler-shared": "^0.26.0",
28
+ "@jay-framework/component": "^0.26.0",
29
+ "@jay-framework/fullstack-component": "^0.26.0",
30
+ "@jay-framework/logger": "^0.26.0",
31
+ "@jay-framework/runtime": "^0.26.0",
32
+ "@jay-framework/stack-client-runtime": "^0.26.0",
33
+ "@jay-framework/stack-route-scanner": "^0.26.0",
34
+ "@jay-framework/stack-server-build": "^0.26.0",
35
+ "@jay-framework/stack-server-runtime": "^0.26.0",
36
+ "@jay-framework/view-state-merge": "^0.26.0",
37
37
  "busboy": "^1.6.0",
38
- "vite": "^5.0.11"
38
+ "vite": "^6.4.4"
39
39
  },
40
40
  "devDependencies": {
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",
41
+ "@jay-framework/dev-environment": "^0.26.0",
42
+ "@jay-framework/jay-cli": "^0.26.0",
43
+ "@jay-framework/stack-client-runtime": "^0.26.0",
44
44
  "@playwright/test": "^1.58.2",
45
45
  "@types/busboy": "^1.5.4",
46
46
  "@types/express": "^5.0.2",
@@ -52,6 +52,6 @@
52
52
  "rimraf": "^5.0.5",
53
53
  "tsup": "^8.0.1",
54
54
  "typescript": "^5.3.3",
55
- "vitest": "^1.2.1"
55
+ "vitest": "^4.1.11"
56
56
  }
57
57
  }