nuxt-devtools-observatory 0.1.15 → 0.1.17

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
@@ -361,8 +361,9 @@ function composableTrackerPlugin() {
361
361
  }
362
362
  const args = path.node.arguments;
363
363
  const loc = path.node.loc;
364
+ const fileName = id.split(/[\\/]/).pop() || id;
364
365
  const meta = t.objectExpression([
365
- t.objectProperty(t.identifier("file"), t.stringLiteral(id)),
366
+ t.objectProperty(t.identifier("file"), t.stringLiteral(fileName)),
366
367
  t.objectProperty(t.identifier("line"), t.numericLiteral(loc?.start.line ?? 0))
367
368
  ]);
368
369
  path.replaceWith(
@@ -547,7 +548,9 @@ const module$1 = defineNuxtModule({
547
548
  if (options.transitionTracker) {
548
549
  addVitePlugin(transitionTrackerPlugin());
549
550
  }
550
- addPlugin(resolver.resolve("./runtime/plugin"));
551
+ if (options.fetchDashboard || options.provideInjectGraph || options.composableTracker || options.renderHeatmap || options.transitionTracker) {
552
+ addPlugin(resolver.resolve("./runtime/plugin"));
553
+ }
551
554
  if (options.fetchDashboard) {
552
555
  addServerPlugin(resolver.resolve("./runtime/nitro/fetch-capture"));
553
556
  }
@@ -616,7 +619,12 @@ const module$1 = defineNuxtModule({
616
619
  });
617
620
  nuxt.options.runtimeConfig.public.observatory = {
618
621
  heatmapThreshold: options.heatmapThreshold ?? 5,
619
- clientOrigin
622
+ clientOrigin,
623
+ fetchDashboard: options.fetchDashboard,
624
+ provideInjectGraph: options.provideInjectGraph,
625
+ composableTracker: options.composableTracker,
626
+ renderHeatmap: options.renderHeatmap,
627
+ transitionTracker: options.transitionTracker
620
628
  };
621
629
  }
622
630
  });
