@getmicdrop/svelte-components 1.0.1

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.
@@ -0,0 +1,212 @@
1
+ // Server-side OpenTelemetry instrumentation for SvelteKit Performers Portal
2
+ import { registerInstrumentations } from '@opentelemetry/instrumentation';
3
+ import { NodeSDK } from '@opentelemetry/sdk-node';
4
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
5
+ import { Resource } from '@opentelemetry/resources';
6
+ import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION, SEMRESATTRS_DEPLOYMENT_ENVIRONMENT } from '@opentelemetry/semantic-conventions';
7
+ import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
8
+ import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
9
+ import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
10
+ import { trace, context, propagation } from '@opentelemetry/api';
11
+
12
+ // Server telemetry configuration
13
+ const SERVER_TELEMETRY_CONFIG = {
14
+ serviceName: process.env.VITE_OTEL_SERVICE_NAME || 'jetbook-performersportal-ssr',
15
+ serviceVersion: process.env.VITE_OTEL_SERVICE_VERSION || '1.0.0',
16
+ environment: process.env.VITE_ENVIRONMENT || 'development',
17
+ otlpEndpoint: process.env.VITE_OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318/v1/traces',
18
+ tracesEnabled: process.env.VITE_OTEL_TRACES_ENABLED !== 'false',
19
+ sampleRate: parseFloat(process.env.VITE_OTEL_TRACES_SAMPLER_ARG || '0.1'),
20
+ consoleDebug: process.env.VITE_OTEL_DEBUG === 'true'
21
+ };
22
+
23
+ let sdk = null;
24
+
25
+ // Initialize server-side telemetry
26
+ export function initServerTelemetry() {
27
+ if (!SERVER_TELEMETRY_CONFIG.tracesEnabled) {
28
+ console.log('[Server Telemetry] Tracing is disabled');
29
+ return;
30
+ }
31
+
32
+ try {
33
+ sdk = new NodeSDK({
34
+ resource: new Resource({
35
+ [SEMRESATTRS_SERVICE_NAME]: SERVER_TELEMETRY_CONFIG.serviceName,
36
+ [SEMRESATTRS_SERVICE_VERSION]: SERVER_TELEMETRY_CONFIG.serviceVersion,
37
+ [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: SERVER_TELEMETRY_CONFIG.environment,
38
+ 'portal.type': 'performers'
39
+ }),
40
+ traceExporter: new OTLPTraceExporter({
41
+ url: SERVER_TELEMETRY_CONFIG.otlpEndpoint,
42
+ headers: {
43
+ 'Content-Type': 'application/json'
44
+ }
45
+ }),
46
+ instrumentations: [
47
+ getNodeAutoInstrumentations({
48
+ '@opentelemetry/instrumentation-fs': {
49
+ enabled: false // Disable fs instrumentation as it's very noisy
50
+ }
51
+ }),
52
+ new HttpInstrumentation({
53
+ requestHook: (span, request) => {
54
+ span.setAttributes({
55
+ 'http.route': request.url,
56
+ 'http.user_agent': request.headers?.['user-agent'],
57
+ 'http.referer': request.headers?.['referer'],
58
+ 'ssr.type': 'request',
59
+ 'portal.type': 'performers'
60
+ });
61
+ }
62
+ }),
63
+ new FetchInstrumentation({
64
+ propagateTraceHeaderCorsUrls: [
65
+ /^http:\/\/localhost:8080\/.*/,
66
+ /^https:\/\/api\.jetbook\.com\/.*/,
67
+ /^https:\/\/.*\.get-micdrop\.com\/.*/
68
+ ]
69
+ })
70
+ ]
71
+ });
72
+
73
+ sdk.start();
74
+ console.log(`[Server Telemetry] Initialized with ${SERVER_TELEMETRY_CONFIG.sampleRate * 100}% sampling rate`);
75
+
76
+ } catch (error) {
77
+ console.error('[Server Telemetry] Failed to initialize:', error);
78
+ }
79
+ }
80
+
81
+ // Create spans for SSR page loads and data fetching
82
+ export function createSSRSpan(name, request, attributes = {}) {
83
+ const tracer = trace.getTracer(
84
+ SERVER_TELEMETRY_CONFIG.serviceName,
85
+ SERVER_TELEMETRY_CONFIG.serviceVersion
86
+ );
87
+
88
+ return tracer.startSpan(name, {
89
+ attributes: {
90
+ 'span.kind': 'server',
91
+ 'ssr.type': 'page_load',
92
+ 'portal.type': 'performers',
93
+ 'http.method': request?.method || 'GET',
94
+ 'http.url': request?.url,
95
+ 'http.user_agent': request?.headers?.['user-agent'],
96
+ 'http.referer': request?.headers?.['referer'],
97
+ ...attributes
98
+ }
99
+ });
100
+ }
101
+
102
+ // Track SSR data loading for performers
103
+ export function trackPerformerSSRDataLoad(loadFunction, context) {
104
+ return async (event) => {
105
+ const span = createSSRSpan(`ssr.performer.load.${context}`, event.request, {
106
+ 'ssr.context': context,
107
+ 'route.id': event.route?.id,
108
+ 'performer.context': true
109
+ });
110
+
111
+ try {
112
+ const result = await loadFunction(event);
113
+ span.setStatus({ code: 1 }); // OK
114
+
115
+ // Add performer-specific attributes if available
116
+ if (result?.performer) {
117
+ span.setAttributes({
118
+ 'performer.id': result.performer.id,
119
+ 'performer.name': result.performer.name,
120
+ 'performer.email': result.performer.email
121
+ });
122
+ }
123
+
124
+ return result;
125
+ } catch (error) {
126
+ span.recordException(error);
127
+ span.setStatus({ code: 2, message: error.message }); // ERROR
128
+ throw error;
129
+ } finally {
130
+ span.end();
131
+ }
132
+ };
133
+ }
134
+
135
+ // Extract trace context from request headers (for connecting client and server spans)
136
+ export function extractTraceContext(request) {
137
+ const headers = {};
138
+ if (request?.headers) {
139
+ // Convert Headers object to plain object
140
+ for (const [key, value] of request.headers.entries()) {
141
+ headers[key] = value;
142
+ }
143
+ }
144
+ return propagation.extract(context.active(), headers);
145
+ }
146
+
147
+ // Inject trace context into response headers (for client-side correlation)
148
+ export function injectTraceContext(response, span) {
149
+ const headers = {};
150
+ propagation.inject(context.active(), headers);
151
+
152
+ // Add trace context headers to response
153
+ Object.entries(headers).forEach(([key, value]) => {
154
+ response.headers?.set(key, value);
155
+ });
156
+
157
+ // Add trace ID for client correlation
158
+ const spanContext = span?.spanContext();
159
+ if (spanContext) {
160
+ response.headers?.set('X-Trace-Id', spanContext.traceId);
161
+ response.headers?.set('X-Span-Id', spanContext.spanId);
162
+ }
163
+
164
+ return response;
165
+ }
166
+
167
+ // Shutdown server telemetry
168
+ export async function shutdownServerTelemetry() {
169
+ if (sdk) {
170
+ try {
171
+ await sdk.shutdown();
172
+ console.log('[Server Telemetry] Shutdown complete');
173
+ } catch (error) {
174
+ console.error('[Server Telemetry] Shutdown error:', error);
175
+ }
176
+ }
177
+ }
178
+
179
+ // SvelteKit-specific helpers for performers portal
180
+ export const performersPortalTelemetry = {
181
+ // Wrap page load functions
182
+ wrapLoad: (loadFn, context) => trackPerformerSSRDataLoad(loadFn, context),
183
+
184
+ // Wrap performer actions
185
+ wrapAction: (actionFn, actionName) => {
186
+ return async (event) => {
187
+ const span = createSSRSpan(`ssr.performer.action.${actionName}`, event.request, {
188
+ 'ssr.context': 'action',
189
+ 'action.name': actionName,
190
+ 'performer.context': true
191
+ });
192
+
193
+ try {
194
+ const result = await actionFn(event);
195
+ span.setStatus({ code: 1 }); // OK
196
+ return result;
197
+ } catch (error) {
198
+ span.recordException(error);
199
+ span.setStatus({ code: 2, message: error.message }); // ERROR
200
+ throw error;
201
+ } finally {
202
+ span.end();
203
+ }
204
+ };
205
+ }
206
+ };
207
+
208
+ // Auto-initialize if in server environment
209
+ if (typeof window === 'undefined' && typeof global !== 'undefined') {
210
+ // Server environment
211
+ initServerTelemetry();
212
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=telemetry.server.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.server.spec.d.ts","sourceRoot":"","sources":["../src/lib/telemetry.server.spec.js"],"names":[],"mappings":""}
@@ -0,0 +1,434 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+
3
+ // Mock all OpenTelemetry modules
4
+ vi.mock('@opentelemetry/instrumentation', () => ({
5
+ registerInstrumentations: vi.fn(),
6
+ }));
7
+
8
+ vi.mock('@opentelemetry/sdk-node', () => ({
9
+ NodeSDK: vi.fn(() => ({
10
+ start: vi.fn(),
11
+ shutdown: vi.fn(() => Promise.resolve()),
12
+ })),
13
+ }));
14
+
15
+ vi.mock('@opentelemetry/exporter-trace-otlp-http', () => ({
16
+ OTLPTraceExporter: vi.fn(),
17
+ }));
18
+
19
+ vi.mock('@opentelemetry/resources', () => ({
20
+ Resource: vi.fn(),
21
+ }));
22
+
23
+ vi.mock('@opentelemetry/semantic-conventions', () => ({
24
+ SEMRESATTRS_SERVICE_NAME: 'service.name',
25
+ SEMRESATTRS_SERVICE_VERSION: 'service.version',
26
+ SEMRESATTRS_DEPLOYMENT_ENVIRONMENT: 'deployment.environment',
27
+ }));
28
+
29
+ vi.mock('@opentelemetry/instrumentation-http', () => ({
30
+ HttpInstrumentation: vi.fn(() => ({})),
31
+ }));
32
+
33
+ vi.mock('@opentelemetry/instrumentation-fetch', () => ({
34
+ FetchInstrumentation: vi.fn(() => ({})),
35
+ }));
36
+
37
+ vi.mock('@opentelemetry/auto-instrumentations-node', () => ({
38
+ getNodeAutoInstrumentations: vi.fn(() => []),
39
+ }));
40
+
41
+ const mockSpan = {
42
+ setAttribute: vi.fn(),
43
+ setAttributes: vi.fn(),
44
+ setStatus: vi.fn(),
45
+ recordException: vi.fn(),
46
+ end: vi.fn(),
47
+ spanContext: vi.fn(() => ({
48
+ traceId: 'test-trace-id',
49
+ spanId: 'test-span-id',
50
+ })),
51
+ };
52
+
53
+ const mockTracer = {
54
+ startSpan: vi.fn(() => mockSpan),
55
+ };
56
+
57
+ const mockContext = {
58
+ active: vi.fn(() => ({})),
59
+ };
60
+
61
+ const mockPropagation = {
62
+ extract: vi.fn(() => ({})),
63
+ inject: vi.fn(),
64
+ };
65
+
66
+ vi.mock('@opentelemetry/api', () => ({
67
+ trace: {
68
+ getTracer: vi.fn(() => mockTracer),
69
+ },
70
+ context: mockContext,
71
+ propagation: mockPropagation,
72
+ }));
73
+
74
+ describe('telemetry.server.js', () => {
75
+ let telemetryServer;
76
+
77
+ beforeEach(async () => {
78
+ vi.resetModules();
79
+ vi.clearAllMocks();
80
+
81
+ // Ensure we're in a server environment
82
+ delete global.window;
83
+
84
+ // Stub environment variables
85
+ vi.stubEnv('VITE_OTEL_TRACES_ENABLED', 'true');
86
+ vi.stubEnv('VITE_OTEL_SERVICE_NAME', 'test-service');
87
+ vi.stubEnv('VITE_ENVIRONMENT', 'test');
88
+
89
+ telemetryServer = await import('./telemetry.server.js');
90
+ });
91
+
92
+ afterEach(() => {
93
+ vi.unstubAllEnvs();
94
+ });
95
+
96
+ describe('initServerTelemetry', () => {
97
+ it('initializes server telemetry when enabled', async () => {
98
+ const consoleSpy = vi.spyOn(console, 'log');
99
+
100
+ // Module auto-initializes, calling initServerTelemetry will re-init
101
+ telemetryServer.initServerTelemetry();
102
+
103
+ expect(consoleSpy).toHaveBeenCalled();
104
+ });
105
+
106
+ it('logs disabled message when tracing is disabled', async () => {
107
+ vi.resetModules();
108
+ vi.stubEnv('VITE_OTEL_TRACES_ENABLED', 'false');
109
+
110
+ const consoleSpy = vi.spyOn(console, 'log');
111
+
112
+ telemetryServer = await import('./telemetry.server.js');
113
+ telemetryServer.initServerTelemetry();
114
+
115
+ expect(consoleSpy).toHaveBeenCalledWith(
116
+ '[Server Telemetry] Tracing is disabled'
117
+ );
118
+ });
119
+ });
120
+
121
+ describe('createSSRSpan', () => {
122
+ it('creates SSR span with correct name and attributes', () => {
123
+ const mockRequest = {
124
+ method: 'GET',
125
+ url: 'http://localhost:3000/test',
126
+ headers: {
127
+ 'user-agent': 'test-agent',
128
+ referer: 'http://localhost:3000/',
129
+ },
130
+ };
131
+
132
+ const span = telemetryServer.createSSRSpan(
133
+ 'ssr.test',
134
+ mockRequest,
135
+ { custom: 'attribute' }
136
+ );
137
+
138
+ expect(mockTracer.startSpan).toHaveBeenCalledWith('ssr.test', {
139
+ attributes: {
140
+ 'span.kind': 'server',
141
+ 'ssr.type': 'page_load',
142
+ 'portal.type': 'performers',
143
+ 'http.method': 'GET',
144
+ 'http.url': 'http://localhost:3000/test',
145
+ 'http.user_agent': 'test-agent',
146
+ 'http.referer': 'http://localhost:3000/',
147
+ custom: 'attribute',
148
+ },
149
+ });
150
+
151
+ expect(span).toBeDefined();
152
+ });
153
+
154
+ it('handles null request gracefully', () => {
155
+ const span = telemetryServer.createSSRSpan('ssr.null-request', null);
156
+
157
+ expect(mockTracer.startSpan).toHaveBeenCalledWith('ssr.null-request', {
158
+ attributes: {
159
+ 'span.kind': 'server',
160
+ 'ssr.type': 'page_load',
161
+ 'portal.type': 'performers',
162
+ 'http.method': 'GET',
163
+ 'http.url': undefined,
164
+ 'http.user_agent': undefined,
165
+ 'http.referer': undefined,
166
+ },
167
+ });
168
+
169
+ expect(span).toBeDefined();
170
+ });
171
+ });
172
+
173
+ describe('trackPerformerSSRDataLoad', () => {
174
+ it('wraps load function and tracks successful execution', async () => {
175
+ const mockLoadFn = vi.fn().mockResolvedValue({
176
+ performer: { id: 1, name: 'Test Performer', email: 'test@test.com' },
177
+ });
178
+
179
+ const mockEvent = {
180
+ request: {
181
+ method: 'GET',
182
+ url: 'http://localhost:3000/performer/1',
183
+ headers: {},
184
+ },
185
+ route: { id: '/performer/[id]' },
186
+ };
187
+
188
+ const wrappedFn = telemetryServer.trackPerformerSSRDataLoad(
189
+ mockLoadFn,
190
+ 'performer-load'
191
+ );
192
+
193
+ const result = await wrappedFn(mockEvent);
194
+
195
+ expect(mockLoadFn).toHaveBeenCalledWith(mockEvent);
196
+ expect(result.performer.id).toBe(1);
197
+ expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: 1 });
198
+ expect(mockSpan.setAttributes).toHaveBeenCalledWith({
199
+ 'performer.id': 1,
200
+ 'performer.name': 'Test Performer',
201
+ 'performer.email': 'test@test.com',
202
+ });
203
+ expect(mockSpan.end).toHaveBeenCalled();
204
+ });
205
+
206
+ it('tracks errors and re-throws them', async () => {
207
+ const testError = new Error('Load failed');
208
+ const mockLoadFn = vi.fn().mockRejectedValue(testError);
209
+
210
+ const mockEvent = {
211
+ request: {
212
+ method: 'GET',
213
+ url: 'http://localhost:3000/test',
214
+ headers: {},
215
+ },
216
+ route: { id: '/test' },
217
+ };
218
+
219
+ const wrappedFn = telemetryServer.trackPerformerSSRDataLoad(
220
+ mockLoadFn,
221
+ 'test-load'
222
+ );
223
+
224
+ await expect(wrappedFn(mockEvent)).rejects.toThrow('Load failed');
225
+
226
+ expect(mockSpan.recordException).toHaveBeenCalledWith(testError);
227
+ expect(mockSpan.setStatus).toHaveBeenCalledWith({
228
+ code: 2,
229
+ message: 'Load failed',
230
+ });
231
+ expect(mockSpan.end).toHaveBeenCalled();
232
+ });
233
+
234
+ it('handles results without performer data', async () => {
235
+ const mockLoadFn = vi.fn().mockResolvedValue({ data: 'test' });
236
+
237
+ const mockEvent = {
238
+ request: {
239
+ method: 'GET',
240
+ url: 'http://localhost:3000/test',
241
+ headers: {},
242
+ },
243
+ route: { id: '/test' },
244
+ };
245
+
246
+ const wrappedFn = telemetryServer.trackPerformerSSRDataLoad(
247
+ mockLoadFn,
248
+ 'test-load'
249
+ );
250
+
251
+ const result = await wrappedFn(mockEvent);
252
+
253
+ expect(result.data).toBe('test');
254
+ expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: 1 });
255
+ // setAttributes should not be called for performer data
256
+ expect(mockSpan.end).toHaveBeenCalled();
257
+ });
258
+ });
259
+
260
+ describe('extractTraceContext', () => {
261
+ it('extracts trace context from request headers', () => {
262
+ const mockRequest = {
263
+ headers: new Map([
264
+ ['x-b3-traceid', 'test-trace'],
265
+ ['x-b3-spanid', 'test-span'],
266
+ ]),
267
+ };
268
+
269
+ mockRequest.headers.entries = function () {
270
+ return this[Symbol.iterator]();
271
+ };
272
+
273
+ telemetryServer.extractTraceContext(mockRequest);
274
+
275
+ expect(mockPropagation.extract).toHaveBeenCalled();
276
+ });
277
+
278
+ it('handles null request', () => {
279
+ const result = telemetryServer.extractTraceContext(null);
280
+
281
+ expect(mockPropagation.extract).toHaveBeenCalled();
282
+ });
283
+
284
+ it('handles request without headers', () => {
285
+ const mockRequest = {};
286
+
287
+ const result = telemetryServer.extractTraceContext(mockRequest);
288
+
289
+ expect(mockPropagation.extract).toHaveBeenCalled();
290
+ });
291
+ });
292
+
293
+ describe('injectTraceContext', () => {
294
+ it('injects trace context into response headers', () => {
295
+ const mockResponse = {
296
+ headers: {
297
+ set: vi.fn(),
298
+ },
299
+ };
300
+
301
+ mockPropagation.inject.mockImplementation((ctx, headers) => {
302
+ headers['x-b3-traceid'] = 'injected-trace';
303
+ });
304
+
305
+ telemetryServer.injectTraceContext(mockResponse, mockSpan);
306
+
307
+ expect(mockPropagation.inject).toHaveBeenCalled();
308
+ expect(mockResponse.headers.set).toHaveBeenCalledWith(
309
+ 'X-Trace-Id',
310
+ 'test-trace-id'
311
+ );
312
+ expect(mockResponse.headers.set).toHaveBeenCalledWith(
313
+ 'X-Span-Id',
314
+ 'test-span-id'
315
+ );
316
+ });
317
+
318
+ it('handles null span context', () => {
319
+ const mockResponse = {
320
+ headers: {
321
+ set: vi.fn(),
322
+ },
323
+ };
324
+
325
+ const nullContextSpan = {
326
+ spanContext: vi.fn(() => null),
327
+ };
328
+
329
+ // Should not throw
330
+ expect(() =>
331
+ telemetryServer.injectTraceContext(mockResponse, nullContextSpan)
332
+ ).not.toThrow();
333
+ });
334
+
335
+ it('handles response without headers', () => {
336
+ const mockResponse = {};
337
+
338
+ // Should not throw
339
+ expect(() =>
340
+ telemetryServer.injectTraceContext(mockResponse, mockSpan)
341
+ ).not.toThrow();
342
+ });
343
+ });
344
+
345
+ describe('shutdownServerTelemetry', () => {
346
+ it('shuts down SDK gracefully', async () => {
347
+ const consoleSpy = vi.spyOn(console, 'log');
348
+
349
+ await telemetryServer.shutdownServerTelemetry();
350
+
351
+ // May or may not log depending on SDK state
352
+ expect(true).toBe(true);
353
+ });
354
+ });
355
+
356
+ describe('performersPortalTelemetry', () => {
357
+ it('exports wrapLoad helper', () => {
358
+ expect(telemetryServer.performersPortalTelemetry.wrapLoad).toBeDefined();
359
+ expect(
360
+ typeof telemetryServer.performersPortalTelemetry.wrapLoad
361
+ ).toBe('function');
362
+ });
363
+
364
+ it('exports wrapAction helper', () => {
365
+ expect(telemetryServer.performersPortalTelemetry.wrapAction).toBeDefined();
366
+ expect(
367
+ typeof telemetryServer.performersPortalTelemetry.wrapAction
368
+ ).toBe('function');
369
+ });
370
+
371
+ it('wrapLoad returns wrapped function', () => {
372
+ const mockFn = vi.fn();
373
+ const wrapped = telemetryServer.performersPortalTelemetry.wrapLoad(
374
+ mockFn,
375
+ 'test'
376
+ );
377
+
378
+ expect(typeof wrapped).toBe('function');
379
+ });
380
+
381
+ it('wrapAction handles successful action', async () => {
382
+ const mockActionFn = vi.fn().mockResolvedValue({ success: true });
383
+
384
+ const mockEvent = {
385
+ request: {
386
+ method: 'POST',
387
+ url: 'http://localhost:3000/action',
388
+ headers: {},
389
+ },
390
+ };
391
+
392
+ const wrappedAction =
393
+ telemetryServer.performersPortalTelemetry.wrapAction(
394
+ mockActionFn,
395
+ 'test-action'
396
+ );
397
+
398
+ const result = await wrappedAction(mockEvent);
399
+
400
+ expect(mockActionFn).toHaveBeenCalledWith(mockEvent);
401
+ expect(result.success).toBe(true);
402
+ expect(mockSpan.setStatus).toHaveBeenCalledWith({ code: 1 });
403
+ expect(mockSpan.end).toHaveBeenCalled();
404
+ });
405
+
406
+ it('wrapAction handles action errors', async () => {
407
+ const testError = new Error('Action failed');
408
+ const mockActionFn = vi.fn().mockRejectedValue(testError);
409
+
410
+ const mockEvent = {
411
+ request: {
412
+ method: 'POST',
413
+ url: 'http://localhost:3000/action',
414
+ headers: {},
415
+ },
416
+ };
417
+
418
+ const wrappedAction =
419
+ telemetryServer.performersPortalTelemetry.wrapAction(
420
+ mockActionFn,
421
+ 'failing-action'
422
+ );
423
+
424
+ await expect(wrappedAction(mockEvent)).rejects.toThrow('Action failed');
425
+
426
+ expect(mockSpan.recordException).toHaveBeenCalledWith(testError);
427
+ expect(mockSpan.setStatus).toHaveBeenCalledWith({
428
+ code: 2,
429
+ message: 'Action failed',
430
+ });
431
+ expect(mockSpan.end).toHaveBeenCalled();
432
+ });
433
+ });
434
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=telemetry.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.spec.d.ts","sourceRoot":"","sources":["../src/lib/telemetry.spec.js"],"names":[],"mappings":""}