@inertiajs/core 3.0.0-beta.6 → 3.0.0-beta.7

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/dist/server.js CHANGED
@@ -170,8 +170,8 @@ function makeRelative(path2, root) {
170
170
  }
171
171
  return path2;
172
172
  }
173
- function formatConsoleError(classified, root, handleErrors = true, suppressedWarnings = []) {
174
- if (!handleErrors) {
173
+ function formatConsoleError(classified, root, formatErrors = true, suppressedWarnings = []) {
174
+ if (!formatErrors) {
175
175
  const component = classified.component ? `[${classified.component}]` : "";
176
176
  return `SSR Error ${component}: ${classified.error}`;
177
177
  }
@@ -239,7 +239,7 @@ var readableToString = (readable) => new Promise((resolve, reject) => {
239
239
  });
240
240
  var server_default = (render, options) => {
241
241
  const opts = typeof options === "number" ? { port: options } : options;
242
- const { port = 13714, cluster: useCluster = false, handleErrors = true } = opts ?? {};
242
+ const { port = 13714, cluster: useCluster = false, formatErrors = true } = opts ?? {};
243
243
  const log = (message) => {
244
244
  console.log(
245
245
  useCluster && !cluster.isPrimary ? `[${cluster.worker?.id ?? "N/A"} / ${cluster.worker?.process?.pid ?? "N/A"}] ${message}` : message
@@ -263,7 +263,7 @@ var server_default = (render, options) => {
263
263
  const handleRender = async (request, response) => {
264
264
  const page = JSON.parse(await readableToString(request));
265
265
  const originalWarn = console.warn;
266
- if (handleErrors) {
266
+ if (formatErrors) {
267
267
  console.warn = () => {
268
268
  };
269
269
  }
@@ -273,7 +273,7 @@ var server_default = (render, options) => {
273
273
  response.write(JSON.stringify(result));
274
274
  } catch (e) {
275
275
  const error = e;
276
- if (!handleErrors) {
276
+ if (!formatErrors) {
277
277
  throw error;
278
278
  }
279
279
  const classified = classifySSRError(error, page.component, page.url);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/server.ts", "../src/ssrErrors.ts"],
4
- "sourcesContent": ["import { originalPositionFor, TraceMap } from '@jridgewell/trace-mapping'\nimport { createServer, IncomingMessage, ServerResponse } from 'http'\nimport cluster from 'node:cluster'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { availableParallelism } from 'node:os'\nimport path from 'node:path'\nimport * as process from 'process'\nimport { classifySSRError, formatConsoleError, setSourceMapResolver } from './ssrErrors'\nimport { InertiaAppResponse, Page } from './types'\n\n// Cache parsed sourcemaps for performance\nconst sourceMaps = new Map<string, TraceMap>()\n\nsetSourceMapResolver((file: string, line: number, column: number) => {\n // Only resolve for bundled SSR files\n if (!file.includes('/ssr/') || !file.endsWith('.js')) {\n return null\n }\n\n const mapFile = file + '.map'\n\n if (!existsSync(mapFile)) {\n return null\n }\n\n let traceMap = sourceMaps.get(mapFile)\n\n if (!traceMap) {\n try {\n const mapContent = readFileSync(mapFile, 'utf-8')\n traceMap = new TraceMap(mapContent)\n sourceMaps.set(mapFile, traceMap)\n } catch {\n return null\n }\n }\n\n const original = originalPositionFor(traceMap, { line, column })\n\n if (original.source) {\n // Resolve the source path relative to the sourcemap location\n const mapDir = path.dirname(mapFile)\n const resolvedPath = path.resolve(mapDir, original.source)\n\n return {\n file: resolvedPath,\n line: original.line ?? line,\n column: original.column ?? column,\n }\n }\n\n return null\n})\n\ntype AppCallback = (page: Page) => InertiaAppResponse\ntype RouteHandler = (request: IncomingMessage, response: ServerResponse) => Promise<unknown>\ntype ServerOptions = {\n port?: number\n cluster?: boolean\n handleErrors?: boolean\n}\ntype Port = number\n\nconst readableToString: (readable: IncomingMessage) => Promise<string> = (readable) =>\n new Promise((resolve, reject) => {\n let data = ''\n readable.on('data', (chunk) => (data += chunk))\n readable.on('end', () => resolve(data))\n readable.on('error', (err) => reject(err))\n })\n\nexport default (render: AppCallback, options?: Port | ServerOptions): AppCallback => {\n const opts = typeof options === 'number' ? { port: options } : options\n const { port = 13714, cluster: useCluster = false, handleErrors = true } = opts ?? {}\n\n const log = (message: string) => {\n console.log(\n useCluster && !cluster.isPrimary\n ? `[${cluster.worker?.id ?? 'N/A'} / ${cluster.worker?.process?.pid ?? 'N/A'}] ${message}`\n : message,\n )\n }\n\n if (useCluster && cluster.isPrimary) {\n log('Primary Inertia SSR server process started...')\n\n for (let i = 0; i < availableParallelism(); i++) {\n cluster.fork()\n }\n\n cluster.on('message', (_worker, message) => {\n if (message === 'shutdown') {\n for (const id in cluster.workers) {\n cluster.workers[id]?.kill()\n }\n\n process.exit()\n }\n })\n\n return render\n }\n\n const handleRender = async (request: IncomingMessage, response: ServerResponse) => {\n const page: Page = JSON.parse(await readableToString(request))\n\n // Suppress framework warnings during render (they clutter the output)\n const originalWarn = console.warn\n\n if (handleErrors) {\n console.warn = () => {}\n }\n\n try {\n const result = await render(page)\n\n response.writeHead(200, { 'Content-Type': 'application/json', Server: 'Inertia.js SSR' })\n response.write(JSON.stringify(result))\n } catch (e) {\n const error = e as Error\n\n if (!handleErrors) {\n throw error\n }\n\n const classified = classifySSRError(error, page.component, page.url)\n console.error(formatConsoleError(classified))\n\n response.writeHead(500, { 'Content-Type': 'application/json', Server: 'Inertia.js SSR' })\n response.write(JSON.stringify(classified))\n } finally {\n console.warn = originalWarn\n }\n }\n\n const routes: Record<string, RouteHandler> = {\n '/health': async () => ({ status: 'OK', timestamp: Date.now() }),\n '/shutdown': async () => {\n if (cluster.isWorker) {\n process.send?.('shutdown')\n }\n\n process.exit()\n },\n '/render': handleRender,\n '/404': async () => ({ status: 'NOT_FOUND', timestamp: Date.now() }),\n }\n\n createServer(async (request, response) => {\n const dispatchRoute = routes[request.url as string] ?? routes['/404']\n const result = await dispatchRoute(request, response)\n\n if (!response.headersSent) {\n response.writeHead(200, { 'Content-Type': 'application/json', Server: 'Inertia.js SSR' })\n response.write(JSON.stringify(result))\n }\n\n response.end()\n }).listen(port, () => log('Inertia SSR server started.'))\n\n log(`Starting SSR server on port ${port}...`)\n\n // Return the render callback so it can be exported for Vite SSR dev mode\n return render\n}\n", "/**\n * SSR Error Classification\n *\n * This module detects common SSR errors and provides helpful hints\n * to developers on how to fix them. The most common issue is using\n * browser-specific APIs (like window, document) that don't exist\n * in the Node.js server environment.\n */\n\nexport type SSRErrorType = 'browser-api' | 'component-resolution' | 'render' | 'unknown'\n\ntype SourceMapResolver = (\n file: string,\n line: number,\n column: number,\n) => { file: string; line: number; column: number } | null\n\nlet sourceMapResolver: SourceMapResolver | null = null\n\nexport function setSourceMapResolver(resolver: SourceMapResolver | null): void {\n sourceMapResolver = resolver\n}\n\nexport interface ClassifiedSSRError {\n error: string\n type: SSRErrorType\n component?: string\n url?: string\n browserApi?: string\n hint: string\n stack?: string\n sourceLocation?: string\n timestamp: string\n}\n\nconst BROWSER_APIS: Record<string, string> = {\n // Global objects\n window: 'The global window object',\n document: 'The DOM document object',\n navigator: 'The navigator object',\n location: 'The location object',\n history: 'The browser history API',\n screen: 'The screen object',\n localStorage: 'Browser local storage',\n sessionStorage: 'Browser session storage',\n\n // Viewport properties (accessed via window.X)\n innerWidth: 'Browser viewport width',\n innerHeight: 'Browser viewport height',\n outerWidth: 'Browser window width',\n outerHeight: 'Browser window height',\n scrollX: 'Horizontal scroll position',\n scrollY: 'Vertical scroll position',\n devicePixelRatio: 'The device pixel ratio',\n matchMedia: 'The matchMedia function',\n\n // Observers (commonly instantiated at module level)\n IntersectionObserver: 'The IntersectionObserver API',\n ResizeObserver: 'The ResizeObserver API',\n MutationObserver: 'The MutationObserver API',\n\n // Timing functions (commonly called at module level)\n requestAnimationFrame: 'The requestAnimationFrame function',\n requestIdleCallback: 'The requestIdleCallback function',\n\n // Constructors that might be used at module level\n Image: 'The Image constructor',\n Audio: 'The Audio constructor',\n Worker: 'The Worker constructor',\n BroadcastChannel: 'The BroadcastChannel constructor',\n\n // Network (older Node.js versions)\n fetch: 'The fetch API',\n XMLHttpRequest: 'The XMLHttpRequest API',\n}\n\nfunction detectBrowserApi(error: Error): string | null {\n const message = error.message.toLowerCase()\n\n for (const api of Object.keys(BROWSER_APIS)) {\n const patterns = [\n `${api.toLowerCase()} is not defined`,\n `'${api.toLowerCase()}' is not defined`,\n `\"${api.toLowerCase()}\" is not defined`,\n `cannot read properties of undefined (reading '${api.toLowerCase()}')`,\n `cannot read property '${api.toLowerCase()}'`,\n ]\n\n if (patterns.some((pattern) => message.includes(pattern))) {\n return api\n }\n }\n\n return null\n}\n\nfunction isComponentResolutionError(error: Error): boolean {\n const message = error.message.toLowerCase()\n\n return (\n message.includes('cannot find module') ||\n message.includes('failed to resolve') ||\n message.includes('module not found') ||\n message.includes('could not resolve')\n )\n}\n\nconst LIFECYCLE_HOOKS = 'onMounted/useEffect/onMount'\n\nfunction getBrowserApiHint(api: string): string {\n const apiDescription = BROWSER_APIS[api] || `The \"${api}\" object`\n\n if (['localStorage', 'sessionStorage'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Check \"typeof ${api} !== 'undefined'\" before using it, ` +\n `or move the code to a ${LIFECYCLE_HOOKS} lifecycle hook.`\n )\n }\n\n if (['window', 'document'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Wrap browser-specific code in a ${LIFECYCLE_HOOKS} lifecycle hook, ` +\n `or check \"typeof ${api} !== 'undefined'\" before using it.`\n )\n }\n\n if (['IntersectionObserver', 'ResizeObserver', 'MutationObserver'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Create observers inside a ${LIFECYCLE_HOOKS} lifecycle hook, not at the module level.`\n )\n }\n\n if (['fetch', 'XMLHttpRequest'].includes(api)) {\n return (\n `${apiDescription} may not be available in all Node.js versions. ` +\n `For SSR, ensure data fetching happens on the server (in your controller) ` +\n `and is passed as props, or use a ${LIFECYCLE_HOOKS} hook for client-side fetching.`\n )\n }\n\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Move this code to a ${LIFECYCLE_HOOKS} lifecycle hook, or guard it with ` +\n `\"typeof ${api} !== 'undefined'\".`\n )\n}\n\nfunction getComponentResolutionHint(component?: string): string {\n const componentPart = component ? ` \"${component}\"` : ''\n\n return (\n `Could not resolve component${componentPart}. ` +\n `Check that the file exists and the path is correct. ` +\n `Ensure the component name matches the file name exactly (case-sensitive).`\n )\n}\n\nfunction getRenderErrorHint(): string {\n return (\n 'An error occurred while rendering the component. ' +\n 'Check the component for browser-specific code that runs during initialization. ' +\n 'Move any code that accesses browser APIs to a lifecycle hook.'\n )\n}\n\nfunction extractSourceLocation(stack?: string): string | undefined {\n if (!stack) {\n return undefined\n }\n\n for (const line of stack.split('\\n')) {\n if (!line.includes('at ')) {\n continue\n }\n\n if (line.includes('node_modules') || line.includes('node:')) {\n continue\n }\n\n let match = line.match(/\\(([^)]+):(\\d+):(\\d+)\\)/)\n\n if (!match) {\n match = line.match(/at\\s+(?:file:\\/\\/)?(.+):(\\d+):(\\d+)\\s*$/)\n }\n\n if (match) {\n const file = match[1].replace(/^file:\\/\\//, '')\n const lineNum = parseInt(match[2], 10)\n const colNum = parseInt(match[3], 10)\n\n if (sourceMapResolver) {\n const resolved = sourceMapResolver(file, lineNum, colNum)\n\n if (resolved) {\n return `${resolved.file}:${resolved.line}:${resolved.column}`\n }\n }\n\n return `${file}:${lineNum}:${colNum}`\n }\n }\n\n return undefined\n}\n\nexport function classifySSRError(error: Error, component?: string, url?: string): ClassifiedSSRError {\n const timestamp = new Date().toISOString()\n const base = {\n error: error.message,\n component,\n url,\n stack: error.stack,\n sourceLocation: extractSourceLocation(error.stack),\n timestamp,\n }\n\n const browserApi = detectBrowserApi(error)\n\n if (browserApi) {\n return {\n ...base,\n type: 'browser-api',\n browserApi,\n hint: getBrowserApiHint(browserApi),\n }\n }\n\n if (isComponentResolutionError(error)) {\n return {\n ...base,\n type: 'component-resolution',\n hint: getComponentResolutionHint(component),\n }\n }\n\n return {\n ...base,\n type: 'render',\n hint: getRenderErrorHint(),\n }\n}\n\nconst colors = {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n dim: '\\x1b[2m',\n bold: '\\x1b[1m',\n bgRed: '\\x1b[41m',\n white: '\\x1b[37m',\n}\n\nfunction makeRelative(path: string, root?: string): string {\n const base = root ?? process.cwd()\n\n if (path.startsWith(base + '/')) {\n return path.slice(base.length + 1)\n }\n\n return path\n}\n\nexport function formatConsoleError(\n classified: ClassifiedSSRError,\n root?: string,\n handleErrors: boolean = true,\n suppressedWarnings: string[] = [],\n): string {\n if (!handleErrors) {\n const component = classified.component ? `[${classified.component}]` : ''\n return `SSR Error ${component}: ${classified.error}`\n }\n\n const componentPart = classified.component ? ` ${colors.cyan}${classified.component}${colors.reset}` : ''\n\n const lines = [\n '',\n ` ${colors.bgRed}${colors.white}${colors.bold} SSR ERROR ${colors.reset}${componentPart}`,\n '',\n ` ${classified.error}`,\n ]\n\n if (classified.sourceLocation) {\n const relativePath = makeRelative(classified.sourceLocation, root)\n lines.push(` ${colors.dim}Source: ${relativePath}${colors.reset}`)\n }\n\n if (classified.url) {\n lines.push(` ${colors.dim}URL: ${classified.url}${colors.reset}`)\n }\n\n lines.push('', ` ${colors.yellow}Hint${colors.reset} ${classified.hint}`, '')\n\n if (classified.stack) {\n lines.push(` ${colors.dim}${classified.stack.split('\\n').join('\\n ')}${colors.reset}`, '')\n }\n\n if (suppressedWarnings.length > 0) {\n lines.push(` ${colors.dim}Suppressed ${suppressedWarnings.length} framework warning(s).${colors.reset}`, '')\n }\n\n return lines.join('\\n')\n}\n"],
4
+ "sourcesContent": ["import { originalPositionFor, TraceMap } from '@jridgewell/trace-mapping'\nimport { createServer, IncomingMessage, ServerResponse } from 'http'\nimport cluster from 'node:cluster'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { availableParallelism } from 'node:os'\nimport path from 'node:path'\nimport * as process from 'process'\nimport { classifySSRError, formatConsoleError, setSourceMapResolver } from './ssrErrors'\nimport { InertiaAppResponse, Page } from './types'\n\n// Cache parsed sourcemaps for performance\nconst sourceMaps = new Map<string, TraceMap>()\n\nsetSourceMapResolver((file: string, line: number, column: number) => {\n // Only resolve for bundled SSR files\n if (!file.includes('/ssr/') || !file.endsWith('.js')) {\n return null\n }\n\n const mapFile = file + '.map'\n\n if (!existsSync(mapFile)) {\n return null\n }\n\n let traceMap = sourceMaps.get(mapFile)\n\n if (!traceMap) {\n try {\n const mapContent = readFileSync(mapFile, 'utf-8')\n traceMap = new TraceMap(mapContent)\n sourceMaps.set(mapFile, traceMap)\n } catch {\n return null\n }\n }\n\n const original = originalPositionFor(traceMap, { line, column })\n\n if (original.source) {\n // Resolve the source path relative to the sourcemap location\n const mapDir = path.dirname(mapFile)\n const resolvedPath = path.resolve(mapDir, original.source)\n\n return {\n file: resolvedPath,\n line: original.line ?? line,\n column: original.column ?? column,\n }\n }\n\n return null\n})\n\ntype AppCallback = (page: Page) => InertiaAppResponse\ntype RouteHandler = (request: IncomingMessage, response: ServerResponse) => Promise<unknown>\ntype ServerOptions = {\n port?: number\n cluster?: boolean\n formatErrors?: boolean\n}\ntype Port = number\n\nconst readableToString: (readable: IncomingMessage) => Promise<string> = (readable) =>\n new Promise((resolve, reject) => {\n let data = ''\n readable.on('data', (chunk) => (data += chunk))\n readable.on('end', () => resolve(data))\n readable.on('error', (err) => reject(err))\n })\n\nexport default (render: AppCallback, options?: Port | ServerOptions): AppCallback => {\n const opts = typeof options === 'number' ? { port: options } : options\n const { port = 13714, cluster: useCluster = false, formatErrors = true } = opts ?? {}\n\n const log = (message: string) => {\n console.log(\n useCluster && !cluster.isPrimary\n ? `[${cluster.worker?.id ?? 'N/A'} / ${cluster.worker?.process?.pid ?? 'N/A'}] ${message}`\n : message,\n )\n }\n\n if (useCluster && cluster.isPrimary) {\n log('Primary Inertia SSR server process started...')\n\n for (let i = 0; i < availableParallelism(); i++) {\n cluster.fork()\n }\n\n cluster.on('message', (_worker, message) => {\n if (message === 'shutdown') {\n for (const id in cluster.workers) {\n cluster.workers[id]?.kill()\n }\n\n process.exit()\n }\n })\n\n return render\n }\n\n const handleRender = async (request: IncomingMessage, response: ServerResponse) => {\n const page: Page = JSON.parse(await readableToString(request))\n\n // Suppress framework warnings during render (they clutter the output)\n const originalWarn = console.warn\n\n if (formatErrors) {\n console.warn = () => {}\n }\n\n try {\n const result = await render(page)\n\n response.writeHead(200, { 'Content-Type': 'application/json', Server: 'Inertia.js SSR' })\n response.write(JSON.stringify(result))\n } catch (e) {\n const error = e as Error\n\n if (!formatErrors) {\n throw error\n }\n\n const classified = classifySSRError(error, page.component, page.url)\n console.error(formatConsoleError(classified))\n\n response.writeHead(500, { 'Content-Type': 'application/json', Server: 'Inertia.js SSR' })\n response.write(JSON.stringify(classified))\n } finally {\n console.warn = originalWarn\n }\n }\n\n const routes: Record<string, RouteHandler> = {\n '/health': async () => ({ status: 'OK', timestamp: Date.now() }),\n '/shutdown': async () => {\n if (cluster.isWorker) {\n process.send?.('shutdown')\n }\n\n process.exit()\n },\n '/render': handleRender,\n '/404': async () => ({ status: 'NOT_FOUND', timestamp: Date.now() }),\n }\n\n createServer(async (request, response) => {\n const dispatchRoute = routes[request.url as string] ?? routes['/404']\n const result = await dispatchRoute(request, response)\n\n if (!response.headersSent) {\n response.writeHead(200, { 'Content-Type': 'application/json', Server: 'Inertia.js SSR' })\n response.write(JSON.stringify(result))\n }\n\n response.end()\n }).listen(port, () => log('Inertia SSR server started.'))\n\n log(`Starting SSR server on port ${port}...`)\n\n // Return the render callback so it can be exported for Vite SSR dev mode\n return render\n}\n", "/**\n * SSR Error Classification\n *\n * This module detects common SSR errors and provides helpful hints\n * to developers on how to fix them. The most common issue is using\n * browser-specific APIs (like window, document) that don't exist\n * in the Node.js server environment.\n */\n\nexport type SSRErrorType = 'browser-api' | 'component-resolution' | 'render' | 'unknown'\n\ntype SourceMapResolver = (\n file: string,\n line: number,\n column: number,\n) => { file: string; line: number; column: number } | null\n\nlet sourceMapResolver: SourceMapResolver | null = null\n\nexport function setSourceMapResolver(resolver: SourceMapResolver | null): void {\n sourceMapResolver = resolver\n}\n\nexport interface ClassifiedSSRError {\n error: string\n type: SSRErrorType\n component?: string\n url?: string\n browserApi?: string\n hint: string\n stack?: string\n sourceLocation?: string\n timestamp: string\n}\n\nconst BROWSER_APIS: Record<string, string> = {\n // Global objects\n window: 'The global window object',\n document: 'The DOM document object',\n navigator: 'The navigator object',\n location: 'The location object',\n history: 'The browser history API',\n screen: 'The screen object',\n localStorage: 'Browser local storage',\n sessionStorage: 'Browser session storage',\n\n // Viewport properties (accessed via window.X)\n innerWidth: 'Browser viewport width',\n innerHeight: 'Browser viewport height',\n outerWidth: 'Browser window width',\n outerHeight: 'Browser window height',\n scrollX: 'Horizontal scroll position',\n scrollY: 'Vertical scroll position',\n devicePixelRatio: 'The device pixel ratio',\n matchMedia: 'The matchMedia function',\n\n // Observers (commonly instantiated at module level)\n IntersectionObserver: 'The IntersectionObserver API',\n ResizeObserver: 'The ResizeObserver API',\n MutationObserver: 'The MutationObserver API',\n\n // Timing functions (commonly called at module level)\n requestAnimationFrame: 'The requestAnimationFrame function',\n requestIdleCallback: 'The requestIdleCallback function',\n\n // Constructors that might be used at module level\n Image: 'The Image constructor',\n Audio: 'The Audio constructor',\n Worker: 'The Worker constructor',\n BroadcastChannel: 'The BroadcastChannel constructor',\n\n // Network (older Node.js versions)\n fetch: 'The fetch API',\n XMLHttpRequest: 'The XMLHttpRequest API',\n}\n\nfunction detectBrowserApi(error: Error): string | null {\n const message = error.message.toLowerCase()\n\n for (const api of Object.keys(BROWSER_APIS)) {\n const patterns = [\n `${api.toLowerCase()} is not defined`,\n `'${api.toLowerCase()}' is not defined`,\n `\"${api.toLowerCase()}\" is not defined`,\n `cannot read properties of undefined (reading '${api.toLowerCase()}')`,\n `cannot read property '${api.toLowerCase()}'`,\n ]\n\n if (patterns.some((pattern) => message.includes(pattern))) {\n return api\n }\n }\n\n return null\n}\n\nfunction isComponentResolutionError(error: Error): boolean {\n const message = error.message.toLowerCase()\n\n return (\n message.includes('cannot find module') ||\n message.includes('failed to resolve') ||\n message.includes('module not found') ||\n message.includes('could not resolve')\n )\n}\n\nconst LIFECYCLE_HOOKS = 'onMounted/useEffect/onMount'\n\nfunction getBrowserApiHint(api: string): string {\n const apiDescription = BROWSER_APIS[api] || `The \"${api}\" object`\n\n if (['localStorage', 'sessionStorage'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Check \"typeof ${api} !== 'undefined'\" before using it, ` +\n `or move the code to a ${LIFECYCLE_HOOKS} lifecycle hook.`\n )\n }\n\n if (['window', 'document'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Wrap browser-specific code in a ${LIFECYCLE_HOOKS} lifecycle hook, ` +\n `or check \"typeof ${api} !== 'undefined'\" before using it.`\n )\n }\n\n if (['IntersectionObserver', 'ResizeObserver', 'MutationObserver'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Create observers inside a ${LIFECYCLE_HOOKS} lifecycle hook, not at the module level.`\n )\n }\n\n if (['fetch', 'XMLHttpRequest'].includes(api)) {\n return (\n `${apiDescription} may not be available in all Node.js versions. ` +\n `For SSR, ensure data fetching happens on the server (in your controller) ` +\n `and is passed as props, or use a ${LIFECYCLE_HOOKS} hook for client-side fetching.`\n )\n }\n\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Move this code to a ${LIFECYCLE_HOOKS} lifecycle hook, or guard it with ` +\n `\"typeof ${api} !== 'undefined'\".`\n )\n}\n\nfunction getComponentResolutionHint(component?: string): string {\n const componentPart = component ? ` \"${component}\"` : ''\n\n return (\n `Could not resolve component${componentPart}. ` +\n `Check that the file exists and the path is correct. ` +\n `Ensure the component name matches the file name exactly (case-sensitive).`\n )\n}\n\nfunction getRenderErrorHint(): string {\n return (\n 'An error occurred while rendering the component. ' +\n 'Check the component for browser-specific code that runs during initialization. ' +\n 'Move any code that accesses browser APIs to a lifecycle hook.'\n )\n}\n\nfunction extractSourceLocation(stack?: string): string | undefined {\n if (!stack) {\n return undefined\n }\n\n for (const line of stack.split('\\n')) {\n if (!line.includes('at ')) {\n continue\n }\n\n if (line.includes('node_modules') || line.includes('node:')) {\n continue\n }\n\n let match = line.match(/\\(([^)]+):(\\d+):(\\d+)\\)/)\n\n if (!match) {\n match = line.match(/at\\s+(?:file:\\/\\/)?(.+):(\\d+):(\\d+)\\s*$/)\n }\n\n if (match) {\n const file = match[1].replace(/^file:\\/\\//, '')\n const lineNum = parseInt(match[2], 10)\n const colNum = parseInt(match[3], 10)\n\n if (sourceMapResolver) {\n const resolved = sourceMapResolver(file, lineNum, colNum)\n\n if (resolved) {\n return `${resolved.file}:${resolved.line}:${resolved.column}`\n }\n }\n\n return `${file}:${lineNum}:${colNum}`\n }\n }\n\n return undefined\n}\n\nexport function classifySSRError(error: Error, component?: string, url?: string): ClassifiedSSRError {\n const timestamp = new Date().toISOString()\n const base = {\n error: error.message,\n component,\n url,\n stack: error.stack,\n sourceLocation: extractSourceLocation(error.stack),\n timestamp,\n }\n\n const browserApi = detectBrowserApi(error)\n\n if (browserApi) {\n return {\n ...base,\n type: 'browser-api',\n browserApi,\n hint: getBrowserApiHint(browserApi),\n }\n }\n\n if (isComponentResolutionError(error)) {\n return {\n ...base,\n type: 'component-resolution',\n hint: getComponentResolutionHint(component),\n }\n }\n\n return {\n ...base,\n type: 'render',\n hint: getRenderErrorHint(),\n }\n}\n\nconst colors = {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n dim: '\\x1b[2m',\n bold: '\\x1b[1m',\n bgRed: '\\x1b[41m',\n white: '\\x1b[37m',\n}\n\nfunction makeRelative(path: string, root?: string): string {\n const base = root ?? process.cwd()\n\n if (path.startsWith(base + '/')) {\n return path.slice(base.length + 1)\n }\n\n return path\n}\n\nexport function formatConsoleError(\n classified: ClassifiedSSRError,\n root?: string,\n formatErrors: boolean = true,\n suppressedWarnings: string[] = [],\n): string {\n if (!formatErrors) {\n const component = classified.component ? `[${classified.component}]` : ''\n return `SSR Error ${component}: ${classified.error}`\n }\n\n const componentPart = classified.component ? ` ${colors.cyan}${classified.component}${colors.reset}` : ''\n\n const lines = [\n '',\n ` ${colors.bgRed}${colors.white}${colors.bold} SSR ERROR ${colors.reset}${componentPart}`,\n '',\n ` ${classified.error}`,\n ]\n\n if (classified.sourceLocation) {\n const relativePath = makeRelative(classified.sourceLocation, root)\n lines.push(` ${colors.dim}Source: ${relativePath}${colors.reset}`)\n }\n\n if (classified.url) {\n lines.push(` ${colors.dim}URL: ${classified.url}${colors.reset}`)\n }\n\n lines.push('', ` ${colors.yellow}Hint${colors.reset} ${classified.hint}`, '')\n\n if (classified.stack) {\n lines.push(` ${colors.dim}${classified.stack.split('\\n').join('\\n ')}${colors.reset}`, '')\n }\n\n if (suppressedWarnings.length > 0) {\n lines.push(` ${colors.dim}Suppressed ${suppressedWarnings.length} framework warning(s).${colors.reset}`, '')\n }\n\n return lines.join('\\n')\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["process", "path"]
7
7
  }
package/dist/ssrErrors.js CHANGED
@@ -161,8 +161,8 @@ function makeRelative(path, root) {
161
161
  }
162
162
  return path;
163
163
  }
164
- function formatConsoleError(classified, root, handleErrors = true, suppressedWarnings = []) {
165
- if (!handleErrors) {
164
+ function formatConsoleError(classified, root, formatErrors = true, suppressedWarnings = []) {
165
+ if (!formatErrors) {
166
166
  const component = classified.component ? `[${classified.component}]` : "";
167
167
  return `SSR Error ${component}: ${classified.error}`;
168
168
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/ssrErrors.ts"],
4
- "sourcesContent": ["/**\n * SSR Error Classification\n *\n * This module detects common SSR errors and provides helpful hints\n * to developers on how to fix them. The most common issue is using\n * browser-specific APIs (like window, document) that don't exist\n * in the Node.js server environment.\n */\n\nexport type SSRErrorType = 'browser-api' | 'component-resolution' | 'render' | 'unknown'\n\ntype SourceMapResolver = (\n file: string,\n line: number,\n column: number,\n) => { file: string; line: number; column: number } | null\n\nlet sourceMapResolver: SourceMapResolver | null = null\n\nexport function setSourceMapResolver(resolver: SourceMapResolver | null): void {\n sourceMapResolver = resolver\n}\n\nexport interface ClassifiedSSRError {\n error: string\n type: SSRErrorType\n component?: string\n url?: string\n browserApi?: string\n hint: string\n stack?: string\n sourceLocation?: string\n timestamp: string\n}\n\nconst BROWSER_APIS: Record<string, string> = {\n // Global objects\n window: 'The global window object',\n document: 'The DOM document object',\n navigator: 'The navigator object',\n location: 'The location object',\n history: 'The browser history API',\n screen: 'The screen object',\n localStorage: 'Browser local storage',\n sessionStorage: 'Browser session storage',\n\n // Viewport properties (accessed via window.X)\n innerWidth: 'Browser viewport width',\n innerHeight: 'Browser viewport height',\n outerWidth: 'Browser window width',\n outerHeight: 'Browser window height',\n scrollX: 'Horizontal scroll position',\n scrollY: 'Vertical scroll position',\n devicePixelRatio: 'The device pixel ratio',\n matchMedia: 'The matchMedia function',\n\n // Observers (commonly instantiated at module level)\n IntersectionObserver: 'The IntersectionObserver API',\n ResizeObserver: 'The ResizeObserver API',\n MutationObserver: 'The MutationObserver API',\n\n // Timing functions (commonly called at module level)\n requestAnimationFrame: 'The requestAnimationFrame function',\n requestIdleCallback: 'The requestIdleCallback function',\n\n // Constructors that might be used at module level\n Image: 'The Image constructor',\n Audio: 'The Audio constructor',\n Worker: 'The Worker constructor',\n BroadcastChannel: 'The BroadcastChannel constructor',\n\n // Network (older Node.js versions)\n fetch: 'The fetch API',\n XMLHttpRequest: 'The XMLHttpRequest API',\n}\n\nfunction detectBrowserApi(error: Error): string | null {\n const message = error.message.toLowerCase()\n\n for (const api of Object.keys(BROWSER_APIS)) {\n const patterns = [\n `${api.toLowerCase()} is not defined`,\n `'${api.toLowerCase()}' is not defined`,\n `\"${api.toLowerCase()}\" is not defined`,\n `cannot read properties of undefined (reading '${api.toLowerCase()}')`,\n `cannot read property '${api.toLowerCase()}'`,\n ]\n\n if (patterns.some((pattern) => message.includes(pattern))) {\n return api\n }\n }\n\n return null\n}\n\nfunction isComponentResolutionError(error: Error): boolean {\n const message = error.message.toLowerCase()\n\n return (\n message.includes('cannot find module') ||\n message.includes('failed to resolve') ||\n message.includes('module not found') ||\n message.includes('could not resolve')\n )\n}\n\nconst LIFECYCLE_HOOKS = 'onMounted/useEffect/onMount'\n\nfunction getBrowserApiHint(api: string): string {\n const apiDescription = BROWSER_APIS[api] || `The \"${api}\" object`\n\n if (['localStorage', 'sessionStorage'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Check \"typeof ${api} !== 'undefined'\" before using it, ` +\n `or move the code to a ${LIFECYCLE_HOOKS} lifecycle hook.`\n )\n }\n\n if (['window', 'document'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Wrap browser-specific code in a ${LIFECYCLE_HOOKS} lifecycle hook, ` +\n `or check \"typeof ${api} !== 'undefined'\" before using it.`\n )\n }\n\n if (['IntersectionObserver', 'ResizeObserver', 'MutationObserver'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Create observers inside a ${LIFECYCLE_HOOKS} lifecycle hook, not at the module level.`\n )\n }\n\n if (['fetch', 'XMLHttpRequest'].includes(api)) {\n return (\n `${apiDescription} may not be available in all Node.js versions. ` +\n `For SSR, ensure data fetching happens on the server (in your controller) ` +\n `and is passed as props, or use a ${LIFECYCLE_HOOKS} hook for client-side fetching.`\n )\n }\n\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Move this code to a ${LIFECYCLE_HOOKS} lifecycle hook, or guard it with ` +\n `\"typeof ${api} !== 'undefined'\".`\n )\n}\n\nfunction getComponentResolutionHint(component?: string): string {\n const componentPart = component ? ` \"${component}\"` : ''\n\n return (\n `Could not resolve component${componentPart}. ` +\n `Check that the file exists and the path is correct. ` +\n `Ensure the component name matches the file name exactly (case-sensitive).`\n )\n}\n\nfunction getRenderErrorHint(): string {\n return (\n 'An error occurred while rendering the component. ' +\n 'Check the component for browser-specific code that runs during initialization. ' +\n 'Move any code that accesses browser APIs to a lifecycle hook.'\n )\n}\n\nfunction extractSourceLocation(stack?: string): string | undefined {\n if (!stack) {\n return undefined\n }\n\n for (const line of stack.split('\\n')) {\n if (!line.includes('at ')) {\n continue\n }\n\n if (line.includes('node_modules') || line.includes('node:')) {\n continue\n }\n\n let match = line.match(/\\(([^)]+):(\\d+):(\\d+)\\)/)\n\n if (!match) {\n match = line.match(/at\\s+(?:file:\\/\\/)?(.+):(\\d+):(\\d+)\\s*$/)\n }\n\n if (match) {\n const file = match[1].replace(/^file:\\/\\//, '')\n const lineNum = parseInt(match[2], 10)\n const colNum = parseInt(match[3], 10)\n\n if (sourceMapResolver) {\n const resolved = sourceMapResolver(file, lineNum, colNum)\n\n if (resolved) {\n return `${resolved.file}:${resolved.line}:${resolved.column}`\n }\n }\n\n return `${file}:${lineNum}:${colNum}`\n }\n }\n\n return undefined\n}\n\nexport function classifySSRError(error: Error, component?: string, url?: string): ClassifiedSSRError {\n const timestamp = new Date().toISOString()\n const base = {\n error: error.message,\n component,\n url,\n stack: error.stack,\n sourceLocation: extractSourceLocation(error.stack),\n timestamp,\n }\n\n const browserApi = detectBrowserApi(error)\n\n if (browserApi) {\n return {\n ...base,\n type: 'browser-api',\n browserApi,\n hint: getBrowserApiHint(browserApi),\n }\n }\n\n if (isComponentResolutionError(error)) {\n return {\n ...base,\n type: 'component-resolution',\n hint: getComponentResolutionHint(component),\n }\n }\n\n return {\n ...base,\n type: 'render',\n hint: getRenderErrorHint(),\n }\n}\n\nconst colors = {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n dim: '\\x1b[2m',\n bold: '\\x1b[1m',\n bgRed: '\\x1b[41m',\n white: '\\x1b[37m',\n}\n\nfunction makeRelative(path: string, root?: string): string {\n const base = root ?? process.cwd()\n\n if (path.startsWith(base + '/')) {\n return path.slice(base.length + 1)\n }\n\n return path\n}\n\nexport function formatConsoleError(\n classified: ClassifiedSSRError,\n root?: string,\n handleErrors: boolean = true,\n suppressedWarnings: string[] = [],\n): string {\n if (!handleErrors) {\n const component = classified.component ? `[${classified.component}]` : ''\n return `SSR Error ${component}: ${classified.error}`\n }\n\n const componentPart = classified.component ? ` ${colors.cyan}${classified.component}${colors.reset}` : ''\n\n const lines = [\n '',\n ` ${colors.bgRed}${colors.white}${colors.bold} SSR ERROR ${colors.reset}${componentPart}`,\n '',\n ` ${classified.error}`,\n ]\n\n if (classified.sourceLocation) {\n const relativePath = makeRelative(classified.sourceLocation, root)\n lines.push(` ${colors.dim}Source: ${relativePath}${colors.reset}`)\n }\n\n if (classified.url) {\n lines.push(` ${colors.dim}URL: ${classified.url}${colors.reset}`)\n }\n\n lines.push('', ` ${colors.yellow}Hint${colors.reset} ${classified.hint}`, '')\n\n if (classified.stack) {\n lines.push(` ${colors.dim}${classified.stack.split('\\n').join('\\n ')}${colors.reset}`, '')\n }\n\n if (suppressedWarnings.length > 0) {\n lines.push(` ${colors.dim}Suppressed ${suppressedWarnings.length} framework warning(s).${colors.reset}`, '')\n }\n\n return lines.join('\\n')\n}\n"],
4
+ "sourcesContent": ["/**\n * SSR Error Classification\n *\n * This module detects common SSR errors and provides helpful hints\n * to developers on how to fix them. The most common issue is using\n * browser-specific APIs (like window, document) that don't exist\n * in the Node.js server environment.\n */\n\nexport type SSRErrorType = 'browser-api' | 'component-resolution' | 'render' | 'unknown'\n\ntype SourceMapResolver = (\n file: string,\n line: number,\n column: number,\n) => { file: string; line: number; column: number } | null\n\nlet sourceMapResolver: SourceMapResolver | null = null\n\nexport function setSourceMapResolver(resolver: SourceMapResolver | null): void {\n sourceMapResolver = resolver\n}\n\nexport interface ClassifiedSSRError {\n error: string\n type: SSRErrorType\n component?: string\n url?: string\n browserApi?: string\n hint: string\n stack?: string\n sourceLocation?: string\n timestamp: string\n}\n\nconst BROWSER_APIS: Record<string, string> = {\n // Global objects\n window: 'The global window object',\n document: 'The DOM document object',\n navigator: 'The navigator object',\n location: 'The location object',\n history: 'The browser history API',\n screen: 'The screen object',\n localStorage: 'Browser local storage',\n sessionStorage: 'Browser session storage',\n\n // Viewport properties (accessed via window.X)\n innerWidth: 'Browser viewport width',\n innerHeight: 'Browser viewport height',\n outerWidth: 'Browser window width',\n outerHeight: 'Browser window height',\n scrollX: 'Horizontal scroll position',\n scrollY: 'Vertical scroll position',\n devicePixelRatio: 'The device pixel ratio',\n matchMedia: 'The matchMedia function',\n\n // Observers (commonly instantiated at module level)\n IntersectionObserver: 'The IntersectionObserver API',\n ResizeObserver: 'The ResizeObserver API',\n MutationObserver: 'The MutationObserver API',\n\n // Timing functions (commonly called at module level)\n requestAnimationFrame: 'The requestAnimationFrame function',\n requestIdleCallback: 'The requestIdleCallback function',\n\n // Constructors that might be used at module level\n Image: 'The Image constructor',\n Audio: 'The Audio constructor',\n Worker: 'The Worker constructor',\n BroadcastChannel: 'The BroadcastChannel constructor',\n\n // Network (older Node.js versions)\n fetch: 'The fetch API',\n XMLHttpRequest: 'The XMLHttpRequest API',\n}\n\nfunction detectBrowserApi(error: Error): string | null {\n const message = error.message.toLowerCase()\n\n for (const api of Object.keys(BROWSER_APIS)) {\n const patterns = [\n `${api.toLowerCase()} is not defined`,\n `'${api.toLowerCase()}' is not defined`,\n `\"${api.toLowerCase()}\" is not defined`,\n `cannot read properties of undefined (reading '${api.toLowerCase()}')`,\n `cannot read property '${api.toLowerCase()}'`,\n ]\n\n if (patterns.some((pattern) => message.includes(pattern))) {\n return api\n }\n }\n\n return null\n}\n\nfunction isComponentResolutionError(error: Error): boolean {\n const message = error.message.toLowerCase()\n\n return (\n message.includes('cannot find module') ||\n message.includes('failed to resolve') ||\n message.includes('module not found') ||\n message.includes('could not resolve')\n )\n}\n\nconst LIFECYCLE_HOOKS = 'onMounted/useEffect/onMount'\n\nfunction getBrowserApiHint(api: string): string {\n const apiDescription = BROWSER_APIS[api] || `The \"${api}\" object`\n\n if (['localStorage', 'sessionStorage'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Check \"typeof ${api} !== 'undefined'\" before using it, ` +\n `or move the code to a ${LIFECYCLE_HOOKS} lifecycle hook.`\n )\n }\n\n if (['window', 'document'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Wrap browser-specific code in a ${LIFECYCLE_HOOKS} lifecycle hook, ` +\n `or check \"typeof ${api} !== 'undefined'\" before using it.`\n )\n }\n\n if (['IntersectionObserver', 'ResizeObserver', 'MutationObserver'].includes(api)) {\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Create observers inside a ${LIFECYCLE_HOOKS} lifecycle hook, not at the module level.`\n )\n }\n\n if (['fetch', 'XMLHttpRequest'].includes(api)) {\n return (\n `${apiDescription} may not be available in all Node.js versions. ` +\n `For SSR, ensure data fetching happens on the server (in your controller) ` +\n `and is passed as props, or use a ${LIFECYCLE_HOOKS} hook for client-side fetching.`\n )\n }\n\n return (\n `${apiDescription} doesn't exist in Node.js. ` +\n `Move this code to a ${LIFECYCLE_HOOKS} lifecycle hook, or guard it with ` +\n `\"typeof ${api} !== 'undefined'\".`\n )\n}\n\nfunction getComponentResolutionHint(component?: string): string {\n const componentPart = component ? ` \"${component}\"` : ''\n\n return (\n `Could not resolve component${componentPart}. ` +\n `Check that the file exists and the path is correct. ` +\n `Ensure the component name matches the file name exactly (case-sensitive).`\n )\n}\n\nfunction getRenderErrorHint(): string {\n return (\n 'An error occurred while rendering the component. ' +\n 'Check the component for browser-specific code that runs during initialization. ' +\n 'Move any code that accesses browser APIs to a lifecycle hook.'\n )\n}\n\nfunction extractSourceLocation(stack?: string): string | undefined {\n if (!stack) {\n return undefined\n }\n\n for (const line of stack.split('\\n')) {\n if (!line.includes('at ')) {\n continue\n }\n\n if (line.includes('node_modules') || line.includes('node:')) {\n continue\n }\n\n let match = line.match(/\\(([^)]+):(\\d+):(\\d+)\\)/)\n\n if (!match) {\n match = line.match(/at\\s+(?:file:\\/\\/)?(.+):(\\d+):(\\d+)\\s*$/)\n }\n\n if (match) {\n const file = match[1].replace(/^file:\\/\\//, '')\n const lineNum = parseInt(match[2], 10)\n const colNum = parseInt(match[3], 10)\n\n if (sourceMapResolver) {\n const resolved = sourceMapResolver(file, lineNum, colNum)\n\n if (resolved) {\n return `${resolved.file}:${resolved.line}:${resolved.column}`\n }\n }\n\n return `${file}:${lineNum}:${colNum}`\n }\n }\n\n return undefined\n}\n\nexport function classifySSRError(error: Error, component?: string, url?: string): ClassifiedSSRError {\n const timestamp = new Date().toISOString()\n const base = {\n error: error.message,\n component,\n url,\n stack: error.stack,\n sourceLocation: extractSourceLocation(error.stack),\n timestamp,\n }\n\n const browserApi = detectBrowserApi(error)\n\n if (browserApi) {\n return {\n ...base,\n type: 'browser-api',\n browserApi,\n hint: getBrowserApiHint(browserApi),\n }\n }\n\n if (isComponentResolutionError(error)) {\n return {\n ...base,\n type: 'component-resolution',\n hint: getComponentResolutionHint(component),\n }\n }\n\n return {\n ...base,\n type: 'render',\n hint: getRenderErrorHint(),\n }\n}\n\nconst colors = {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n dim: '\\x1b[2m',\n bold: '\\x1b[1m',\n bgRed: '\\x1b[41m',\n white: '\\x1b[37m',\n}\n\nfunction makeRelative(path: string, root?: string): string {\n const base = root ?? process.cwd()\n\n if (path.startsWith(base + '/')) {\n return path.slice(base.length + 1)\n }\n\n return path\n}\n\nexport function formatConsoleError(\n classified: ClassifiedSSRError,\n root?: string,\n formatErrors: boolean = true,\n suppressedWarnings: string[] = [],\n): string {\n if (!formatErrors) {\n const component = classified.component ? `[${classified.component}]` : ''\n return `SSR Error ${component}: ${classified.error}`\n }\n\n const componentPart = classified.component ? ` ${colors.cyan}${classified.component}${colors.reset}` : ''\n\n const lines = [\n '',\n ` ${colors.bgRed}${colors.white}${colors.bold} SSR ERROR ${colors.reset}${componentPart}`,\n '',\n ` ${classified.error}`,\n ]\n\n if (classified.sourceLocation) {\n const relativePath = makeRelative(classified.sourceLocation, root)\n lines.push(` ${colors.dim}Source: ${relativePath}${colors.reset}`)\n }\n\n if (classified.url) {\n lines.push(` ${colors.dim}URL: ${classified.url}${colors.reset}`)\n }\n\n lines.push('', ` ${colors.yellow}Hint${colors.reset} ${classified.hint}`, '')\n\n if (classified.stack) {\n lines.push(` ${colors.dim}${classified.stack.split('\\n').join('\\n ')}${colors.reset}`, '')\n }\n\n if (suppressedWarnings.length > 0) {\n lines.push(` ${colors.dim}Suppressed ${suppressedWarnings.length} framework warning(s).${colors.reset}`, '')\n }\n\n return lines.join('\\n')\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inertiajs/core",
3
- "version": "3.0.0-beta.6",
3
+ "version": "3.0.0-beta.7",
4
4
  "license": "MIT",
5
5
  "description": "A framework for creating server-driven single page apps.",
6
6
  "contributors": [
@@ -51,7 +51,7 @@
51
51
  "dependencies": {
52
52
  "@jridgewell/trace-mapping": "^0.3.31",
53
53
  "es-toolkit": "^1.33.0",
54
- "laravel-precognition": "2.0.0-beta.5"
54
+ "laravel-precognition": "^2.0.0"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "axios": "^1.13.2"
@@ -1,16 +1,17 @@
1
1
  import { HttpResponse } from './types';
2
- export declare class HttpResponseError extends Error {
3
- readonly response: HttpResponse;
2
+ export declare class HttpError extends Error {
3
+ readonly code: string;
4
4
  readonly url?: string;
5
+ constructor(message: string, code: string, url?: string);
6
+ }
7
+ export declare class HttpResponseError extends HttpError {
8
+ readonly response: HttpResponse;
5
9
  constructor(message: string, response: HttpResponse, url?: string);
6
10
  }
7
- export declare class HttpCancelledError extends Error {
8
- readonly url?: string;
11
+ export declare class HttpCancelledError extends HttpError {
9
12
  constructor(message?: string, url?: string);
10
13
  }
11
- export declare class HttpNetworkError extends Error {
14
+ export declare class HttpNetworkError extends HttpError {
12
15
  readonly cause?: Error;
13
- readonly code = "ERR_NETWORK";
14
- readonly url?: string;
15
16
  constructor(message: string, url?: string, cause?: Error);
16
17
  }
package/types/index.d.ts CHANGED
@@ -9,9 +9,9 @@ export { objectToFormData } from './formData';
9
9
  export { formDataToObject } from './formObject';
10
10
  export { default as createHeadManager } from './head';
11
11
  export { http } from './http';
12
- export { HttpCancelledError, HttpNetworkError, HttpResponseError } from './httpErrors';
12
+ export { HttpCancelledError, HttpError, HttpNetworkError, HttpResponseError } from './httpErrors';
13
13
  export { default as useInfiniteScroll } from './infiniteScroll';
14
- export { createLayoutPropsStore, normalizeLayouts, type LayoutDefinition, type LayoutPropsStore } from './layout';
14
+ export { createLayoutPropsStore, isPropsObject, normalizeLayouts, type LayoutCallbackReturn, type LayoutDefinition, type LayoutPropsStore, } from './layout';
15
15
  export { shouldIntercept, shouldNavigate } from './navigationEvents';
16
16
  export { progress, default as setupProgress } from './progress';
17
17
  export { FormComponentResetSymbol, resetFormFields } from './resetFormFields';
package/types/layout.d.ts CHANGED
@@ -4,6 +4,13 @@ export interface LayoutDefinition<Component> {
4
4
  props: Record<string, unknown>;
5
5
  name?: string;
6
6
  }
7
+ export type LayoutCallbackReturn<C> = C | [C, Record<string, unknown>?] | C[] | (C | [C, Record<string, unknown>?])[] | {
8
+ component: C;
9
+ props?: Record<string, unknown>;
10
+ } | Record<string, C | [C, Record<string, unknown>?] | {
11
+ component: C;
12
+ props?: Record<string, unknown>;
13
+ }> | Partial<LayoutProps>;
7
14
  export interface LayoutPropsStore {
8
15
  set(props: Partial<LayoutProps>): void;
9
16
  setFor<K extends keyof NamedLayoutProps>(name: K, props: Partial<NamedLayoutProps[K]>): void;
@@ -16,6 +23,7 @@ export interface LayoutPropsStore {
16
23
  }
17
24
  export declare function createLayoutPropsStore(): LayoutPropsStore;
18
25
  type ComponentCheck<T> = (value: unknown) => value is T;
26
+ export declare function isPropsObject<T>(value: unknown, isComponent: ComponentCheck<T>): boolean;
19
27
  /**
20
28
  * Normalizes layout definitions into a consistent structure.
21
29
  */
package/types/router.d.ts CHANGED
@@ -20,10 +20,6 @@ export declare class Router {
20
20
  remember(data: unknown, key?: string): void;
21
21
  restore<T = unknown>(key?: string): T | undefined;
22
22
  on<TEventName extends GlobalEventNames>(type: TEventName, callback: (event: GlobalEvent<TEventName>) => GlobalEventResult<TEventName>): VoidFunction;
23
- /**
24
- * @deprecated Use cancelAll() instead.
25
- */
26
- cancel(): void;
27
23
  hasPendingOptimistic(): boolean;
28
24
  cancelAll({ async, prefetch, sync }?: {
29
25
  async?: boolean | undefined;
package/types/server.d.ts CHANGED
@@ -3,7 +3,7 @@ type AppCallback = (page: Page) => InertiaAppResponse;
3
3
  type ServerOptions = {
4
4
  port?: number;
5
5
  cluster?: boolean;
6
- handleErrors?: boolean;
6
+ formatErrors?: boolean;
7
7
  };
8
8
  type Port = number;
9
9
  declare const _default: (render: AppCallback, options?: Port | ServerOptions) => AppCallback;
@@ -25,5 +25,5 @@ export interface ClassifiedSSRError {
25
25
  timestamp: string;
26
26
  }
27
27
  export declare function classifySSRError(error: Error, component?: string, url?: string): ClassifiedSSRError;
28
- export declare function formatConsoleError(classified: ClassifiedSSRError, root?: string, handleErrors?: boolean, suppressedWarnings?: string[]): string;
28
+ export declare function formatConsoleError(classified: ClassifiedSSRError, root?: string, formatErrors?: boolean, suppressedWarnings?: string[]): string;
29
29
  export {};
package/types/types.d.ts CHANGED
@@ -290,7 +290,7 @@ export type GlobalEventsMap<T extends RequestPayload = RequestPayload> = {
290
290
  networkError: {
291
291
  parameters: [Error];
292
292
  details: {
293
- exception: Error;
293
+ error: Error;
294
294
  };
295
295
  result: boolean | void;
296
296
  };
@@ -379,7 +379,8 @@ type FirstLevelOptional<T> = {
379
379
  type PagesOption = string | {
380
380
  path: string;
381
381
  extension?: string | string[];
382
- transform?: (name: string) => string;
382
+ lazy?: boolean;
383
+ transform?: (name: string, page: Page) => string;
383
384
  };
384
385
  export type ProgressOptions = {
385
386
  delay?: number;
@@ -656,7 +657,7 @@ export type UseHttpOptions<TResponse = unknown> = {
656
657
  };
657
658
  export type UseHttpSubmitOptions<TResponse = unknown, TForm = unknown> = UseHttpOptions<TResponse> & {
658
659
  headers?: HttpRequestHeaders;
659
- optimistic?: (currentData: TForm) => Partial<TForm>;
660
+ optimistic?: (currentData: TForm) => Partial<TForm> | void;
660
661
  };
661
662
  declare global {
662
663
  interface DocumentEventMap {