nuxt-devtools-observatory 0.1.26 → 0.1.30

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/module.mjs CHANGED
@@ -1,4 +1,6 @@
1
1
  import { defineNuxtModule, createResolver, addVitePlugin, addPlugin, addServerPlugin } from '@nuxt/kit';
2
+ import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
3
+ import sirv from 'sirv';
2
4
  import { parse } from '@babel/parser';
3
5
  import _traverse from '@babel/traverse';
4
6
  import _generate from '@babel/generator';
@@ -22,6 +24,156 @@ function extractScriptBlock(code) {
22
24
 
23
25
  const traverse$2 = _traverse.default ?? _traverse;
24
26
  const generate$2 = _generate.default ?? _generate;
27
+ const COMPOSABLE_RE = /\buse[A-Z]/;
28
+ const SKIP_LIST = /* @__PURE__ */ new Set([
29
+ // useFetch family — tracked by the fetch dashboard
30
+ "useFetch",
31
+ "useAsyncData",
32
+ "useLazyFetch",
33
+ "useLazyAsyncData",
34
+ // Nuxt auto-imports
35
+ "useCookie",
36
+ "useRequestEvent",
37
+ "useRequestHeaders",
38
+ "useRequestURL",
39
+ "useResponseHeader",
40
+ "useNuxtApp",
41
+ "useRuntimeConfig",
42
+ "useRoute",
43
+ "useRouter",
44
+ "useNuxtData",
45
+ "useError",
46
+ "useState",
47
+ "useAppConfig",
48
+ // Nuxt head
49
+ "useHead",
50
+ "useSeoMeta",
51
+ "useServerSeoMeta",
52
+ "useHeadSafe",
53
+ // Nuxt i18n (common plugin)
54
+ "useI18n",
55
+ "useLocalePath",
56
+ "useLocaleRoute",
57
+ // Vue built-ins
58
+ "useSlots",
59
+ "useAttrs",
60
+ "useModel",
61
+ "useTemplateRef",
62
+ "useId",
63
+ "useCssModule",
64
+ "useCssVars"
65
+ ]);
66
+ function composableTrackerPlugin() {
67
+ return {
68
+ name: "vite-plugin-observatory-composables",
69
+ enforce: "pre",
70
+ transform(code, id) {
71
+ const isVue = id.endsWith(".vue");
72
+ if (!isVue && !id.endsWith(".ts") && !id.endsWith(".js")) {
73
+ return;
74
+ }
75
+ if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry")) {
76
+ return;
77
+ }
78
+ let scriptCode = code;
79
+ let scriptStart = 0;
80
+ if (isVue) {
81
+ const block = extractScriptBlock(code);
82
+ if (!block) {
83
+ return null;
84
+ }
85
+ scriptCode = block.content;
86
+ scriptStart = block.start;
87
+ }
88
+ if (!COMPOSABLE_RE.test(scriptCode)) {
89
+ return;
90
+ }
91
+ try {
92
+ const ast = parse(scriptCode, {
93
+ sourceType: "module",
94
+ plugins: ["typescript"]
95
+ });
96
+ let modified = false;
97
+ traverse$2(ast, {
98
+ CallExpression(path) {
99
+ const callee = path.node.callee;
100
+ if (!t.isIdentifier(callee)) {
101
+ return;
102
+ }
103
+ const name = callee.name;
104
+ if (!COMPOSABLE_RE.test(name)) {
105
+ return;
106
+ }
107
+ if (SKIP_LIST.has(name)) {
108
+ return;
109
+ }
110
+ const binding = path.scope.getBinding(name);
111
+ if (binding?.path.isImportSpecifier() || binding?.path.isImportDefaultSpecifier()) {
112
+ const importDecl = binding.path.parentPath?.node;
113
+ const source = importDecl?.source?.value ?? "";
114
+ if (source && !source.startsWith(".") && !source.startsWith("/")) {
115
+ return;
116
+ }
117
+ }
118
+ let parent = path.parentPath;
119
+ let isWrapped = false;
120
+ while (parent) {
121
+ if (t.isCallExpression(parent.node) && t.isIdentifier(parent.node.callee) && parent.node.callee.name === "__trackComposable") {
122
+ isWrapped = true;
123
+ break;
124
+ }
125
+ parent = parent.parentPath ?? null;
126
+ }
127
+ if (isWrapped) {
128
+ return;
129
+ }
130
+ const comments = (path.node.leadingComments ?? []).concat(path.parentPath?.node?.leadingComments ?? []);
131
+ const ignored = comments.some((c) => c.value.includes("@devtools-ignore"));
132
+ if (ignored) {
133
+ return;
134
+ }
135
+ const args = path.node.arguments;
136
+ const loc = path.node.loc;
137
+ const fileName = id.split(/[\\/]/).pop() || id;
138
+ const meta = t.objectExpression([
139
+ t.objectProperty(t.identifier("file"), t.stringLiteral(fileName)),
140
+ t.objectProperty(t.identifier("line"), t.numericLiteral(loc?.start.line ?? 0))
141
+ ]);
142
+ path.replaceWith(
143
+ t.callExpression(t.identifier("__trackComposable"), [
144
+ t.stringLiteral(name),
145
+ t.arrowFunctionExpression([], t.callExpression(t.identifier(name), args)),
146
+ meta
147
+ ])
148
+ );
149
+ modified = true;
150
+ }
151
+ });
152
+ if (!modified) {
153
+ return null;
154
+ }
155
+ const importLine = `import { __trackComposable } from 'nuxt-devtools-observatory/runtime/composable-registry';
156
+ `;
157
+ const output = generate$2(ast, { retainLines: true }, scriptCode);
158
+ const alreadyImported = output.code.includes("nuxt-devtools-observatory/runtime/composable-registry");
159
+ const prefix = alreadyImported ? "" : importLine;
160
+ let finalCode;
161
+ if (isVue) {
162
+ finalCode = code.slice(0, scriptStart) + prefix + output.code + code.slice(scriptStart + scriptCode.length);
163
+ } else {
164
+ finalCode = prefix + output.code;
165
+ }
166
+ return { code: finalCode, map: output.map };
167
+ } catch (err) {
168
+ console.warn("[observatory] composable transform error:", err);
169
+ return null;
170
+ }
171
+ }
172
+ };
173
+ }
174
+
175
+ const traverse$1 = _traverse.default ?? _traverse;
176
+ const generate$1 = _generate.default ?? _generate;
25
177
  const FETCH_FNS = /* @__PURE__ */ new Set(["useFetch", "useAsyncData", "useLazyFetch", "useLazyAsyncData"]);
