nuxt-devtools-observatory 0.1.0

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.
Files changed (33) hide show
  1. package/README.md +209 -0
  2. package/client/dist/assets/index-C76d764s.js +17 -0
  3. package/client/dist/assets/index-yIuOV1_N.css +1 -0
  4. package/client/dist/index.html +47 -0
  5. package/client/index.html +46 -0
  6. package/client/src/App.vue +114 -0
  7. package/client/src/main.ts +5 -0
  8. package/client/src/stores/observatory.ts +65 -0
  9. package/client/src/style.css +261 -0
  10. package/client/src/views/ComposableTracker.vue +347 -0
  11. package/client/src/views/FetchDashboard.vue +492 -0
  12. package/client/src/views/ProvideInjectGraph.vue +481 -0
  13. package/client/src/views/RenderHeatmap.vue +492 -0
  14. package/client/src/views/TransitionTimeline.vue +527 -0
  15. package/client/tsconfig.json +16 -0
  16. package/client/vite.config.ts +12 -0
  17. package/dist/module.d.mts +38 -0
  18. package/dist/module.json +12 -0
  19. package/dist/module.mjs +562 -0
  20. package/dist/runtime/composables/composable-registry.d.ts +40 -0
  21. package/dist/runtime/composables/composable-registry.js +135 -0
  22. package/dist/runtime/composables/fetch-registry.d.ts +63 -0
  23. package/dist/runtime/composables/fetch-registry.js +83 -0
  24. package/dist/runtime/composables/provide-inject-registry.d.ts +57 -0
  25. package/dist/runtime/composables/provide-inject-registry.js +96 -0
  26. package/dist/runtime/composables/render-registry.d.ts +36 -0
  27. package/dist/runtime/composables/render-registry.js +85 -0
  28. package/dist/runtime/composables/transition-registry.d.ts +21 -0
  29. package/dist/runtime/composables/transition-registry.js +125 -0
  30. package/dist/runtime/plugin.d.ts +2 -0
  31. package/dist/runtime/plugin.js +66 -0
  32. package/dist/types.d.mts +3 -0
  33. package/package.json +89 -0
