@analogjs/vite-plugin-nitro 3.0.0-alpha.61 → 3.0.0-alpha.63

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analogjs/vite-plugin-nitro",
3
- "version": "3.0.0-alpha.61",
3
+ "version": "3.0.0-alpha.63",
4
4
  "description": "A Vite plugin for adding a nitro API server",
5
5
  "type": "module",
6
6
  "author": "Brandon Roberts <robertsbt@gmail.com>",
@@ -50,4 +50,4 @@ export declare function clientRenderer(): string;
50
50
  * rewritten path, which avoids falling through to the HTML renderer when
51
51
  * SSR code makes relative API requests.
52
52
  */
53
- export declare const apiMiddleware = "\nimport { defineHandler, fetchWithEvent, proxyRequest } from 'nitro/h3';\nimport { useRuntimeConfig } from 'nitro/runtime-config';\n\nexport default defineHandler(async (event) => {\n const prefix = useRuntimeConfig().prefix;\n const apiPrefix = `${prefix}/${useRuntimeConfig().apiPrefix}`;\n\n if (event.path?.startsWith(apiPrefix)) {\n const reqUrl = event.path?.replace(apiPrefix, '');\n\n if (\n event.method === 'GET' &&\n // in the case of XML routes, we want to proxy the request so that nitro gets the correct headers\n // and can render the XML correctly as a static asset\n !event.path?.endsWith('.xml')\n ) {\n return fetchWithEvent(event, reqUrl, {\n headers: Object.fromEntries(event.req.headers.entries()),\n });\n }\n\n return proxyRequest(event, reqUrl);\n }\n});";
53
+ export declare const apiMiddleware = "\nimport { createError, defineHandler, fetchWithEvent, proxyRequest } from 'nitro/h3';\nimport { useRuntimeConfig } from 'nitro/runtime-config';\n\nexport default defineHandler(async (event) => {\n const prefix = useRuntimeConfig().prefix;\n const apiPrefix = `${prefix}/${useRuntimeConfig().apiPrefix}`;\n\n const path = event.path || '';\n\n // only match the prefix on a path boundary, otherwise a URL such as\n // '/apihttp://internal-host' would pass startsWith() and be forwarded verbatim\n if (\n path === apiPrefix ||\n path.startsWith(`${apiPrefix}/`) ||\n path.startsWith(`${apiPrefix}?`)\n ) {\n let reqUrl = path.slice(apiPrefix.length);\n if (reqUrl === '' || reqUrl.startsWith('?')) {\n reqUrl = `/${reqUrl}`;\n }\n\n // reject absolute and protocol-relative targets to prevent SSRF\n if (!reqUrl.startsWith('/') || reqUrl.startsWith('//')) {\n throw createError({ statusCode: 400, statusMessage: 'Invalid API route' });\n }\n\n if (\n event.method === 'GET' &&\n // in the case of XML routes, we want to proxy the request so that nitro gets the correct headers\n // and can render the XML correctly as a static asset\n !event.path?.endsWith('.xml')\n ) {\n return fetchWithEvent(event, reqUrl, {\n headers: Object.fromEntries(event.req.headers.entries()),\n });\n }\n\n return proxyRequest(event, reqUrl);\n }\n});";
@@ -113,15 +113,31 @@ export default defineHandler(async (event) => {
113
113
  * SSR code makes relative API requests.
114
114
  */
115
115
  var apiMiddleware = `
116
- import { defineHandler, fetchWithEvent, proxyRequest } from 'nitro/h3';
116
+ import { createError, defineHandler, fetchWithEvent, proxyRequest } from 'nitro/h3';
117
117
  import { useRuntimeConfig } from 'nitro/runtime-config';
118
118
 
119
119
  export default defineHandler(async (event) => {
120
120
  const prefix = useRuntimeConfig().prefix;
121
121
  const apiPrefix = \`\${prefix}/\${useRuntimeConfig().apiPrefix}\`;
122
122
 
123
- if (event.path?.startsWith(apiPrefix)) {
124
- const reqUrl = event.path?.replace(apiPrefix, '');
123
+ const path = event.path || '';
124
+
125
+ // only match the prefix on a path boundary, otherwise a URL such as
126
+ // '/apihttp://internal-host' would pass startsWith() and be forwarded verbatim
127
+ if (
128
+ path === apiPrefix ||
129
+ path.startsWith(\`\${apiPrefix}/\`) ||
130
+ path.startsWith(\`\${apiPrefix}?\`)
131
+ ) {
132
+ let reqUrl = path.slice(apiPrefix.length);
133
+ if (reqUrl === '' || reqUrl.startsWith('?')) {
134
+ reqUrl = \`/\${reqUrl}\`;
135
+ }
136
+
137
+ // reject absolute and protocol-relative targets to prevent SSRF
138
+ if (!reqUrl.startsWith('/') || reqUrl.startsWith('//')) {
139
+ throw createError({ statusCode: 400, statusMessage: 'Invalid API route' });
140
+ }
125
141
 
126
142
  if (
127
143
  event.method === 'GET' &&
@@ -1 +1 @@
1
- {"version":3,"file":"renderers.js","names":[],"sources":["../../../../src/lib/utils/renderers.ts"],"sourcesContent":["/**\n * Code snippet emitted into virtual modules to create a request-scoped\n * fetch using ofetch's `createFetch` + h3's `fetchWithEvent`.\n *\n * Shared between the SSR renderer and page-endpoint virtual modules so\n * the fetch-wiring logic stays in sync.\n *\n * The emitted variable is named `serverFetch` — callers should reference it\n * by that name.\n */\nexport const SERVER_FETCH_FACTORY_SNIPPET = `\n const serverFetch = createFetch({\n fetch: (resource, init) => {\n const url = resource instanceof Request ? resource.url : resource.toString();\n return fetchWithEvent(event, url, init);\n }\n });`;\n\n/**\n * SSR renderer virtual module content.\n *\n * This code runs inside Nitro's server runtime (Node.js context) where\n * event.node is always populated. In h3 v2, event.node is typed as optional,\n * so we use h3's first-class event properties (event.path, event.method) where\n * possible and apply optional chaining when accessing the Node.js context for\n * the Angular renderer which requires raw req/res objects.\n *\n * h3 v2 idiomatic APIs used:\n * - defineHandler (replaces defineEventHandler / eventHandler)\n * - event.path (replaces event.node.req.url)\n * - getResponseHeader compat shim (still available in h3 v2)\n */\nexport function ssrRenderer() {\n return `\nimport { createFetch } from 'ofetch';\nimport { defineHandler, fetchWithEvent } from 'nitro/h3';\n// @ts-ignore\nimport renderer from '#analog/ssr';\nimport template from '#analog/index';\n\nconst normalizeHtmlRequestUrl = (url) =>\n url.replace(/\\\\/index\\\\.html(?=$|[?#])/, '/');\n\nexport default defineHandler(async (event) => {\n event.res.headers.set('content-type', 'text/html; charset=utf-8');\n const noSSR = event.res.headers.get('x-analog-no-ssr');\n const requestPath = normalizeHtmlRequestUrl(event.path);\n\n if (noSSR === 'true') {\n return template;\n }\n\n // event.path is the canonical h3 v2 way to access the request URL.\n // event.node?.req and event.node?.res are needed by the Angular SSR renderer\n // which operates on raw Node.js request/response objects.\n // During prerendering (Nitro v3 fetch-based pipeline), event.node is undefined.\n // The Angular renderer requires a req object with at least { headers, url },\n // so we provide a minimal stub to avoid runtime errors in prerender context.\n const req = event.node?.req\n ? {\n ...event.node.req,\n url: requestPath,\n originalUrl: requestPath,\n }\n : {\n headers: { host: 'localhost' },\n url: requestPath,\n originalUrl: requestPath,\n connection: {},\n };\n const res = event.node?.res;\n${SERVER_FETCH_FACTORY_SNIPPET}\n\n const html = await renderer(requestPath, template, { req, res, fetch: serverFetch });\n\n return html;\n});`;\n}\n\n/**\n * Client-only renderer virtual module content.\n *\n * Used when SSR is disabled — simply serves the static index.html template\n * for every route, letting the client-side Angular router handle navigation.\n */\nexport function clientRenderer() {\n return `\nimport { defineHandler } from 'nitro/h3';\nimport template from '#analog/index';\n\nexport default defineHandler(async (event) => {\n event.res.headers.set('content-type', 'text/html; charset=utf-8');\n return template;\n});\n`;\n}\n\n/**\n * API middleware virtual module content.\n *\n * Intercepts requests matching the configured API prefix and either:\n * - Uses event-bound internal forwarding for GET requests (except .xml routes)\n * - Uses request proxying for all other methods to forward the full request\n *\n * h3 v2 idiomatic APIs used:\n * - defineHandler (replaces defineEventHandler / eventHandler)\n * - event.path (replaces event.node.req.url)\n * - event.method (replaces event.node.req.method)\n * - proxyRequest is retained internally because it preserves Nitro route\n * matching for event-bound server requests during SSR/prerender\n * - Object.fromEntries(event.req.headers.entries()) replaces direct event.node.req.headers access\n *\n * `fetchWithEvent` keeps the active event context while forwarding to a\n * rewritten path, which avoids falling through to the HTML renderer when\n * SSR code makes relative API requests.\n */\nexport const apiMiddleware = `\nimport { defineHandler, fetchWithEvent, proxyRequest } from 'nitro/h3';\nimport { useRuntimeConfig } from 'nitro/runtime-config';\n\nexport default defineHandler(async (event) => {\n const prefix = useRuntimeConfig().prefix;\n const apiPrefix = \\`\\${prefix}/\\${useRuntimeConfig().apiPrefix}\\`;\n\n if (event.path?.startsWith(apiPrefix)) {\n const reqUrl = event.path?.replace(apiPrefix, '');\n\n if (\n event.method === 'GET' &&\n // in the case of XML routes, we want to proxy the request so that nitro gets the correct headers\n // and can render the XML correctly as a static asset\n !event.path?.endsWith('.xml')\n ) {\n return fetchWithEvent(event, reqUrl, {\n headers: Object.fromEntries(event.req.headers.entries()),\n });\n }\n\n return proxyRequest(event, reqUrl);\n }\n});`;\n"],"mappings":";;;;;;;;;;;AAUA,IAAa,+BAA+B;;;;;;;;;;;;;;;;;;;;;AAsB5C,SAAgB,cAAc;AAC5B,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsCP,6BAA6B;;;;;;;;;;;;;AAc/B,SAAgB,iBAAiB;AAC/B,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BT,IAAa,gBAAgB"}
1
+ {"version":3,"file":"renderers.js","names":[],"sources":["../../../../src/lib/utils/renderers.ts"],"sourcesContent":["/**\n * Code snippet emitted into virtual modules to create a request-scoped\n * fetch using ofetch's `createFetch` + h3's `fetchWithEvent`.\n *\n * Shared between the SSR renderer and page-endpoint virtual modules so\n * the fetch-wiring logic stays in sync.\n *\n * The emitted variable is named `serverFetch` — callers should reference it\n * by that name.\n */\nexport const SERVER_FETCH_FACTORY_SNIPPET = `\n const serverFetch = createFetch({\n fetch: (resource, init) => {\n const url = resource instanceof Request ? resource.url : resource.toString();\n return fetchWithEvent(event, url, init);\n }\n });`;\n\n/**\n * SSR renderer virtual module content.\n *\n * This code runs inside Nitro's server runtime (Node.js context) where\n * event.node is always populated. In h3 v2, event.node is typed as optional,\n * so we use h3's first-class event properties (event.path, event.method) where\n * possible and apply optional chaining when accessing the Node.js context for\n * the Angular renderer which requires raw req/res objects.\n *\n * h3 v2 idiomatic APIs used:\n * - defineHandler (replaces defineEventHandler / eventHandler)\n * - event.path (replaces event.node.req.url)\n * - getResponseHeader compat shim (still available in h3 v2)\n */\nexport function ssrRenderer() {\n return `\nimport { createFetch } from 'ofetch';\nimport { defineHandler, fetchWithEvent } from 'nitro/h3';\n// @ts-ignore\nimport renderer from '#analog/ssr';\nimport template from '#analog/index';\n\nconst normalizeHtmlRequestUrl = (url) =>\n url.replace(/\\\\/index\\\\.html(?=$|[?#])/, '/');\n\nexport default defineHandler(async (event) => {\n event.res.headers.set('content-type', 'text/html; charset=utf-8');\n const noSSR = event.res.headers.get('x-analog-no-ssr');\n const requestPath = normalizeHtmlRequestUrl(event.path);\n\n if (noSSR === 'true') {\n return template;\n }\n\n // event.path is the canonical h3 v2 way to access the request URL.\n // event.node?.req and event.node?.res are needed by the Angular SSR renderer\n // which operates on raw Node.js request/response objects.\n // During prerendering (Nitro v3 fetch-based pipeline), event.node is undefined.\n // The Angular renderer requires a req object with at least { headers, url },\n // so we provide a minimal stub to avoid runtime errors in prerender context.\n const req = event.node?.req\n ? {\n ...event.node.req,\n url: requestPath,\n originalUrl: requestPath,\n }\n : {\n headers: { host: 'localhost' },\n url: requestPath,\n originalUrl: requestPath,\n connection: {},\n };\n const res = event.node?.res;\n${SERVER_FETCH_FACTORY_SNIPPET}\n\n const html = await renderer(requestPath, template, { req, res, fetch: serverFetch });\n\n return html;\n});`;\n}\n\n/**\n * Client-only renderer virtual module content.\n *\n * Used when SSR is disabled — simply serves the static index.html template\n * for every route, letting the client-side Angular router handle navigation.\n */\nexport function clientRenderer() {\n return `\nimport { defineHandler } from 'nitro/h3';\nimport template from '#analog/index';\n\nexport default defineHandler(async (event) => {\n event.res.headers.set('content-type', 'text/html; charset=utf-8');\n return template;\n});\n`;\n}\n\n/**\n * API middleware virtual module content.\n *\n * Intercepts requests matching the configured API prefix and either:\n * - Uses event-bound internal forwarding for GET requests (except .xml routes)\n * - Uses request proxying for all other methods to forward the full request\n *\n * h3 v2 idiomatic APIs used:\n * - defineHandler (replaces defineEventHandler / eventHandler)\n * - event.path (replaces event.node.req.url)\n * - event.method (replaces event.node.req.method)\n * - proxyRequest is retained internally because it preserves Nitro route\n * matching for event-bound server requests during SSR/prerender\n * - Object.fromEntries(event.req.headers.entries()) replaces direct event.node.req.headers access\n *\n * `fetchWithEvent` keeps the active event context while forwarding to a\n * rewritten path, which avoids falling through to the HTML renderer when\n * SSR code makes relative API requests.\n */\nexport const apiMiddleware = `\nimport { createError, defineHandler, fetchWithEvent, proxyRequest } from 'nitro/h3';\nimport { useRuntimeConfig } from 'nitro/runtime-config';\n\nexport default defineHandler(async (event) => {\n const prefix = useRuntimeConfig().prefix;\n const apiPrefix = \\`\\${prefix}/\\${useRuntimeConfig().apiPrefix}\\`;\n\n const path = event.path || '';\n\n // only match the prefix on a path boundary, otherwise a URL such as\n // '/apihttp://internal-host' would pass startsWith() and be forwarded verbatim\n if (\n path === apiPrefix ||\n path.startsWith(\\`\\${apiPrefix}/\\`) ||\n path.startsWith(\\`\\${apiPrefix}?\\`)\n ) {\n let reqUrl = path.slice(apiPrefix.length);\n if (reqUrl === '' || reqUrl.startsWith('?')) {\n reqUrl = \\`/\\${reqUrl}\\`;\n }\n\n // reject absolute and protocol-relative targets to prevent SSRF\n if (!reqUrl.startsWith('/') || reqUrl.startsWith('//')) {\n throw createError({ statusCode: 400, statusMessage: 'Invalid API route' });\n }\n\n if (\n event.method === 'GET' &&\n // in the case of XML routes, we want to proxy the request so that nitro gets the correct headers\n // and can render the XML correctly as a static asset\n !event.path?.endsWith('.xml')\n ) {\n return fetchWithEvent(event, reqUrl, {\n headers: Object.fromEntries(event.req.headers.entries()),\n });\n }\n\n return proxyRequest(event, reqUrl);\n }\n});`;\n"],"mappings":";;;;;;;;;;;AAUA,IAAa,+BAA+B;;;;;;;;;;;;;;;;;;;;;AAsB5C,SAAgB,cAAc;AAC5B,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsCP,6BAA6B;;;;;;;;;;;;;AAc/B,SAAgB,iBAAiB;AAC/B,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BT,IAAa,gBAAgB"}