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.
Files changed (39) hide show
  1. package/README.md +63 -4
  2. package/client/.env +2 -1
  3. package/client/.env.example +2 -1
  4. package/client/dist/assets/index-BuMXDBO9.js +17 -0
  5. package/client/dist/assets/{index-BmGW_M3W.css → index-CwcspZ6w.css} +1 -1
  6. package/client/dist/index.html +2 -2
  7. package/client/src/App.vue +4 -0
  8. package/client/src/components/Flamegraph.vue +443 -0
  9. package/client/src/components/SpanInspector.vue +446 -0
  10. package/client/src/components/TraceFilter.vue +344 -0
  11. package/client/src/components/WaterfallView.vue +443 -0
  12. package/client/src/composables/useTraceFilter.ts +164 -0
  13. package/client/src/stores/observatory.ts +5 -1
  14. package/client/src/views/TraceViewer.vue +599 -0
  15. package/client/src/views/TransitionTimeline.vue +1 -6
  16. package/dist/module.d.mts +5 -0
  17. package/dist/module.json +1 -1
  18. package/dist/module.mjs +30 -44
  19. package/dist/runtime/composables/render-registry.js +66 -110
  20. package/dist/runtime/composables/transition-registry.js +103 -28
  21. package/dist/runtime/instrumentation/asyncData.d.ts +9 -0
  22. package/dist/runtime/instrumentation/asyncData.js +49 -0
  23. package/dist/runtime/instrumentation/component.d.ts +2 -0
  24. package/dist/runtime/instrumentation/component.js +126 -0
  25. package/dist/runtime/instrumentation/fetch.d.ts +2 -0
  26. package/dist/runtime/instrumentation/fetch.js +89 -0
  27. package/dist/runtime/instrumentation/route.d.ts +6 -0
  28. package/dist/runtime/instrumentation/route.js +41 -0
  29. package/dist/runtime/plugin.js +38 -2
  30. package/dist/runtime/tracing/context.d.ts +9 -0
  31. package/dist/runtime/tracing/context.js +15 -0
  32. package/dist/runtime/tracing/trace.d.ts +25 -0
  33. package/dist/runtime/tracing/trace.js +0 -0
  34. package/dist/runtime/tracing/traceStore.d.ts +39 -0
  35. package/dist/runtime/tracing/traceStore.js +101 -0
  36. package/dist/runtime/tracing/tracing.d.ts +27 -0
  37. package/dist/runtime/tracing/tracing.js +48 -0
  38. package/package.json +1 -1
  39. package/client/dist/assets/index-BCaKoHBH.js +0 -17
