nuxt-devtools-observatory 0.1.32 → 0.1.34
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 +37 -1
- package/client/.env.example +2 -0
- package/client/dist/assets/index-BO7neKEi.css +1 -0
- package/client/dist/assets/index-fFBuk6M6.js +20 -0
- package/client/dist/index.html +2 -2
- package/client/src/App.vue +8 -0
- package/client/src/components/Flamegraph.vue +4 -4
- package/client/src/components/SpanInspector.vue +1 -1
- package/client/src/composables/composable-search.ts +3 -0
- package/client/src/composables/trace-render-aggregation.ts +11 -2
- package/client/src/composables/useVirtualizationConfig.ts +40 -0
- package/client/src/composables/useVirtualizationFlags.ts +129 -0
- package/client/src/stores/observatory.ts +20 -0
- package/client/src/views/ComposableTracker.vue +212 -71
- package/client/src/views/FetchDashboard.vue +181 -16
- package/client/src/views/PiniaStoreTracker.vue +343 -0
- package/client/src/views/ProvideInjectGraph.vue +66 -18
- package/client/src/views/RenderHeatmap.vue +329 -75
- package/client/src/views/TraceViewer.vue +190 -20
- package/client/src/views/TransitionTimeline.vue +112 -19
- package/dist/module.d.mts +15 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +28 -24
- package/dist/runtime/composables/pinia-store-registry.d.ts +44 -0
- package/dist/runtime/composables/pinia-store-registry.js +447 -0
- package/dist/runtime/composables/provide-inject-registry.js +13 -8
- package/dist/runtime/composables/render-registry.js +6 -4
- package/dist/runtime/instrumentation/asyncData.d.ts +1 -1
- package/dist/runtime/instrumentation/fetch.d.ts +7 -1
- package/dist/runtime/instrumentation/fetch.js +22 -1
- package/dist/runtime/plugin.js +39 -2
- package/dist/runtime/test-bridge.d.ts +18 -0
- package/dist/runtime/test-bridge.js +100 -0
- package/package.json +14 -3
- package/client/dist/assets/index-5Wl1XYRH.js +0 -17
- package/client/dist/assets/index-DT_QUiIh.css +0 -1
package/dist/module.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, addVitePlugin, addPlugin, addServerPlugin } from '@nuxt/kit';
|
|
1
|
+
import { defineNuxtModule, createResolver, addVitePlugin, addImports, addPlugin, addServerPlugin } from '@nuxt/kit';
|
|
2
2
|
import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
|
|
3
3
|
import sirv from 'sirv';
|
|
4
4
|
import { parse } from '@babel/parser';
|
|
@@ -209,10 +209,6 @@ function fetchInstrumentPlugin() {
|
|
|
209
209
|
plugins: ["typescript"]
|
|
210
210
|
});
|
|
211
211
|
let modified = false;
|
|
212
|
-
let needsFetchCallHelper = false;
|
|
213
|
-
let needsTracedAsyncDataHelper = false;
|
|
214
|
-
const hasFetchCallImport = scriptCode.includes("__devFetchCall");
|
|
215
|
-
const hasTracedAsyncDataImport = scriptCode.includes("useTracedAsyncData");
|
|
216
212
|
traverse$1(ast, {
|
|
217
213
|
CallExpression(path) {
|
|
218
214
|
if (path.node.__observatoryTransformed) {
|
|
@@ -284,7 +280,6 @@ function fetchInstrumentPlugin() {
|
|
|
284
280
|
]);
|
|
285
281
|
newCall.__observatoryTransformed = true;
|
|
286
282
|
path.replaceWith(newCall);
|
|
287
|
-
needsTracedAsyncDataHelper = true;
|
|
288
283
|
modified = true;
|
|
289
284
|
} else {
|
|
290
285
|
const newCall = t.callExpression(t.identifier("__devFetchCall"), [
|
|
@@ -294,7 +289,6 @@ function fetchInstrumentPlugin() {
|
|
|
294
289
|
meta
|
|
295
290
|
]);
|
|
296
291
|
newCall.__observatoryTransformed = true;
|
|
297
|
-
needsFetchCallHelper = true;
|
|
298
292
|
path.replaceWith(newCall);
|
|
299
293
|
modified = true;
|
|
300
294
|
}
|
|
@@ -303,18 +297,12 @@ function fetchInstrumentPlugin() {
|
|
|
303
297
|
if (!modified) {
|
|
304
298
|
return null;
|
|
305
299
|
}
|
|
306
|
-
const fetchImportNames = [needsFetchCallHelper && !hasFetchCallImport ? "__devFetchCall" : ""].filter(Boolean);
|
|
307
|
-
const fetchImportStatement = fetchImportNames.length ? `import { ${fetchImportNames.join(", ")} } from 'nuxt-devtools-observatory/runtime/fetch-registry';
|
|
308
|
-
` : "";
|
|
309
|
-
const asyncDataImportStatement = needsTracedAsyncDataHelper && !hasTracedAsyncDataImport ? `import { useTracedAsyncData } from 'nuxt-devtools-observatory/runtime/async-data-instrumentation';
|
|
310
|
-
` : "";
|
|
311
|
-
const importStatement = fetchImportStatement + asyncDataImportStatement;
|
|
312
300
|
const output = generate$1(ast, { retainLines: true }, scriptCode);
|
|
313
301
|
let finalCode;
|
|
314
302
|
if (isVue) {
|
|
315
|
-
finalCode = code.slice(0, scriptStart) +
|
|
303
|
+
finalCode = code.slice(0, scriptStart) + output.code + code.slice(scriptStart + scriptCode.length);
|
|
316
304
|
} else {
|
|
317
|
-
finalCode =
|
|
305
|
+
finalCode = output.code;
|
|
318
306
|
}
|
|
319
307
|
return {
|
|
320
308
|
code: finalCode,
|
|
@@ -530,6 +518,7 @@ const defaults = {
|
|
|
530
518
|
fetchDashboard: process.env.OBSERVATORY_FETCH_DASHBOARD === "true",
|
|
531
519
|
provideInjectGraph: process.env.OBSERVATORY_PROVIDE_INJECT_GRAPH === "true",
|
|
532
520
|
composableTracker: process.env.OBSERVATORY_COMPOSABLE_TRACKER === "true",
|
|
521
|
+
piniaTracker: process.env.OBSERVATORY_PINIA_TRACKER === "true",
|
|
533
522
|
renderHeatmap: process.env.OBSERVATORY_RENDER_HEATMAP === "true",
|
|
534
523
|
transitionTracker: process.env.OBSERVATORY_TRANSITION_TRACKER === "true",
|
|
535
524
|
traceViewer: process.env.OBSERVATORY_TRACE_VIEWER === "true",
|
|
@@ -537,9 +526,11 @@ const defaults = {
|
|
|
537
526
|
heatmapThresholdTime: process.env.OBSERVATORY_HEATMAP_THRESHOLD_TIME ? Number(process.env.OBSERVATORY_HEATMAP_THRESHOLD_TIME) : 1600,
|
|
538
527
|
maxFetchEntries: process.env.OBSERVATORY_MAX_FETCH_ENTRIES ? Number(process.env.OBSERVATORY_MAX_FETCH_ENTRIES) : 200,
|
|
539
528
|
maxPayloadBytes: process.env.OBSERVATORY_MAX_PAYLOAD_BYTES ? Number(process.env.OBSERVATORY_MAX_PAYLOAD_BYTES) : 1e4,
|
|
529
|
+
fetchPageSize: process.env.OBSERVATORY_FETCH_PAGE_SIZE ? Number(process.env.OBSERVATORY_FETCH_PAGE_SIZE) : 20,
|
|
540
530
|
maxTransitions: process.env.OBSERVATORY_MAX_TRANSITIONS ? Number(process.env.OBSERVATORY_MAX_TRANSITIONS) : 500,
|
|
541
531
|
maxComposableHistory: process.env.OBSERVATORY_MAX_COMPOSABLE_HISTORY ? Number(process.env.OBSERVATORY_MAX_COMPOSABLE_HISTORY) : 50,
|
|
542
532
|
maxComposableEntries: process.env.OBSERVATORY_MAX_COMPOSABLE_ENTRIES ? Number(process.env.OBSERVATORY_MAX_COMPOSABLE_ENTRIES) : 300,
|
|
533
|
+
maxPiniaTimeline: process.env.OBSERVATORY_MAX_PINIA_TIMELINE ? Number(process.env.OBSERVATORY_MAX_PINIA_TIMELINE) : 100,
|
|
543
534
|
maxRenderTimeline: process.env.OBSERVATORY_MAX_RENDER_TIMELINE ? Number(process.env.OBSERVATORY_MAX_RENDER_TIMELINE) : 100,
|
|
544
535
|
composableNavigationMode: process.env.OBSERVATORY_COMPOSABLE_NAVIGATION_MODE === "session" ? "session" : "route",
|
|
545
536
|
heatmapHideInternals: process.env.OBSERVATORY_HEATMAP_HIDE_INTERNALS === "true",
|
|
@@ -568,6 +559,7 @@ const module$1 = defineNuxtModule({
|
|
|
568
559
|
fetchDashboard: options.fetchDashboard ?? (process.env.OBSERVATORY_FETCH_DASHBOARD ? process.env.OBSERVATORY_FETCH_DASHBOARD === "true" : true),
|
|
569
560
|
provideInjectGraph: options.provideInjectGraph ?? (process.env.OBSERVATORY_PROVIDE_INJECT_GRAPH ? process.env.OBSERVATORY_PROVIDE_INJECT_GRAPH === "true" : true),
|
|
570
561
|
composableTracker: options.composableTracker ?? (process.env.OBSERVATORY_COMPOSABLE_TRACKER ? process.env.OBSERVATORY_COMPOSABLE_TRACKER === "true" : true),
|
|
562
|
+
piniaTracker: options.piniaTracker ?? (process.env.OBSERVATORY_PINIA_TRACKER ? process.env.OBSERVATORY_PINIA_TRACKER === "true" : true),
|
|
571
563
|
renderHeatmap: options.renderHeatmap ?? (process.env.OBSERVATORY_RENDER_HEATMAP ? process.env.OBSERVATORY_RENDER_HEATMAP === "true" : true),
|
|
572
564
|
transitionTracker: options.transitionTracker ?? (process.env.OBSERVATORY_TRANSITION_TRACKER ? process.env.OBSERVATORY_TRANSITION_TRACKER === "true" : true),
|
|
573
565
|
traceViewer: options.traceViewer ?? (process.env.OBSERVATORY_TRACE_VIEWER ? process.env.OBSERVATORY_TRACE_VIEWER === "true" : true),
|
|
@@ -576,9 +568,11 @@ const module$1 = defineNuxtModule({
|
|
|
576
568
|
heatmapThresholdTime: options.heatmapThresholdTime ?? (process.env.OBSERVATORY_HEATMAP_THRESHOLD_TIME ? Number(process.env.OBSERVATORY_HEATMAP_THRESHOLD_TIME) : 1600),
|
|
577
569
|
maxFetchEntries: options.maxFetchEntries ?? (process.env.OBSERVATORY_MAX_FETCH_ENTRIES ? Number(process.env.OBSERVATORY_MAX_FETCH_ENTRIES) : 200),
|
|
578
570
|
maxPayloadBytes: options.maxPayloadBytes ?? (process.env.OBSERVATORY_MAX_PAYLOAD_BYTES ? Number(process.env.OBSERVATORY_MAX_PAYLOAD_BYTES) : 1e4),
|
|
571
|
+
fetchPageSize: options.fetchPageSize ?? (process.env.OBSERVATORY_FETCH_PAGE_SIZE ? Number(process.env.OBSERVATORY_FETCH_PAGE_SIZE) : 20),
|
|
579
572
|
maxTransitions: options.maxTransitions ?? (process.env.OBSERVATORY_MAX_TRANSITIONS ? Number(process.env.OBSERVATORY_MAX_TRANSITIONS) : 500),
|
|
580
573
|
maxComposableHistory: options.maxComposableHistory ?? (process.env.OBSERVATORY_MAX_COMPOSABLE_HISTORY ? Number(process.env.OBSERVATORY_MAX_COMPOSABLE_HISTORY) : 50),
|
|
581
574
|
maxComposableEntries: options.maxComposableEntries ?? (process.env.OBSERVATORY_MAX_COMPOSABLE_ENTRIES ? Number(process.env.OBSERVATORY_MAX_COMPOSABLE_ENTRIES) : 300),
|
|
575
|
+
maxPiniaTimeline: options.maxPiniaTimeline ?? (process.env.OBSERVATORY_MAX_PINIA_TIMELINE ? Number(process.env.OBSERVATORY_MAX_PINIA_TIMELINE) : 100),
|
|
582
576
|
maxRenderTimeline: options.maxRenderTimeline ?? (process.env.OBSERVATORY_MAX_RENDER_TIMELINE ? Number(process.env.OBSERVATORY_MAX_RENDER_TIMELINE) : 100),
|
|
583
577
|
composableNavigationMode: options.composableNavigationMode ?? (process.env.OBSERVATORY_COMPOSABLE_NAVIGATION_MODE === "session" ? "session" : "route"),
|
|
584
578
|
debugRpc: options.debugRpc ?? (process.env.OBSERVATORY_DEBUG_RPC ? process.env.OBSERVATORY_DEBUG_RPC === "true" : false)
|
|
@@ -599,6 +593,10 @@ const module$1 = defineNuxtModule({
|
|
|
599
593
|
const vitePluginScope = resolved.instrumentServer ? { server: true, client: true } : { server: false, client: true };
|
|
600
594
|
if (resolved.fetchDashboard) {
|
|
601
595
|
addVitePlugin(fetchInstrumentPlugin(), vitePluginScope);
|
|
596
|
+
addImports([
|
|
597
|
+
{ name: "__devFetchCall", from: resolver.resolve("./runtime/composables/fetch-registry") },
|
|
598
|
+
{ name: "useTracedAsyncData", from: resolver.resolve("./runtime/instrumentation/asyncData") }
|
|
599
|
+
]);
|
|
602
600
|
}
|
|
603
601
|
if (resolved.provideInjectGraph) {
|
|
604
602
|
addVitePlugin(provideInjectPlugin(), vitePluginScope);
|
|
@@ -609,7 +607,7 @@ const module$1 = defineNuxtModule({
|
|
|
609
607
|
if (resolved.transitionTracker) {
|
|
610
608
|
addVitePlugin(transitionTrackerPlugin(), vitePluginScope);
|
|
611
609
|
}
|
|
612
|
-
if (resolved.fetchDashboard || resolved.provideInjectGraph || resolved.composableTracker || resolved.renderHeatmap || resolved.transitionTracker) {
|
|
610
|
+
if (resolved.fetchDashboard || resolved.provideInjectGraph || resolved.composableTracker || resolved.piniaTracker || resolved.renderHeatmap || resolved.transitionTracker) {
|
|
613
611
|
addPlugin(resolver.resolve("./runtime/plugin"));
|
|
614
612
|
}
|
|
615
613
|
if (resolved.fetchDashboard || resolved.traceViewer && resolved.instrumentServer) {
|
|
@@ -626,6 +624,7 @@ const module$1 = defineNuxtModule({
|
|
|
626
624
|
fetch: [],
|
|
627
625
|
provideInject: { provides: [], injects: [] },
|
|
628
626
|
composables: [],
|
|
627
|
+
piniaStores: [],
|
|
629
628
|
renders: [],
|
|
630
629
|
transitions: [],
|
|
631
630
|
traces: [],
|
|
@@ -633,7 +632,9 @@ const module$1 = defineNuxtModule({
|
|
|
633
632
|
fetchDashboard: !!resolved.fetchDashboard,
|
|
634
633
|
provideInjectGraph: !!resolved.provideInjectGraph,
|
|
635
634
|
composableTracker: !!resolved.composableTracker,
|
|
635
|
+
piniaTracker: !!resolved.piniaTracker,
|
|
636
636
|
composableNavigationMode: resolved.composableNavigationMode,
|
|
637
|
+
fetchPageSize: resolved.fetchPageSize,
|
|
637
638
|
renderHeatmap: !!resolved.renderHeatmap,
|
|
638
639
|
transitionTracker: !!resolved.transitionTracker,
|
|
639
640
|
traceViewer: !!resolved.traceViewer
|
|
@@ -660,6 +661,7 @@ const module$1 = defineNuxtModule({
|
|
|
660
661
|
debugLog("received host snapshot", {
|
|
661
662
|
fetch: Array.isArray(snapshot.fetch) ? snapshot.fetch.length : 0,
|
|
662
663
|
composables: Array.isArray(snapshot.composables) ? snapshot.composables.length : 0,
|
|
664
|
+
piniaStores: Array.isArray(snapshot.piniaStores) ? snapshot.piniaStores.length : 0,
|
|
663
665
|
renders: Array.isArray(snapshot.renders) ? snapshot.renders.length : 0,
|
|
664
666
|
transitions: Array.isArray(snapshot.transitions) ? snapshot.transitions.length : 0
|
|
665
667
|
});
|
|
@@ -685,21 +687,20 @@ const module$1 = defineNuxtModule({
|
|
|
685
687
|
},
|
|
686
688
|
async editComposableValue(id, key, value) {
|
|
687
689
|
emitCommand({ cmd: "edit-composable", id, key, value });
|
|
690
|
+
},
|
|
691
|
+
async clearPiniaStores() {
|
|
692
|
+
emitCommand({ cmd: "clear-pinia" });
|
|
693
|
+
},
|
|
694
|
+
async editPiniaState(storeId, path, value) {
|
|
695
|
+
emitCommand({ cmd: "edit-pinia", storeId, path, value });
|
|
688
696
|
}
|
|
689
697
|
},
|
|
690
698
|
nuxt
|
|
691
699
|
);
|
|
692
700
|
rpc.broadcast.onSnapshot.asEvent(latestSnapshot);
|
|
693
701
|
}, nuxt);
|
|
694
|
-
nuxt.hook("render:response", (response, { url }) => {
|
|
695
|
-
if (url.startsWith("/trackers") || url === "/" || url.startsWith("/index.html")) {
|
|
696
|
-
const configScript = `<script>window.__observatoryConfig = ${JSON.stringify(nuxt.options.runtimeConfig.public.observatory)};<\/script>`;
|
|
697
|
-
response.body = response.body.replace("<head>", `<head>
|
|
698
|
-
${configScript}`);
|
|
699
|
-
}
|
|
700
|
-
});
|
|
701
702
|
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
702
|
-
if (resolved.fetchDashboard || resolved.provideInjectGraph || resolved.composableTracker || resolved.renderHeatmap || resolved.transitionTracker) {
|
|
703
|
+
if (resolved.fetchDashboard || resolved.provideInjectGraph || resolved.composableTracker || resolved.piniaTracker || resolved.renderHeatmap || resolved.transitionTracker) {
|
|
703
704
|
tabs.push({
|
|
704
705
|
name: "observatory-trackers",
|
|
705
706
|
title: "Observatory Trackers",
|
|
@@ -713,14 +714,17 @@ ${configScript}`);
|
|
|
713
714
|
fetchDashboard: resolved.fetchDashboard,
|
|
714
715
|
provideInjectGraph: resolved.provideInjectGraph,
|
|
715
716
|
composableTracker: resolved.composableTracker,
|
|
717
|
+
piniaTracker: resolved.piniaTracker,
|
|
716
718
|
renderHeatmap: resolved.renderHeatmap,
|
|
717
719
|
transitionTracker: resolved.transitionTracker,
|
|
718
720
|
traceViewer: resolved.traceViewer,
|
|
719
721
|
maxFetchEntries: resolved.maxFetchEntries,
|
|
720
722
|
maxPayloadBytes: resolved.maxPayloadBytes,
|
|
723
|
+
fetchPageSize: resolved.fetchPageSize,
|
|
721
724
|
maxTransitions: resolved.maxTransitions,
|
|
722
725
|
maxComposableHistory: resolved.maxComposableHistory,
|
|
723
726
|
maxComposableEntries: resolved.maxComposableEntries,
|
|
727
|
+
maxPiniaTimeline: resolved.maxPiniaTimeline,
|
|
724
728
|
maxRenderTimeline: resolved.maxRenderTimeline,
|
|
725
729
|
composableNavigationMode: resolved.composableNavigationMode,
|
|
726
730
|
heatmapHideInternals: resolved.heatmapHideInternals,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { PiniaHydrationEvent, PiniaStateDiff, PiniaStoreDependency } from '../../types/snapshot.js';
|
|
2
|
+
export declare function setupPiniaStoreRegistry(options: {
|
|
3
|
+
pinia?: unknown;
|
|
4
|
+
nuxtPayload?: unknown;
|
|
5
|
+
maxTimeline?: number;
|
|
6
|
+
stackProvider?: () => string[];
|
|
7
|
+
}): {
|
|
8
|
+
clear: () => void;
|
|
9
|
+
editState: (storeId: string, path: string, value: unknown) => void;
|
|
10
|
+
getAll: () => {
|
|
11
|
+
state: unknown;
|
|
12
|
+
dependencies: PiniaStoreDependency[];
|
|
13
|
+
hydrationTimeline: {
|
|
14
|
+
at: number;
|
|
15
|
+
source: "nuxt-payload" | "persistedstate" | "runtime" | "unknown";
|
|
16
|
+
details?: string;
|
|
17
|
+
}[];
|
|
18
|
+
timeline: {
|
|
19
|
+
beforeState: unknown;
|
|
20
|
+
afterState: unknown;
|
|
21
|
+
diff: PiniaStateDiff[];
|
|
22
|
+
id: string;
|
|
23
|
+
storeId: string;
|
|
24
|
+
storeName: string;
|
|
25
|
+
kind: "action" | "mutation";
|
|
26
|
+
name: string;
|
|
27
|
+
startTime: number;
|
|
28
|
+
endTime?: number;
|
|
29
|
+
durationMs?: number;
|
|
30
|
+
status: "active" | "ok" | "error";
|
|
31
|
+
callerStack?: string[];
|
|
32
|
+
payload?: unknown;
|
|
33
|
+
error?: string;
|
|
34
|
+
}[];
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
lastMutationAt?: number;
|
|
38
|
+
lastActionAt?: number;
|
|
39
|
+
hydration?: PiniaHydrationEvent;
|
|
40
|
+
}[];
|
|
41
|
+
getSnapshot: () => string;
|
|
42
|
+
onChange: (cb: () => void) => () => void;
|
|
43
|
+
teardown: () => void;
|
|
44
|
+
};
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import { getCurrentInstance } from "vue";
|
|
2
|
+
function nowMs() {
|
|
3
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
4
|
+
}
|
|
5
|
+
function safeSnapshot(value) {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(JSON.stringify(value));
|
|
8
|
+
} catch {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function parsePath(path) {
|
|
13
|
+
return path.replace(/\[(\d+)\]/g, ".$1").split(".").map((part) => part.trim()).filter(Boolean).map((part) => /^\d+$/.test(part) ? Number(part) : part);
|
|
14
|
+
}
|
|
15
|
+
function setAtPath(target, path, value) {
|
|
16
|
+
const parts = parsePath(path);
|
|
17
|
+
if (parts.length === 0) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
let cursor = target;
|
|
21
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
22
|
+
const key = parts[i];
|
|
23
|
+
if (!cursor || typeof cursor !== "object") {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const objectCursor = cursor;
|
|
27
|
+
const next = objectCursor[String(key)];
|
|
28
|
+
if (next === void 0 || next === null || typeof next !== "object") {
|
|
29
|
+
objectCursor[String(key)] = typeof parts[i + 1] === "number" ? [] : {};
|
|
30
|
+
}
|
|
31
|
+
cursor = objectCursor[String(key)];
|
|
32
|
+
}
|
|
33
|
+
if (!cursor || typeof cursor !== "object") {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
;
|
|
37
|
+
cursor[String(parts[parts.length - 1])] = value;
|
|
38
|
+
}
|
|
39
|
+
function collectDiff(before, after, path = "", depth = 0, out = []) {
|
|
40
|
+
if (out.length >= 80 || depth > 6) {
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
if (Object.is(before, after)) {
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
const beforeIsObj = before !== null && typeof before === "object";
|
|
47
|
+
const afterIsObj = after !== null && typeof after === "object";
|
|
48
|
+
if (!beforeIsObj || !afterIsObj) {
|
|
49
|
+
out.push({ path: path || "$", before, after });
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
const beforeRec = before;
|
|
53
|
+
const afterRec = after;
|
|
54
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(beforeRec), ...Object.keys(afterRec)]);
|
|
55
|
+
for (const key of keys) {
|
|
56
|
+
const nextPath = path ? `${path}.${key}` : key;
|
|
57
|
+
collectDiff(beforeRec[key], afterRec[key], nextPath, depth + 1, out);
|
|
58
|
+
if (out.length >= 80) {
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function stackFromError() {
|
|
65
|
+
const stack = new Error().stack;
|
|
66
|
+
if (!stack) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
return stack.split("\n").slice(2, 10).map((line) => line.trim());
|
|
70
|
+
}
|
|
71
|
+
function inferDependencyFromInstance() {
|
|
72
|
+
const instance = getCurrentInstance();
|
|
73
|
+
if (!instance) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const file = instance.type?.__file;
|
|
77
|
+
const name = instance.type?.__name || instance.type?.name || (file?.split("/").pop() ?? `component:${instance.uid}`);
|
|
78
|
+
return {
|
|
79
|
+
id: `component:${instance.uid}`,
|
|
80
|
+
kind: "component",
|
|
81
|
+
name,
|
|
82
|
+
file
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function parseStackLine(line) {
|
|
86
|
+
const callsiteMatch = line.match(/^at\s+(.+?)\s+\((.+?):\d+:\d+\)$/);
|
|
87
|
+
if (callsiteMatch) {
|
|
88
|
+
return {
|
|
89
|
+
name: callsiteMatch[1]?.trim(),
|
|
90
|
+
file: callsiteMatch[2]?.trim()
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const fileOnlyMatch = line.match(/^at\s+(.+?):\d+:\d+$/);
|
|
94
|
+
if (fileOnlyMatch) {
|
|
95
|
+
return {
|
|
96
|
+
file: fileOnlyMatch[1]?.trim()
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
function inferDependenciesFromStack(stack) {
|
|
102
|
+
const result = [];
|
|
103
|
+
for (const line of stack) {
|
|
104
|
+
const parsed = parseStackLine(line);
|
|
105
|
+
const file = parsed.file;
|
|
106
|
+
const rawName = parsed.name;
|
|
107
|
+
const name = rawName?.includes(".") ? rawName.split(".").pop() : rawName;
|
|
108
|
+
if (name && /^use[A-Z]/.test(name)) {
|
|
109
|
+
result.push({
|
|
110
|
+
id: `composable:${file ?? name}`,
|
|
111
|
+
kind: "composable",
|
|
112
|
+
name,
|
|
113
|
+
file
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (file?.includes("/composables/")) {
|
|
117
|
+
const fileName = file.split("/").pop() ?? "";
|
|
118
|
+
const composableName = fileName.replace(/\.(mjs|cjs|js|ts|tsx|vue)$/i, "");
|
|
119
|
+
if (/^use[A-Z]/.test(composableName)) {
|
|
120
|
+
result.push({
|
|
121
|
+
id: `composable-file:${file}`,
|
|
122
|
+
kind: "composable",
|
|
123
|
+
name: composableName,
|
|
124
|
+
file
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (file?.endsWith(".vue")) {
|
|
129
|
+
const fileName = file.split("/").pop() ?? file;
|
|
130
|
+
const componentName = fileName.replace(/\.vue$/i, "");
|
|
131
|
+
result.push({
|
|
132
|
+
id: `component-file:${file}`,
|
|
133
|
+
kind: "component",
|
|
134
|
+
name: componentName,
|
|
135
|
+
file
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
function pushUniqueDependency(deps, dependency) {
|
|
142
|
+
if (!dependency.id) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (deps.some((item) => item.id === dependency.id)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
deps.push(dependency);
|
|
149
|
+
}
|
|
150
|
+
export function setupPiniaStoreRegistry(options) {
|
|
151
|
+
const pinia = options.pinia;
|
|
152
|
+
const maxTimeline = typeof options.maxTimeline === "number" ? options.maxTimeline : 100;
|
|
153
|
+
const stackProvider = options.stackProvider ?? stackFromError;
|
|
154
|
+
const entries = /* @__PURE__ */ new Map();
|
|
155
|
+
const stores = /* @__PURE__ */ new Map();
|
|
156
|
+
const stopHandles = /* @__PURE__ */ new Map();
|
|
157
|
+
const activeActions = /* @__PURE__ */ new Map();
|
|
158
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
159
|
+
let dirty = true;
|
|
160
|
+
let cached = "[]";
|
|
161
|
+
function notifyChange() {
|
|
162
|
+
dirty = true;
|
|
163
|
+
for (const listener of listeners) {
|
|
164
|
+
listener();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function inferPersistedStorageDetails(store) {
|
|
168
|
+
const persistConfig = store.$persist ?? store.$options?.persist ?? store.persist;
|
|
169
|
+
if (!persistConfig) {
|
|
170
|
+
return "Persist plugin detected";
|
|
171
|
+
}
|
|
172
|
+
const list = Array.isArray(persistConfig) ? persistConfig : [persistConfig];
|
|
173
|
+
const storageLabels = /* @__PURE__ */ new Set();
|
|
174
|
+
for (const item of list) {
|
|
175
|
+
if (!item || typeof item !== "object") {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const storage = item.storage;
|
|
179
|
+
if (!storage || typeof storage !== "object") {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const candidate = storage;
|
|
183
|
+
if (typeof localStorage !== "undefined" && candidate.getItem === localStorage.getItem) {
|
|
184
|
+
storageLabels.add("localStorage");
|
|
185
|
+
} else if (typeof sessionStorage !== "undefined" && candidate.getItem === sessionStorage.getItem) {
|
|
186
|
+
storageLabels.add("sessionStorage");
|
|
187
|
+
} else {
|
|
188
|
+
storageLabels.add("custom storage");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (storageLabels.size === 0) {
|
|
192
|
+
return "Persist plugin detected";
|
|
193
|
+
}
|
|
194
|
+
return `Persist plugin detected (${[...storageLabels].join(", ")})`;
|
|
195
|
+
}
|
|
196
|
+
function inferHydrationTimeline(store) {
|
|
197
|
+
const payload = options.nuxtPayload;
|
|
198
|
+
const fromPayload = !!payload?.pinia?.[store.$id];
|
|
199
|
+
const hasPersist = !!store.$persist || !!store.$options?.persist || !!store.persist;
|
|
200
|
+
const events = [];
|
|
201
|
+
let at = nowMs();
|
|
202
|
+
if (fromPayload) {
|
|
203
|
+
events.push({
|
|
204
|
+
at,
|
|
205
|
+
source: "nuxt-payload",
|
|
206
|
+
details: "Nuxt payload state detected"
|
|
207
|
+
});
|
|
208
|
+
at += 0.01;
|
|
209
|
+
}
|
|
210
|
+
if (hasPersist) {
|
|
211
|
+
events.push({
|
|
212
|
+
at,
|
|
213
|
+
source: "persistedstate",
|
|
214
|
+
details: inferPersistedStorageDetails(store)
|
|
215
|
+
});
|
|
216
|
+
at += 0.01;
|
|
217
|
+
}
|
|
218
|
+
if (events.length === 0) {
|
|
219
|
+
events.push({
|
|
220
|
+
at,
|
|
221
|
+
source: "runtime",
|
|
222
|
+
details: void 0
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return events;
|
|
226
|
+
}
|
|
227
|
+
function trimTimeline(entry) {
|
|
228
|
+
if (entry.timeline.length <= maxTimeline) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
entry.timeline.splice(0, entry.timeline.length - maxTimeline);
|
|
232
|
+
}
|
|
233
|
+
function ensureStore(store) {
|
|
234
|
+
if (!store?.$id || stores.has(store.$id)) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
stores.set(store.$id, store);
|
|
238
|
+
const entry = {
|
|
239
|
+
id: store.$id,
|
|
240
|
+
name: store.$id,
|
|
241
|
+
state: safeSnapshot(store.$state),
|
|
242
|
+
dependencies: [],
|
|
243
|
+
timeline: [],
|
|
244
|
+
hydrationTimeline: inferHydrationTimeline(store),
|
|
245
|
+
hydration: void 0
|
|
246
|
+
};
|
|
247
|
+
entry.hydration = entry.hydrationTimeline[0];
|
|
248
|
+
entries.set(store.$id, entry);
|
|
249
|
+
const offAction = store.$onAction(({ name, args, after, onError }) => {
|
|
250
|
+
const actionId = `${store.$id}:action:${name}:${nowMs()}:${Math.random().toString(36).slice(2, 8)}`;
|
|
251
|
+
const start = nowMs();
|
|
252
|
+
const startSnapshot = safeSnapshot(store.$state);
|
|
253
|
+
const stack = stackProvider();
|
|
254
|
+
const dependenciesFromStack = inferDependenciesFromStack(stack);
|
|
255
|
+
const dependencyFromInstance = inferDependencyFromInstance();
|
|
256
|
+
const current = entries.get(store.$id);
|
|
257
|
+
if (!current) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (dependencyFromInstance) {
|
|
261
|
+
pushUniqueDependency(current.dependencies, dependencyFromInstance);
|
|
262
|
+
}
|
|
263
|
+
for (const dependency of dependenciesFromStack) {
|
|
264
|
+
pushUniqueDependency(current.dependencies, dependency);
|
|
265
|
+
}
|
|
266
|
+
const event = {
|
|
267
|
+
id: actionId,
|
|
268
|
+
storeId: store.$id,
|
|
269
|
+
storeName: store.$id,
|
|
270
|
+
kind: "action",
|
|
271
|
+
name,
|
|
272
|
+
startTime: start,
|
|
273
|
+
status: "active",
|
|
274
|
+
beforeState: startSnapshot,
|
|
275
|
+
afterState: startSnapshot,
|
|
276
|
+
diff: [],
|
|
277
|
+
payload: safeSnapshot(args),
|
|
278
|
+
callerStack: stack
|
|
279
|
+
};
|
|
280
|
+
current.timeline.push(event);
|
|
281
|
+
trimTimeline(current);
|
|
282
|
+
current.lastActionAt = start;
|
|
283
|
+
activeActions.set(actionId, event);
|
|
284
|
+
notifyChange();
|
|
285
|
+
after(() => {
|
|
286
|
+
const end = nowMs();
|
|
287
|
+
const done = entries.get(store.$id);
|
|
288
|
+
if (!done) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const afterState = safeSnapshot(store.$state);
|
|
292
|
+
event.endTime = end;
|
|
293
|
+
event.durationMs = end - start;
|
|
294
|
+
event.status = "ok";
|
|
295
|
+
event.afterState = afterState;
|
|
296
|
+
event.diff = collectDiff(event.beforeState, afterState);
|
|
297
|
+
done.state = afterState;
|
|
298
|
+
done.lastMutationAt = end;
|
|
299
|
+
activeActions.delete(actionId);
|
|
300
|
+
notifyChange();
|
|
301
|
+
});
|
|
302
|
+
onError((error) => {
|
|
303
|
+
const end = nowMs();
|
|
304
|
+
const done = entries.get(store.$id);
|
|
305
|
+
if (!done) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const afterState = safeSnapshot(store.$state);
|
|
309
|
+
event.endTime = end;
|
|
310
|
+
event.durationMs = end - start;
|
|
311
|
+
event.status = "error";
|
|
312
|
+
event.afterState = afterState;
|
|
313
|
+
event.diff = collectDiff(event.beforeState, afterState);
|
|
314
|
+
event.error = error instanceof Error ? error.message : String(error);
|
|
315
|
+
done.state = afterState;
|
|
316
|
+
done.lastMutationAt = end;
|
|
317
|
+
activeActions.delete(actionId);
|
|
318
|
+
notifyChange();
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
const offSub = store.$subscribe(
|
|
322
|
+
(mutation, state) => {
|
|
323
|
+
const current = entries.get(store.$id);
|
|
324
|
+
if (!current) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const beforeState = safeSnapshot(current.state);
|
|
328
|
+
const afterState = safeSnapshot(state);
|
|
329
|
+
const at = nowMs();
|
|
330
|
+
const event = {
|
|
331
|
+
id: `${store.$id}:mutation:${at}:${Math.random().toString(36).slice(2, 8)}`,
|
|
332
|
+
storeId: store.$id,
|
|
333
|
+
storeName: store.$id,
|
|
334
|
+
kind: "mutation",
|
|
335
|
+
name: mutation.type,
|
|
336
|
+
startTime: at,
|
|
337
|
+
endTime: at,
|
|
338
|
+
durationMs: 0,
|
|
339
|
+
status: "ok",
|
|
340
|
+
beforeState,
|
|
341
|
+
afterState,
|
|
342
|
+
diff: collectDiff(beforeState, afterState),
|
|
343
|
+
payload: safeSnapshot(mutation.payload),
|
|
344
|
+
callerStack: stackProvider()
|
|
345
|
+
};
|
|
346
|
+
current.timeline.push(event);
|
|
347
|
+
trimTimeline(current);
|
|
348
|
+
current.state = afterState;
|
|
349
|
+
current.lastMutationAt = at;
|
|
350
|
+
notifyChange();
|
|
351
|
+
},
|
|
352
|
+
{ detached: true, flush: "sync" }
|
|
353
|
+
);
|
|
354
|
+
stopHandles.set(store.$id, [offAction, offSub]);
|
|
355
|
+
notifyChange();
|
|
356
|
+
}
|
|
357
|
+
function clear() {
|
|
358
|
+
for (const [id, entry] of entries) {
|
|
359
|
+
const store = stores.get(id);
|
|
360
|
+
entry.timeline = [];
|
|
361
|
+
entry.dependencies = [];
|
|
362
|
+
entry.state = safeSnapshot(store?.$state ?? entry.state);
|
|
363
|
+
entry.lastActionAt = void 0;
|
|
364
|
+
entry.lastMutationAt = void 0;
|
|
365
|
+
}
|
|
366
|
+
notifyChange();
|
|
367
|
+
}
|
|
368
|
+
function editState(storeId, path, value) {
|
|
369
|
+
const store = stores.get(storeId);
|
|
370
|
+
if (!store || !path) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (typeof store.$patch === "function") {
|
|
374
|
+
;
|
|
375
|
+
store.$patch((state) => {
|
|
376
|
+
setAtPath(state, path, value);
|
|
377
|
+
});
|
|
378
|
+
} else {
|
|
379
|
+
setAtPath(store.$state, path, value);
|
|
380
|
+
}
|
|
381
|
+
notifyChange();
|
|
382
|
+
}
|
|
383
|
+
function getAll() {
|
|
384
|
+
return [...entries.values()].map((entry) => ({
|
|
385
|
+
...entry,
|
|
386
|
+
state: safeSnapshot(entry.state),
|
|
387
|
+
dependencies: [...entry.dependencies],
|
|
388
|
+
hydrationTimeline: entry.hydrationTimeline.map((event) => ({ ...event })),
|
|
389
|
+
timeline: entry.timeline.map((event) => ({
|
|
390
|
+
...event,
|
|
391
|
+
beforeState: safeSnapshot(event.beforeState),
|
|
392
|
+
afterState: safeSnapshot(event.afterState),
|
|
393
|
+
diff: [...event.diff]
|
|
394
|
+
}))
|
|
395
|
+
}));
|
|
396
|
+
}
|
|
397
|
+
function getSnapshot() {
|
|
398
|
+
if (!dirty) {
|
|
399
|
+
return cached;
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
cached = JSON.stringify(getAll());
|
|
403
|
+
} catch {
|
|
404
|
+
cached = "[]";
|
|
405
|
+
}
|
|
406
|
+
dirty = false;
|
|
407
|
+
return cached;
|
|
408
|
+
}
|
|
409
|
+
function onChange(cb) {
|
|
410
|
+
listeners.add(cb);
|
|
411
|
+
return () => {
|
|
412
|
+
listeners.delete(cb);
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function teardown() {
|
|
416
|
+
for (const handlers of stopHandles.values()) {
|
|
417
|
+
for (const off of handlers) {
|
|
418
|
+
off();
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
stopHandles.clear();
|
|
422
|
+
stores.clear();
|
|
423
|
+
entries.clear();
|
|
424
|
+
activeActions.clear();
|
|
425
|
+
listeners.clear();
|
|
426
|
+
dirty = true;
|
|
427
|
+
cached = "[]";
|
|
428
|
+
}
|
|
429
|
+
if (pinia?._s) {
|
|
430
|
+
for (const store of pinia._s.values()) {
|
|
431
|
+
ensureStore(store);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (typeof pinia?.use === "function") {
|
|
435
|
+
pinia.use(({ store }) => {
|
|
436
|
+
ensureStore(store);
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
return {
|
|
440
|
+
clear,
|
|
441
|
+
editState,
|
|
442
|
+
getAll,
|
|
443
|
+
getSnapshot,
|
|
444
|
+
onChange,
|
|
445
|
+
teardown
|
|
446
|
+
};
|
|
447
|
+
}
|