nuxt-devtools-observatory 0.1.30 → 0.1.31
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/README.md +63 -4
- package/client/.env +2 -1
- package/client/.env.example +2 -1
- package/client/dist/assets/index-BuMXDBO9.js +17 -0
- package/client/dist/assets/{index-BmGW_M3W.css → index-CwcspZ6w.css} +1 -1
- package/client/dist/index.html +2 -2
- package/client/src/App.vue +4 -0
- package/client/src/components/Flamegraph.vue +443 -0
- package/client/src/components/SpanInspector.vue +446 -0
- package/client/src/components/TraceFilter.vue +344 -0
- package/client/src/components/WaterfallView.vue +443 -0
- package/client/src/composables/useTraceFilter.ts +164 -0
- package/client/src/stores/observatory.ts +5 -1
- package/client/src/views/TraceViewer.vue +599 -0
- package/client/src/views/TransitionTimeline.vue +1 -6
- package/dist/module.d.mts +5 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +30 -44
- package/dist/runtime/composables/render-registry.js +66 -110
- package/dist/runtime/composables/transition-registry.js +103 -28
- package/dist/runtime/instrumentation/asyncData.d.ts +9 -0
- package/dist/runtime/instrumentation/asyncData.js +49 -0
- package/dist/runtime/instrumentation/component.d.ts +2 -0
- package/dist/runtime/instrumentation/component.js +126 -0
- package/dist/runtime/instrumentation/fetch.d.ts +2 -0
- package/dist/runtime/instrumentation/fetch.js +89 -0
- package/dist/runtime/instrumentation/route.d.ts +6 -0
- package/dist/runtime/instrumentation/route.js +41 -0
- package/dist/runtime/plugin.js +38 -2
- package/dist/runtime/tracing/context.d.ts +9 -0
- package/dist/runtime/tracing/context.js +15 -0
- package/dist/runtime/tracing/trace.d.ts +25 -0
- package/dist/runtime/tracing/trace.js +0 -0
- package/dist/runtime/tracing/traceStore.d.ts +39 -0
- package/dist/runtime/tracing/traceStore.js +101 -0
- package/dist/runtime/tracing/tracing.d.ts +27 -0
- package/dist/runtime/tracing/tracing.js +48 -0
- package/package.json +1 -1
- package/client/dist/assets/index-BCaKoHBH.js +0 -17
package/dist/module.mjs
CHANGED
|
@@ -187,7 +187,7 @@ function fetchInstrumentPlugin() {
|
|
|
187
187
|
if (!isVue && !id.endsWith(".ts") && !id.endsWith(".js")) {
|
|
188
188
|
return;
|
|
189
189
|
}
|
|
190
|
-
if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry")) {
|
|
190
|
+
if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry") || id.includes("instrumentation/asyncData")) {
|
|
191
191
|
return;
|
|
192
192
|
}
|
|
193
193
|
let scriptCode = code;
|
|
@@ -210,9 +210,9 @@ function fetchInstrumentPlugin() {
|
|
|
210
210
|
});
|
|
211
211
|
let modified = false;
|
|
212
212
|
let needsFetchCallHelper = false;
|
|
213
|
-
let
|
|
213
|
+
let needsTracedAsyncDataHelper = false;
|
|
214
214
|
const hasFetchCallImport = scriptCode.includes("__devFetchCall");
|
|
215
|
-
const
|
|
215
|
+
const hasTracedAsyncDataImport = scriptCode.includes("useTracedAsyncData");
|
|
216
216
|
traverse$1(ast, {
|
|
217
217
|
CallExpression(path) {
|
|
218
218
|
if (path.node.__observatoryTransformed) {
|
|
@@ -225,7 +225,7 @@ function fetchInstrumentPlugin() {
|
|
|
225
225
|
if (!FETCH_FNS.has(callee.name)) {
|
|
226
226
|
return;
|
|
227
227
|
}
|
|
228
|
-
if (path.parent && t.isCallExpression(path.parent) && t.isIdentifier(path.parent.callee) && ["__devFetchCall", "
|
|
228
|
+
if (path.parent && t.isCallExpression(path.parent) && t.isIdentifier(path.parent.callee) && ["__devFetchCall", "useTracedAsyncData"].includes(path.parent.callee.name)) {
|
|
229
229
|
return;
|
|
230
230
|
}
|
|
231
231
|
const originalName = callee.name;
|
|
@@ -273,40 +273,18 @@ function fetchInstrumentPlugin() {
|
|
|
273
273
|
t.objectProperty(t.identifier("originalFn"), t.stringLiteral(originalName))
|
|
274
274
|
]);
|
|
275
275
|
if ((originalName === "useAsyncData" || originalName === "useLazyAsyncData") && handlerArg) {
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
meta
|
|
289
|
-
]),
|
|
290
|
-
[t.spreadElement(t.identifier("args"))]
|
|
291
|
-
),
|
|
292
|
-
t.callExpression(handlerArg, [t.spreadElement(t.identifier("args"))])
|
|
293
|
-
)
|
|
294
|
-
);
|
|
295
|
-
wrappedHandler.__observatoryTransformed = true;
|
|
296
|
-
needsFetchHandlerHelper = true;
|
|
297
|
-
if (keyArg) {
|
|
298
|
-
const newCall = t.callExpression(t.identifier(originalName), [
|
|
299
|
-
keyArg,
|
|
300
|
-
wrappedHandler,
|
|
301
|
-
optsArg ?? t.objectExpression([])
|
|
302
|
-
]);
|
|
303
|
-
newCall.__observatoryTransformed = true;
|
|
304
|
-
path.replaceWith(newCall);
|
|
305
|
-
} else {
|
|
306
|
-
const newCall = t.callExpression(t.identifier(originalName), [wrappedHandler]);
|
|
307
|
-
newCall.__observatoryTransformed = true;
|
|
308
|
-
path.replaceWith(newCall);
|
|
309
|
-
}
|
|
276
|
+
const rewrittenArgs = keyArg ? [keyArg, handlerArg, optsArg ?? t.objectExpression([])] : [handlerArg];
|
|
277
|
+
const handlerIndex = keyArg ? 1 : 0;
|
|
278
|
+
const newCall = t.callExpression(t.identifier("useTracedAsyncData"), [
|
|
279
|
+
t.identifier(originalName),
|
|
280
|
+
t.arrayExpression(rewrittenArgs),
|
|
281
|
+
t.numericLiteral(handlerIndex),
|
|
282
|
+
keyArg ?? t.stringLiteral(key),
|
|
283
|
+
meta
|
|
284
|
+
]);
|
|
285
|
+
newCall.__observatoryTransformed = true;
|
|
286
|
+
path.replaceWith(newCall);
|
|
287
|
+
needsTracedAsyncDataHelper = true;
|
|
310
288
|
modified = true;
|
|
311
289
|
} else {
|
|
312
290
|
const newCall = t.callExpression(t.identifier("__devFetchCall"), [
|
|
@@ -325,12 +303,12 @@ function fetchInstrumentPlugin() {
|
|
|
325
303
|
if (!modified) {
|
|
326
304
|
return null;
|
|
327
305
|
}
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
needsFetchHandlerHelper && !hasFetchHandlerImport ? "__devFetchHandler" : ""
|
|
331
|
-
].filter(Boolean);
|
|
332
|
-
const importStatement = importNames.length ? `import { ${importNames.join(", ")} } from 'nuxt-devtools-observatory/runtime/fetch-registry';
|
|
306
|
+
const fetchImportNames = [needsFetchCallHelper && !hasFetchCallImport ? "__devFetchCall" : ""].filter(Boolean);
|
|
307
|
+
const fetchImportStatement = fetchImportNames.length ? `import { ${fetchImportNames.join(", ")} } from 'nuxt-devtools-observatory/runtime/fetch-registry';
|
|
333
308
|
` : "";
|
|
309
|
+
const asyncDataImportStatement = needsTracedAsyncDataHelper && !hasTracedAsyncDataImport ? `import { useTracedAsyncData } from 'nuxt-devtools-observatory/runtime/async-data-instrumentation';
|
|
310
|
+
` : "";
|
|
311
|
+
const importStatement = fetchImportStatement + asyncDataImportStatement;
|
|
334
312
|
const output = generate$1(ast, { retainLines: true }, scriptCode);
|
|
335
313
|
let finalCode;
|
|
336
314
|
if (isVue) {
|
|
@@ -554,6 +532,7 @@ const defaults = {
|
|
|
554
532
|
composableTracker: process.env.OBSERVATORY_COMPOSABLE_TRACKER === "true",
|
|
555
533
|
renderHeatmap: process.env.OBSERVATORY_RENDER_HEATMAP === "true",
|
|
556
534
|
transitionTracker: process.env.OBSERVATORY_TRANSITION_TRACKER === "true",
|
|
535
|
+
traceViewer: process.env.OBSERVATORY_TRACE_VIEWER === "true",
|
|
557
536
|
heatmapThresholdCount: process.env.OBSERVATORY_HEATMAP_THRESHOLD_COUNT ? Number(process.env.OBSERVATORY_HEATMAP_THRESHOLD_COUNT) : 3,
|
|
558
537
|
heatmapThresholdTime: process.env.OBSERVATORY_HEATMAP_THRESHOLD_TIME ? Number(process.env.OBSERVATORY_HEATMAP_THRESHOLD_TIME) : 1600,
|
|
559
538
|
maxFetchEntries: process.env.OBSERVATORY_MAX_FETCH_ENTRIES ? Number(process.env.OBSERVATORY_MAX_FETCH_ENTRIES) : 200,
|
|
@@ -591,6 +570,7 @@ const module$1 = defineNuxtModule({
|
|
|
591
570
|
composableTracker: options.composableTracker ?? (process.env.OBSERVATORY_COMPOSABLE_TRACKER ? process.env.OBSERVATORY_COMPOSABLE_TRACKER === "true" : true),
|
|
592
571
|
renderHeatmap: options.renderHeatmap ?? (process.env.OBSERVATORY_RENDER_HEATMAP ? process.env.OBSERVATORY_RENDER_HEATMAP === "true" : true),
|
|
593
572
|
transitionTracker: options.transitionTracker ?? (process.env.OBSERVATORY_TRANSITION_TRACKER ? process.env.OBSERVATORY_TRANSITION_TRACKER === "true" : true),
|
|
573
|
+
traceViewer: options.traceViewer ?? (process.env.OBSERVATORY_TRACE_VIEWER ? process.env.OBSERVATORY_TRACE_VIEWER === "true" : true),
|
|
594
574
|
instrumentServer: options.instrumentServer ?? (process.env.OBSERVATORY_INSTRUMENT_SERVER ? process.env.OBSERVATORY_INSTRUMENT_SERVER === "true" : nuxt.options.ssr !== false),
|
|
595
575
|
heatmapThresholdCount: options.heatmapThresholdCount ?? (process.env.OBSERVATORY_HEATMAP_THRESHOLD_COUNT ? Number(process.env.OBSERVATORY_HEATMAP_THRESHOLD_COUNT) : 3),
|
|
596
576
|
heatmapThresholdTime: options.heatmapThresholdTime ?? (process.env.OBSERVATORY_HEATMAP_THRESHOLD_TIME ? Number(process.env.OBSERVATORY_HEATMAP_THRESHOLD_TIME) : 1600),
|
|
@@ -611,6 +591,9 @@ const module$1 = defineNuxtModule({
|
|
|
611
591
|
"./runtime/composables/provide-inject-registry"
|
|
612
592
|
);
|
|
613
593
|
aliases["nuxt-devtools-observatory/runtime/fetch-registry"] = resolver.resolve("./runtime/composables/fetch-registry");
|
|
594
|
+
aliases["nuxt-devtools-observatory/runtime/async-data-instrumentation"] = resolver.resolve(
|
|
595
|
+
"./runtime/instrumentation/asyncData"
|
|
596
|
+
);
|
|
614
597
|
config.resolve = { ...config.resolve, alias: aliases };
|
|
615
598
|
});
|
|
616
599
|
const vitePluginScope = resolved.instrumentServer ? { server: true, client: true } : { server: false, client: true };
|
|
@@ -645,13 +628,15 @@ const module$1 = defineNuxtModule({
|
|
|
645
628
|
composables: [],
|
|
646
629
|
renders: [],
|
|
647
630
|
transitions: [],
|
|
631
|
+
traces: [],
|
|
648
632
|
features: {
|
|
649
633
|
fetchDashboard: !!resolved.fetchDashboard,
|
|
650
634
|
provideInjectGraph: !!resolved.provideInjectGraph,
|
|
651
635
|
composableTracker: !!resolved.composableTracker,
|
|
652
636
|
composableNavigationMode: resolved.composableNavigationMode,
|
|
653
637
|
renderHeatmap: !!resolved.renderHeatmap,
|
|
654
|
-
transitionTracker: !!resolved.transitionTracker
|
|
638
|
+
transitionTracker: !!resolved.transitionTracker,
|
|
639
|
+
traceViewer: !!resolved.traceViewer
|
|
655
640
|
}
|
|
656
641
|
};
|
|
657
642
|
let rpc = null;
|
|
@@ -730,6 +715,7 @@ ${configScript}`);
|
|
|
730
715
|
composableTracker: resolved.composableTracker,
|
|
731
716
|
renderHeatmap: resolved.renderHeatmap,
|
|
732
717
|
transitionTracker: resolved.transitionTracker,
|
|
718
|
+
traceViewer: resolved.traceViewer,
|
|
733
719
|
maxFetchEntries: resolved.maxFetchEntries,
|
|
734
720
|
maxPayloadBytes: resolved.maxPayloadBytes,
|
|
735
721
|
maxTransitions: resolved.maxTransitions,
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { useRuntimeConfig } from "#app";
|
|
2
|
+
import { traceStore } from "../tracing/traceStore.js";
|
|
2
3
|
export function setupRenderRegistry(nuxtApp, options = {}) {
|
|
3
4
|
const entries = /* @__PURE__ */ new Map();
|
|
4
|
-
const pendingTriggeredRenders = /* @__PURE__ */ new Set();
|
|
5
|
-
const renderStartTimes = /* @__PURE__ */ new Map();
|
|
6
5
|
let currentRoute = "/";
|
|
7
6
|
const config = useRuntimeConfig().public.observatory;
|
|
8
7
|
const MAX_TIMELINE = config.maxRenderTimeline ?? 100;
|
|
9
8
|
const HIDE_INTERNALS = config.heatmapHideInternals ?? false;
|
|
10
9
|
let dirty = true;
|
|
11
10
|
let cachedSnapshot = "[]";
|
|
11
|
+
const liveElements = /* @__PURE__ */ new Map();
|
|
12
12
|
function markDirty() {
|
|
13
13
|
dirty = true;
|
|
14
14
|
}
|
|
@@ -41,81 +41,26 @@ export function setupRenderRegistry(nuxtApp, options = {}) {
|
|
|
41
41
|
}
|
|
42
42
|
return entries.get(uid);
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
markDirty();
|
|
48
|
-
}
|
|
49
|
-
function flushDirtyRects() {
|
|
50
|
-
if (dirtyRects.size === 0) {
|
|
44
|
+
function refreshEntryRect(uid) {
|
|
45
|
+
const entry = entries.get(uid);
|
|
46
|
+
if (!entry) {
|
|
51
47
|
return;
|
|
52
48
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (!entry) {
|
|
56
|
-
dirtyRects.delete(uid);
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
const el = _liveElements.get(uid);
|
|
60
|
-
if (!el) {
|
|
61
|
-
dirtyRects.delete(uid);
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
const rect = el.getBoundingClientRect?.();
|
|
65
|
-
entry.rect = rect ? {
|
|
66
|
-
x: Math.round(rect.x),
|
|
67
|
-
y: Math.round(rect.y),
|
|
68
|
-
width: Math.round(rect.width),
|
|
69
|
-
height: Math.round(rect.height),
|
|
70
|
-
top: Math.round(rect.top),
|
|
71
|
-
left: Math.round(rect.left)
|
|
72
|
-
} : void 0;
|
|
73
|
-
dirtyRects.delete(uid);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const _liveElements = /* @__PURE__ */ new Map();
|
|
77
|
-
function removeEntry(instance) {
|
|
78
|
-
const uid = instance.$.uid;
|
|
79
|
-
entries.delete(uid);
|
|
80
|
-
markDirty();
|
|
81
|
-
}
|
|
82
|
-
function startRenderTimer(uid) {
|
|
83
|
-
if (typeof performance === "undefined") {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
renderStartTimes.set(uid, performance.now());
|
|
87
|
-
}
|
|
88
|
-
function recordRenderDuration(entry, kind) {
|
|
89
|
-
if (typeof performance === "undefined") {
|
|
49
|
+
const el = liveElements.get(uid);
|
|
50
|
+
if (!el?.getBoundingClientRect) {
|
|
90
51
|
return;
|
|
91
52
|
}
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
entry.avgMs = Math.round(entry.totalMs / totalEvents * 10) / 10;
|
|
101
|
-
const lastTrigger = entry.triggers.length > 0 ? entry.triggers[entry.triggers.length - 1] : void 0;
|
|
102
|
-
const event = {
|
|
103
|
-
kind,
|
|
104
|
-
t: startedAt,
|
|
105
|
-
durationMs: Math.round(durationMs * 10) / 10,
|
|
106
|
-
triggerKey: kind === "update" && lastTrigger ? `${lastTrigger.type}: ${lastTrigger.key}` : void 0,
|
|
107
|
-
route: currentRoute
|
|
53
|
+
const rect = el.getBoundingClientRect();
|
|
54
|
+
entry.rect = {
|
|
55
|
+
x: Math.round(rect.x),
|
|
56
|
+
y: Math.round(rect.y),
|
|
57
|
+
width: Math.round(rect.width),
|
|
58
|
+
height: Math.round(rect.height),
|
|
59
|
+
top: Math.round(rect.top),
|
|
60
|
+
left: Math.round(rect.left)
|
|
108
61
|
};
|
|
109
|
-
entry.timeline.push(event);
|
|
110
|
-
if (entry.timeline.length > MAX_TIMELINE) {
|
|
111
|
-
entry.timeline.shift();
|
|
112
|
-
}
|
|
113
|
-
markDirty();
|
|
114
62
|
}
|
|
115
63
|
function reset() {
|
|
116
|
-
pendingTriggeredRenders.clear();
|
|
117
|
-
renderStartTimes.clear();
|
|
118
|
-
dirtyRects.clear();
|
|
119
64
|
for (const entry of entries.values()) {
|
|
120
65
|
entry.isPersistent = true;
|
|
121
66
|
entry.rerenders = 0;
|
|
@@ -126,65 +71,76 @@ export function setupRenderRegistry(nuxtApp, options = {}) {
|
|
|
126
71
|
}
|
|
127
72
|
markDirty();
|
|
128
73
|
}
|
|
74
|
+
function aggregateFromComponentSpans() {
|
|
75
|
+
const componentSpans = traceStore.getAllTraces().flatMap((trace) => trace.spans).filter((span) => span.type === "component");
|
|
76
|
+
const spansByUid = /* @__PURE__ */ new Map();
|
|
77
|
+
for (const span of componentSpans) {
|
|
78
|
+
const uidValue = span.metadata?.uid;
|
|
79
|
+
const uid = typeof uidValue === "number" ? uidValue : Number(uidValue);
|
|
80
|
+
if (!Number.isFinite(uid)) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const list = spansByUid.get(uid) ?? [];
|
|
84
|
+
list.push(span);
|
|
85
|
+
spansByUid.set(uid, list);
|
|
86
|
+
}
|
|
87
|
+
for (const [uid, entry] of entries.entries()) {
|
|
88
|
+
const spans = spansByUid.get(uid) ?? [];
|
|
89
|
+
spans.sort((a, b) => a.startTime - b.startTime);
|
|
90
|
+
const timeline = spans.slice(-MAX_TIMELINE).map((span) => {
|
|
91
|
+
const lifecycle = span.metadata?.lifecycle === "mounted" ? "mount" : "update";
|
|
92
|
+
const routeValue = span.metadata?.route;
|
|
93
|
+
const route = typeof routeValue === "string" && routeValue.length > 0 ? routeValue : entry.route;
|
|
94
|
+
return {
|
|
95
|
+
kind: lifecycle,
|
|
96
|
+
t: span.startTime,
|
|
97
|
+
durationMs: Math.round((span.durationMs ?? 0) * 10) / 10,
|
|
98
|
+
route
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
const mountCount = spans.filter((span) => span.metadata?.lifecycle === "mounted").length;
|
|
102
|
+
const rerenders = spans.filter((span) => span.metadata?.lifecycle !== "mounted").length;
|
|
103
|
+
const totalMs = spans.reduce((sum, span) => sum + (span.durationMs ?? 0), 0);
|
|
104
|
+
const eventsCount = Math.max(mountCount + rerenders, 1);
|
|
105
|
+
entry.mountCount = mountCount;
|
|
106
|
+
entry.rerenders = rerenders;
|
|
107
|
+
entry.totalMs = Math.round(totalMs * 10) / 10;
|
|
108
|
+
entry.avgMs = Math.round(totalMs / eventsCount * 10) / 10;
|
|
109
|
+
entry.timeline = timeline;
|
|
110
|
+
entry.triggers = [];
|
|
111
|
+
refreshEntryRect(uid);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
129
114
|
nuxtApp.vueApp.mixin({
|
|
130
|
-
beforeMount() {
|
|
131
|
-
startRenderTimer(this.$.uid);
|
|
132
|
-
},
|
|
133
115
|
mounted() {
|
|
134
116
|
const entry = ensureEntry(this);
|
|
135
117
|
if (!entry) {
|
|
136
118
|
return;
|
|
137
119
|
}
|
|
138
|
-
entry.mountCount++;
|
|
139
120
|
const isHydration = options.isHydrating?.() ?? false;
|
|
140
|
-
if (isHydration && entry.mountCount ===
|
|
121
|
+
if (isHydration && entry.mountCount === 0) {
|
|
141
122
|
entry.isHydrationMount = true;
|
|
142
|
-
} else if (entry.mountCount > 1) {
|
|
143
|
-
entry.rerenders++;
|
|
144
|
-
}
|
|
145
|
-
_liveElements.set(entry.uid, this.$el);
|
|
146
|
-
markRectDirty(entry.uid);
|
|
147
|
-
if (!entry.isPersistent || entry.mountCount === 1) {
|
|
148
|
-
recordRenderDuration(entry, "mount");
|
|
149
|
-
} else {
|
|
150
|
-
renderStartTimes.delete(entry.uid);
|
|
151
123
|
}
|
|
124
|
+
liveElements.set(entry.uid, this.$el);
|
|
125
|
+
refreshEntryRect(entry.uid);
|
|
152
126
|
markDirty();
|
|
153
127
|
emit("render:update", { uid: entry.uid, renders: entry.rerenders });
|
|
154
128
|
},
|
|
155
|
-
beforeUpdate() {
|
|
156
|
-
startRenderTimer(this.$.uid);
|
|
157
|
-
},
|
|
158
|
-
renderTriggered({ key, type }) {
|
|
159
|
-
const entry = ensureEntry(this);
|
|
160
|
-
if (!entry) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
entry.triggers.push({ key: String(key), type, timestamp: performance.now() });
|
|
164
|
-
pendingTriggeredRenders.add(entry.uid);
|
|
165
|
-
if (entry.triggers.length > 50) {
|
|
166
|
-
entry.triggers.shift();
|
|
167
|
-
}
|
|
168
|
-
markDirty();
|
|
169
|
-
},
|
|
170
129
|
updated() {
|
|
171
130
|
const entry = ensureEntry(this);
|
|
172
131
|
if (!entry) {
|
|
173
132
|
return;
|
|
174
133
|
}
|
|
175
|
-
|
|
176
|
-
entry.
|
|
177
|
-
markRectDirty(entry.uid);
|
|
178
|
-
recordRenderDuration(entry, "update");
|
|
134
|
+
liveElements.set(entry.uid, this.$el);
|
|
135
|
+
refreshEntryRect(entry.uid);
|
|
179
136
|
emit("render:update", { uid: entry.uid, renders: entry.rerenders });
|
|
137
|
+
markDirty();
|
|
180
138
|
},
|
|
181
139
|
unmounted() {
|
|
182
140
|
const uid = this.$.uid;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
_liveElements.delete(uid);
|
|
187
|
-
removeEntry(this);
|
|
141
|
+
liveElements.delete(uid);
|
|
142
|
+
entries.delete(uid);
|
|
143
|
+
markDirty();
|
|
188
144
|
emit("render:remove", { uid });
|
|
189
145
|
}
|
|
190
146
|
});
|
|
@@ -215,14 +171,14 @@ export function setupRenderRegistry(nuxtApp, options = {}) {
|
|
|
215
171
|
};
|
|
216
172
|
}
|
|
217
173
|
function getAll() {
|
|
218
|
-
|
|
174
|
+
aggregateFromComponentSpans();
|
|
219
175
|
return [...entries.values()].map(sanitize);
|
|
220
176
|
}
|
|
221
177
|
function getSnapshot() {
|
|
222
178
|
if (!dirty) {
|
|
223
179
|
return cachedSnapshot;
|
|
224
180
|
}
|
|
225
|
-
|
|
181
|
+
aggregateFromComponentSpans();
|
|
226
182
|
try {
|
|
227
183
|
cachedSnapshot = JSON.stringify([...entries.values()].map(sanitize)) ?? "[]";
|
|
228
184
|
} catch {
|
|
@@ -1,65 +1,140 @@
|
|
|
1
1
|
import { h, defineComponent, getCurrentInstance, onUnmounted, Transition as VueTransition } from "vue";
|
|
2
|
+
import { startSpan } from "../tracing/tracing.js";
|
|
3
|
+
import { traceStore } from "../tracing/traceStore.js";
|
|
2
4
|
const MAX_TRANSITIONS = typeof process !== "undefined" && process.env.OBSERVATORY_MAX_TRANSITIONS ? Number(process.env.OBSERVATORY_MAX_TRANSITIONS) : 500;
|
|
3
5
|
export function setupTransitionRegistry() {
|
|
4
|
-
const
|
|
6
|
+
const activeSpans = /* @__PURE__ */ new Map();
|
|
7
|
+
const entryState = /* @__PURE__ */ new Map();
|
|
5
8
|
let dirty = true;
|
|
6
9
|
let cachedSnapshot = "[]";
|
|
7
10
|
function markDirty() {
|
|
8
11
|
dirty = true;
|
|
9
12
|
}
|
|
10
13
|
function register(entry) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const spanHandle = startSpan({
|
|
15
|
+
name: `transition:${entry.transitionName}`,
|
|
16
|
+
type: "transition",
|
|
17
|
+
metadata: {
|
|
18
|
+
id: entry.id,
|
|
19
|
+
transitionName: entry.transitionName,
|
|
20
|
+
parentComponent: entry.parentComponent,
|
|
21
|
+
direction: entry.direction,
|
|
22
|
+
phase: entry.phase,
|
|
23
|
+
cancelled: entry.cancelled,
|
|
24
|
+
appear: entry.appear,
|
|
25
|
+
mode: entry.mode
|
|
26
|
+
},
|
|
27
|
+
startTime: entry.startTime
|
|
28
|
+
});
|
|
29
|
+
activeSpans.set(entry.id, spanHandle);
|
|
30
|
+
entryState.set(entry.id, {
|
|
31
|
+
id: entry.id,
|
|
32
|
+
transitionName: entry.transitionName,
|
|
33
|
+
parentComponent: entry.parentComponent,
|
|
34
|
+
direction: entry.direction,
|
|
35
|
+
phase: entry.phase,
|
|
36
|
+
startTime: entry.startTime,
|
|
37
|
+
endTime: entry.endTime,
|
|
38
|
+
cancelled: entry.cancelled,
|
|
39
|
+
appear: entry.appear,
|
|
40
|
+
mode: entry.mode
|
|
41
|
+
});
|
|
18
42
|
markDirty();
|
|
19
43
|
emit("transition:register", entry);
|
|
20
44
|
}
|
|
21
45
|
function clear() {
|
|
22
|
-
|
|
46
|
+
activeSpans.clear();
|
|
47
|
+
entryState.clear();
|
|
23
48
|
markDirty();
|
|
24
49
|
emit("transition:clear", {});
|
|
25
50
|
}
|
|
26
51
|
function update(id, patch) {
|
|
27
|
-
const existing =
|
|
52
|
+
const existing = entryState.get(id);
|
|
28
53
|
if (!existing) {
|
|
29
54
|
return;
|
|
30
55
|
}
|
|
31
56
|
const updated = { ...existing, ...patch };
|
|
32
|
-
|
|
33
|
-
|
|
57
|
+
entryState.set(id, updated);
|
|
58
|
+
const active = activeSpans.get(id);
|
|
59
|
+
if (active) {
|
|
60
|
+
active.span.metadata = {
|
|
61
|
+
...active.span.metadata ?? {},
|
|
62
|
+
id: updated.id,
|
|
63
|
+
transitionName: updated.transitionName,
|
|
64
|
+
parentComponent: updated.parentComponent,
|
|
65
|
+
direction: updated.direction,
|
|
66
|
+
phase: updated.phase,
|
|
67
|
+
cancelled: updated.cancelled,
|
|
68
|
+
appear: updated.appear,
|
|
69
|
+
mode: updated.mode
|
|
70
|
+
};
|
|
71
|
+
if (patch.endTime !== void 0) {
|
|
72
|
+
active.end({
|
|
73
|
+
endTime: patch.endTime,
|
|
74
|
+
status: patch.cancelled === true ? "cancelled" : "ok",
|
|
75
|
+
metadata: {
|
|
76
|
+
phase: updated.phase,
|
|
77
|
+
cancelled: updated.cancelled
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
activeSpans.delete(id);
|
|
81
|
+
}
|
|
34
82
|
}
|
|
35
|
-
entries.set(id, updated);
|
|
36
83
|
markDirty();
|
|
37
|
-
emit("transition:update", updated);
|
|
84
|
+
emit("transition:update", toTransitionEntry(updated, active?.span));
|
|
38
85
|
}
|
|
39
|
-
function
|
|
86
|
+
function toTransitionEntry(base, span) {
|
|
87
|
+
const durationMs = span?.durationMs ?? (base.endTime !== void 0 ? Math.round((base.endTime - base.startTime) * 10) / 10 : void 0);
|
|
40
88
|
return {
|
|
41
|
-
id:
|
|
42
|
-
transitionName:
|
|
43
|
-
parentComponent:
|
|
44
|
-
direction:
|
|
45
|
-
phase:
|
|
46
|
-
startTime:
|
|
47
|
-
endTime:
|
|
48
|
-
durationMs
|
|
49
|
-
cancelled:
|
|
50
|
-
appear:
|
|
51
|
-
mode:
|
|
89
|
+
id: base.id,
|
|
90
|
+
transitionName: base.transitionName,
|
|
91
|
+
parentComponent: base.parentComponent,
|
|
92
|
+
direction: base.direction,
|
|
93
|
+
phase: base.phase,
|
|
94
|
+
startTime: base.startTime,
|
|
95
|
+
endTime: base.endTime,
|
|
96
|
+
durationMs,
|
|
97
|
+
cancelled: base.cancelled,
|
|
98
|
+
appear: base.appear,
|
|
99
|
+
mode: base.mode
|
|
52
100
|
};
|
|
53
101
|
}
|
|
54
102
|
function getAll() {
|
|
55
|
-
|
|
103
|
+
const spans = traceStore.getAllTraces().flatMap((trace) => trace.spans).filter((span) => span.type === "transition").sort((a, b) => a.startTime - b.startTime);
|
|
104
|
+
const entries = spans.map((span) => {
|
|
105
|
+
const metadata = span.metadata ?? {};
|
|
106
|
+
const id = typeof metadata.id === "string" ? metadata.id : span.id;
|
|
107
|
+
const transitionName = typeof metadata.transitionName === "string" ? metadata.transitionName : "default";
|
|
108
|
+
const parentComponent = typeof metadata.parentComponent === "string" ? metadata.parentComponent : "unknown";
|
|
109
|
+
const direction = metadata.direction === "leave" ? "leave" : "enter";
|
|
110
|
+
const knownPhase = metadata.phase;
|
|
111
|
+
const phase = knownPhase === "entering" || knownPhase === "entered" || knownPhase === "leaving" || knownPhase === "left" || knownPhase === "enter-cancelled" || knownPhase === "leave-cancelled" || knownPhase === "interrupted" ? knownPhase : span.endTime ? direction === "enter" ? "entered" : "left" : direction === "enter" ? "entering" : "leaving";
|
|
112
|
+
return {
|
|
113
|
+
id,
|
|
114
|
+
transitionName,
|
|
115
|
+
parentComponent,
|
|
116
|
+
direction,
|
|
117
|
+
phase,
|
|
118
|
+
startTime: span.startTime,
|
|
119
|
+
endTime: span.endTime,
|
|
120
|
+
durationMs: span.durationMs,
|
|
121
|
+
cancelled: metadata.cancelled === true || span.status === "cancelled",
|
|
122
|
+
appear: metadata.appear === true,
|
|
123
|
+
mode: typeof metadata.mode === "string" ? metadata.mode : void 0
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
const overflow = entries.length - MAX_TRANSITIONS;
|
|
127
|
+
if (overflow > 0) {
|
|
128
|
+
return entries.slice(overflow);
|
|
129
|
+
}
|
|
130
|
+
return entries;
|
|
56
131
|
}
|
|
57
132
|
function getSnapshot() {
|
|
58
133
|
if (!dirty) {
|
|
59
134
|
return cachedSnapshot;
|
|
60
135
|
}
|
|
61
136
|
try {
|
|
62
|
-
cachedSnapshot = JSON.stringify(
|
|
137
|
+
cachedSnapshot = JSON.stringify(getAll()) ?? "[]";
|
|
63
138
|
} catch {
|
|
64
139
|
cachedSnapshot = "[]";
|
|
65
140
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface AsyncDataMeta {
|
|
2
|
+
key: string;
|
|
3
|
+
file: string;
|
|
4
|
+
line: number;
|
|
5
|
+
originalFn?: string;
|
|
6
|
+
}
|
|
7
|
+
type AnyFn = (...args: any[]) => any;
|
|
8
|
+
export declare function useTracedAsyncData<TFn extends AnyFn>(originalFn: TFn, args: unknown[], handlerIndex: number, key: unknown, meta: AsyncDataMeta): ReturnType<TFn>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { startSpan } from "../tracing/tracing.js";
|
|
2
|
+
function getNormalizedKey(key) {
|
|
3
|
+
return typeof key === "string" && key.length > 0 ? key : "useAsyncData";
|
|
4
|
+
}
|
|
5
|
+
export function useTracedAsyncData(originalFn, args, handlerIndex, key, meta) {
|
|
6
|
+
if (!import.meta.dev || !import.meta.client) {
|
|
7
|
+
return originalFn(...args);
|
|
8
|
+
}
|
|
9
|
+
const nextArgs = [...args];
|
|
10
|
+
const originalHandler = nextArgs[handlerIndex];
|
|
11
|
+
if (typeof originalHandler !== "function") {
|
|
12
|
+
return originalFn(...args);
|
|
13
|
+
}
|
|
14
|
+
const normalizedKey = getNormalizedKey(key);
|
|
15
|
+
nextArgs[handlerIndex] = (...handlerArgs) => {
|
|
16
|
+
const span = startSpan({
|
|
17
|
+
name: meta.originalFn ?? "useAsyncData",
|
|
18
|
+
type: "fetch",
|
|
19
|
+
metadata: {
|
|
20
|
+
key: normalizedKey,
|
|
21
|
+
status: "pending",
|
|
22
|
+
file: meta.file,
|
|
23
|
+
line: meta.line,
|
|
24
|
+
source: "async-data"
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return Promise.resolve(originalHandler(...handlerArgs)).then((result) => {
|
|
28
|
+
span.end({
|
|
29
|
+
status: "ok",
|
|
30
|
+
metadata: {
|
|
31
|
+
key: normalizedKey,
|
|
32
|
+
status: "ok"
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
return result;
|
|
36
|
+
}).catch((error) => {
|
|
37
|
+
span.end({
|
|
38
|
+
status: "error",
|
|
39
|
+
metadata: {
|
|
40
|
+
key: normalizedKey,
|
|
41
|
+
status: "error",
|
|
42
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
throw error;
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
return originalFn(...nextArgs);
|
|
49
|
+
}
|