@@ -0,0 +1,126 @@
1
+ import { startSpan } from "../tracing/tracing.js";
2
+ const instrumentedApps = /* @__PURE__ */ new WeakSet();
3
+ const renderStartTimes = /* @__PURE__ */ new WeakMap();
4
+ function getComponentFile(instance) {
5
+ return instance.$.type.__file;
6
+ }
7
+ function getComponentName(instance) {
8
+ const type = instance.$.type;
9
+ if (type.__name && type.__name.length > 0) {
10
+ return type.__name;
11
+ }
12
+ if (type.name && type.name.length > 0) {
13
+ return type.name;
14
+ }
15
+ if (type.__file) {
16
+ const fileName = type.__file.split("/").pop() ?? type.__file;
17
+ return fileName.replace(/\.[^.]+$/, "");
18
+ }
19
+ return "AnonymousComponent";
20
+ }
21
+ function shouldTrack(instance) {
22
+ const file = getComponentFile(instance);
23
+ if (!file) {
24
+ return false;
25
+ }
26
+ return !file.includes("node_modules");
27
+ }
28
+ function trackLifecycle(instance, lifecycle) {
29
+ if (!shouldTrack(instance)) {
30
+ return;
31
+ }
32
+ const componentName = getComponentName(instance);
33
+ const file = getComponentFile(instance);
34
+ const uid = instance.$.uid;
35
+ const route = typeof window !== "undefined" ? window.location.pathname : "/";
36
+ const span = startSpan({
37
+ name: `component:${lifecycle}`,
38
+ type: "component",
39
+ metadata: {
40
+ lifecycle,
41
+ componentName,
42
+ uid,
43
+ file,
44
+ route,
45
+ status: "ok"
46
+ }
47
+ });
48
+ span.end({
49
+ status: "ok",
50
+ metadata: {
51
+ lifecycle,
52
+ componentName,
53
+ uid,
54
+ file,
55
+ route,
56
+ status: "ok"
57
+ }
58
+ });
59
+ }
60
+ function trackRender(instance, phase, startTime, endTime) {
61
+ if (!shouldTrack(instance)) {
62
+ return;
63
+ }
64
+ const componentName = getComponentName(instance);
65
+ const file = getComponentFile(instance);
66
+ const uid = instance.$.uid;
67
+ const route = typeof window !== "undefined" ? window.location.pathname : "/";
68
+ const span = startSpan({
69
+ name: "component:render",
70
+ type: "render",
71
+ startTime,
72
+ metadata: {
73
+ lifecycle: `render:${phase}`,
74
+ componentName,
75
+ uid,
76
+ file,
77
+ route
78
+ }
79
+ });
80
+ span.end({
81
+ endTime,
82
+ status: "ok",
83
+ metadata: {
84
+ lifecycle: `render:${phase}`,
85
+ componentName,
86
+ uid,
87
+ file,
88
+ route
89
+ }
90
+ });
91
+ }
92
+ export function setupComponentInstrumentation(nuxtApp) {
93
+ const app = nuxtApp.vueApp;
94
+ if (instrumentedApps.has(app)) {
95
+ return;
96
+ }
97
+ instrumentedApps.add(app);
98
+ app.mixin({
99
+ beforeMount() {
100
+ if (shouldTrack(this)) {
101
+ renderStartTimes.set(this, performance.now());
102
+ }
103
+ },
104
+ mounted() {
105
+ const startTime = renderStartTimes.get(this);
106
+ renderStartTimes.delete(this);
107
+ if (startTime !== void 0) {
108
+ trackRender(this, "mount", startTime, performance.now());
109
+ }
110
+ trackLifecycle(this, "mounted");
111
+ },
112
+ beforeUpdate() {
113
+ if (shouldTrack(this)) {
114
+ renderStartTimes.set(this, performance.now());
115
+ }
116
+ },
117
+ updated() {
118
+ const startTime = renderStartTimes.get(this);
119
+ renderStartTimes.delete(this);
120
+ if (startTime !== void 0) {
121
+ trackRender(this, "update", startTime, performance.now());
122
+ }
123
+ trackLifecycle(this, "updated");
124
+ }
125
+ });
126
+ }
@@ -0,0 +1,2 @@
1
+ import type { NuxtApp } from '#app';
2
+ export declare function setupFetchInstrumentation(nuxtApp: NuxtApp): void;
@@ -0,0 +1,89 @@
1
+ import { startSpan } from "../tracing/tracing.js";
2
+ function resolveUrl(input) {
3
+ if (typeof input === "string") {
4
+ return input;
5
+ }
6
+ if (input && typeof input === "object" && "url" in input) {
7
+ const maybeUrl = input.url;
8
+ return typeof maybeUrl === "string" ? maybeUrl : String(maybeUrl ?? "");
9
+ }
10
+ return String(input ?? "");
11
+ }
12
+ function resolveMethod(input, options) {
13
+ const method = options?.method;
14
+ if (typeof method === "string" && method.length > 0) {
15
+ return method.toUpperCase();
16
+ }
17
+ if (input && typeof input === "object" && "method" in input) {
18
+ const requestMethod = input.method;
19
+ if (typeof requestMethod === "string" && requestMethod.length > 0) {
20
+ return requestMethod.toUpperCase();
21
+ }
22
+ }
23
+ return "GET";
24
+ }
25
+ function resolveErrorStatus(error) {
26
+ const target = error;
27
+ return target?.response?.status ?? target?.statusCode ?? target?.status;
28
+ }
29
+ const WRAPPED_FETCH_FLAG = "__observatory_wrapped_fetch__";
30
+ export function setupFetchInstrumentation(nuxtApp) {
31
+ const original = nuxtApp.$fetch;
32
+ if (!original) {
33
+ return;
34
+ }
35
+ if (original[WRAPPED_FETCH_FLAG]) {
36
+ return;
37
+ }
38
+ const wrapped = ((request, options) => {
39
+ const url = resolveUrl(request);
40
+ const method = resolveMethod(request, options);
41
+ const startedAt = performance.now();
42
+ const span = startSpan({
43
+ name: "$fetch",
44
+ type: "fetch",
45
+ metadata: {
46
+ source: "$fetch",
47
+ url,
48
+ method,
49
+ status: "pending"
50
+ }
51
+ });
52
+ return Promise.resolve(original(request, options)).then((result) => {
53
+ const durationMs = Math.max(performance.now() - startedAt, 0);
54
+ span.end({
55
+ status: "ok",
56
+ metadata: {
57
+ source: "$fetch",
58
+ url,
59
+ method,
60
+ status: "ok",
61
+ durationMs: Math.round(durationMs * 10) / 10
62
+ }
63
+ });
64
+ return result;
65
+ }).catch((error) => {
66
+ const durationMs = Math.max(performance.now() - startedAt, 0);
67
+ const statusCode = resolveErrorStatus(error);
68
+ span.end({
69
+ status: "error",
70
+ metadata: {
71
+ source: "$fetch",
72
+ url,
73
+ method,
74
+ status: "error",
75
+ statusCode,
76
+ durationMs: Math.round(durationMs * 10) / 10
77
+ }
78
+ });
79
+ throw error;
80
+ });
81
+ });
82
+ Object.assign(wrapped, original);
83
+ wrapped[WRAPPED_FETCH_FLAG] = true;
84
+ nuxtApp.$fetch = wrapped;
85
+ const globalTarget = globalThis;
86
+ if (typeof globalTarget.$fetch === "function") {
87
+ globalTarget.$fetch = wrapped;
88
+ }
89
+ }
@@ -0,0 +1,6 @@
1
+ import type { NuxtApp } from '#app';
2
+ export interface RouteInstrumentationOptions {
3
+ getCurrentPath: () => string;
4
+ carrier?: object;
5
+ }
6
+ export declare function setupRouteInstrumentation(nuxtApp: NuxtApp, options: RouteInstrumentationOptions): void;
@@ -0,0 +1,41 @@
1
+ import { getCurrentTraceId, setCurrentTraceId } from "../tracing/context.js";
2
+ import { traceStore } from "../tracing/traceStore.js";
3
+ export function setupRouteInstrumentation(nuxtApp, options) {
4
+ let activeTraceId;
5
+ const getRoutePath = () => {
6
+ const path = options.getCurrentPath();
7
+ return path && path.length > 0 ? path : "/";
8
+ };
9
+ nuxtApp.hook("page:start", () => {
10
+ if (activeTraceId) {
11
+ traceStore.endTrace(activeTraceId, { status: "cancelled" });
12
+ activeTraceId = void 0;
13
+ }
14
+ const route = getRoutePath();
15
+ const previousRoute = getCurrentTraceId(options.carrier);
16
+ const trace = traceStore.createTrace({
17
+ name: `route:${route}`,
18
+ metadata: {
19
+ kind: "route-navigation",
20
+ route,
21
+ previousTraceId: previousRoute
22
+ }
23
+ });
24
+ activeTraceId = trace.id;
25
+ setCurrentTraceId(trace.id, options.carrier);
26
+ });
27
+ nuxtApp.hook("page:finish", () => {
28
+ if (!activeTraceId) {
29
+ return;
30
+ }
31
+ const route = getRoutePath();
32
+ traceStore.endTrace(activeTraceId, {
33
+ status: "ok",
34
+ metadata: {
35
+ route
36
+ }
37
+ });
38
+ setCurrentTraceId(void 0, options.carrier);
39
+ activeTraceId = void 0;
40
+ });
41
+ }
@@ -5,6 +5,10 @@ import { setupFetchRegistry } from "./composables/fetch-registry.js";
5
5
  import { setupProvideInjectRegistry } from "./composables/provide-inject-registry.js";
