@tourmind-frontend/monitor-plugin-nuxt 1.1.0 → 1.2.1

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/index.cjs CHANGED
@@ -34,19 +34,63 @@ __export(src_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(src_exports);
36
36
  var import_node_path = require("path");
37
+ var import_node_fs = require("fs");
37
38
  var import_monitor_plugin_webpack = __toESM(require("@tourmind-frontend/monitor-plugin-webpack"));
38
39
  var LOG_PREFIX = "[frontend-monitor]";
40
+ function extractFirstFrame(stack) {
41
+ const re = /\(?([^()\s][^():\n]*?\.(?:vue|ts|tsx|js|jsx|mjs)):(\d+)(?::(\d+))?\)?/;
42
+ for (const raw of stack.split("\n")) {
43
+ const m = raw.match(re);
44
+ if (!m) continue;
45
+ const file = m[1];
46
+ if (!file || file.includes("node_modules") || file.startsWith(".nuxt/")) continue;
47
+ return { file, line: Number(m[2]), col: m[3] ? Number(m[3]) : 0 };
48
+ }
49
+ return null;
50
+ }
51
+ function refineErrorLine(lines, hintLine, hintCol) {
52
+ var _a, _b;
53
+ if (hintCol > 0) return hintLine;
54
+ const isBoilerplate = (s) => /^\s*[\])},;]*\s*$/.test(s);
55
+ const cur = (_a = lines[hintLine - 1]) != null ? _a : "";
56
+ if (!isBoilerplate(cur)) return hintLine;
57
+ for (let i = hintLine - 2; i >= Math.max(0, hintLine - 4); i--) {
58
+ const prev = (_b = lines[i]) != null ? _b : "";
59
+ if (!isBoilerplate(prev) && /[A-Za-z_$][\w$]*/.test(prev)) return i + 1;
60
+ }
61
+ return hintLine;
62
+ }
63
+ function readSourceContext(rootDir, file, hintLine, hintCol, contextLines = 10) {
64
+ const abs = (0, import_node_path.isAbsolute)(file) ? file : (0, import_node_path.resolve)(rootDir, file);
65
+ if (!(0, import_node_fs.existsSync)(abs)) return null;
66
+ try {
67
+ const all = (0, import_node_fs.readFileSync)(abs, "utf-8").split("\n");
68
+ const line = refineErrorLine(all, hintLine, hintCol);
69
+ const idx = line - 1;
70
+ if (idx < 0 || idx >= all.length) return null;
71
+ const start = Math.max(0, idx - contextLines);
72
+ const end = Math.min(all.length - 1, idx + contextLines);
73
+ return {
74
+ file,
75
+ line,
76
+ context: all.slice(start, end + 1).join("\n"),
77
+ context_line: idx - start + 1
78
+ };
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
39
83
  function MonitorModule(options) {
40
- if (!(options == null ? void 0 : options.token)) throw new Error(`${LOG_PREFIX} "token" is required`);
41
- if (!options.reportUrl) throw new Error(`${LOG_PREFIX} "reportUrl" is required`);
42
- if (!options.uploadUrl) throw new Error(`${LOG_PREFIX} "uploadUrl" is required`);
84
+ if (!options.url) throw new Error(`${LOG_PREFIX} "url" is required`);
85
+ if (!options.token) throw new Error(`${LOG_PREFIX} "token" is required`);
86
+ if (!options.commit) throw new Error(`${LOG_PREFIX} "commit" is required`);
43
87
  this.extendBuild((config, { isClient, isDev }) => {
44
88
  if (isDev || !isClient) return;
45
89
  config.devtool = "hidden-source-map";
46
90
  config.plugins = config.plugins || [];
47
91
  config.plugins.push(
48
92
  new import_monitor_plugin_webpack.default({
49
- url: options.uploadUrl,
93
+ url: options.url,
50
94
  token: options.token,
51
95
  commit: options.commit
52
96
  })
@@ -57,8 +101,40 @@ function MonitorModule(options) {
57
101
  fileName: "monitor-tracking.client.js",
58
102
  mode: "client",
59
103
  options: {
60
- url: options.reportUrl,
104
+ url: options.url,
61
105
  token: options.token
62
106
  }
63
107
  });
108
+ const ssrReportUrl = `${options.url.replace(/\/+$/, "")}/api/ssr-report`;
109
+ const token = options.token;
110
+ const rootDir = this.options.rootDir || this.options.srcDir || process.cwd();
111
+ this.nuxt.hook("render:errorMiddleware", (app) => {
112
+ app.use((err, req, _res, next) => {
113
+ if (typeof fetch === "function") {
114
+ try {
115
+ const e = err;
116
+ const stack = e && e.stack ? e.stack : String(e && e.message || e);
117
+ const frame = extractFirstFrame(stack);
118
+ const sourceCtx = frame ? readSourceContext(rootDir, frame.file, frame.line, frame.col) : null;
119
+ const refinedStack = sourceCtx && frame && sourceCtx.line !== frame.line ? stack.replace(
120
+ new RegExp(`(${frame.file.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:)${frame.line}(:)`),
121
+ `$1${sourceCtx.line}$2`
122
+ ) : stack;
123
+ fetch(ssrReportUrl, {
124
+ method: "POST",
125
+ headers: { "Content-Type": "application/json" },
126
+ body: JSON.stringify({
127
+ token,
128
+ stack: refinedStack,
129
+ url: req.url || "",
130
+ ...sourceCtx || {}
131
+ })
132
+ }).catch(() => {
133
+ });
134
+ } catch {
135
+ }
136
+ }
137
+ next(err);
138
+ });
139
+ });
64
140
  }
package/dist/index.d.ts CHANGED
@@ -1,12 +1,10 @@
1
1
  export interface MonitorModuleOptions {
2
2
  /** monitor 项目 token,从 web 控制台创建项目后获取,内部已绑定仓库 + 分支。 */
3
3
  token: string;
4
- /** 上报接口地址,例如 `https://monitor.example.com/api/report`。 */
5
- reportUrl: string;
6
- /** sourcemap 上传接口地址,例如 `https://monitor.example.com/api/upload`。 */
7
- uploadUrl: string;
4
+ /** monitor 服务的 base URL,例如 `https://monitor.example.com`。`/api/report`、`/api/upload` 由下游 tracking / plugin-webpack 内部拼接。 */
5
+ url: string;
8
6
  /** Commit hash,由调用方自行解析(如 `git rev-parse HEAD`)。 */
9
- commit?: string;
7
+ commit: string;
10
8
  }
11
9
  interface NuxtBuildContext {
12
10
  isClient: boolean;
@@ -17,8 +15,20 @@ interface NuxtWebpackConfig {
17
15
  plugins?: unknown[];
18
16
  devtool?: string | false;
19
17
  }
18
+ interface ConnectApp {
19
+ use(handler: (err: unknown, req: {
20
+ url?: string;
21
+ }, res: unknown, next: (err?: unknown) => void) => void): void;
22
+ }
23
+ interface NuxtInstance {
24
+ hook(name: "render:errorMiddleware", handler: (app: ConnectApp) => void): void;
25
+ }
20
26
  interface NuxtModuleThis {
21
- options: unknown;
27
+ options: {
28
+ rootDir?: string;
29
+ srcDir?: string;
30
+ } & Record<string, unknown>;
31
+ nuxt: NuxtInstance;
22
32
  extendBuild(fn: (config: NuxtWebpackConfig, ctx: NuxtBuildContext) => void): void;
23
33
  addPlugin(opts: {
24
34
  src: string;
@@ -42,8 +52,7 @@ interface NuxtModuleThis {
42
52
  * module.exports = {
43
53
  * modules: [
44
54
  * ['@tourmind-frontend/monitor-plugin-nuxt', {
45
- * reportUrl: 'https://monitor.example.com/api/report',
46
- * uploadUrl: 'https://monitor.example.com/api/upload',
55
+ * url: 'https://monitor.example.com',
47
56
  * token: process.env.MONITOR_TOKEN,
48
57
  * commit: process.env.GIT_COMMIT,
49
58
  * }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tourmind-frontend/monitor-plugin-nuxt",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Nuxt 2 module that captures runtime errors and uploads sourcemaps to a frontend-monitor server.",
5
5
  "main": "./dist/index.cjs",
6
6
  "types": "./dist/index.d.ts",
@@ -17,12 +17,12 @@
17
17
  "node": ">=13"
18
18
  },
19
19
  "dependencies": {
20
- "@tourmind-frontend/monitor-plugin-webpack": "^1.1.0"
20
+ "@tourmind-frontend/monitor-plugin-webpack": "^1.2.0"
21
21
  },
22
22
  "peerDependencies": {
23
23
  "nuxt": "^2.4.0",
24
24
  "vue": "^2.5.0",
25
- "@tourmind-frontend/monitor-tracking": "^1.1.0"
25
+ "@tourmind-frontend/monitor-tracking": "^1.2.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^20.0.0",