{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport {\n  originalPositionFor,\n  TraceMap,\n  type SourceMapInput,\n} from '@jridgewell/trace-mapping';\nimport type {\n  ErrorPayload,\n  ModuleGraph,\n  Plugin,\n  ResolvedConfig,\n  ViteDevServer,\n  WebSocketClient,\n} from 'vite';\n\nconst packageName = 'runtime-error-plugin';\n\n/**\n * Returns the HMR channel for listening to custom events from clients.\n *\n * Vite 5–7 expose `server.ws` (a WebSocketServer). Future versions may\n * move this to `server.environments.client.hot`. We try both so the\n * plugin keeps working if `server.ws` is eventually removed.\n */\nfunction getHmrChannel(server: ViteDevServer) {\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  const s = server as any;\n\n  if (s.ws) {\n    return s.ws;\n  }\n\n  // Vite Environment API fallback (experimental in Vite 6+)\n  if (s.environments?.client?.hot) {\n    return s.environments.client.hot;\n  }\n\n  return undefined;\n}\n\n/**\n * Returns the module graph used to resolve source-mapped stack traces.\n *\n * Similar to `getHmrChannel`, we fall back to the per-environment module\n * graph when `server.moduleGraph` is not available.\n */\nfunction getModuleGraph(server: ViteDevServer): ModuleGraph | undefined {\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  const s = server as any;\n\n  if (s.moduleGraph) {\n    return s.moduleGraph;\n  }\n\n  if (s.environments?.client?.moduleGraph) {\n    return s.environments.client.moduleGraph;\n  }\n\n  return undefined;\n}\n\nexport default function viteRuntimeErrorOverlayPlugin(options?: {\n  filter?: (error: Error) => boolean;\n}): Plugin {\n  let resolvedBase = '/';\n\n  return {\n    name: packageName,\n\n    apply(config, env) {\n      return env.command === 'serve' && !config.ssr;\n    },\n\n    configResolved(config: ResolvedConfig) {\n      resolvedBase = config.base ?? '/';\n    },\n\n    transformIndexHtml() {\n      // Use the resolved base path so the import works when Vite is\n      // served under a non-root base (e.g. /my-app/@vite/client).\n      const viteClientPath = `${resolvedBase}@vite/client`.replace(\n        /\\/\\//g,\n        '/',\n      );\n\n      return [\n        {\n          tag: 'script',\n          attrs: { type: 'module' },\n          children: clientScript(viteClientPath),\n        },\n      ];\n    },\n\n    configureServer(server) {\n      const hmr = getHmrChannel(server);\n      if (!hmr) {\n        server.config.logger.warn(\n          `[${packageName}] Could not find HMR channel (server.ws or server.environments.client.hot). Runtime error overlay is disabled.`,\n        );\n\n        return;\n      }\n\n      hmr.on(MESSAGE_TYPE, (data: unknown, client: WebSocketClient) => {\n        const error = Object.assign(new Error(), data);\n        if (!error.stack) {\n          return;\n        }\n\n        if (options?.filter && !options.filter(error)) {\n          return;\n        }\n\n        const moduleGraph = getModuleGraph(server);\n        if (!moduleGraph) {\n          return;\n        }\n\n        const { stack, loc } = rewriteStacktrace(\n          error.stack,\n          moduleGraph,\n          resolvedBase,\n        );\n\n        const err: ErrorPayload['err'] = {\n          name: error.name,\n          message: error.message,\n          stack,\n          loc,\n          plugin: packageName,\n        };\n\n        if (loc?.file) {\n          err.id = loc?.file;\n          const source = readFileSync(loc.file, 'utf-8');\n          err.frame = generateCodeFrame(source, {\n            line: loc.line,\n            column: loc.column - 1,\n          });\n        }\n\n        // Log structured error so the runtime error can be passed to the agentReadWorkflowOutputBuffer\n        server.config.logger.error(\n          `[RUNTIME_ERROR]${JSON.stringify({\n            type: 'runtime-error',\n            timestamp: Date.now(),\n            name: error.name,\n            message: error.message,\n            stack,\n            loc,\n            frame: err.frame,\n          })}`,\n        );\n\n        client.send({\n          type: 'error',\n          err,\n        });\n\n        // Notify the client that the error passed the filter so it can\n        // inform the parent frame for analytics tracking.\n        client.send(NOTIFY_TYPE, {});\n      });\n    },\n  };\n}\n\nconst MESSAGE_TYPE = `${packageName}:error`;\nconst NOTIFY_TYPE = `${packageName}:notify-parent`;\n\nfunction clientScript(viteClientPath: string): string {\n  return `\nimport { createHotContext } from \"${viteClientPath}\";\nconst hot = createHotContext(\"/__dummy__${packageName}\");\n\nfunction sendError(error) {\n  if (!(error instanceof Error)) {\n    error = new Error(\"(unknown runtime error)\");\n  }\n  const serialized = {\n    message: error.message,\n    stack: error.stack,\n  };\n  hot.send(\"${MESSAGE_TYPE}\", serialized);\n}\n\n// Only notify the parent frame after the server confirms the error passed\n// the filter and the overlay will be shown.\nhot.on(\"${NOTIFY_TYPE}\", () => {\n  try {\n    window.parent.postMessage({ type: \"runtime-error\", id: null }, \"*\");\n  } catch (_) {}\n});\n\nwindow.addEventListener(\"error\", (evt) => {\n  sendError(evt.error);\n});\n\nwindow.addEventListener(\"unhandledrejection\", (evt) => {\n  sendError(evt.reason);\n});\n`;\n}\n\nfunction cleanStack(stack: string) {\n  return stack\n    .split(/\\n/g)\n    .filter((l) => /^\\s*at/.test(l))\n    .join('\\n');\n}\n\nfunction rewriteStacktrace(\n  stack: string,\n  moduleGraph: ModuleGraph,\n  base: string,\n): {\n  stack: string;\n  loc?: Pos & { file: string };\n} {\n  // When a non-root base is configured (e.g. \"/my-app/\"), browser stack\n  // traces contain URLs like /my-app/src/App.tsx but the module graph\n  // keys use the path without the base prefix (/src/App.tsx). We strip\n  // the base prefix so lookups succeed.\n  const basePrefix =\n    base !== '/' && base.length > 1 ? base.replace(/\\/$/, '') : '';\n\n  let loc: (Pos & { file: string }) | undefined = undefined;\n  const rewrittenStack = cleanStack(stack)\n    .split('\\n')\n    .map((stackLine) => {\n      return stackLine.replace(\n        /^ {4}at (?:(\\S+?) )?\\(?(?:https|http):\\/\\/[^/]+(\\/[^\\s?]+).*:(\\d+):(\\d+)\\)?$/,\n        (input, varName, url, line, column) => {\n          if (!url) {\n            return input;\n          }\n\n          // Strip the base prefix for module graph lookups\n          const lookupUrl =\n            basePrefix && url.startsWith(basePrefix)\n              ? url.slice(basePrefix.length)\n              : url;\n\n          const moduleEntry =\n            moduleGraph.urlToModuleMap.get(lookupUrl) ||\n            moduleGraph.urlToModuleMap.get(url);\n          if (!moduleEntry) {\n            return '';\n          }\n\n          const rawSourceMap = moduleEntry?.transformResult?.map;\n\n          if (rawSourceMap) {\n            const traced = new TraceMap(rawSourceMap as SourceMapInput);\n            const pos = originalPositionFor(traced, {\n              line: Number(line),\n              // stacktrace's column is 1-indexed, but sourcemap's one is 0-indexed\n              column: Number(column) - 1,\n            });\n\n            if (pos.source && pos.line >= 0 && pos.column >= 0) {\n              line = pos.line;\n              column = pos.column + 1;\n            }\n          }\n\n          const trimmedVarName = varName?.trim();\n          const sourceFile = moduleEntry.file;\n          const source = `${moduleEntry.file}:${line}:${column}`;\n\n          if (sourceFile) {\n            loc ??= {\n              line: Number(line),\n              column: Number(column),\n              file: sourceFile,\n            };\n          }\n\n          if (!trimmedVarName || trimmedVarName === 'eval') {\n            return `    at ${source}`;\n          } else {\n            return `    at ${trimmedVarName} ${source}`;\n          }\n        },\n      );\n    })\n    .join('\\n');\n\n  return {\n    stack: rewrittenStack,\n    loc,\n  };\n}\n\ntype Pos = {\n  /** 1-based */\n  line: number;\n  /** 0-based */\n  column: number;\n};\n\nconst splitRE = /\\r?\\n/g;\nconst range: number = 2;\n\nfunction posToNumber(source: string, pos: number | Pos): number {\n  if (typeof pos === 'number') {\n    return pos;\n  }\n\n  const lines = source.split(splitRE);\n  const { line, column } = pos;\n  let start = 0;\n  for (let i = 0; i < line - 1 && i < lines.length; i++) {\n    start += lines[i].length + 1;\n  }\n\n  return start + column;\n}\n\nfunction generateCodeFrame(\n  source: string,\n  start: number | Pos = 0,\n  end?: number | Pos,\n): string {\n  start = Math.max(posToNumber(source, start), 0);\n  end = Math.min(\n    end !== undefined ? posToNumber(source, end) : start,\n    source.length,\n  );\n  const lines = source.split(splitRE);\n  let count = 0;\n  const res: Array<string> = [];\n  for (let i = 0; i < lines.length; i++) {\n    count += lines[i].length;\n    if (count >= start) {\n      for (let j = i - range; j <= i + range || end > count; j++) {\n        if (j < 0 || j >= lines.length) {\n          continue;\n        }\n\n        const line = j + 1;\n        res.push(\n          `${line}${' '.repeat(Math.max(3 - String(line).length, 0))}|  ${\n            lines[j]\n          }`,\n        );\n        const lineLength = lines[j].length;\n        if (j === i) {\n          const pad = Math.max(start - (count - lineLength), 0);\n          const length = Math.max(\n            1,\n            end > count ? lineLength - pad : end - start,\n          );\n          res.push(`   |  ` + ' '.repeat(pad) + '^'.repeat(length));\n        } else if (j > i) {\n          if (end > count) {\n            const length = Math.max(Math.min(end - count, lineLength), 1);\n            res.push(`   |  ` + '^'.repeat(length));\n          }\n\n          count += lineLength + 1;\n        }\n      }\n\n      break;\n    }\n\n    count++;\n  }\n\n  return res.join('\\n');\n}\n"],"mappings":"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","names":[]}