@@ -80,15 +80,29 @@ export function __devFetchCall(originalFn, url, opts, meta) {
80
80
  if (!registry) {
81
81
  return originalFn(url, opts);
82
82
  }
83
+ function resolveUrl(u) {
84
+ if (u && typeof u === "object" && "value" in u) {
85
+ return resolveUrl(u.value);
86
+ }
87
+ if (typeof u === "function") {
88
+ try {
89
+ return resolveUrl(u());
90
+ } catch {
91
+ return String(u);
92
+ }
93
+ }
94
+ return typeof u === "string" ? u : String(u);
95
+ }
83
96
  const id = `${meta.key}::${Date.now()}`;
84
97
  const startTime = performance.now();
85
98
  const payload = window.__NUXT__?.data ?? {};
86
99
  const fromPayload = Object.prototype.hasOwnProperty.call(payload, meta.key);
87
100
  const origin = fromPayload ? "ssr" : "csr";
101
+ const resolvedUrl = resolveUrl(url);
88
102
  registry.register({
89
103
  id,
90
104
  key: meta.key,
91
- url: typeof url === "string" ? url : String(url),
105
+ url: resolvedUrl,
92
106
  status: fromPayload ? "cached" : "pending",
93
107
  origin,
94
108
  startTime,
@@ -98,7 +112,21 @@ export function __devFetchCall(originalFn, url, opts, meta) {
98
112
  line: meta.line
99
113
  });
100
114
  if (fromPayload) {
101
- return originalFn(url, opts);
115
+ const optsWithHooks = {
116
+ ...opts,
117
+ onResponse: function(ctx) {
118
+ const entry = registry.getAll().find((e) => e.id === id);
119
+ if (entry) {
120
+ registry.update(id, { payload: ctx.response._data });
121
+ }
122
+ if (typeof opts.onResponse === "function") {
123
+ opts.onResponse(ctx);
124
+ }
125
+ },
126
+ onResponseError: typeof opts.onResponseError === "function" ? opts.onResponseError : () => {
127
+ }
128
+ };
129
+ return originalFn(url, optsWithHooks);
102
130
  }
103
131
  return originalFn(url, {
104
132
  ...opts,
@@ -44,26 +44,40 @@ export default defineNuxtPlugin(() => {
44
44
  }
45
45
  const nuxtApp = useNuxtApp();
46
46
  const config = useRuntimeConfig().public.observatory;
47
- nuxtApp.vueApp.config.performance = true;
48
- const fetchRegistry = setupFetchRegistry();
49
- const provideInjectRegistry = setupProvideInjectRegistry();
50
- const composableRegistry = setupComposableRegistry();
51
- const renderRegistry = setupRenderRegistry(nuxtApp, {
52
- // Only flag as hydration when the page was actually server-rendered.
53
- // nuxtApp.isHydrating is true on ALL initial mounts (including CSR-only),
54
- // so we gate on payload.serverRendered to avoid false positives.
55
- isHydrating: () => (nuxtApp.isHydrating ?? false) && nuxtApp.payload?.serverRendered === true
56
- });
57
- const transitionRegistry = setupTransitionRegistry();
47
+ if (config.renderHeatmap) {
48
+ nuxtApp.vueApp.config.performance = true;
49
+ }
50
+ const registries = {};
51
+ let fetchRegistry;
52
+ let provideInjectRegistry;
53
+ let composableRegistry;
54
+ let renderRegistry;
55
+ let transitionRegistry;
56
+ if (config.fetchDashboard) {
57
+ fetchRegistry = setupFetchRegistry();
58
+ registries.fetch = fetchRegistry;
59
+ }
60
+ if (config.provideInjectGraph) {
61
+ provideInjectRegistry = setupProvideInjectRegistry();
62
+ registries.provideInject = provideInjectRegistry;
63
+ }
64
+ if (config.composableTracker) {
65
+ composableRegistry = setupComposableRegistry();
66
+ registries.composable = composableRegistry;
67
+ }
68
+ if (config.renderHeatmap) {
69
+ renderRegistry = setupRenderRegistry(nuxtApp, {
70
+ isHydrating: () => (nuxtApp.isHydrating ?? false) && nuxtApp.payload?.serverRendered === true
71
+ });
72
+ registries.render = renderRegistry;
73
+ }
74
+ if (config.transitionTracker) {
75
+ transitionRegistry = setupTransitionRegistry();
76
+ registries.transition = transitionRegistry;
77
+ }
58
78
  if (import.meta.client) {
59
79
  delete window.__observatory__;
60
- window.__observatory__ = {
61
- fetch: fetchRegistry,
62
- provideInject: provideInjectRegistry,
63
- composable: composableRegistry,
64
- render: renderRegistry,
65
- transition: transitionRegistry
66
- };
80
+ window.__observatory__ = registries;
67
81
  let lastMessageSource = null;
68
82
  let lastMessageOrigin = "";
69
83
  window.addEventListener("message", (event) => {
@@ -79,13 +93,17 @@ export default defineNuxtPlugin(() => {
79
93
  return;
80
94
  }
81
95
  if (type === "observatory:clear-composables") {
82
- composableRegistry.clear();
96
+ if (composableRegistry) {
97
+ composableRegistry.clear();
98
+ }
83
99
  const source = event.source;
84
100
  source?.postMessage({ type: "observatory:snapshot", data: buildSnapshot() }, event.origin);
85
101
  }
86
102
  if (type === "observatory:edit-composable") {
87
103
  const { id, key, value } = event.data;
88
- composableRegistry.editValue(id, key, value);
104
+ if (composableRegistry) {
105
+ composableRegistry.editValue(id, key, value);
106
+ }
89
107
  }
90
108
  if (type === "observatory:open-in-editor") {
91
109
  const { file } = event.data;
@@ -96,18 +114,20 @@ export default defineNuxtPlugin(() => {
96
114
  }
97
115
  }
98
116
  });
99
- composableRegistry.onComposableChange(() => {
100
- if (!lastMessageSource || !lastMessageOrigin) {
101
- return;
102
- }
103
- lastMessageSource.postMessage(
104
- {
105
- type: "observatory:snapshot",
106
- data: buildSnapshot()
107
- },
108
- lastMessageOrigin
109
- );
110
- });
117
+ if (composableRegistry && composableRegistry.onComposableChange) {
118
+ composableRegistry.onComposableChange(() => {
119
+ if (!lastMessageSource || !lastMessageOrigin) {
120
+ return;
121
+ }
122
+ lastMessageSource.postMessage(
123
+ {
124
+ type: "observatory:snapshot",
125
+ data: buildSnapshot()
126
+ },
127
+ lastMessageOrigin
128
+ );
129
+ });
130
+ }
111
131
  }
112
132
  nuxtApp.hook("app:mounted", () => {
113
133
  broadcastAll();
@@ -119,14 +139,23 @@ export default defineNuxtPlugin(() => {
119
139
  if (!from || from.name === void 0) {
120
140
  return;
121
141
  }
122
- renderRegistry.reset();
123
- provideInjectRegistry.clear?.();
124
- composableRegistry.clear();
142
+ if (renderRegistry) {
143
+ renderRegistry.reset();
144
+ }
145
+ ;
146
+ provideInjectRegistry?.clear?.();
147
+ if (composableRegistry) {
148
+ composableRegistry.clear();
149
+ }
125
150
  }
126
151
  );
127
152
  router.afterEach((to) => {
128
- composableRegistry.setRoute(to.path ?? "/");
129
- renderRegistry.setRoute(to.path ?? "/");
153
+ if (composableRegistry) {
154
+ composableRegistry.setRoute(to.path ?? "/");
155
+ }
156
+ if (renderRegistry) {
157
+ renderRegistry.setRoute(to.path ?? "/");
158
+ }
130
159
  nextTick(() => broadcastAll());
131
160
  });
132
161
  }
@@ -141,13 +170,30 @@ export default defineNuxtPlugin(() => {
141
170
  channel.send("observatory:snapshot", buildSnapshot());
142
171
  }
143
172
  function buildSnapshot() {
144
- return toSerializable({
145
- fetch: fetchRegistry.getAll(),
146
- provideInject: provideInjectRegistry.getAll(),
147
- composables: composableRegistry.getAll(),
148
- renders: renderRegistry.getAll(),
149
- transitions: transitionRegistry.getAll()
150
- });
173
+ const snap = {};
174
+ if (fetchRegistry) {
175
+ snap.fetch = fetchRegistry.getAll();
176
+ }
177
+ if (provideInjectRegistry) {
178
+ snap.provideInject = provideInjectRegistry.getAll();
179
+ }
180
+ if (composableRegistry) {
181
+ snap.composables = composableRegistry.getAll();
182
+ }
183
+ if (renderRegistry) {
184
+ snap.renders = renderRegistry.getAll();
185
+ }
186
+ if (transitionRegistry) {
187
+ snap.transitions = transitionRegistry.getAll();
188
+ }
189
+ snap.features = {
190
+ fetchDashboard: !!fetchRegistry,
191
+ provideInjectGraph: !!provideInjectRegistry,
192
+ composableTracker: !!composableRegistry,
193
+ renderHeatmap: !!renderRegistry,
194
+ transitionTracker: !!transitionRegistry
195
+ };
196
+ return toSerializable(snap);
151
197
  }
152
198
  function getDevtoolsChannel() {
153
199
  return window.__nuxt_devtools__?.channel ?? null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-devtools-observatory",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Nuxt DevTools: useFetch Dashboard, provide/inject Graph, Composable Tracker, Render Heatmap",
5
5
  "type": "module",
6
6
  "main": "./dist/module.mjs",
@@ -37,7 +37,9 @@
37
37
  "typecheck": "vue-tsc --noEmit",
38
38
  "test": "vitest run",
39
39
  "test:watch": "vitest",
40
- "test:coverage": "vitest run --coverage"
40
+ "test:coverage": "vitest run --coverage",
41
+ "test:screenshots": "playwright test scripts/playwright/screenshot-trackers.spec.ts",
42
+ "capture:screenshots": "node scripts/playwright/capture-observatory-screenshots.cjs"
41
43
  },
42
44
  "dependencies": {
43
45
  "@babel/generator": "^7.29.1",
@@ -51,6 +53,7 @@
51
53
  "@nuxt/kit": "^3.0.0",
52
54
  "@nuxt/module-builder": "^1.0.2",
53
55
  "@nuxt/schema": "^3.0.0",
56
+ "@playwright/test": "^1.58.2",
54
57
  "@types/babel__generator": "^7.27.0",
55
58
  "@types/babel__traverse": "^7.28.0",
56
59
  "@types/node": "^25.5.0",
@@ -65,6 +68,7 @@
65
68
  "globals": "^17.3.0",
66
69
  "happy-dom": "^20.8.4",
67
70
  "nuxt": "^3.0.0 || ^4.0.0",
71
+ "playwright": "^1.58.2",
68
72
  "postcss-html": "^1.8.1",
69
73
  "prettier": "^3.8.1",
70
74
  "stylelint": "^17.4.0",