@nuxt/nitro-server 3.21.0 → 3.21.2

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/README.md CHANGED
@@ -1,15 +1,15 @@
1
- [![Nuxt banner](https://github.com/nuxt/nuxt/blob/main/.github/assets/banner.svg)](https://nuxt.com)
1
+ <a href="https://nuxt.com"><img width="830" height="213" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/banner.svg" alt="Nuxt banner"></a>
2
2
 
3
3
  # Nuxt
4
4
 
5
5
  <p>
6
- <a href="https://www.npmjs.com/package/nuxt"><img src="https://img.shields.io/npm/v/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a>
7
- <a href="https://www.npmjs.com/package/nuxt"><img src="https://img.shields.io/npm/dm/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"></a>
6
+ <a href="https://npmx.dev/package/nuxt"><img src="https://npmx.dev/api/registry/badge/version/nuxt" alt="Version"></a>
7
+ <a href="https://npmx.dev/package/nuxt"><img src="https://npmx.dev/api/registry/badge/downloads/nuxt" alt="Downloads"></a>
8
8
  <a href="https://github.com/nuxt/nuxt/blob/main/LICENSE"><img src="https://img.shields.io/github/license/nuxt/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="License"></a>
9
9
  <a href="https://nuxt.com/modules"><img src="https://img.shields.io/badge/dynamic/json?url=https://nuxt.com/api/v1/modules&query=$.stats.modules&label=Modules&style=flat&colorA=18181B&colorB=28CF8D" alt="Modules"></a>
10
10
  <a href="https://nuxt.com"><img src="https://img.shields.io/badge/Nuxt%20Docs-18181B?logo=nuxt" alt="Website"></a>
11
11
  <a href="https://chat.nuxt.dev"><img src="https://img.shields.io/badge/Nuxt%20Discord-18181B?logo=discord" alt="Discord"></a>
12
- <a href="https://securityscorecards.dev/"><img src="https://api.securityscorecards.dev/projects/github.com/nuxt/nuxt/badge" alt="Nuxt openssf scorecard score"></a>
12
+ <a href="https://securityscorecards.dev/viewer/?uri=github.com/nuxt/nuxt"><img src="https://api.securityscorecards.dev/projects/github.com/nuxt/nuxt/badge" alt="Nuxt openssf scorecard score"></a>
13
13
  <a href="https://deepwiki.com/nuxt/nuxt"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
14
14
  </p>
15
15
 
@@ -111,7 +111,7 @@ Follow the docs to [Set Up Your Local Development Environment](https://nuxt.com/
111
111
  ## <a name="follow-us">🔗 Follow Us</a>
112
112
 
113
113
  <p valign="center">
114
- <a href="https://go.nuxt.com/discord"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/discord.svg" alt="Discord"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/x"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/twitter.svg" alt="Twitter"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/github"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/github.svg" alt="GitHub"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/bluesky"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/bluesky.svg" alt="Bluesky"></a>
114
+ <a href="https://go.nuxt.com/discord"><img width="20" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/discord.svg" alt="Discord"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/x"><img width="20" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/twitter.svg" alt="Twitter"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/github"><img width="20" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/github.svg" alt="GitHub"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/bluesky"><img width="20" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/bluesky.svg" alt="Bluesky"></a>
115
115
  </p>
116
116
 
117
117
  ## <a name="license">⚖️ License</a>
@@ -0,0 +1,11 @@
1
+ # Licenses of Bundled Dependencies
2
+
3
+ The published artifact additionally contains code with the following licenses:
4
+ MIT
5
+
6
+ # Bundled Dependencies
7
+
8
+ ## nuxt
9
+
10
+ License: MIT
11
+ Repository: https://github.com/nuxt/nuxt
package/dist/index.d.mts CHANGED
@@ -246,7 +246,7 @@ declare module "nuxt/schema" {
246
246
  * @example
247
247
  * ```js
248
248
  * serverHandlers: [
249
- * { route: '/path/foo/**:name', handler: '~/server/foohandler.ts' }
249
+ * { route: '/path/foo/**:name', handler: '#server/foohandler.ts' }
250
250
  * ]
251
251
  * ```
252
252
  */
package/dist/index.mjs CHANGED
@@ -19,10 +19,8 @@ import { isWindows } from "std-env";
19
19
  import { ImpoundPlugin } from "impound";
20
20
  import { resolveModulePath } from "exsolve";
21
21
  import { runtimeDependencies } from "nitropack/runtime/meta";
22
-
23
22
  //#region package.json
24
- var version = "3.21.0";
25
-
23
+ var version = "3.21.2";
26
24
  //#endregion
27
25
  //#region src/utils.ts
28
26
  function toArray(value) {
@@ -31,13 +29,11 @@ function toArray(value) {
31
29
  let _distDir = dirname(fileURLToPath(import.meta.url));
32
30
  if (/(?:chunks|shared)$/.test(_distDir)) _distDir = dirname(_distDir);
33
31
  const distDir = _distDir;
34
-
35
32
  //#endregion
36
33
  //#region ../ui-templates/dist/templates/spa-loading-icon.ts
37
34
  const template = () => {
38
35
  return "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"80\" fill=\"none\" class=\"nuxt-spa-loading\" viewBox=\"0 0 37 25\"><path d=\"M24.236 22.006h10.742L25.563 5.822l-8.979 14.31a4 4 0 0 1-3.388 1.874H2.978l11.631-20 5.897 10.567\"/></svg><style>.nuxt-spa-loading{left:50%;position:fixed;top:50%;transform:translate(-50%,-50%)}.nuxt-spa-loading>path{animation:nuxt-spa-loading-move 3s linear infinite;fill:none;stroke:#00dc82;stroke-dasharray:128;stroke-dashoffset:128;stroke-linecap:round;stroke-linejoin:round;stroke-width:4px}@keyframes nuxt-spa-loading-move{to{stroke-dashoffset:-128}}</style>";
39
36
  };
40
-
41
37
  //#endregion
42
38
  //#region ../nuxt/src/core/plugins/import-protection.ts
43
39
  function createImportProtectionPatterns(nuxt, options) {
@@ -46,7 +42,7 @@ function createImportProtectionPatterns(nuxt, options) {
46
42
  patterns.push([/^(nuxt|nuxt3|nuxt-nightly)$/, `\`nuxt\`, or \`nuxt-nightly\` cannot be imported directly in ${context}.` + (options.context === "nuxt-app" ? " Instead, import runtime Nuxt composables from `#app` or `#imports`." : "")]);
47
43
  patterns.push([/^((~|~~|@|@@)?\/)?nuxt\.config(\.|$)/, "Importing directly from a `nuxt.config` file is not allowed. Instead, use runtime config or a module."]);
48
44
  patterns.push([/(^|node_modules\/)@vue\/composition-api/]);
49
- for (const mod of nuxt.options._installedModules) if (mod.entryPath) patterns.push([/* @__PURE__ */ new RegExp(`^${escapeRE(mod.entryPath)}$`), "Importing directly from module entry-points is not allowed."]);
45
+ for (const mod of nuxt.options._installedModules) if (mod.entryPath) patterns.push([new RegExp(`^${escapeRE(mod.entryPath)}$`), "Importing directly from module entry-points is not allowed."]);
50
46
  for (const i of [
51
47
  /(^|node_modules\/)@nuxt\/(cli|kit|test-utils)/,
52
48
  /(^|node_modules\/)nuxi/,
@@ -55,7 +51,7 @@ function createImportProtectionPatterns(nuxt, options) {
55
51
  ]) patterns.push([i, `This module cannot be imported in ${context}.`]);
56
52
  if (options.context === "nitro-app" || options.context === "shared") for (const i of ["#app", /^#build(\/|$)/]) patterns.push([i, `Vue app aliases are not allowed in ${context}.`]);
57
53
  if (options.context === "nuxt-app" || options.context === "shared") {
58
- patterns.push([/* @__PURE__ */ new RegExp(escapeRE(relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, nuxt.options.serverDir || "server"))) + "\\/(api|routes|middleware|plugins)\\/"), `Importing from server is not allowed in ${context}.`]);
54
+ patterns.push([new RegExp(escapeRE(relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, nuxt.options.serverDir || "server"))) + "\\/(api|routes|middleware|plugins)\\/"), `Importing from server is not allowed in ${context}.`]);
59
55
  patterns.push([/^#server(\/|$)/, `Server aliases are not allowed in ${context}.`]);
60
56
  }
61
57
  return patterns;
@@ -65,7 +61,6 @@ const contextFlags = {
65
61
  "nuxt-app": "the Vue part of your app",
66
62
  "shared": "the #shared directory"
67
63
  };
68
-
69
64
  //#endregion
70
65
  //#region src/templates.ts
71
66
  const nitroSchemaTemplate = {
@@ -77,12 +72,9 @@ const nitroSchemaTemplate = {
77
72
  references,
78
73
  declarations
79
74
  });
80
- const sourceDir = join(nuxt.options.buildDir, "types");
75
+ const typesDir = join(nuxt.options.buildDir, "types");
81
76
  return `
82
- ${[...references.map((ref) => {
83
- if ("path" in ref && isAbsolute(ref.path)) ref.path = relative(sourceDir, ref.path);
84
- return `/// <reference ${renderAttrs(ref)} />`;
85
- }), ...declarations].join("\n")}
77
+ ${[...references.map((ref) => renderReference(ref, typesDir)), ...declarations].join("\n")}
86
78
  /// <reference path="./schema.d.ts" />
87
79
 
88
80
  import type { RuntimeConfig } from 'nuxt/schema'
@@ -119,15 +111,9 @@ declare module 'nitropack' {
119
111
  `;
120
112
  }
121
113
  };
122
- function renderAttr(key, value) {
123
- return value ? `${key}="${value}"` : "";
124
- }
125
- function renderAttrs(obj) {
126
- const attrs = [];
127
- for (const key in obj) attrs.push(renderAttr(key, obj[key]));
128
- return attrs.join(" ");
114
+ function renderReference(ref, baseDir) {
115
+ return `/// <reference ${"path" in ref ? `path="${isAbsolute(ref.path) ? relative(baseDir, ref.path) : ref.path}"` : `types="${ref.types}"`} />`;
129
116
  }
130
-
131
117
  //#endregion
132
118
  //#region src/index.ts
133
119
  const logLevelMapReverse = {
@@ -146,7 +132,7 @@ async function bundle(nuxt) {
146
132
  }
147
133
  const layerPublicAssetsDirs = [];
148
134
  for (const dirs of layerDirs) if (existsSync(dirs.public)) layerPublicAssetsDirs.push({ dir: dirs.public });
149
- const excludePattern = excludePaths.length ? [/* @__PURE__ */ new RegExp(`node_modules\\/(?!${excludePaths.join("|")})`)] : [/node_modules/];
135
+ const excludePattern = excludePaths.length ? [new RegExp(`node_modules\\/(?!${excludePaths.join("|")})`)] : [/node_modules/];
150
136
  const rootDirWithSlash = withTrailingSlash(nuxt.options.rootDir);
151
137
  const moduleEntryPaths = [];
152
138
  for (const m of nuxt.options._installedModules) {
@@ -376,7 +362,8 @@ async function bundle(nuxt) {
376
362
  "appLayout",
377
363
  "cache",
378
364
  "isr",
379
- "swr"
365
+ "swr",
366
+ "ssr"
380
367
  ];
381
368
  function getRouteRulesRouter() {
382
369
  const routeRulesRouter = createRouter();
@@ -425,18 +412,19 @@ async function bundle(nuxt) {
425
412
  }
426
413
  });
427
414
  if (nuxt.options.experimental.payloadExtraction) {
428
- if (nuxt.options.dev) nuxt.hook("nitro:config", (nitroConfig$1) => {
429
- nitroConfig$1.prerender ||= {};
430
- nitroConfig$1.prerender.routes ||= [];
431
- nitroConfig$1.routeRules ||= {};
432
- for (const route of nitroConfig$1.prerender.routes) {
415
+ if (nuxt.options.dev) nuxt.hook("nitro:config", (nitroConfig) => {
416
+ nitroConfig.prerender ||= {};
417
+ nitroConfig.prerender.routes ||= [];
418
+ nitroConfig.routeRules ||= {};
419
+ for (const route of nitroConfig.prerender.routes) {
433
420
  if (!route) continue;
434
- nitroConfig$1.routeRules[route] = defu(nitroConfig$1.routeRules[route], { prerender: true });
421
+ nitroConfig.routeRules[route] = defu(nitroConfig.routeRules[route], { prerender: true });
435
422
  }
436
423
  });
437
- nuxt.hook("nitro:init", (nitro$1) => {
438
- nitro$1.hooks.hook("build:before", (nitro$2) => {
439
- for (const [route, value] of Object.entries(nitro$2.options.routeRules)) if (!route.endsWith("*") && !route.endsWith("/_payload.json")) {
424
+ nuxt.hook("nitro:init", (nitro) => {
425
+ nitro.hooks.hook("build:before", (nitro) => {
426
+ for (const [route, value] of Object.entries(nitro.options.routeRules)) if (!route.endsWith("*") && !route.endsWith("/_payload.json")) {
427
+ if (value.ssr === false) continue;
440
428
  if (value.isr || value.cache || value.prerender && nuxt.options.dev) {
441
429
  const payloadKey = route + "/_payload.json";
442
430
  const defaults = {};
@@ -445,7 +433,7 @@ async function bundle(nuxt) {
445
433
  "cache",
446
434
  ...nuxt.options.dev ? ["prerender"] : []
447
435
  ]) if (key in value) defaults[key] = value[key];
448
- nitro$2.options.routeRules[payloadKey] = defu(nitro$2.options.routeRules[payloadKey], defaults);
436
+ nitro.options.routeRules[payloadKey] = defu(nitro.options.routeRules[payloadKey], defaults);
449
437
  }
450
438
  }
451
439
  });
@@ -483,13 +471,13 @@ async function bundle(nuxt) {
483
471
  config.alias ||= {};
484
472
  config.alias["#app-manifest"] = join(tempDir, `meta/${buildId}.json`);
485
473
  });
486
- nuxt.hook("nitro:init", (nitro$1) => {
487
- nitro$1.hooks.hook("rollup:before", async (nitro$2) => {
474
+ nuxt.hook("nitro:init", (nitro) => {
475
+ nitro.hooks.hook("rollup:before", async (nitro) => {
488
476
  const prerenderedRoutes = /* @__PURE__ */ new Set();
489
477
  const routeRulesMatcher = getRouteRulesRouter();
490
- if (nitro$2._prerenderedRoutes?.length) {
478
+ if (nitro._prerenderedRoutes?.length) {
491
479
  const payloadSuffix = nuxt.options.experimental.renderJsonPayloads ? "/_payload.json" : "/_payload.js";
492
- for (const route of nitro$2._prerenderedRoutes) if (!route.error && route.route.endsWith(payloadSuffix)) {
480
+ for (const route of nitro._prerenderedRoutes) if (!route.error && route.route.endsWith(payloadSuffix)) {
493
481
  const url = route.route.slice(0, -payloadSuffix.length) || "/";
494
482
  if (!defu({}, ...findAllRoutes(routeRulesMatcher, void 0, url).reverse()).prerender) prerenderedRoutes.add(url);
495
483
  }
@@ -531,8 +519,8 @@ async function bundle(nuxt) {
531
519
  const relativeSharedDir = withTrailingSlash(relative(nuxt.options.rootDir, resolve(nuxt.options.rootDir, nuxt.options.dir.shared)));
532
520
  const sharedPatterns = [
533
521
  /^#shared\//,
534
- /* @__PURE__ */ new RegExp("^" + escapeRE(sharedDir)),
535
- /* @__PURE__ */ new RegExp("^" + escapeRE(relativeSharedDir))
522
+ new RegExp("^" + escapeRE(sharedDir)),
523
+ new RegExp("^" + escapeRE(relativeSharedDir))
536
524
  ];
537
525
  nitroConfig.rollupConfig.plugins.push(ImpoundPlugin.rollup({
538
526
  cwd: nuxt.options.rootDir,
@@ -623,6 +611,22 @@ async function bundle(nuxt) {
623
611
  await nuxt.callHook("nitro:init", nitro);
624
612
  nuxt["~runtimeDependencies"] ||= [];
625
613
  nuxt["~runtimeDependencies"].push(...runtimeDependencies, "unhead", "@unhead/vue", "@nuxt/devalue", "unstorage", ...nitro.options.inlineDynamicImports ? ["vue", "@vue/server-renderer"] : []);
614
+ addVitePlugin({
615
+ name: "nuxt:nitro:ssr-conditions",
616
+ configEnvironment(name, config) {
617
+ if (name === "ssr") {
618
+ config.resolve ||= {};
619
+ config.resolve.conditions = [...nitro.options.exportConditions || [], "import"];
620
+ }
621
+ }
622
+ });
623
+ addVitePlugin({
624
+ name: "nuxt:nitro:vue-feature-flags",
625
+ applyToEnvironment: (environment) => environment.name === "ssr" && environment.config.isProduction,
626
+ configResolved(config) {
627
+ for (const key in config.define) if (key.startsWith("__VUE")) nitro.options.replace[key] = config.define[key];
628
+ }
629
+ });
626
630
  nitro.vfs = nuxt.vfs = nitro.vfs || nuxt.vfs || {};
627
631
  nuxt.hook("close", () => nitro.hooks.callHook("close"));
628
632
  nitro.hooks.hook("prerender:routes", (routes) => {
@@ -656,12 +660,12 @@ async function bundle(nuxt) {
656
660
  handler: resolve(distDir, "runtime/handlers/renderer")
657
661
  });
658
662
  if (nuxt.options.experimental.chromeDevtoolsProjectSettings) {
659
- const cacheDir$1 = resolve(nuxt.options.rootDir, "node_modules/.cache/nuxt");
660
- let projectConfiguration = await readFile(join(cacheDir$1, "chrome-workspace.json"), "utf-8").then((r) => JSON.parse(r)).catch(() => null);
663
+ const cacheDir = resolve(nuxt.options.rootDir, "node_modules/.cache/nuxt");
664
+ let projectConfiguration = await readFile(join(cacheDir, "chrome-workspace.json"), "utf-8").then((r) => JSON.parse(r)).catch(() => null);
661
665
  if (!projectConfiguration) {
662
666
  projectConfiguration = { uuid: randomUUID() };
663
- await mkdir(cacheDir$1, { recursive: true });
664
- await writeFile(join(cacheDir$1, "chrome-workspace.json"), JSON.stringify(projectConfiguration), "utf-8");
667
+ await mkdir(cacheDir, { recursive: true });
668
+ await writeFile(join(cacheDir, "chrome-workspace.json"), JSON.stringify(projectConfiguration), "utf-8");
665
669
  }
666
670
  nitro.options.devHandlers.push({
667
671
  route: "/.well-known/appspecific/com.chrome.devtools.json",
@@ -671,12 +675,12 @@ async function bundle(nuxt) {
671
675
  } }))
672
676
  });
673
677
  }
674
- if (!nuxt.options.dev && nuxt.options.experimental.noVueServer) nitro.hooks.hook("rollup:before", (nitro$1) => {
675
- if (nitro$1.options.preset === "nitro-prerender") return;
676
- const nuxtErrorHandler = nitro$1.options.handlers.findIndex((h) => h.route === "/__nuxt_error");
677
- if (nuxtErrorHandler >= 0) nitro$1.options.handlers.splice(nuxtErrorHandler, 1);
678
- nitro$1.options.renderer = void 0;
679
- nitro$1.options.errorHandler = "nitropack/runtime/error";
678
+ if (!nuxt.options.dev && nuxt.options.experimental.noVueServer) nitro.hooks.hook("rollup:before", (nitro) => {
679
+ if (nitro.options.preset === "nitro-prerender") return;
680
+ const nuxtErrorHandler = nitro.options.handlers.findIndex((h) => h.route === "/__nuxt_error");
681
+ if (nuxtErrorHandler >= 0) nitro.options.handlers.splice(nuxtErrorHandler, 1);
682
+ nitro.options.renderer = void 0;
683
+ nitro.options.errorHandler = "nitropack/runtime/error";
680
684
  });
681
685
  nuxt.hook("prepare:types", async (opts) => {
682
686
  if (!nuxt.options.dev) {
@@ -691,14 +695,14 @@ async function bundle(nuxt) {
691
695
  for (const route of ["/200.html", "/404.html"]) routes.add(route);
692
696
  if (!nuxt.options.ssr) routes.add("/index.html");
693
697
  });
694
- if (!nuxt.options.dev) nitro.hooks.hook("rollup:before", async (nitro$1) => {
695
- await copyPublicAssets(nitro$1);
696
- await nuxt.callHook("nitro:build:public-assets", nitro$1);
698
+ if (!nuxt.options.dev) nitro.hooks.hook("rollup:before", async (nitro) => {
699
+ await copyPublicAssets(nitro);
700
+ await nuxt.callHook("nitro:build:public-assets", nitro);
697
701
  });
698
702
  async function symlinkDist() {
699
703
  if (nitro.options.static) {
700
- const distDir$1 = resolve(nuxt.options.rootDir, "dist");
701
- if (!existsSync(distDir$1)) await promises.symlink(nitro.options.output.publicDir, distDir$1, "junction").catch(() => {});
704
+ const distDir = resolve(nuxt.options.rootDir, "dist");
705
+ if (!existsSync(distDir)) await promises.symlink(nitro.options.output.publicDir, distDir, "junction").catch(() => {});
702
706
  }
703
707
  }
704
708
  nuxt.hook("build:done", async () => {
@@ -736,7 +740,7 @@ async function bundle(nuxt) {
736
740
  }));
737
741
  });
738
742
  nuxt.server = createDevServer(nitro);
739
- const waitUntilCompile = new Promise((resolve$1) => nitro.hooks.hook("compiled", () => resolve$1()));
743
+ const waitUntilCompile = new Promise((resolve) => nitro.hooks.hook("compiled", () => resolve()));
740
744
  nuxt.hook("build:done", () => waitUntilCompile);
741
745
  }
742
746
  }
@@ -750,14 +754,13 @@ async function spaLoadingTemplatePath(nuxt) {
750
754
  }
751
755
  async function spaLoadingTemplate(nuxt) {
752
756
  if (nuxt.options.spaLoadingTemplate === false) return "";
753
- const spaLoadingTemplate$1 = await spaLoadingTemplatePath(nuxt);
757
+ const spaLoadingTemplate = await spaLoadingTemplatePath(nuxt);
754
758
  try {
755
- if (existsSync(spaLoadingTemplate$1)) return readFileSync(spaLoadingTemplate$1, "utf-8").trim();
759
+ if (existsSync(spaLoadingTemplate)) return readFileSync(spaLoadingTemplate, "utf-8").trim();
756
760
  } catch {}
757
761
  if (nuxt.options.spaLoadingTemplate === true) return template();
758
762
  if (nuxt.options.spaLoadingTemplate) logger.warn(`Could not load custom \`spaLoadingTemplate\` path as it does not exist: \`${nuxt.options.spaLoadingTemplate}\`.`);
759
763
  return "";
760
764
  }
761
-
762
765
  //#endregion
763
- export { bundle };
766
+ export { bundle };
@@ -26,8 +26,8 @@ export default (async function errorhandler(error, event, { defaultHandler }) {
26
26
  // remove proto/hostname/port from URL
27
27
  const url = new URL(errorObject.url);
28
28
  errorObject.url = withoutBase(url.pathname, useRuntimeConfig(event).app.baseURL) + url.search + url.hash;
29
- // add default server message
30
- errorObject.message ||= "Server Error";
29
+ // add default server message (keep sanitized for unhandled errors)
30
+ errorObject.message = error.unhandled ? errorObject.message || "Server Error" : error.message || errorObject.message || "Server Error";
31
31
  // we will be rendering this error internally so we can pass along the error.data safely
32
32
  errorObject.data ||= error.data;
33
33
  errorObject.statusText ||= error.statusText || error.statusMessage;
@@ -1,2 +1,3 @@
1
- declare const _default;
2
- export default _default;
1
+ import type { EventHandler } from "h3";
2
+ declare const handler: EventHandler;
3
+ export default handler;
@@ -1,5 +1,5 @@
1
1
  import { destr } from "destr";
2
- import { defineEventHandler, getQuery, readBody, setResponseHeaders } from "h3";
2
+ import { createError, defineEventHandler, getQuery, readBody, setResponseHeaders } from "h3";
3
3
  import { resolveUnrefHeadInput } from "@unhead/vue";
4
4
  import { getRequestDependencies } from "vue-bundle-renderer/runtime";
5
5
  import { getQuery as getURLQuery } from "ufo";
@@ -10,7 +10,7 @@ import { getSSRRenderer } from "../utils/renderer/build-files.mjs";
10
10
  import { renderInlineStyles } from "../utils/renderer/inline-styles.mjs";
11
11
  import { getClientIslandResponse, getServerComponentHTML, getSlotIslandResponse } from "../utils/renderer/islands.mjs";
12
12
  const ISLAND_SUFFIX_RE = /\.json(?:\?.*)?$/;
13
- export default defineEventHandler(async (event) => {
13
+ const handler = defineEventHandler(async (event) => {
14
14
  const nitroApp = useNitroApp();
15
15
  setResponseHeaders(event, {
16
16
  "content-type": "application/json;charset=utf-8",
@@ -98,26 +98,38 @@ export default defineEventHandler(async (event) => {
98
98
  }
99
99
  return islandResponse;
100
100
  });
101
+ export default handler;
102
+ const ISLAND_PATH_PREFIX = "/__nuxt_island/";
103
+ const VALID_COMPONENT_NAME_RE = /^[a-z][\w.-]*$/i;
101
104
  async function getIslandContext(event) {
102
- // TODO: Strict validation for url
103
105
  let url = event.path || "";
104
106
  if (import.meta.prerender && event.path && await islandPropCache.hasItem(event.path)) {
105
107
  // rehydrate props from cache so we can rerender island if cache does not have it any more
106
108
  url = await islandPropCache.getItem(event.path);
107
109
  }
108
- const componentParts = url.substring("/__nuxt_island".length + 1).replace(ISLAND_SUFFIX_RE, "").split("_");
110
+ if (!url.startsWith(ISLAND_PATH_PREFIX)) {
111
+ throw createError({
112
+ statusCode: 400,
113
+ statusMessage: "Invalid island request path"
114
+ });
115
+ }
116
+ const componentParts = url.substring(ISLAND_PATH_PREFIX.length).replace(ISLAND_SUFFIX_RE, "").split("_");
109
117
  const hashId = componentParts.length > 1 ? componentParts.pop() : undefined;
110
118
  const componentName = componentParts.join("_");
111
- // TODO: Validate context
119
+ if (!componentName || !VALID_COMPONENT_NAME_RE.test(componentName)) {
120
+ throw createError({
121
+ statusCode: 400,
122
+ statusMessage: "Invalid island component name"
123
+ });
124
+ }
112
125
  const context = event.method === "GET" ? getQuery(event) : await readBody(event);
113
- const ctx = {
114
- url: "/",
115
- ...context,
126
+ // Only extract known context fields to prevent arbitrary data injection
127
+ return {
128
+ url: typeof context?.url === "string" ? context.url : "/",
116
129
  id: hashId,
117
130
  name: componentName,
118
131
  props: destr(context.props) || {},
119
132
  slots: {},
120
133
  components: {}
121
134
  };
122
- return ctx;
123
135
  }
@@ -1,2 +1,3 @@
1
- declare const _default;
2
- export default _default;
1
+ import type { EventHandler } from "h3";
2
+ declare const handler: EventHandler;
3
+ export default handler;
@@ -38,7 +38,7 @@ const APP_TELEPORT_CLOSE_TAG = HAS_APP_TELEPORTS ? `</${appTeleportTag}>` : "";
38
38
  const PAYLOAD_URL_RE = NUXT_JSON_PAYLOADS ? /^[^?]*\/_payload.json(?:\?.*)?$/ : /^[^?]*\/_payload.js(?:\?.*)?$/;
39
39
  const PAYLOAD_FILENAME = NUXT_JSON_PAYLOADS ? "_payload.json" : "_payload.js";
40
40
  let entryPath;
41
- export default defineRenderHandler(async (event) => {
41
+ const handler = defineRenderHandler(async (event) => {
42
42
  const nitroApp = useNitroApp();
43
43
  // Whether we're rendering an error page
44
44
  const ssrError = event.path.startsWith("/__nuxt_error") ? getQuery(event) : null;
@@ -282,6 +282,7 @@ export default defineRenderHandler(async (event) => {
282
282
  }
283
283
  };
284
284
  });
285
+ export default handler;
285
286
  function normalizeChunks(chunks) {
286
287
  const result = [];
287
288
  for (const _chunk of chunks) {
@@ -1,2 +1,3 @@
1
- declare const _default;
2
- export default _default;
1
+ import type { EventHandler } from "h3";
2
+ declare const handler: EventHandler;
3
+ export default handler;
@@ -1,7 +1,8 @@
1
1
  import { defineEventHandler, getRequestHeader } from "h3";
2
- export default defineEventHandler((event) => {
2
+ const handler = defineEventHandler((event) => {
3
3
  if (getRequestHeader(event, "x-nuxt-no-ssr")) {
4
4
  event.context.nuxt ||= {};
5
5
  event.context.nuxt.noSSR = true;
6
6
  }
7
7
  });
8
+ export default handler;
@@ -1,2 +1,3 @@
1
- declare const _default;
1
+ import type { NitroApp } from "nitropack/runtime/app";
2
+ declare const _default: (nitroApp: NitroApp) => void;
2
3
  export default _default;
@@ -1,5 +1,11 @@
1
- export declare const payloadCache: unknown;
2
- export declare const islandCache: unknown;
3
- export declare const islandPropCache: unknown;
4
- export declare const sharedPrerenderPromises: unknown;
5
- export declare const sharedPrerenderCache: unknown;
1
+ import type { Storage } from "unstorage";
2
+ export declare const payloadCache: Storage | null;
3
+ export declare const islandCache: Storage | null;
4
+ export declare const islandPropCache: Storage | null;
5
+ export declare const sharedPrerenderPromises: Map<string, Promise<any>> | null;
6
+ interface SharedPrerenderCache {
7
+ get<T = unknown>(key: string): Promise<T> | undefined;
8
+ set<T>(key: string, value: Promise<T>): Promise<void>;
9
+ }
10
+ export declare const sharedPrerenderCache: SharedPrerenderCache | null;
11
+ export {};
@@ -1 +1 @@
1
- export declare const defineAppConfig: unknown;
1
+ export declare const defineAppConfig: (config: any) => any;
@@ -357,7 +357,7 @@ function webComponentScript(base64HTML, startMinimized) {
357
357
  iframe.id = 'frame';
358
358
  iframe.src = 'data:text/html;base64,${base64HTML}';
359
359
  iframe.title = 'Detailed error stack trace';
360
- iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
360
+ iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-top-navigation-by-user-activation');
361
361
 
362
362
  const preview = el('div');
363
363
  preview.id = 'preview';
@@ -3,4 +3,4 @@ import type { H3Event } from "h3";
3
3
  * Nitro internal functions extracted from https://github.com/nitrojs/nitro/blob/v2/src/runtime/internal/utils.ts
4
4
  */
5
5
  export declare function isJsonRequest(event: H3Event): boolean;
6
- export declare function hasReqHeader(event: H3Event, name: string, includes: string);
6
+ export declare function hasReqHeader(event: H3Event, name: string, includes: string): boolean;
@@ -11,5 +11,5 @@ export function isJsonRequest(event) {
11
11
  }
12
12
  export function hasReqHeader(event, name, includes) {
13
13
  const value = getRequestHeader(event, name);
14
- return value && typeof value === "string" && value.toLowerCase().includes(includes);
14
+ return !!(value && typeof value === "string" && value.toLowerCase().includes(includes));
15
15
  }
@@ -1,4 +1,3 @@
1
- import { decodePath } from "ufo";
2
1
  import { useRuntimeConfig } from "nitropack/runtime";
3
2
  import { createHead } from "@unhead/vue/server";
4
3
  import { sharedPrerenderCache } from "../cache.mjs";
@@ -13,7 +12,7 @@ const PRERENDER_NO_SSR_ROUTES = new Set([
13
12
  ]);
14
13
  export function createSSRContext(event) {
15
14
  const ssrContext = {
16
- url: decodePath(event.path),
15
+ url: event.path,
17
16
  event,
18
17
  runtimeConfig: useRuntimeConfig(event),
19
18
  noSSR: !!NUXT_NO_SSR || event.context.nuxt?.noSSR || (import.meta.prerender ? PRERENDER_NO_SSR_ROUTES.has(event.path) : false),
@@ -10,9 +10,7 @@ interface Renderer {
10
10
  renderScripts: () => string;
11
11
  }>;
12
12
  }
13
- // -- SSR Renderer --
14
- export declare const getSSRRenderer: unknown;
13
+ export declare const getSSRRenderer: () => Promise<Renderer>;
15
14
  export declare function getRenderer(ssrContext: NuxtSSRContext): Promise<Renderer>;
16
- // @ts-expect-error file will be produced after app build
17
- export declare const getSSRStyles: unknown;
15
+ export declare const getSSRStyles: () => Promise<Record<string, () => Promise<string[]>>>;
18
16
  export {};
@@ -7,7 +7,7 @@ import { appId, multiApp } from "#internal/nuxt.config.mjs";
7
7
  import { NUXT_JSON_PAYLOADS, NUXT_NO_SSR, NUXT_PAYLOAD_EXTRACTION, NUXT_RUNTIME_PAYLOAD_EXTRACTION } from "#internal/nuxt/nitro-config.mjs";
8
8
  export function renderPayloadResponse(ssrContext) {
9
9
  return {
10
- body: NUXT_JSON_PAYLOADS ? stringify(splitPayload(ssrContext).payload, ssrContext["~payloadReducers"]) : `export default ${devalue(splitPayload(ssrContext).payload)}`,
10
+ body: NUXT_JSON_PAYLOADS ? encodeForwardSlashes(stringify(splitPayload(ssrContext).payload, ssrContext["~payloadReducers"])) : `export default ${devalue(splitPayload(ssrContext).payload)}`,
11
11
  statusCode: getResponseStatus(ssrContext.event),
12
12
  statusMessage: getResponseStatusText(ssrContext.event),
13
13
  headers: {
@@ -17,7 +17,7 @@ export function renderPayloadResponse(ssrContext) {
17
17
  };
18
18
  }
19
19
  export function renderPayloadJsonScript(opts) {
20
- const contents = opts.data ? stringify(opts.data, opts.ssrContext["~payloadReducers"]) : "";
20
+ const contents = opts.data ? encodeForwardSlashes(stringify(opts.data, opts.ssrContext["~payloadReducers"])) : "";
21
21
  const payload = {
22
22
  "type": "application/json",
23
23
  "innerHTML": contents,
@@ -33,13 +33,30 @@ export function renderPayloadJsonScript(opts) {
33
33
  const config = uneval(opts.ssrContext.config);
34
34
  return [payload, { innerHTML: multiApp ? `window.__NUXT__=window.__NUXT__||{};window.__NUXT__[${JSON.stringify(appId)}]={config:${config}}` : `window.__NUXT__={};window.__NUXT__.config=${config}` }];
35
35
  }
36
+ /**
37
+ * Encode forward slashes as unicode escape sequences to prevent
38
+ * Google from treating them as internal links and trying to crawl them.
39
+ * @see https://github.com/nuxt/nuxt/issues/24175
40
+ */
41
+ function encodeForwardSlashes(str) {
42
+ return str.replaceAll("/", "\\u002F");
43
+ }
44
+ /**
45
+ * Escape a string for safe interpolation inside a double-quoted JavaScript string literal.
46
+ * Prevents XSS when user-controlled URLs are embedded in inline `<script>` tags.
47
+ */
48
+ function escapeJsString(str) {
49
+ return str.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll("/", "\\u002F").replaceAll("<", "\\u003C");
50
+ }
36
51
  export function renderPayloadScript(opts) {
37
52
  opts.data.config = opts.ssrContext.config;
38
53
  const _PAYLOAD_EXTRACTION = !opts.ssrContext.noSSR && (import.meta.prerender && NUXT_PAYLOAD_EXTRACTION || NUXT_RUNTIME_PAYLOAD_EXTRACTION && (opts.routeOptions.isr || opts.routeOptions.cache));
39
54
  const nuxtData = devalue(opts.data);
40
55
  if (_PAYLOAD_EXTRACTION) {
41
- const singleAppPayload = `import p from "${opts.src}";window.__NUXT__={...p,...(${nuxtData})}`;
42
- const multiAppPayload = `import p from "${opts.src}";window.__NUXT__=window.__NUXT__||{};window.__NUXT__[${JSON.stringify(appId)}]={...p,...(${nuxtData})}`;
56
+ // Escape the URL to prevent XSS when interpolated into a JS string literal
57
+ const escapedSrc = escapeJsString(opts.src);
58
+ const singleAppPayload = `import p from "${escapedSrc}";window.__NUXT__={...p,...(${nuxtData})}`;
59
+ const multiAppPayload = `import p from "${escapedSrc}";window.__NUXT__=window.__NUXT__||{};window.__NUXT__[${JSON.stringify(appId)}]={...p,...(${nuxtData})}`;
43
60
  return [{
44
61
  type: "module",
45
62
  innerHTML: multiApp ? multiAppPayload : singleAppPayload
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuxt/nitro-server",
3
- "version": "3.21.0",
3
+ "version": "3.21.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/nuxt/nuxt.git",
@@ -19,41 +19,41 @@
19
19
  ],
20
20
  "dependencies": {
21
21
  "@nuxt/devalue": "^2.0.2",
22
- "@unhead/vue": "^2.1.2",
23
- "@vue/shared": "^3.5.27",
22
+ "@unhead/vue": "^2.1.12",
23
+ "@vue/shared": "^3.5.30",
24
24
  "consola": "^3.4.2",
25
25
  "defu": "^6.1.4",
26
26
  "destr": "^2.0.5",
27
- "devalue": "^5.6.2",
27
+ "devalue": "^5.6.3",
28
28
  "errx": "^0.1.0",
29
29
  "escape-string-regexp": "^5.0.0",
30
30
  "exsolve": "^1.0.8",
31
- "h3": "^1.15.5",
32
- "impound": "^1.0.0",
31
+ "h3": "^1.15.6",
32
+ "impound": "^1.1.5",
33
33
  "klona": "^2.0.6",
34
34
  "mocked-exports": "^0.1.1",
35
35
  "nitropack": "^2.13.1",
36
36
  "ohash": "^2.0.11",
37
37
  "pathe": "^2.0.3",
38
38
  "pkg-types": "^2.3.0",
39
- "rou3": "^0.7.12",
40
- "std-env": "^3.10.0",
39
+ "rou3": "^0.8.1",
40
+ "std-env": "^4.0.0",
41
41
  "ufo": "^1.6.3",
42
42
  "unctx": "^2.5.0",
43
43
  "unstorage": "^1.17.4",
44
- "vue": "^3.5.27",
44
+ "vue": "^3.5.30",
45
45
  "vue-bundle-renderer": "^2.2.0",
46
46
  "vue-devtools-stub": "^0.1.0",
47
- "@nuxt/kit": "3.21.0"
47
+ "@nuxt/kit": "3.21.2"
48
48
  },
49
49
  "peerDependencies": {
50
- "nuxt": "^3.21.0"
50
+ "nuxt": "^3.21.2"
51
51
  },
52
52
  "devDependencies": {
53
- "obuild": "0.4.14",
54
- "vitest": "3.2.4",
55
- "nuxt": "3.21.0",
56
- "@nuxt/schema": "3.21.0"
53
+ "obuild": "0.4.32",
54
+ "vitest": "4.0.18",
55
+ "@nuxt/schema": "3.21.2",
56
+ "nuxt": "3.21.2"
57
57
  },
58
58
  "engines": {
59
59
  "node": "^20.19.0 || >=22.12.0"