26
178
  function isHandlerExpression(node) {
27
179
  return Boolean(node && (t.isIdentifier(node) || t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)));
@@ -61,7 +213,7 @@ function fetchInstrumentPlugin() {
61
213
  let needsFetchHandlerHelper = false;
62
214
  const hasFetchCallImport = scriptCode.includes("__devFetchCall");
63
215
  const hasFetchHandlerImport = scriptCode.includes("__devFetchHandler");
64
- traverse$2(ast, {
216
+ traverse$1(ast, {
65
217
  CallExpression(path) {
66
218
  if (path.node.__observatoryTransformed) {
67
219
  return;
@@ -179,7 +331,7 @@ function fetchInstrumentPlugin() {
179
331
  ].filter(Boolean);
180
332
  const importStatement = importNames.length ? `import { ${importNames.join(", ")} } from 'nuxt-devtools-observatory/runtime/fetch-registry';
181
333
  ` : "";
182
- const output = generate$2(ast, { retainLines: true }, scriptCode);
334
+ const output = generate$1(ast, { retainLines: true }, scriptCode);
183
335
  let finalCode;
184
336
  if (isVue) {
185
337
  finalCode = code.slice(0, scriptStart) + importStatement + output.code + code.slice(scriptStart + scriptCode.length);
@@ -198,8 +350,8 @@ function fetchInstrumentPlugin() {
198
350
  };
199
351
  }
200
352
 
201
- const traverse$1 = _traverse.default ?? _traverse;
202
- const generate$1 = _generate.default ?? _generate;
353
+ const traverse = _traverse.default ?? _traverse;
354
+ const generate = _generate.default ?? _generate;
203
355
  function provideInjectPlugin() {
204
356
  return {
205
357
  name: "vite-plugin-observatory-provide-inject",
@@ -231,7 +383,7 @@ function provideInjectPlugin() {
231
383
  plugins: ["typescript"]
232
384
  });
233
385
  let modified = false;
234
- traverse$1(ast, {
386
+ traverse(ast, {
235
387
  CallExpression(path) {
236
388
  const callee = path.node.callee;
237
389
  if (!t.isIdentifier(callee)) {
@@ -265,7 +417,7 @@ function provideInjectPlugin() {
265
417
  }
266
418
  const importLine = `import { __devProvide, __devInject } from 'nuxt-devtools-observatory/runtime/provide-inject-registry';
267
419
  `;
268
- const output = generate$1(ast, { retainLines: true }, scriptCode);
420
+ const output = generate(ast, { retainLines: true }, scriptCode);
269
421
  let finalCode;
270
422
  if (isVue) {
271
423
  finalCode = code.slice(0, scriptStart) + (scriptCode.includes("__devProvide") ? "" : importLine) + output.code + code.slice(scriptStart + scriptCode.length);
@@ -281,156 +433,6 @@ function provideInjectPlugin() {
281
433
  };
282
434
  }
283
435
 
284
- const traverse = _traverse.default ?? _traverse;
285
- const generate = _generate.default ?? _generate;
286
- const COMPOSABLE_RE = /\buse[A-Z]/;
287
- const SKIP_LIST = /* @__PURE__ */ new Set([
288
- // useFetch family — tracked by the fetch dashboard
289
- "useFetch",
290
- "useAsyncData",
291
- "useLazyFetch",
292
- "useLazyAsyncData",
293
- // Nuxt auto-imports
294
- "useCookie",
295
- "useRequestEvent",
296
- "useRequestHeaders",
297
- "useRequestURL",
298
- "useResponseHeader",
299
- "useNuxtApp",
300
- "useRuntimeConfig",
301
- "useRoute",
302
- "useRouter",
303
- "useNuxtData",
304
- "useError",
305
- "useState",
306
- "useAppConfig",
307
- // Nuxt head
308
- "useHead",
309
- "useSeoMeta",
310
- "useServerSeoMeta",
311
- "useHeadSafe",
312
- // Nuxt i18n (common plugin)
313
- "useI18n",
314
- "useLocalePath",
315
- "useLocaleRoute",
316
- // Vue built-ins
317
- "useSlots",
318
- "useAttrs",
319
- "useModel",
320
- "useTemplateRef",
321
- "useId",
322
- "useCssModule",
323
- "useCssVars"
324
- ]);
325
- function composableTrackerPlugin() {
326
- return {
327
- name: "vite-plugin-observatory-composables",
328
- enforce: "pre",
329
- transform(code, id) {
330
- const isVue = id.endsWith(".vue");
331
- if (!isVue && !id.endsWith(".ts") && !id.endsWith(".js")) {
332
- return;
333
- }
334
- if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry")) {
335
- return;
336
- }
337
- let scriptCode = code;
338
- let scriptStart = 0;
339
- if (isVue) {
340
- const block = extractScriptBlock(code);
341
- if (!block) {
342
- return null;
343
- }
344
- scriptCode = block.content;
345
- scriptStart = block.start;
346
- }
347
- if (!COMPOSABLE_RE.test(scriptCode)) {
348
- return;
349
- }
350
- try {
351
- const ast = parse(scriptCode, {
352
- sourceType: "module",
353
- plugins: ["typescript"]
354
- });
355
- let modified = false;
356
- traverse(ast, {
357
- CallExpression(path) {
358
- const callee = path.node.callee;
359
- if (!t.isIdentifier(callee)) {
360
- return;
361
- }
362
- const name = callee.name;
363
- if (!COMPOSABLE_RE.test(name)) {
364
- return;
365
- }
366
- if (SKIP_LIST.has(name)) {
367
- return;
368
- }
369
- const binding = path.scope.getBinding(name);
370
- if (binding?.path.isImportSpecifier() || binding?.path.isImportDefaultSpecifier()) {
371
- const importDecl = binding.path.parentPath?.node;
372
- const source = importDecl?.source?.value ?? "";
373
- if (source && !source.startsWith(".") && !source.startsWith("/")) {
374
- return;
375
- }
376
- }
377
- let parent = path.parentPath;
378
- let isWrapped = false;
379
- while (parent) {
380
- if (t.isCallExpression(parent.node) && t.isIdentifier(parent.node.callee) && parent.node.callee.name === "__trackComposable") {
381
- isWrapped = true;
382
- break;
383
- }
384
- parent = parent.parentPath ?? null;
385
- }
386
- if (isWrapped) {
387
- return;
388
- }
389
- const comments = (path.node.leadingComments ?? []).concat(path.parentPath?.node?.leadingComments ?? []);
390
- const ignored = comments.some((c) => c.value.includes("@devtools-ignore"));
391
- if (ignored) {
392
- return;
393
- }
394
- const args = path.node.arguments;
395
- const loc = path.node.loc;
396
- const fileName = id.split(/[\\/]/).pop() || id;
397
- const meta = t.objectExpression([
398
- t.objectProperty(t.identifier("file"), t.stringLiteral(fileName)),
399
- t.objectProperty(t.identifier("line"), t.numericLiteral(loc?.start.line ?? 0))
400
- ]);
401
- path.replaceWith(
402
- t.callExpression(t.identifier("__trackComposable"), [
403
- t.stringLiteral(name),
404
- t.arrowFunctionExpression([], t.callExpression(t.identifier(name), args)),
405
- meta
406
- ])
407
- );
408
- modified = true;
409
- }
410
- });
411
- if (!modified) {
412
- return null;
413
- }
414
- const importLine = `import { __trackComposable } from 'nuxt-devtools-observatory/runtime/composable-registry';
415
- `;
416
- const output = generate(ast, { retainLines: true }, scriptCode);
417
- const alreadyImported = output.code.includes("nuxt-devtools-observatory/runtime/composable-registry");
418
- const prefix = alreadyImported ? "" : importLine;
419
- let finalCode;
420
- if (isVue) {
421
- finalCode = code.slice(0, scriptStart) + prefix + output.code + code.slice(scriptStart + scriptCode.length);
422
- } else {
423
- finalCode = prefix + output.code;
424
- }
425
- return { code: finalCode, map: output.map };
426
- } catch (err) {
427
- console.warn("[observatory] composable transform error:", err);
428
- return null;
429
- }
430
- }
431
- };
432
- }
433
-
434
436
  const VIRTUAL_ID = "\0obs:vue-proxy";
435
437
  const PROXY_MODULE = `
436
438
  import {
@@ -544,6 +546,8 @@ function transitionTrackerPlugin() {
544
546
  }
545
547
 
546
548
  const defaults = {
549
+ // Auto-enable server instrumentation for SSR projects unless explicitly overridden.
550
+ // This ensures initial SSR snapshots include server-side composables/fetch events.
547
551
  instrumentServer: process.env.OBSERVATORY_INSTRUMENT_SERVER === "true",
548
552
  fetchDashboard: process.env.OBSERVATORY_FETCH_DASHBOARD === "true",
549
553
  provideInjectGraph: process.env.OBSERVATORY_PROVIDE_INJECT_GRAPH === "true",
@@ -559,7 +563,8 @@ const defaults = {
559
563
  maxComposableEntries: process.env.OBSERVATORY_MAX_COMPOSABLE_ENTRIES ? Number(process.env.OBSERVATORY_MAX_COMPOSABLE_ENTRIES) : 300,
560
564
  maxRenderTimeline: process.env.OBSERVATORY_MAX_RENDER_TIMELINE ? Number(process.env.OBSERVATORY_MAX_RENDER_TIMELINE) : 100,
561
565
  composableNavigationMode: process.env.OBSERVATORY_COMPOSABLE_NAVIGATION_MODE === "session" ? "session" : "route",
562
- heatmapHideInternals: process.env.OBSERVATORY_HEATMAP_HIDE_INTERNALS === "true"
566
+ heatmapHideInternals: process.env.OBSERVATORY_HEATMAP_HIDE_INTERNALS === "true",
567
+ debugRpc: process.env.OBSERVATORY_DEBUG_RPC === "true"
563
568
  };
564
569
  const module$1 = defineNuxtModule({
565
570
  meta: {
@@ -586,7 +591,7 @@ const module$1 = defineNuxtModule({
586
591
  composableTracker: options.composableTracker ?? (process.env.OBSERVATORY_COMPOSABLE_TRACKER ? process.env.OBSERVATORY_COMPOSABLE_TRACKER === "true" : true),
587
592
  renderHeatmap: options.renderHeatmap ?? (process.env.OBSERVATORY_RENDER_HEATMAP ? process.env.OBSERVATORY_RENDER_HEATMAP === "true" : true),
588
593
  transitionTracker: options.transitionTracker ?? (process.env.OBSERVATORY_TRANSITION_TRACKER ? process.env.OBSERVATORY_TRANSITION_TRACKER === "true" : true),
589
- instrumentServer: options.instrumentServer ?? (process.env.OBSERVATORY_INSTRUMENT_SERVER ? process.env.OBSERVATORY_INSTRUMENT_SERVER === "true" : false),
594
+ instrumentServer: options.instrumentServer ?? (process.env.OBSERVATORY_INSTRUMENT_SERVER ? process.env.OBSERVATORY_INSTRUMENT_SERVER === "true" : nuxt.options.ssr !== false),
590
595
  heatmapThresholdCount: options.heatmapThresholdCount ?? (process.env.OBSERVATORY_HEATMAP_THRESHOLD_COUNT ? Number(process.env.OBSERVATORY_HEATMAP_THRESHOLD_COUNT) : 3),
591
596
  heatmapThresholdTime: options.heatmapThresholdTime ?? (process.env.OBSERVATORY_HEATMAP_THRESHOLD_TIME ? Number(process.env.OBSERVATORY_HEATMAP_THRESHOLD_TIME) : 1600),
592
597
  maxFetchEntries: options.maxFetchEntries ?? (process.env.OBSERVATORY_MAX_FETCH_ENTRIES ? Number(process.env.OBSERVATORY_MAX_FETCH_ENTRIES) : 200),
@@ -595,7 +600,8 @@ const module$1 = defineNuxtModule({
595
600
  maxComposableHistory: options.maxComposableHistory ?? (process.env.OBSERVATORY_MAX_COMPOSABLE_HISTORY ? Number(process.env.OBSERVATORY_MAX_COMPOSABLE_HISTORY) : 50),
596
601
  maxComposableEntries: options.maxComposableEntries ?? (process.env.OBSERVATORY_MAX_COMPOSABLE_ENTRIES ? Number(process.env.OBSERVATORY_MAX_COMPOSABLE_ENTRIES) : 300),
597
602
  maxRenderTimeline: options.maxRenderTimeline ?? (process.env.OBSERVATORY_MAX_RENDER_TIMELINE ? Number(process.env.OBSERVATORY_MAX_RENDER_TIMELINE) : 100),
598
- composableNavigationMode: options.composableNavigationMode ?? (process.env.OBSERVATORY_COMPOSABLE_NAVIGATION_MODE === "session" ? "session" : "route")
603
+ composableNavigationMode: options.composableNavigationMode ?? (process.env.OBSERVATORY_COMPOSABLE_NAVIGATION_MODE === "session" ? "session" : "route"),
604
+ debugRpc: options.debugRpc ?? (process.env.OBSERVATORY_DEBUG_RPC ? process.env.OBSERVATORY_DEBUG_RPC === "true" : false)
599
605
  };
600
606
  nuxt.hook("vite:extendConfig", (config) => {
601
607
  const alias = config.resolve?.alias;
@@ -626,34 +632,80 @@ const module$1 = defineNuxtModule({
626
632
  if (resolved.fetchDashboard) {
627
633
  addServerPlugin(resolver.resolve("./runtime/nitro/fetch-capture"));
628
634
  }
629
- const CLIENT_PORT = 4949;
630
- const clientOrigin = `http://localhost:${CLIENT_PORT}`;
631
- let innerServer = null;
632
- nuxt.hook("vite:serverCreated", async (_viteServer, env) => {
633
- if (!env.isClient) {
634
- return;
635
+ const base = "/__observatory";
636
+ const debugRpc = resolved.debugRpc === true;
637
+ const debugLog = (...args) => {
638
+ if (debugRpc) {
639
+ console.info("[observatory][rpc][server]", ...args);
635
640
  }
636
- if (innerServer) {
641
+ };
642
+ let latestSnapshot = {
643
+ fetch: [],
644
+ provideInject: { provides: [], injects: [] },
645
+ composables: [],
646
+ renders: [],
647
+ transitions: [],
648
+ features: {
649
+ fetchDashboard: !!resolved.fetchDashboard,
650
+ provideInjectGraph: !!resolved.provideInjectGraph,
651
+ composableTracker: !!resolved.composableTracker,
652
+ composableNavigationMode: resolved.composableNavigationMode,
653
+ renderHeatmap: !!resolved.renderHeatmap,
654
+ transitionTracker: !!resolved.transitionTracker
655
+ }
656
+ };
657
+ let rpc = null;
658
+ let viteServer = null;
659
+ const emitCommand = (command) => {
660
+ if (!viteServer) {
661
+ console.warn("[observatory][rpc][server] command dropped (vite ws not ready)", command);
637
662
  return;
638
663
  }
639
- const { createServer } = await import('vite');
640
- const { default: vue } = await import('@vitejs/plugin-vue');
641
- innerServer = await createServer({
642
- root: resolver.resolve("../client"),
643
- base: "/",
644
- server: { port: CLIENT_PORT, strictPort: true, cors: true },
645
- appType: "spa",
646
- configFile: false,
647
- plugins: [vue()],
648
- logLevel: "warn"
649
- });
650
- await innerServer.listen();
651
- nuxt.hook("close", () => {
652
- innerServer?.close();
653
- innerServer = null;
654
- });
664
+ debugLog("send command", command);
665
+ viteServer.ws.send("observatory:command", command);
666
+ };
667
+ addVitePlugin({
668
+ name: "nuxt-devtools-observatory:sirv-client",
669
+ configureServer(server) {
670
+ viteServer = server;
671
+ const clientDist = resolver.resolve("../client/dist");
672
+ server.middlewares.use(base, sirv(clientDist, { dev: true, single: true }));
673
+ server.ws.on("observatory:snapshot", (snapshot) => {
674
+ latestSnapshot = snapshot;
675
+ debugLog("received host snapshot", {
676
+ fetch: Array.isArray(snapshot.fetch) ? snapshot.fetch.length : 0,
677
+ composables: Array.isArray(snapshot.composables) ? snapshot.composables.length : 0,
678
+ renders: Array.isArray(snapshot.renders) ? snapshot.renders.length : 0,
679
+ transitions: Array.isArray(snapshot.transitions) ? snapshot.transitions.length : 0
680
+ });
681
+ rpc?.broadcast.onSnapshot.asEvent(snapshot);
682
+ });
683
+ }
655
684
  });
656
- const base = clientOrigin;
685
+ onDevToolsInitialized(() => {
686
+ rpc = extendServerRpc(
687
+ "observatory",
688
+ {
689
+ async getSnapshot() {
690
+ return latestSnapshot;
691
+ },
692
+ async requestSnapshot() {
693
+ emitCommand({ cmd: "request-snapshot" });
694
+ },
695
+ async clearComposables() {
696
+ emitCommand({ cmd: "clear-composables" });
697
+ },
698
+ async setComposableMode(mode) {
699
+ emitCommand({ cmd: "set-mode", mode });
700
+ },
701
+ async editComposableValue(id, key, value) {
702
+ emitCommand({ cmd: "edit-composable", id, key, value });
703
+ }
704
+ },
705
+ nuxt
706
+ );
707
+ rpc.broadcast.onSnapshot.asEvent(latestSnapshot);
708
+ }, nuxt);
657
709
  nuxt.hook("render:response", (response, { url }) => {
658
710
  if (url.startsWith("/trackers") || url === "/" || url.startsWith("/index.html")) {
659
711
  const configScript = `<script>window.__observatoryConfig = ${JSON.stringify(nuxt.options.runtimeConfig.public.observatory)};<\/script>`;
@@ -667,13 +719,12 @@ ${configScript}`);
667
719
  name: "observatory-trackers",
668
720
  title: "Observatory Trackers",
669
721
  icon: "carbon:heat-map",
670
- view: { type: "iframe", src: `${base}/trackers` }
722
+ view: { type: "iframe", src: `${base}/trackers${resolved.debugRpc ? "?debugRpc=1" : ""}` }
671
723
  });
672
724
  }
673
725
  });
674
726
  nuxt.options.runtimeConfig.public.observatory = {
675
727
  instrumentServer: resolved.instrumentServer,
676
- clientOrigin,
677
728
  fetchDashboard: resolved.fetchDashboard,
678
729
  provideInjectGraph: resolved.provideInjectGraph,
679
730
  composableTracker: resolved.composableTracker,
@@ -688,7 +739,8 @@ ${configScript}`);
688
739
  composableNavigationMode: resolved.composableNavigationMode,
689
740
  heatmapHideInternals: resolved.heatmapHideInternals,
690
741
  heatmapThresholdCount: resolved.heatmapThresholdCount,
691
- heatmapThresholdTime: resolved.heatmapThresholdTime
742
+ heatmapThresholdTime: resolved.heatmapThresholdTime,
743
+ debugRpc: resolved.debugRpc
692
744
  };
693
745
  }
694
746
  });
@@ -218,6 +218,9 @@ export function __devFetchCall(originalFn, url, opts, meta) {
218
218
  }
219
219
  },
220
220
  onResponseError({ response }) {
221
+ const endTime = performance.now();
222
+ const ms = Math.round(endTime - lastCallStart);
223
+ registry.update(id, { status: "error", endTime, ms });
221
224
  if (typeof opts.onResponseError === "function") {
222
225
  opts.onResponseError({ response });
223
226
  }