@@ -0,0 +1,562 @@
1
+ import { defineNuxtModule, createResolver, addVitePlugin, addPlugin } from '@nuxt/kit';
2
+ import { parse } from '@babel/parser';
3
+ import _traverse from '@babel/traverse';
4
+ import _generate from '@babel/generator';
5
+ import * as t from '@babel/types';
6
+
7
+ const traverse$2 = _traverse.default ?? _traverse;
8
+ const generate$2 = _generate.default ?? _generate;
9
+ function extractScriptBlock$2(code) {
10
+ const openTagRE = /<script(\s[^>]*)?>/i;
11
+ const openMatch = openTagRE.exec(code);
12
+ if (!openMatch) {
13
+ return null;
14
+ }
15
+ const start = openMatch.index + openMatch[0].length;
16
+ const end = code.indexOf("<\/script>", start);
17
+ if (end === -1) {
18
+ return null;
19
+ }
20
+ return { content: code.slice(start, end), start, end };
21
+ }
22
+ const FETCH_FNS = /* @__PURE__ */ new Set(["useFetch", "useAsyncData", "useLazyFetch", "useLazyAsyncData"]);
23
+ function fetchInstrumentPlugin() {
24
+ return {
25
+ name: "vite-plugin-observatory-fetch",
26
+ enforce: "pre",
27
+ transform(code, id) {
28
+ const isVue = id.endsWith(".vue");
29
+ if (!isVue && !id.endsWith(".ts") && !id.endsWith(".js")) {
30
+ return;
31
+ }
32
+ if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry")) {
33
+ return;
34
+ }
35
+ let scriptCode = code;
36
+ let scriptStart = 0;
37
+ if (isVue) {
38
+ const block = extractScriptBlock$2(code);
39
+ if (!block) {
40
+ return null;
41
+ }
42
+ scriptCode = block.content;
43
+ scriptStart = block.start;
44
+ }
45
+ if (![...FETCH_FNS].some((fn) => scriptCode.includes(fn))) {
46
+ return;
47
+ }
48
+ try {
49
+ const ast = parse(scriptCode, {
50
+ sourceType: "module",
51
+ plugins: ["typescript"]
52
+ });
53
+ let modified = false;
54
+ const hasImport = scriptCode.includes("__devFetch");
55
+ traverse$2(ast, {
56
+ CallExpression(path) {
57
+ const callee = path.node.callee;
58
+ if (!t.isIdentifier(callee)) {
59
+ return;
60
+ }
61
+ if (!FETCH_FNS.has(callee.name)) {
62
+ return;
63
+ }
64
+ if (path.parent && t.isCallExpression(path.parent) && t.isIdentifier(path.parent.callee) && path.parent.callee.name === "__devFetch") {
65
+ return;
66
+ }
67
+ const originalName = callee.name;
68
+ const args = path.node.arguments;
69
+ const urlArg = args[0] ?? t.stringLiteral("");
70
+ const optsArg = args[1] ?? t.objectExpression([]);
71
+ let key = originalName;
72
+ if (t.isObjectExpression(optsArg)) {
73
+ const keyProp = optsArg.properties.find(
74
+ (p) => t.isObjectProperty(p) && t.isIdentifier(p.key) && p.key.name === "key"
75
+ );
76
+ if (keyProp && t.isStringLiteral(keyProp.value)) {
77
+ key = keyProp.value.value;
78
+ } else {
79
+ if (t.isStringLiteral(urlArg)) {
80
+ key = urlArg.value.replace(/[^a-z0-9]/gi, "-").replace(/^-+|-+$/g, "");
81
+ }
82
+ }
83
+ }
84
+ const loc = path.node.loc;
85
+ const meta = t.objectExpression([
86
+ t.objectProperty(t.identifier("key"), t.stringLiteral(key)),
87
+ t.objectProperty(t.identifier("file"), t.stringLiteral(id.split("/").pop() ?? id)),
88
+ t.objectProperty(t.identifier("line"), t.numericLiteral(loc?.start.line ?? 0)),
89
+ t.objectProperty(t.identifier("originalFn"), t.stringLiteral(originalName))
90
+ ]);
91
+ path.replaceWith(t.callExpression(t.identifier("__devFetch"), [t.identifier(originalName), urlArg, optsArg, meta]));
92
+ modified = true;
93
+ }
94
+ });
95
+ if (!modified) {
96
+ return null;
97
+ }
98
+ const importStatement = hasImport ? "" : `import { __devFetch } from 'nuxt-devtools-observatory/runtime/fetch-registry';
99
+ `;
100
+ const output = generate$2(ast, { retainLines: true }, scriptCode);
101
+ if (isVue) {
102
+ const newCode = code.slice(0, scriptStart) + importStatement + output.code + code.slice(scriptStart + scriptCode.length);
103
+ return { code: newCode };
104
+ }
105
+ return {
106
+ code: importStatement + output.code,
107
+ map: output.map
108
+ };
109
+ } catch (err) {
110
+ console.warn("[observatory] fetch transform error:", err);
111
+ return null;
112
+ }
113
+ }
114
+ };
115
+ }
116
+
117
+ const traverse$1 = _traverse.default ?? _traverse;
118
+ const generate$1 = _generate.default ?? _generate;
119
+ function extractScriptBlock$1(code) {
120
+ const openTagRE = /<script(\s[^>]*)?>/i;
121
+ const openMatch = openTagRE.exec(code);
122
+ if (!openMatch) {
123
+ return null;
124
+ }
125
+ const start = openMatch.index + openMatch[0].length;
126
+ const end = code.indexOf("<\/script>", start);
127
+ if (end === -1) {
128
+ return null;
129
+ }
130
+ return { content: code.slice(start, end), start, end };
131
+ }
132
+ function provideInjectPlugin() {
133
+ return {
134
+ name: "vite-plugin-observatory-provide-inject",
135
+ enforce: "pre",
136
+ transform(code, id) {
137
+ const isVue = id.endsWith(".vue");
138
+ if (!isVue && !id.endsWith(".ts")) {
139
+ return;
140
+ }
141
+ if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry")) {
142
+ return;
143
+ }
144
+ let scriptCode = code;
145
+ let scriptStart = 0;
146
+ if (isVue) {
147
+ const block = extractScriptBlock$1(code);
148
+ if (!block) {
149
+ return null;
150
+ }
151
+ scriptCode = block.content;
152
+ scriptStart = block.start;
153
+ }
154
+ if (!scriptCode.includes("provide(") && !scriptCode.includes("inject(")) {
155
+ return;
156
+ }
157
+ try {
158
+ const ast = parse(scriptCode, {
159
+ sourceType: "module",
160
+ plugins: ["typescript"]
161
+ });
162
+ let modified = false;
163
+ traverse$1(ast, {
164
+ CallExpression(path) {
165
+ const callee = path.node.callee;
166
+ if (!t.isIdentifier(callee)) {
167
+ return;
168
+ }
169
+ const name = callee.name;
170
+ if (name !== "provide" && name !== "inject") {
171
+ return;
172
+ }
173
+ if (path.parent && t.isCallExpression(path.parent) && t.isIdentifier(path.parent.callee) && (path.parent.callee.name === "__devProvide" || path.parent.callee.name === "__devInject")) {
174
+ return;
175
+ }
176
+ const args = path.node.arguments;
177
+ const loc = path.node.loc;
178
+ const meta = t.objectExpression([
179
+ t.objectProperty(t.identifier("file"), t.stringLiteral(id.split("/").pop() ?? id)),
180
+ t.objectProperty(t.identifier("line"), t.numericLiteral(loc?.start.line ?? 0))
181
+ ]);
182
+ if (name === "provide") {
183
+ path.replaceWith(t.callExpression(t.identifier("__devProvide"), [...args, meta]));
184
+ } else {
185
+ const injectArgs = args.length >= 2 ? args : [...args, t.identifier("undefined")];
186
+ path.replaceWith(t.callExpression(t.identifier("__devInject"), [...injectArgs, meta]));
187
+ }
188
+ modified = true;
189
+ }
190
+ });
191
+ if (!modified) {
192
+ return null;
193
+ }
194
+ const importLine = `import { __devProvide, __devInject } from 'nuxt-devtools-observatory/runtime/provide-inject-registry';
195
+ `;
196
+ const output = generate$1(ast, { retainLines: true }, scriptCode);
197
+ if (isVue) {
198
+ const newCode = code.slice(0, scriptStart) + importLine + output.code + code.slice(scriptStart + scriptCode.length);
199
+ return { code: newCode };
200
+ }
201
+ return { code: importLine + output.code, map: output.map };
202
+ } catch (err) {
203
+ console.warn("[observatory] provide/inject transform error:", err);
204
+ return null;
205
+ }
206
+ }
207
+ };
208
+ }
209
+
210
+ const traverse = _traverse.default ?? _traverse;
211
+ const generate = _generate.default ?? _generate;
212
+ function extractScriptBlock(code) {
213
+ const openTagRE = /<script(\s[^>]*)?>/i;
214
+ const openMatch = openTagRE.exec(code);
215
+ if (!openMatch) {
216
+ return null;
217
+ }
218
+ const start = openMatch.index + openMatch[0].length;
219
+ const end = code.indexOf("<\/script>", start);
220
+ if (end === -1) {
221
+ return null;
222
+ }
223
+ return { content: code.slice(start, end), start, end };
224
+ }
225
+ const COMPOSABLE_RE = /\buse[A-Z]/;
226
+ const SKIP_LIST = /* @__PURE__ */ new Set([
227
+ "useFetch",
228
+ "useAsyncData",
229
+ "useLazyFetch",
230
+ "useLazyAsyncData",
231
+ "useState",
232
+ "useRoute",
233
+ "useRouter",
234
+ "useNuxtApp",
235
+ "useRuntimeConfig",
236
+ "useHead",
237
+ "useSeoMeta",
238
+ "useServerSeoMeta",
239
+ "useNuxtData",
240
+ "useError",
241
+ "useRequestHeaders"
242
+ ]);
243
+ function composableTrackerPlugin() {
244
+ return {
245
+ name: "vite-plugin-observatory-composables",
246
+ enforce: "pre",
247
+ transform(code, id) {
248
+ const isVue = id.endsWith(".vue");
249
+ if (!isVue && !id.endsWith(".ts")) {
250
+ return;
251
+ }
252
+ if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry")) {
253
+ return;
254
+ }
255
+ let scriptCode = code;
256
+ let scriptStart = 0;
257
+ if (isVue) {
258
+ const block = extractScriptBlock(code);
259
+ if (!block) {
260
+ return null;
261
+ }
262
+ scriptCode = block.content;
263
+ scriptStart = block.start;
264
+ }
265
+ if (!COMPOSABLE_RE.test(scriptCode)) {
266
+ return;
267
+ }
268
+ try {
269
+ const ast = parse(scriptCode, {
270
+ sourceType: "module",
271
+ plugins: ["typescript"]
272
+ });
273
+ let modified = false;
274
+ traverse(ast, {
275
+ CallExpression(path) {
276
+ const callee = path.node.callee;
277
+ if (!t.isIdentifier(callee)) {
278
+ return;
279
+ }
280
+ const name = callee.name;
281
+ if (!COMPOSABLE_RE.test(name)) {
282
+ return;
283
+ }
284
+ if (SKIP_LIST.has(name)) {
285
+ return;
286
+ }
287
+ let parent = path.parentPath;
288
+ let isWrapped = false;
289
+ while (parent) {
290
+ if (t.isCallExpression(parent.node) && t.isIdentifier(parent.node.callee) && parent.node.callee.name === "__trackComposable") {
291
+ isWrapped = true;
292
+ break;
293
+ }
294
+ parent = parent.parentPath ?? null;
295
+ }
296
+ if (isWrapped) {
297
+ return;
298
+ }
299
+ const comments = (path.node.leadingComments ?? []).concat(path.parentPath?.node?.leadingComments ?? []);
300
+ const ignored = comments.some((c) => c.value.includes("@devtools-ignore"));
301
+ if (ignored) {
302
+ return;
303
+ }
304
+ const args = path.node.arguments;
305
+ const loc = path.node.loc;
306
+ const meta = t.objectExpression([
307
+ t.objectProperty(t.identifier("file"), t.stringLiteral(id.split("/").pop() ?? id)),
308
+ t.objectProperty(t.identifier("line"), t.numericLiteral(loc?.start.line ?? 0))
309
+ ]);
310
+ path.replaceWith(
311
+ t.callExpression(t.identifier("__trackComposable"), [
312
+ t.stringLiteral(name),
313
+ t.arrowFunctionExpression([], t.callExpression(t.identifier(name), args)),
314
+ meta
315
+ ])
316
+ );
317
+ modified = true;
318
+ }
319
+ });
320
+ if (!modified) {
321
+ return null;
322
+ }
323
+ const importLine = `import { __trackComposable } from 'nuxt-devtools-observatory/runtime/composable-registry';
324
+ `;
325
+ const output = generate(ast, { retainLines: true }, scriptCode);
326
+ if (isVue) {
327
+ const newCode = code.slice(0, scriptStart) + importLine + output.code + code.slice(scriptStart + scriptCode.length);
328
+ return { code: newCode };
329
+ }
330
+ return { code: importLine + output.code, map: output.map };
331
+ } catch (err) {
332
+ console.warn("[observatory] composable transform error:", err);
333
+ return null;
334
+ }
335
+ }
336
+ };
337
+ }
338
+
339
+ const VIRTUAL_ID = "\0obs:vue-proxy";
340
+ const PROXY_MODULE = `
341
+ import {
342
+ defineComponent as _obsDefineComponent,
343
+ h as _obsH,
344
+ getCurrentInstance as _obsGetCurrentInstance,
345
+ onUnmounted as _obsOnUnmounted,
346
+ Transition as _ObsRealTransition
347
+ } from 'vue'
348
+
349
+ function _obsRegistry() {
350
+ if (typeof window === 'undefined') return undefined
351
+ return window.__observatory__ && window.__observatory__.transition
352
+ }
353
+
354
+ function _obsMergeHook(original, fn) {
355
+ return function(el) { fn(el); if (original) original(el) }
356
+ }
357
+
358
+ const _ObservedTransition = _obsDefineComponent({
359
+ name: 'Transition',
360
+ inheritAttrs: false,
361
+ setup(_, ctx) {
362
+ const inst = _obsGetCurrentInstance()
363
+ const par = inst && inst.parent
364
+ const parentComponent = (par && par.type && (par.type.__name || par.type.name)) || 'unknown'
365
+ let enterEntryId = null
366
+ let leaveEntryId = null
367
+ _obsOnUnmounted(function() {
368
+ const r = _obsRegistry()
369
+ if (!r) return
370
+ if (enterEntryId) { r.update(enterEntryId, { phase: 'interrupted', endTime: performance.now() }); enterEntryId = null }
371
+ if (leaveEntryId) { r.update(leaveEntryId, { phase: 'interrupted', endTime: performance.now() }); leaveEntryId = null }
372
+ })
373
+ return function() {
374
+ const attrs = ctx.attrs
375
+ const slots = ctx.slots
376
+ const r = _obsRegistry()
377
+ if (!r) return _obsH(_ObsRealTransition, attrs, slots)
378
+ const transitionName = String(attrs.name != null ? attrs.name : 'default')
379
+ const isAppear = Boolean(attrs.appear)
380
+ const mode = typeof attrs.mode === 'string' ? attrs.mode : undefined
381
+ const hookedAttrs = Object.assign({}, attrs, {
382
+ onBeforeEnter: _obsMergeHook(attrs.onBeforeEnter, function() {
383
+ const t = performance.now()
384
+ const id = transitionName + '::enter::' + t
385
+ enterEntryId = id
386
+ r.register({ id, transitionName, parentComponent, direction: 'enter', phase: 'entering', startTime: t, cancelled: false, appear: isAppear, mode })
387
+ }),
388
+ onAfterEnter: _obsMergeHook(attrs.onAfterEnter, function() {
389
+ if (enterEntryId) { r.update(enterEntryId, { phase: 'entered', endTime: performance.now() }); enterEntryId = null }
390
+ }),
391
+ onEnterCancelled: _obsMergeHook(attrs.onEnterCancelled, function() {
392
+ if (enterEntryId) { r.update(enterEntryId, { phase: 'enter-cancelled', cancelled: true, endTime: performance.now() }); enterEntryId = null }
393
+ }),
394
+ onBeforeLeave: _obsMergeHook(attrs.onBeforeLeave, function() {
395
+ const t = performance.now()
396
+ const id = transitionName + '::leave::' + t
397
+ leaveEntryId = id
398
+ r.register({ id, transitionName, parentComponent, direction: 'leave', phase: 'leaving', startTime: t, cancelled: false, appear: false, mode })
399
+ }),
400
+ onAfterLeave: _obsMergeHook(attrs.onAfterLeave, function() {
401
+ if (leaveEntryId) { r.update(leaveEntryId, { phase: 'left', endTime: performance.now() }); leaveEntryId = null }
402
+ }),
403
+ onLeaveCancelled: _obsMergeHook(attrs.onLeaveCancelled, function() {
404
+ if (leaveEntryId) { r.update(leaveEntryId, { phase: 'leave-cancelled', cancelled: true, endTime: performance.now() }); leaveEntryId = null }
405
+ }),
406
+ })
407
+ return _obsH(_ObsRealTransition, hookedAttrs, slots)
408
+ }
409
+ }
410
+ })
411
+
412
+ export * from 'vue'
413
+ export { _ObservedTransition as Transition }
414
+ `.trim();
415
+ function transitionTrackerPlugin() {
416
+ return {
417
+ name: "observatory:transition-tracker",
418
+ enforce: "pre",
419
+ resolveId(id, importer) {
420
+ if (id !== "vue") {
421
+ return;
422
+ }
423
+ if (!importer) {
424
+ return;
425
+ }
426
+ if (importer.includes("node_modules")) {
427
+ return;
428
+ }
429
+ if (importer.includes("obs:vue-proxy")) {
430
+ return;
431
+ }
432
+ if (importer.includes("/src/runtime/") || importer.includes("/dist/runtime/")) {
433
+ return;
434
+ }
435
+ return VIRTUAL_ID;
436
+ },
437
+ load(id) {
438
+ if (id !== VIRTUAL_ID && id !== "obs:vue-proxy") {
439
+ return null;
440
+ }
441
+ return PROXY_MODULE;
442
+ }
443
+ };
444
+ }
445
+
446
+ const module$1 = defineNuxtModule({
447
+ meta: {
448
+ name: "nuxt-devtools-observatory",
449
+ configKey: "observatory",
450
+ compatibility: { nuxt: "^3.0.0" }
451
+ },
452
+ defaults: {
453
+ fetchDashboard: true,
454
+ provideInjectGraph: true,
455
+ composableTracker: true,
456
+ renderHeatmap: true,
457
+ transitionTracker: true,
458
+ heatmapThreshold: 5
459
+ },
460
+ setup(options, nuxt) {
461
+ if (!nuxt.options.dev) {
462
+ return;
463
+ }
464
+ const resolver = createResolver(import.meta.url);
465
+ nuxt.hook("vite:extendConfig", (config) => {
466
+ const alias = config.resolve?.alias;
467
+ const aliases = Array.isArray(alias) ? {} : alias ?? {};
468
+ aliases["nuxt-devtools-observatory/runtime/composable-registry"] = resolver.resolve("./runtime/composables/composable-registry");
469
+ aliases["nuxt-devtools-observatory/runtime/provide-inject-registry"] = resolver.resolve(
470
+ "./runtime/composables/provide-inject-registry"
471
+ );
472
+ aliases["nuxt-devtools-observatory/runtime/fetch-registry"] = resolver.resolve("./runtime/composables/fetch-registry");
473
+ config.resolve = { ...config.resolve, alias: aliases };
474
+ });
475
+ if (options.fetchDashboard) {
476
+ addVitePlugin(fetchInstrumentPlugin());
477
+ }
478
+ if (options.provideInjectGraph) {
479
+ addVitePlugin(provideInjectPlugin());
480
+ }
481
+ if (options.composableTracker) {
482
+ addVitePlugin(composableTrackerPlugin());
483
+ }
484
+ if (options.transitionTracker) {
485
+ addVitePlugin(transitionTrackerPlugin());
486
+ }
487
+ addPlugin(resolver.resolve("./runtime/plugin"));
488
+ if (options.fetchDashboard) {
489
+ nuxt.hook("nitro:config", (nitroConfig) => {
490
+ nitroConfig.plugins = nitroConfig.plugins || [];
491
+ nitroConfig.plugins.push(resolver.resolve("./nitro/fetch-capture"));
492
+ });
493
+ }
494
+ const CLIENT_PORT = 4949;
495
+ nuxt.hook("vite:serverCreated", async (_viteServer, env) => {
496
+ if (!env.isClient) {
497
+ return;
498
+ }
499
+ const { createServer } = await import('vite');
500
+ const { default: vue } = await import('@vitejs/plugin-vue');
501
+ const inner = await createServer({
502
+ root: resolver.resolve("../client"),
503
+ base: "/",
504
+ server: { port: CLIENT_PORT, strictPort: false, cors: true },
505
+ appType: "spa",
506
+ configFile: false,
507
+ plugins: [vue()],
508
+ logLevel: "warn"
509
+ });
510
+ await inner.listen();
511
+ nuxt.hook("close", () => inner.close());
512
+ });
513
+ const base = `http://localhost:${CLIENT_PORT}`;
514
+ nuxt.hook("devtools:customTabs", (tabs) => {
515
+ if (options.fetchDashboard) {
516
+ tabs.push({
517
+ name: "observatory-fetch",
518
+ title: "useFetch",
519
+ icon: "carbon:radio-button",
520
+ view: { type: "iframe", src: `${base}/fetch` }
521
+ });
522
+ }
523
+ if (options.provideInjectGraph) {
524
+ tabs.push({
525
+ name: "observatory-provide",
526
+ title: "provide/inject",
527
+ icon: "carbon:branch",
528
+ view: { type: "iframe", src: `${base}/provide` }
529
+ });
530
+ }
531
+ if (options.composableTracker) {
532
+ tabs.push({
533
+ name: "observatory-composables",
534
+ title: "Composables",
535
+ icon: "carbon:function",
536
+ view: { type: "iframe", src: `${base}/composables` }
537
+ });
538
+ }
539
+ if (options.renderHeatmap) {
540
+ tabs.push({
541
+ name: "observatory-heatmap",
542
+ title: "Heatmap",
543
+ icon: "carbon:heat-map",
544
+ view: { type: "iframe", src: `${base}/heatmap` }
545
+ });
546
+ }
547
+ if (options.transitionTracker) {
548
+ tabs.push({
549
+ name: "observatory-transitions",
550
+ title: "Transitions",
551
+ icon: "carbon:movement",
552
+ view: { type: "iframe", src: `${base}/transitions` }
553
+ });
554
+ }
555
+ });
556
+ nuxt.options.runtimeConfig.public.observatory = {
557
+ heatmapThreshold: options.heatmapThreshold ?? 5
558
+ };
559
+ }
560
+ });
561
+
562
+ export { module$1 as default };
@@ -0,0 +1,40 @@
1
+ export interface ComposableEntry {
2
+ id: string;
3
+ name: string;
4
+ componentFile: string;
5
+ componentUid: number;
6
+ status: 'mounted' | 'unmounted';
7
+ leak: boolean;
8
+ leakReason?: string;
9
+ refs: Record<string, {
10
+ type: 'ref' | 'computed' | 'reactive';
11
+ value: unknown;
12
+ }>;
13
+ watcherCount: number;
14
+ intervalCount: number;
15
+ lifecycle: {
16
+ hasOnMounted: boolean;
17
+ hasOnUnmounted: boolean;
18
+ watchersCleaned: boolean;
19
+ intervalsCleaned: boolean;
20
+ };
21
+ file: string;
22
+ line: number;
23
+ }
24
+ /**
25
+ * Registers a new composable entry, updates an existing one, or retrieves all entries.
26
+ * @remarks The returned object exposes the following methods:
27
+ * - `register`: Registers a new composable entry.
28
+ * - `update`: Updates an existing composable entry.
29
+ * - `getAll`: Retrieves all composable entries.
30
+ * @returns {{ register: (entry: ComposableEntry) => void, update: (id: string, patch: Partial<ComposableEntry>) => void, getAll: () => ComposableEntry[] }} An object with `register`, `update`, and `getAll` methods.
31
+ */
32
+ export declare function setupComposableRegistry(): {
33
+ register: (entry: ComposableEntry) => void;
34
+ update: (id: string, patch: Partial<ComposableEntry>) => void;
35
+ getAll: () => ComposableEntry[];
36
+ };
37
+ export declare function __trackComposable<T>(name: string, callFn: () => T, meta: {
38
+ file: string;
39
+ line: number;
40
+ }): T;