@nuxt/nitro-server 3.21.1 → 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.
Files changed (47) hide show
  1. package/README.md +4 -4
  2. package/dist/THIRD-PARTY-LICENSES.md +1 -3816
  3. package/dist/index.d.mts +5 -3144
  4. package/dist/index.mjs +32 -2
  5. package/dist/runtime/handlers/island.d.mts +3 -2
  6. package/dist/runtime/handlers/island.mjs +21 -9
  7. package/dist/runtime/handlers/renderer.d.mts +3 -2
  8. package/dist/runtime/handlers/renderer.mjs +2 -1
  9. package/dist/runtime/middleware/no-ssr.d.mts +3 -2
  10. package/dist/runtime/middleware/no-ssr.mjs +2 -1
  11. package/dist/runtime/plugins/dev-server-logs.d.mts +2 -1
  12. package/dist/runtime/utils/cache.d.mts +11 -5
  13. package/dist/runtime/utils/config.d.mts +1 -1
  14. package/dist/runtime/utils/dev.mjs +1 -1
  15. package/dist/runtime/utils/error.d.mts +1 -1
  16. package/dist/runtime/utils/error.mjs +1 -1
  17. package/dist/runtime/utils/renderer/build-files.d.mts +2 -2
  18. package/dist/runtime/utils/renderer/payload.mjs +21 -4
  19. package/package.json +14 -14
  20. package/dist/_chunks/libs/@babel/parser.d.mts +0 -1536
  21. package/dist/_chunks/libs/@jridgewell/trace-mapping.d.mts +0 -82
  22. package/dist/_chunks/libs/@types/estree.d.mts +0 -525
  23. package/dist/_chunks/libs/@types/pug.d.mts +0 -123
  24. package/dist/_chunks/libs/@vitejs/plugin-vue-jsx.d.mts +0 -17996
  25. package/dist/_chunks/libs/@vitejs/plugin-vue.d.mts +0 -83
  26. package/dist/_chunks/libs/@volar/language-core.d.mts +0 -56
  27. package/dist/_chunks/libs/@volar/source-map.d.mts +0 -10
  28. package/dist/_chunks/libs/@vue/compiler-dom.d.mts +0 -45
  29. package/dist/_chunks/libs/@vue/language-core.d.mts +0 -194
  30. package/dist/_chunks/libs/autoprefixer.d.mts +0 -83
  31. package/dist/_chunks/libs/c12.d.mts +0 -146
  32. package/dist/_chunks/libs/compatx.d.mts +0 -47
  33. package/dist/_chunks/libs/css-minimizer-webpack-plugin.d.mts +0 -9
  34. package/dist/_chunks/libs/esbuild-loader.d.mts +0 -16
  35. package/dist/_chunks/libs/mini-css-extract-plugin.d.mts +0 -1
  36. package/dist/_chunks/libs/mlly.d.mts +0 -57
  37. package/dist/_chunks/libs/ofetch.d.mts +0 -870
  38. package/dist/_chunks/libs/open.d.mts +0 -1
  39. package/dist/_chunks/libs/oxc-transform.d.mts +0 -422
  40. package/dist/_chunks/libs/rollup-plugin-visualizer.d.mts +0 -90
  41. package/dist/_chunks/libs/scule.d.mts +0 -15
  42. package/dist/_chunks/libs/unimport.d.mts +0 -386
  43. package/dist/_chunks/libs/untyped.d.mts +0 -44
  44. package/dist/_chunks/libs/vue-loader.d.mts +0 -31
  45. package/dist/_chunks/libs/vue-router.d.mts +0 -1413
  46. package/dist/_chunks/libs/webpack-dev-middleware.d.mts +0 -2
  47. package/dist/_chunks/rolldown-runtime.mjs +0 -12
package/dist/index.mjs CHANGED
@@ -19,16 +19,23 @@ 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
- var version = "3.21.1";
22
+ //#region package.json
23
+ var version = "3.21.2";
24
+ //#endregion
25
+ //#region src/utils.ts
23
26
  function toArray(value) {
24
27
  return Array.isArray(value) ? value : [value];
25
28
  }
26
29
  let _distDir = dirname(fileURLToPath(import.meta.url));
27
30
  if (/(?:chunks|shared)$/.test(_distDir)) _distDir = dirname(_distDir);
