nuxt-devtools-observatory 0.1.7 → 0.1.9

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,12 +1,10 @@
1
- import { defineNuxtModule, createResolver, addVitePlugin, addPlugin } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addVitePlugin, addPlugin, addServerPlugin } from '@nuxt/kit';
2
2
  import { parse } from '@babel/parser';
3
3
  import _traverse from '@babel/traverse';
4
4
  import _generate from '@babel/generator';
5
5
  import * as t from '@babel/types';
6
6
 
7
- const traverse$2 = _traverse.default ?? _traverse;
8
- const generate$2 = _generate.default ?? _generate;
9
- function extractScriptBlock$2(code) {
7
+ function extractScriptBlock(code) {
10
8
  const openTagRE = /<script(\s[^>]*)?>/i;
11
9
  const openMatch = openTagRE.exec(code);
12
10
  if (!openMatch) {
@@ -19,7 +17,13 @@ function extractScriptBlock$2(code) {
19
17
  }
20
18
  return { content: code.slice(start, end), start, end };
21
19
  }
20
+
21
+ const traverse$2 = _traverse.default ?? _traverse;
22
+ const generate$2 = _generate.default ?? _generate;
22
23
  const FETCH_FNS = /* @__PURE__ */ new Set(["useFetch", "useAsyncData", "useLazyFetch", "useLazyAsyncData"]);
24
+ function isHandlerExpression(node) {
25
+ return Boolean(node && (t.isIdentifier(node) || t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)));
26
+ }
23
27
  function fetchInstrumentPlugin() {
24
28
  return {
25
29
  name: "vite-plugin-observatory-fetch",
@@ -29,13 +33,13 @@ function fetchInstrumentPlugin() {
29
33
  if (!isVue && !id.endsWith(".ts") && !id.endsWith(".js")) {
30
34
  return;
31
35
  }
32
- if (id.includes("node_modules")) {
36
+ if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry")) {
33
37
  return;
34
38
  }
35
39
  let scriptCode = code;
36
40
  let scriptStart = 0;
37
41
  if (isVue) {
38
- const block = extractScriptBlock$2(code);
42
+ const block = extractScriptBlock(code);
39
43
  if (!block) {
40
44
  return null;
41
45
  }
@@ -51,7 +55,10 @@ function fetchInstrumentPlugin() {
51
55
  plugins: ["typescript"]
52
56
  });
53
57
  let modified = false;
54
- const hasImport = scriptCode.includes("__devFetch");
58
+ let needsFetchCallHelper = false;
59
+ let needsFetchHandlerHelper = false;
60
+ const hasFetchCallImport = scriptCode.includes("__devFetchCall");
61
+ const hasFetchHandlerImport = scriptCode.includes("__devFetchHandler");
55
62
  traverse$2(ast, {
56
63
  CallExpression(path) {
57
64
  if (path.node.__observatoryTransformed) {
@@ -64,7 +71,7 @@ function fetchInstrumentPlugin() {
64
71
  if (!FETCH_FNS.has(callee.name)) {
65
72
  return;
66
73
  }
67
- if (path.parent && t.isCallExpression(path.parent) && t.isIdentifier(path.parent.callee) && path.parent.callee.name === "__devFetch") {
74
+ if (path.parent && t.isCallExpression(path.parent) && t.isIdentifier(path.parent.callee) && ["__devFetchCall", "__devFetchHandler"].includes(path.parent.callee.name)) {
68
75
  return;
69
76
  }
70
77
  const originalName = callee.name;
@@ -76,22 +83,35 @@ function fetchInstrumentPlugin() {
76
83
  return t.isExpression(node) ? node : void 0;
77
84
  }
78
85
  if (originalName === "useAsyncData" || originalName === "useLazyAsyncData") {
79
- if (args.length === 1 && getExpr(args[0])) {
86
+ if (args.length === 1 && isHandlerExpression(getExpr(args[0]))) {
80
87
  handlerArg = getExpr(args[0]);
81
- } else if (args.length >= 2 && getExpr(args[0]) && getExpr(args[1])) {
88
+ } else if (args.length >= 2 && getExpr(args[0]) && isHandlerExpression(getExpr(args[1]))) {
82
89
  keyArg = getExpr(args[0]);
83
90
  handlerArg = getExpr(args[1]);
84
91
  optsArg = getExpr(args[2]) ?? t.objectExpression([]);
85
92
  } else {
86
- return;
93
+ keyArg = getExpr(args[0]) ?? t.stringLiteral("");
94
+ optsArg = getExpr(args[1]) ?? t.objectExpression([]);
95
+ handlerArg = void 0;
87
96
  }
88
97
  } else {
89
98
  keyArg = getExpr(args[0]) ?? t.stringLiteral("");
90
99
  optsArg = getExpr(args[1]) ?? t.objectExpression([]);
91
100
  }
92
101
  let key = originalName;
93
- if (keyArg && t.isStringLiteral(keyArg)) {
94
- key = keyArg.value.replace(/[^a-z0-9]/gi, "-").replace(/^-+|-+$/g, "");
102
+ if (originalName === "useFetch" || originalName === "useLazyFetch") {
103
+ if (optsArg && t.isObjectExpression(optsArg)) {
104
+ const keyProp = optsArg.properties.find(
105
+ (property) => t.isObjectProperty(property) && t.isIdentifier(property.key) && property.key.name === "key" && t.isStringLiteral(property.value)
106
+ );
107
+ if (keyProp && t.isStringLiteral(keyProp.value)) {
108
+ key = keyProp.value.value;
109
+ } else if (keyArg && t.isStringLiteral(keyArg)) {
110
+ key = keyArg.value.replace(/[^a-z0-9]/gi, "-").replace(/^-+|-+$/g, "");
111
+ }
112
+ } else if (keyArg && t.isStringLiteral(keyArg)) {
113
+ key = keyArg.value.replace(/[^a-z0-9]/gi, "-").replace(/^-+|-+$/g, "");
114
+ }
95
115
  }
96
116
  const loc = path.node.loc;
97
117
  const meta = t.objectExpression([
@@ -100,9 +120,9 @@ function fetchInstrumentPlugin() {
100
120
  t.objectProperty(t.identifier("line"), t.numericLiteral(loc?.start.line ?? 0)),
101
121
  t.objectProperty(t.identifier("originalFn"), t.stringLiteral(originalName))
102
122
  ]);
103
- if (originalName === "useAsyncData" || originalName === "useLazyAsyncData") {
123
+ if ((originalName === "useAsyncData" || originalName === "useLazyAsyncData") && handlerArg) {
104
124
  if (handlerArg) {
105
- const wrappedHandlerFn = t.arrowFunctionExpression(
125
+ const wrappedHandler = t.arrowFunctionExpression(
106
126
  [t.restElement(t.identifier("args"))],
107
127
  t.conditionalExpression(
108
128
  t.logicalExpression(
@@ -111,41 +131,38 @@ function fetchInstrumentPlugin() {
111
131
  t.memberExpression(t.identifier("process"), t.identifier("client"))
112
132
  ),
113
133
  t.callExpression(
114
- t.callExpression(
115
- t.identifier("__devFetch"),
116
- [handlerArg, keyArg ?? t.stringLiteral(key), meta]
117
- ),
134
+ t.callExpression(t.identifier("__devFetchHandler"), [handlerArg, keyArg ?? t.stringLiteral(key), meta]),
118
135
  [t.spreadElement(t.identifier("args"))]
119
136
  ),
120
- t.callExpression(
121
- handlerArg,
122
- [t.spreadElement(t.identifier("args"))]
123
- )
137
+ t.callExpression(handlerArg, [t.spreadElement(t.identifier("args"))])
124
138
  )
125
139
  );
126
- wrappedHandlerFn.__observatoryTransformed = true;
127
- let newCall;
140
+ wrappedHandler.__observatoryTransformed = true;
141
+ needsFetchHandlerHelper = true;
128
142
  if (keyArg) {
129
- newCall = t.callExpression(t.identifier(originalName), [
143
+ const newCall = t.callExpression(t.identifier(originalName), [
130
144
  keyArg,
131
- wrappedHandlerFn,
145
+ wrappedHandler,
132
146
  optsArg ?? t.objectExpression([])
133
147
  ]);
148
+ newCall.__observatoryTransformed = true;
149
+ path.replaceWith(newCall);
134
150
  } else {
135
- newCall = t.callExpression(t.identifier(originalName), [wrappedHandlerFn]);
151
+ const newCall = t.callExpression(t.identifier(originalName), [wrappedHandler]);
152
+ newCall.__observatoryTransformed = true;
153
+ path.replaceWith(newCall);
136
154
  }
137
- newCall.__observatoryTransformed = true;
138
- path.replaceWith(newCall);
139
155
  modified = true;
140
156
  }
141
157
  } else {
142
- const newCall = t.callExpression(t.identifier("__devFetch"), [
158
+ const newCall = t.callExpression(t.identifier("__devFetchCall"), [
143
159
  t.identifier(originalName),
144
160
  keyArg ?? t.stringLiteral(""),
145
161
  optsArg ?? t.objectExpression([]),
146
162
  meta
147
163
  ]);
148
164
  newCall.__observatoryTransformed = true;
165
+ needsFetchCallHelper = true;
149
166
  path.replaceWith(newCall);
150
167
  modified = true;
151
168
  }
@@ -154,8 +171,12 @@ function fetchInstrumentPlugin() {
154
171
  if (!modified) {
155
172
  return null;
156
173
  }
157
- const importStatement = hasImport ? "" : `import { __devFetch } from 'nuxt-devtools-observatory/runtime/fetch-registry';
158
- `;
174
+ const importNames = [
175
+ needsFetchCallHelper && !hasFetchCallImport ? "__devFetchCall" : "",
176
+ needsFetchHandlerHelper && !hasFetchHandlerImport ? "__devFetchHandler" : ""
177
+ ].filter(Boolean);
178
+ const importStatement = importNames.length ? `import { ${importNames.join(", ")} } from 'nuxt-devtools-observatory/runtime/fetch-registry';
179
+ ` : "";
159
180
  const output = generate$2(ast, { retainLines: true }, scriptCode);
160
181
  let finalCode;
161
182
  if (isVue) {
@@ -177,26 +198,13 @@ function fetchInstrumentPlugin() {
177
198
 
178
199
  const traverse$1 = _traverse.default ?? _traverse;
179
200
  const generate$1 = _generate.default ?? _generate;
180
- function extractScriptBlock$1(code) {
181
- const openTagRE = /<script(\s[^>]*)?>/i;
182
- const openMatch = openTagRE.exec(code);
183
- if (!openMatch) {
184
- return null;
185
- }
186
- const start = openMatch.index + openMatch[0].length;
187
- const end = code.indexOf("<\/script>", start);
188
- if (end === -1) {
189
- return null;
190
- }
191
- return { content: code.slice(start, end), start, end };
192
- }
193
201
  function provideInjectPlugin() {
194
202
  return {
195
203
  name: "vite-plugin-observatory-provide-inject",
196
204
  enforce: "pre",
197
205
  transform(code, id) {
198
206
  const isVue = id.endsWith(".vue");
199
- if (!isVue && !id.endsWith(".ts")) {
207
+ if (!isVue && !id.endsWith(".ts") && !id.endsWith(".js")) {
200
208
  return;
201
209
  }
202
210
  if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry")) {
@@ -205,7 +213,7 @@ function provideInjectPlugin() {
205
213
  let scriptCode = code;
206
214
  let scriptStart = 0;
207
215
  if (isVue) {
208
- const block = extractScriptBlock$1(code);
216
+ const block = extractScriptBlock(code);
209
217
  if (!block) {
210
218
  return null;
211
219
  }
@@ -272,19 +280,6 @@ function provideInjectPlugin() {
272
280
 
273
281
  const traverse = _traverse.default ?? _traverse;
274
282
  const generate = _generate.default ?? _generate;
275
- function extractScriptBlock(code) {
276
- const openTagRE = /<script(\s[^>]*)?>/i;
277
- const openMatch = openTagRE.exec(code);
278
- if (!openMatch) {
279
- return null;
280
- }
281
- const start = openMatch.index + openMatch[0].length;
282
- const end = code.indexOf("<\/script>", start);
283
- if (end === -1) {
284
- return null;
285
- }
286
- return { content: code.slice(start, end), start, end };
287
- }
288
283
  const COMPOSABLE_RE = /\buse[A-Z]/;
289
284
  const SKIP_LIST = /* @__PURE__ */ new Set([
290
285
  "useFetch",
@@ -309,7 +304,7 @@ function composableTrackerPlugin() {
309
304
  enforce: "pre",
310
305
  transform(code, id) {
311
306
  const isVue = id.endsWith(".vue");
312
- if (!isVue && !id.endsWith(".ts")) {
307
+ if (!isVue && !id.endsWith(".ts") && !id.endsWith(".js")) {
313
308
  return;
314
309
  }
315
310
  if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry")) {
@@ -551,12 +546,10 @@ const module$1 = defineNuxtModule({
551
546
  }
552
547
  addPlugin(resolver.resolve("./runtime/plugin"));
553
548
  if (options.fetchDashboard) {
554
- nuxt.hook("nitro:config", (nitroConfig) => {
555
- nitroConfig.plugins = nitroConfig.plugins || [];
556
- nitroConfig.plugins.push(resolver.resolve("./nitro/fetch-capture"));
557
- });
549
+ addServerPlugin(resolver.resolve("./runtime/nitro/fetch-capture"));
558
550
  }
559
551
  const CLIENT_PORT = 4949;
552
+ const clientOrigin = `http://localhost:${CLIENT_PORT}`;
560
553
  nuxt.hook("vite:serverCreated", async (_viteServer, env) => {
561
554
  if (!env.isClient) {
562
555
  return;
@@ -566,7 +559,7 @@ const module$1 = defineNuxtModule({
566
559
  const inner = await createServer({
567
560
  root: resolver.resolve("../client"),
568
561
  base: "/",
569
- server: { port: CLIENT_PORT, strictPort: false, cors: true },
562
+ server: { port: CLIENT_PORT, strictPort: true, cors: true },
570
563
  appType: "spa",
571
564
  configFile: false,
572
565
  plugins: [vue()],
@@ -575,7 +568,7 @@ const module$1 = defineNuxtModule({
575
568
  await inner.listen();
576
569
  nuxt.hook("close", () => inner.close());
577
570
  });
578
- const base = `http://localhost:${CLIENT_PORT}`;
571
+ const base = clientOrigin;
579
572
  nuxt.hook("devtools:customTabs", (tabs) => {
580
573
  if (options.fetchDashboard) {
581
574
  tabs.push({
@@ -619,7 +612,8 @@ const module$1 = defineNuxtModule({
619
612
  }
620
613
  });
621
614
  nuxt.options.runtimeConfig.public.observatory = {
622
- heatmapThreshold: options.heatmapThreshold ?? 5
615
+ heatmapThreshold: options.heatmapThreshold ?? 5,
616
+ clientOrigin
623
617
  };
624
618
  }
625
619
  });
@@ -95,10 +95,16 @@ export function __trackComposable(name, callFn, meta) {
95
95
  }
96
96
  originalClearInterval(id2);
97
97
  });
98
- const trackedWatchers = [];
98
+ const effectsBefore = new Set(instance?.scope?.effects ?? []);
99
+ const mountedHooksBefore = instance?.bm?.length ?? 0;
100
+ const unmountedHooksBefore = instance?.um?.length ?? 0;
99
101
  const result = callFn();
100
102
  window.setInterval = originalSetInterval;
101
103
  window.clearInterval = originalClearInterval;
104
+ const trackedWatchers = (instance?.scope?.effects ?? []).filter((effect) => !effectsBefore.has(effect)).map((effect) => ({
105
+ effect,
106
+ stop: () => effect.stop?.()
107
+ }));
102
108
  const refs = {};
103
109
  if (result && typeof result === "object") {
104
110
  for (const [key, val] of Object.entries(result)) {
@@ -121,8 +127,8 @@ export function __trackComposable(name, callFn, meta) {
121
127
  watcherCount: trackedWatchers.length,
122
128
  intervalCount: trackedIntervals.length,
123
129
  lifecycle: {
124
- hasOnMounted: false,
125
- hasOnUnmounted: false,
130
+ hasOnMounted: (instance?.bm?.length ?? 0) > mountedHooksBefore,
131
+ hasOnUnmounted: (instance?.um?.length ?? 0) > unmountedHooksBefore,
126
132
  watchersCleaned: true,
127
133
  intervalsCleaned: true
128
134
  },
@@ -131,7 +137,7 @@ export function __trackComposable(name, callFn, meta) {
131
137
  };
132
138
  registry.register(entry);
133
139
  onUnmounted(() => {
134
- const leakedWatchers = trackedWatchers.filter((w) => !w.stopped);
140
+ const leakedWatchers = trackedWatchers.filter((w) => w.effect.active);
135
141
  const leakedIntervals = trackedIntervals.filter((id2) => !clearedIntervals.has(id2));
136
142
  const leak = leakedWatchers.length > 0 || leakedIntervals.length > 0;
137
143
  const reasons = [];
@@ -14,6 +14,24 @@ export interface FetchEntry {
14
14
  file?: string;
15
15
  line?: number;
16
16
  }
17
+ interface FetchResponse extends Response {
18
+ _data?: unknown;
19
+ }
20
+ interface FetchOptions {
21
+ server?: boolean;
22
+ onResponse?: (ctx: {
23
+ response: FetchResponse;
24
+ }) => void;
25
+ onResponseError?: (ctx: {
26
+ response: FetchResponse;
27
+ }) => void;
28
+ [key: string]: unknown;
29
+ }
30
+ interface FetchMeta {
31
+ key: string;
32
+ file: string;
33
+ line: number;
34
+ }
17
35
  /**
18
36
  * Sets up the fetch registry, which tracks all fetch requests and their
19
37
  * associated metadata (e.g. duration, size, origin).
@@ -56,8 +74,6 @@ export declare function setupFetchRegistry(): {
56
74
  readonly line?: number | undefined;
57
75
  }>>>;
58
76
  };
59
- export declare function __devFetch(originalFn: (...args: unknown[]) => unknown, arg1: ((...args: unknown[]) => unknown) | string, arg2: Record<string, unknown>, meta: {
60
- key: string;
61
- file: string;
62
- line: number;
63
- }): unknown;
77
+ export declare function __devFetchHandler(handler: (...args: unknown[]) => unknown, key: unknown, meta: FetchMeta): (...args: unknown[]) => Promise<unknown>;
78
+ export declare function __devFetchCall(originalFn: (url: string, opts: FetchOptions) => Promise<unknown>, url: string, opts: FetchOptions, meta: FetchMeta): Promise<unknown>;
79
+ export {};
@@ -14,42 +14,8 @@ export function setupFetchRegistry() {
14
14
  entries.value.set(id, updated);
15
15
  emit("fetch:update", updated);
16
16
  }
17
- function safeValue(val) {
18
- if (val === void 0 || val === null) {
19
- return val;
20
- }
21
- if (typeof val === "function") {
22
- return void 0;
23
- }
24
- if (typeof val === "object") {
25
- try {
26
- return JSON.parse(JSON.stringify(val));
27
- } catch {
28
- return String(val);
29
- }
30
- }
31
- return val;
32
- }
33
- function sanitize(entry) {
34
- return {
35
- id: entry.id,
36
- key: entry.key,
37
- url: entry.url,
38
- status: entry.status,
39
- origin: entry.origin,
40
- startTime: entry.startTime,
41
- endTime: entry.endTime,
42
- ms: entry.ms,
43
- size: entry.size,
44
- cached: entry.cached,
45
- payload: safeValue(entry.payload),
46
- error: safeValue(entry.error),
47
- file: entry.file,
48
- line: entry.line
49
- };
50
- }
51
17
  function getAll() {
52
- return [...entries.value.values()].map(sanitize);
18
+ return [...entries.value.values()];
53
19
  }
54
20
  function clear() {
55
21
  entries.value.clear();
@@ -64,97 +30,103 @@ export function setupFetchRegistry() {
64
30
  }
65
31
  return { register, update, getAll, clear, entries: readonly(entries) };
66
32
  }
67
- export function __devFetch(originalFn, arg1, arg2, meta) {
33
+ export function __devFetchHandler(handler, key, meta) {
68
34
  if (!import.meta.dev || !import.meta.client) {
69
- if (typeof arg1 === "function") {
70
- return (...args) => arg1(...args);
71
- }
72
- return originalFn(arg1, arg2);
35
+ return (...args) => Promise.resolve(handler(...args));
73
36
  }
74
37
  const registry = window.__observatory__?.fetch;
75
38
  if (!registry) {
76
- return typeof arg1 === "function" ? originalFn(arg1, arg2) : originalFn(arg1, arg2);
39
+ return (...args) => Promise.resolve(handler(...args));
77
40
  }
78
- if (typeof arg1 === "function") {
79
- const handler = arg1;
80
- return function wrappedHandler(...handlerArgs) {
81
- const id = `${meta.key}::${Date.now()}`;
82
- const startTime = performance.now();
83
- const origin = import.meta.server ? "ssr" : "csr";
84
- registry.register({
85
- id,
86
- key: meta.key,
87
- url: meta.key,
88
- // No URL in useAsyncData, use key as identifier
89
- status: "pending",
90
- origin,
91
- startTime,
92
- cached: false,
93
- file: meta.file,
94
- line: meta.line
95
- });
96
- return Promise.resolve(handler(...handlerArgs)).then((result) => {
97
- registry.update(id, {
98
- status: "ok",
99
- endTime: performance.now(),
100
- ms: Math.round(performance.now() - startTime),
101
- payload: result
102
- });
103
- return result;
104
- }).catch((error) => {
105
- registry.update(id, {
106
- status: "error",
107
- endTime: performance.now(),
108
- ms: Math.round(performance.now() - startTime),
109
- error
110
- });
111
- throw error;
112
- });
113
- };
114
- } else {
115
- const url = arg1;
116
- const opts = arg2 || {};
117
- const id = `${meta.key}::${Date.now()}`;
41
+ const normalizedKey = typeof key === "string" ? key : "useAsyncData";
42
+ return (...args) => {
43
+ const id = `${normalizedKey}::${Date.now()}`;
118
44
  const startTime = performance.now();
119
- const origin = import.meta.server ? "ssr" : "csr";
120
45
  registry.register({
121
46
  id,
122
- key: meta.key,
123
- url: typeof url === "string" ? url : String(url),
47
+ key: normalizedKey,
48
+ url: normalizedKey,
124
49
  status: "pending",
125
- origin,
50
+ origin: "csr",
126
51
  startTime,
127
52
  cached: false,
128
53
  file: meta.file,
129
54
  line: meta.line
130
55
  });
131
- return originalFn(url, {
132
- ...opts,
133
- onResponse({ response }) {
134
- const ms = Math.round(performance.now() - startTime);
135
- const size = Number(response.headers?.get("content-length")) || void 0;
136
- const cached = response.headers?.get("x-nuxt-cache") === "HIT";
137
- registry.update(id, {
138
- status: response.ok ? "ok" : "error",
139
- endTime: performance.now(),
140
- ms,
141
- size,
142
- cached
143
- });
144
- if (typeof opts.onResponse === "function") {
145
- opts.onResponse({ response });
146
- }
147
- },
148
- onResponseError({ response }) {
149
- registry.update(id, {
150
- status: "error",
151
- endTime: performance.now(),
152
- ms: Math.round(performance.now() - startTime)
153
- });
154
- if (typeof opts.onResponseError === "function") {
155
- opts.onResponseError({ response });
156
- }
157
- }
56
+ return Promise.resolve(handler(...args)).then((result) => {
57
+ registry.update(id, {
58
+ status: "ok",
59
+ endTime: performance.now(),
60
+ ms: Math.round(performance.now() - startTime),
61
+ payload: result
62
+ });
63
+ return result;
64
+ }).catch((error) => {
65
+ registry.update(id, {
66
+ status: "error",
67
+ endTime: performance.now(),
68
+ ms: Math.round(performance.now() - startTime),
69
+ error
70
+ });
71
+ throw error;
158
72
  });
73
+ };
74
+ }
75
+ export function __devFetchCall(originalFn, url, opts, meta) {
76
+ if (!import.meta.dev || !import.meta.client) {
77
+ return originalFn(url, opts);
78
+ }
79
+ const registry = window.__observatory__?.fetch;
80
+ if (!registry) {
81
+ return originalFn(url, opts);
82
+ }
83
+ const id = `${meta.key}::${Date.now()}`;
84
+ const startTime = performance.now();
85
+ const payload = window.__NUXT__?.data ?? {};
86
+ const fromPayload = Object.prototype.hasOwnProperty.call(payload, meta.key);
87
+ const origin = fromPayload ? "ssr" : "csr";
88
+ registry.register({
89
+ id,
90
+ key: meta.key,
91
+ url: typeof url === "string" ? url : String(url),
92
+ status: fromPayload ? "cached" : "pending",
93
+ origin,
94
+ startTime,
95
+ cached: fromPayload,
96
+ payload: fromPayload ? payload[meta.key] : void 0,
97
+ file: meta.file,
98
+ line: meta.line
99
+ });
100
+ if (fromPayload) {
101
+ return originalFn(url, opts);
159
102
  }
103
+ return originalFn(url, {
104
+ ...opts,
105
+ onResponse({ response }) {
106
+ const ms = Math.round(performance.now() - startTime);
107
+ const size = Number(response.headers?.get("content-length")) || void 0;
108
+ const cached = response.headers?.get("x-nuxt-cache") === "HIT";
109
+ registry.update(id, {
110
+ status: cached ? "cached" : response.ok ? "ok" : "error",
111
+ endTime: performance.now(),
112
+ ms,
113
+ size,
114
+ cached,
115
+ payload: response._data
116
+ });
117
+ if (typeof opts.onResponse === "function") {
118
+ opts.onResponse({ response });
119
+ }
120
+ },
121
+ onResponseError({ response }) {
122
+ registry.update(id, {
123
+ status: "error",
124
+ endTime: performance.now(),
125
+ ms: Math.round(performance.now() - startTime)
126
+ });
127
+ if (typeof opts.onResponseError === "function") {
128
+ opts.onResponseError({ response });
129
+ }
130
+ }
131
+ });
160
132
  }
@@ -3,6 +3,8 @@ export interface ProvideEntry {
3
3
  componentName: string;
4
4
  componentFile: string;
5
5
  componentUid: number;
6
+ parentUid?: number;
7
+ parentFile?: string;
6
8
  isReactive: boolean;
7
9
  valueSnapshot: unknown;
8
10
  line: number;
@@ -12,6 +14,8 @@ export interface InjectEntry {
12
14
  componentName: string;
13
15
  componentFile: string;
14
16
  componentUid: number;
17
+ parentUid?: number;
18
+ parentFile?: string;
15
19
  resolved: boolean;
16
20
  resolvedFromFile?: string;
17
21
  resolvedFromUid?: number;
@@ -79,6 +79,8 @@ export function __devProvide(key, value, meta) {
79
79
  componentName: instance?.type?.__name ?? "unknown",
80
80
  componentFile: meta.file,
81
81
  componentUid: instance?.uid ?? -1,
82
+ parentUid: instance?.parent?.uid,
83
+ parentFile: instance?.parent?.type?.__file,
82
84
  isReactive: isRef(value) || isReactive(value),
83
85
  valueSnapshot: safeSnapshot(unref(value)),
84
86
  line: meta.line
@@ -100,6 +102,8 @@ export function __devInject(key, defaultValue, meta) {
100
102
  componentName: instance?.type?.__name ?? "unknown",
101
103
  componentFile: meta.file,
102
104
  componentUid: instance?.uid ?? -1,
105
+ parentUid: instance?.parent?.uid,
106
+ parentFile: instance?.parent?.type?.__file,
103
107
  resolved: resolved !== void 0,
104
108
  resolvedFromFile: providerInfo?.file,
105
109
  resolvedFromUid: providerInfo?.uid,
@@ -25,12 +25,11 @@ export interface RenderEntry {
25
25
  * Sets up a render registry for the given Nuxt app.
26
26
  * @param {{ vueApp: import('vue').App }} nuxtApp - The Nuxt app object.
27
27
  * @param {object} nuxtApp.vueApp - The Vue app instance.
28
- * @param {number} threshold - The minimum number of renders required for a component to be tracked.
29
28
  * @returns {object} The render registry object.
30
29
  */
31
30
  export declare function setupRenderRegistry(nuxtApp: {
32
31
  vueApp: import('vue').App;
33
- }, threshold: number): {
32
+ }): {
34
33
  getAll: () => RenderEntry[];
35
34
  snapshot: () => RenderEntry[];
36
35
  };