6
6
  import { setupRenderRegistry } from "./composables/render-registry.js";
7
7
  import { setupTransitionRegistry } from "./composables/transition-registry.js";
8
+ import { setupComponentInstrumentation } from "./instrumentation/component.js";
9
+ import { setupFetchInstrumentation } from "./instrumentation/fetch.js";
10
+ import { setupRouteInstrumentation } from "./instrumentation/route.js";
11
+ import { traceStore } from "./tracing/traceStore.js";
8
12
  export default defineNuxtPlugin(() => {
9
13
  if (!import.meta.dev) {
10
14
  return;
@@ -42,6 +46,10 @@ export default defineNuxtPlugin(() => {
42
46
  registries.transition = setupTransitionRegistry();
43
47
  }
44
48
  if (import.meta.client) {
49
+ if (config.traceViewer) {
50
+ setupComponentInstrumentation(nuxtApp);
51
+ setupFetchInstrumentation(nuxtApp);
52
+ }
45
53
  delete window.__observatory__;
46
54
  window.__observatory__ = registries;
47
55
  const composableRegistry = registries.composable;
@@ -121,6 +129,11 @@ export default defineNuxtPlugin(() => {
121
129
  });
122
130
  if (import.meta.client) {
123
131
  const router = useRouter();
132
+ if (config.traceViewer) {
133
+ setupRouteInstrumentation(nuxtApp, {
134
+ getCurrentPath: () => router.currentRoute.value.path ?? "/"
135
+ });
136
+ }
124
137
  router.beforeEach(
125
138
  (_to, from) => {
126
139
  if (!from || from.name === void 0) {
@@ -175,7 +188,8 @@ export default defineNuxtPlugin(() => {
175
188
  fetch: Array.isArray(snapshot.fetch) ? snapshot.fetch.length : 0,
176
189
  composables: Array.isArray(snapshot.composables) ? snapshot.composables.length : 0,
177
190
  renders: Array.isArray(snapshot.renders) ? snapshot.renders.length : 0,
178
- transitions: Array.isArray(snapshot.transitions) ? snapshot.transitions.length : 0
191
+ transitions: Array.isArray(snapshot.transitions) ? snapshot.transitions.length : 0,
192
+ traces: Array.isArray(snapshot.traces) ? snapshot.traces.length : 0
179
193
  });
180
194
  lastSnapshotSignature = JSON.stringify(snapshot);
181
195
  import.meta.hot.send("observatory:snapshot", snapshot);
@@ -207,13 +221,35 @@ export default defineNuxtPlugin(() => {
207
221
  const hasGetSnapshot = reg && typeof reg.getSnapshot === "function";
208
222
  snapshot[key === "composable" ? "composables" : key === "render" ? "renders" : key === "transition" ? "transitions" : key] = hasGetSnapshot ? safeParse(reg.getSnapshot(), fallback) : fallback;
209
223
  }
224
+ snapshot.traces = config.traceViewer ? traceStore.getAllTraces().map((trace) => ({
225
+ id: trace.id,
226
+ name: trace.name,
227
+ startTime: trace.startTime,
228
+ endTime: trace.endTime,
229
+ durationMs: trace.durationMs,
230
+ status: trace.status,
231
+ metadata: trace.metadata,
232
+ spans: trace.spans.map((span) => ({
233
+ id: span.id,
234
+ traceId: span.traceId,
235
+ parentSpanId: span.parentSpanId,
236
+ name: span.name,
237
+ type: span.type,
238
+ startTime: span.startTime,
239
+ endTime: span.endTime,
240
+ durationMs: span.durationMs,
241
+ status: span.status,
242
+ metadata: span.metadata
243
+ }))
244
+ })) : [];
210
245
  snapshot.features = {
211
246
  fetchDashboard: !!registries.fetch,
212
247
  provideInjectGraph: !!registries.provideInject,
213
248
  composableTracker: !!registries.composable,
214
249
  composableNavigationMode,
215
250
  renderHeatmap: !!registries.render,
216
- transitionTracker: !!registries.transition
251
+ transitionTracker: !!registries.transition,
252
+ traceViewer: !!config.traceViewer
217
253
  };
218
254
  return snapshot;
219
255
  }
@@ -0,0 +1,9 @@
1
+ declare const TRACE_CONTEXT_KEY = "__observatory_trace_context__";
2
+ type TraceContextCarrier = {
3
+ [TRACE_CONTEXT_KEY]?: {
4
+ currentTraceId?: string;
5
+ };
6
+ };
7
+ export declare function setCurrentTraceId(traceId: string | undefined, carrier?: TraceContextCarrier): void;
8
+ export declare function getCurrentTraceId(carrier?: TraceContextCarrier): string | undefined;
9
+ export {};
@@ -0,0 +1,15 @@
1
+ const TRACE_CONTEXT_KEY = "__observatory_trace_context__";
2
+ function getGlobalCarrier() {
3
+ return globalThis;
4
+ }
5
+ export function setCurrentTraceId(traceId, carrier) {
6
+ const target = carrier ?? getGlobalCarrier();
7
+ if (!target[TRACE_CONTEXT_KEY]) {
8
+ target[TRACE_CONTEXT_KEY] = {};
9
+ }
10
+ target[TRACE_CONTEXT_KEY].currentTraceId = traceId;
11
+ }
12
+ export function getCurrentTraceId(carrier) {
13
+ const target = carrier ?? getGlobalCarrier();
14
+ return target[TRACE_CONTEXT_KEY]?.currentTraceId;
15
+ }
@@ -0,0 +1,25 @@
1
+ export type SpanType = 'render' | 'transition' | 'fetch' | 'composable' | 'navigation' | 'custom' | (string & {});
2
+ export type SpanStatus = 'active' | 'ok' | 'error' | 'cancelled';
3
+ export interface Span {
4
+ id: string;
5
+ traceId: string;
6
+ parentSpanId?: string;
7
+ name: string;
8
+ type: SpanType;
9
+ startTime: number;
10
+ endTime?: number;
11
+ durationMs?: number;
12
+ status: SpanStatus;
13
+ metadata?: Record<string, unknown>;
14
+ }
15
+ export type TraceStatus = 'active' | 'ok' | 'error' | 'cancelled';
16
+ export interface Trace {
17
+ id: string;
18
+ name: string;
19
+ startTime: number;
20
+ endTime?: number;
21
+ durationMs?: number;
22
+ status: TraceStatus;
23
+ metadata?: Record<string, unknown>;
24
+ spans: Span[];
25
+ }
File without changes
@@ -0,0 +1,39 @@
1
+ import type { Span, SpanStatus, Trace, TraceStatus } from './trace.js';
2
+ export interface CreateTraceInput {
3
+ id?: string;
4
+ name?: string;
5
+ startTime?: number;
6
+ metadata?: Record<string, unknown>;
7
+ }
8
+ export interface AddSpanInput {
9
+ id?: string;
10
+ traceId: string;
11
+ parentSpanId?: string;
12
+ name: string;
13
+ type: Span['type'];
14
+ startTime?: number;
15
+ endTime?: number;
16
+ status?: SpanStatus;
17
+ metadata?: Record<string, unknown>;
18
+ }
19
+ export interface EndTraceInput {
20
+ endTime?: number;
21
+ status?: TraceStatus;
22
+ metadata?: Record<string, unknown>;
23
+ }
24
+ export declare class TraceStore {
25
+ private readonly traces;
26
+ createTrace(input?: CreateTraceInput): Trace;
27
+ addSpan(input: AddSpanInput): Span;
28
+ endTrace(traceId: string, input?: EndTraceInput): Trace | null;
29
+ endSpan(spanId: string, traceId: string, input?: {
30
+ endTime?: number;
31
+ status?: SpanStatus;
32
+ metadata?: Record<string, unknown>;
33
+ }): Span | null;
34
+ getTrace(traceId: string): Trace | undefined;
35
+ getAllTraces(): Trace[];
36
+ clear(): void;
37
+ private ensureTrace;
38
+ }
39
+ export declare const traceStore: TraceStore;
@@ -0,0 +1,101 @@
1
+ function createId(prefix) {
2
+ const random = Math.random().toString(36).slice(2, 10);
3
+ return `${prefix}_${Date.now()}_${random}`;
4
+ }
5
+ function computeDuration(startTime, endTime) {
6
+ return Math.max(endTime - startTime, 0);
7
+ }
8
+ export class TraceStore {
9
+ traces = /* @__PURE__ */ new Map();
10
+ createTrace(input = {}) {
11
+ const startTime = input.startTime ?? performance.now();
12
+ const trace = {
13
+ id: input.id ?? createId("trace"),
14
+ name: input.name ?? "trace",
15
+ startTime,
16
+ status: "active",
17
+ metadata: input.metadata,
18
+ spans: []
19
+ };
20
+ this.traces.set(trace.id, trace);
21
+ return trace;
22
+ }
23
+ addSpan(input) {
24
+ const trace = this.ensureTrace(input.traceId, input.startTime);
25
+ const startTime = input.startTime ?? performance.now();
26
+ const endTime = input.endTime;
27
+ const span = {
28
+ id: input.id ?? createId("span"),
29
+ traceId: trace.id,
30
+ parentSpanId: input.parentSpanId,
31
+ name: input.name,
32
+ type: input.type,
33
+ startTime,
34
+ endTime,
35
+ durationMs: endTime !== void 0 ? computeDuration(startTime, endTime) : void 0,
36
+ status: input.status ?? (endTime !== void 0 ? "ok" : "active"),
37
+ metadata: input.metadata
38
+ };
39
+ trace.spans.push(span);
40
+ if (trace.endTime !== void 0) {
41
+ trace.durationMs = computeDuration(trace.startTime, trace.endTime);
42
+ }
43
+ return span;
44
+ }
45
+ endTrace(traceId, input = {}) {
46
+ const trace = this.traces.get(traceId);
47
+ if (!trace) {
48
+ return null;
49
+ }
50
+ const endTime = input.endTime ?? performance.now();
51
+ trace.endTime = endTime;
52
+ trace.durationMs = computeDuration(trace.startTime, endTime);
53
+ trace.status = input.status ?? "ok";
54
+ if (input.metadata) {
55
+ trace.metadata = {
56
+ ...trace.metadata ?? {},
57
+ ...input.metadata
58
+ };
59
+ }
60
+ return trace;
61
+ }
62
+ endSpan(spanId, traceId, input = {}) {
63
+ const trace = this.traces.get(traceId);
64
+ if (!trace) {
65
+ return null;
66
+ }
67
+ const span = trace.spans.find((item) => item.id === spanId);
68
+ if (!span) {
69
+ return null;
70
+ }
71
+ const endTime = input.endTime ?? performance.now();
72
+ span.endTime = endTime;
73
+ span.durationMs = computeDuration(span.startTime, endTime);
74
+ span.status = input.status ?? "ok";
75
+ if (input.metadata) {
76
+ span.metadata = {
77
+ ...span.metadata ?? {},
78
+ ...input.metadata
79
+ };
80
+ }
81
+ return span;
82
+ }
83
+ getTrace(traceId) {
84
+ return this.traces.get(traceId);
85
+ }
86
+ getAllTraces() {
87
+ return [...this.traces.values()];
88
+ }
89
+ clear() {
90
+ this.traces.clear();
91
+ }
92
+ ensureTrace(traceId, startTime) {
93
+ const existing = this.traces.get(traceId);
94
+ if (existing) {
95
+ return existing;
96
+ }
97
+ const trace = this.createTrace({ id: traceId, startTime });
98
+ return trace;
99
+ }
100
+ }
101
+ export const traceStore = new TraceStore();
@@ -0,0 +1,27 @@
1
+ import { type TraceStore } from './traceStore.js';
2
+ import type { Span, SpanStatus, SpanType, Trace } from './trace.js';
3
+ export interface StartSpanInput {
4
+ name: string;
5
+ type: SpanType;
6
+ traceId?: string;
7
+ parentSpanId?: string;
8
+ metadata?: Record<string, unknown>;
9
+ startTime?: number;
10
+ }
11
+ export interface EndSpanInput {
12
+ endTime?: number;
13
+ status?: SpanStatus;
14
+ metadata?: Record<string, unknown>;
15
+ }
16
+ export interface SpanHandle {
17
+ trace: Trace;
18
+ span: Span;
19
+ end: (input?: EndSpanInput) => Span;
20
+ }
21
+ export interface StartSpanOptions {
22
+ store?: TraceStore;
23
+ carrier?: object;
24
+ traceName?: string;
25
+ traceMetadata?: Record<string, unknown>;
26
+ }
27
+ export declare function startSpan(input: StartSpanInput, options?: StartSpanOptions): SpanHandle;
@@ -0,0 +1,48 @@
1
+ import { getCurrentTraceId, setCurrentTraceId } from "./context.js";
2
+ import { traceStore } from "./traceStore.js";
3
+ export function startSpan(input, options = {}) {
4
+ const store = options.store ?? traceStore;
5
+ const activeTraceId = input.traceId ?? getCurrentTraceId(options.carrier);
6
+ let trace = activeTraceId ? store.getTrace(activeTraceId) : void 0;
7
+ if (!trace) {
8
+ trace = store.createTrace({
9
+ id: activeTraceId,
10
+ name: options.traceName ?? input.name,
11
+ metadata: options.traceMetadata,
12
+ startTime: input.startTime
13
+ });
14
+ }
15
+ setCurrentTraceId(trace.id, options.carrier);
16
+ const span = store.addSpan({
17
+ traceId: trace.id,
18
+ parentSpanId: input.parentSpanId,
19
+ name: input.name,
20
+ type: input.type,
21
+ metadata: input.metadata,
22
+ startTime: input.startTime
23
+ });
24
+ let ended = false;
25
+ const end = (endInput = {}) => {
26
+ if (ended) {
27
+ return span;
28
+ }
29
+ const endedSpan = store.endSpan(span.id, trace.id, {
30
+ endTime: endInput.endTime,
31
+ status: endInput.status,
32
+ metadata: endInput.metadata
33
+ });
34
+ ended = true;
35
+ if (endedSpan) {
36
+ span.endTime = endedSpan.endTime;
37
+ span.durationMs = endedSpan.durationMs;
38
+ span.status = endedSpan.status;
39
+ span.metadata = endedSpan.metadata;
40
+ }
41
+ return span;
42
+ };
43
+ return {
44
+ trace,
45
+ span,
46
+ end
47
+ };
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-devtools-observatory",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "Nuxt DevTools: useFetch Dashboard, provide/inject Graph, Composable Tracker, Render Heatmap",
5
5
  "license": "MIT",
6
6
  "repository": {