28
31
  const distDir = _distDir;
32
+ //#endregion
33
+ //#region ../ui-templates/dist/templates/spa-loading-icon.ts
29
34
  const template = () => {
30
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>";
31
36
  };
37
+ //#endregion
38
+ //#region ../nuxt/src/core/plugins/import-protection.ts
32
39
  function createImportProtectionPatterns(nuxt, options) {
33
40
  const patterns = [];
34
41
  const context = contextFlags[options.context];
@@ -54,6 +61,8 @@ const contextFlags = {
54
61
  "nuxt-app": "the Vue part of your app",
55
62
  "shared": "the #shared directory"
56
63
  };
64
+ //#endregion
65
+ //#region src/templates.ts
57
66
  const nitroSchemaTemplate = {
58
67
  filename: "types/nitro-nuxt.d.ts",
59
68
  async getContents({ nuxt }) {
@@ -105,6 +114,8 @@ declare module 'nitropack' {
105
114
  function renderReference(ref, baseDir) {
106
115
  return `/// <reference ${"path" in ref ? `path="${isAbsolute(ref.path) ? relative(baseDir, ref.path) : ref.path}"` : `types="${ref.types}"`} />`;
107
116
  }
117
+ //#endregion
118
+ //#region src/index.ts
108
119
  const logLevelMapReverse = {
109
120
  silent: 0,
110
121
  info: 3,
@@ -351,7 +362,8 @@ async function bundle(nuxt) {
351
362
  "appLayout",
352
363
  "cache",
353
364
  "isr",
354
- "swr"
365
+ "swr",
366
+ "ssr"
355
367
  ];
356
368
  function getRouteRulesRouter() {
357
369
  const routeRulesRouter = createRouter();
@@ -412,6 +424,7 @@ async function bundle(nuxt) {
412
424
  nuxt.hook("nitro:init", (nitro) => {
413
425
  nitro.hooks.hook("build:before", (nitro) => {
414
426
  for (const [route, value] of Object.entries(nitro.options.routeRules)) if (!route.endsWith("*") && !route.endsWith("/_payload.json")) {
427
+ if (value.ssr === false) continue;
415
428
  if (value.isr || value.cache || value.prerender && nuxt.options.dev) {
416
429
  const payloadKey = route + "/_payload.json";
417
430
  const defaults = {};
@@ -598,6 +611,22 @@ async function bundle(nuxt) {
598
611
  await nuxt.callHook("nitro:init", nitro);
599
612
  nuxt["~runtimeDependencies"] ||= [];
600
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
+ });
601
630
  nitro.vfs = nuxt.vfs = nitro.vfs || nuxt.vfs || {};
602
631
  nuxt.hook("close", () => nitro.hooks.callHook("close"));
603
632
  nitro.hooks.hook("prerender:routes", (routes) => {
@@ -733,4 +762,5 @@ async function spaLoadingTemplate(nuxt) {
733
762
  if (nuxt.options.spaLoadingTemplate) logger.warn(`Could not load custom \`spaLoadingTemplate\` path as it does not exist: \`${nuxt.options.spaLoadingTemplate}\`.`);
734
763
  return "";
735
764
  }
765
+ //#endregion
736
766
  export { bundle };
@@ -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
  }
@@ -10,7 +10,7 @@ interface Renderer {
10
10
  renderScripts: () => string;
11
11
  }>;
12
12
  }
13
- export declare const getSSRRenderer: unknown;
13
+ export declare const getSSRRenderer: () => Promise<Renderer>;
14
14
  export declare function getRenderer(ssrContext: NuxtSSRContext): Promise<Renderer>;
15
- export declare const getSSRStyles: unknown;
15
+ export declare const getSSRStyles: () => Promise<Record<string, () => Promise<string[]>>>;
16
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.1",
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.3",
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.1"
47
+ "@nuxt/kit": "3.21.2"
48
48
  },
49
49
  "peerDependencies": {
50
- "nuxt": "^3.21.1"
50
+ "nuxt": "^3.21.2"
51
51
  },
52
52
  "devDependencies": {
53
- "obuild": "0.4.27",
53
+ "obuild": "0.4.32",
54
54
  "vitest": "4.0.18",
55
- "nuxt": "3.21.1",
56
- "@nuxt/schema": "3.21.1"
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"