@zapier/zapier-sdk 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +23 -9
- package/dist/api/schemas.d.ts +2 -2
- package/dist/index.cjs +276 -204
- package/dist/index.d.mts +203 -86
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.mjs +270 -205
- package/dist/plugins/createClientCredentials/schemas.d.ts +0 -3
- package/dist/plugins/createClientCredentials/schemas.d.ts.map +1 -1
- package/dist/plugins/createClientCredentials/schemas.js +0 -2
- package/dist/plugins/deleteClientCredentials/schemas.d.ts +0 -3
- package/dist/plugins/deleteClientCredentials/schemas.d.ts.map +1 -1
- package/dist/plugins/deleteClientCredentials/schemas.js +0 -2
- package/dist/plugins/fetch/index.d.ts +0 -3
- package/dist/plugins/fetch/index.d.ts.map +1 -1
- package/dist/plugins/fetch/index.js +59 -53
- package/dist/plugins/findFirstConnection/index.d.ts.map +1 -1
- package/dist/plugins/findFirstConnection/index.js +0 -1
- package/dist/plugins/findFirstConnection/schemas.d.ts +1 -1
- package/dist/plugins/findFirstConnection/schemas.d.ts.map +1 -1
- package/dist/plugins/findFirstConnection/schemas.js +0 -1
- package/dist/plugins/findUniqueConnection/index.d.ts.map +1 -1
- package/dist/plugins/findUniqueConnection/index.js +0 -1
- package/dist/plugins/findUniqueConnection/schemas.d.ts +1 -1
- package/dist/plugins/findUniqueConnection/schemas.d.ts.map +1 -1
- package/dist/plugins/findUniqueConnection/schemas.js +0 -1
- package/dist/plugins/getAction/index.d.ts.map +1 -1
- package/dist/plugins/getAction/index.js +1 -3
- package/dist/plugins/getAction/schemas.d.ts +0 -3
- package/dist/plugins/getAction/schemas.d.ts.map +1 -1
- package/dist/plugins/getAction/schemas.js +0 -2
- package/dist/plugins/getApp/index.d.ts.map +1 -1
- package/dist/plugins/getApp/index.js +0 -1
- package/dist/plugins/listActions/schemas.d.ts +0 -3
- package/dist/plugins/listActions/schemas.d.ts.map +1 -1
- package/dist/plugins/listActions/schemas.js +0 -2
- package/dist/plugins/listApps/schemas.d.ts +0 -3
- package/dist/plugins/listApps/schemas.d.ts.map +1 -1
- package/dist/plugins/listApps/schemas.js +0 -2
- package/dist/plugins/listClientCredentials/schemas.d.ts +0 -3
- package/dist/plugins/listClientCredentials/schemas.d.ts.map +1 -1
- package/dist/plugins/listClientCredentials/schemas.js +0 -2
- package/dist/plugins/listConnections/schemas.d.ts +1 -4
- package/dist/plugins/listConnections/schemas.d.ts.map +1 -1
- package/dist/plugins/listConnections/schemas.js +0 -2
- package/dist/plugins/request/index.d.ts.map +1 -1
- package/dist/plugins/request/index.js +0 -1
- package/dist/plugins/request/schemas.d.ts +0 -6
- package/dist/plugins/request/schemas.d.ts.map +1 -1
- package/dist/plugins/request/schemas.js +0 -2
- package/dist/plugins/runAction/index.d.ts.map +1 -1
- package/dist/plugins/runAction/index.js +0 -1
- package/dist/schemas/Action.d.ts +2 -2
- package/dist/types/credentials.d.ts +137 -17
- package/dist/types/credentials.d.ts.map +1 -1
- package/dist/types/credentials.js +80 -0
- package/dist/types/meta.d.ts +9 -0
- package/dist/types/meta.d.ts.map +1 -0
- package/dist/types/meta.js +3 -0
- package/dist/types/sdk.d.ts +61 -35
- package/dist/types/sdk.d.ts.map +1 -1
- package/dist/types/sdk.js +55 -1
- package/dist/utils/function-utils.d.ts +1 -14
- package/dist/utils/function-utils.d.ts.map +1 -1
- package/dist/utils/function-utils.js +115 -123
- package/dist/utils/function-utils.test.js +79 -1
- package/dist/utils/telemetry-context.d.ts +3 -0
- package/dist/utils/telemetry-context.d.ts.map +1 -0
- package/dist/utils/telemetry-context.js +23 -0
- package/dist/utils/telemetry-context.test.d.ts +2 -0
- package/dist/utils/telemetry-context.test.d.ts.map +1 -0
- package/dist/utils/telemetry-context.test.js +92 -0
- package/package.json +1 -1
|
@@ -1,21 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generic utility functions for creating paginated SDK functions
|
|
3
3
|
*/
|
|
4
|
-
import { z } from "zod";
|
|
5
4
|
import { ZapierError, ZapierUnknownError, ZapierAuthenticationError, } from "../types/errors";
|
|
6
5
|
import { paginate } from "./pagination-utils";
|
|
7
6
|
import { createValidator, validateOptions } from "./validation";
|
|
8
|
-
|
|
9
|
-
* Zod schema for telemetry marker to prevent nested telemetry emissions.
|
|
10
|
-
* Used to extend options schemas with proper typing.
|
|
11
|
-
*/
|
|
12
|
-
export const TelemetryMarkerSchema = z.object({
|
|
13
|
-
_telemetry: z
|
|
14
|
-
.object({
|
|
15
|
-
isNested: z.boolean().optional(),
|
|
16
|
-
})
|
|
17
|
-
.optional(),
|
|
18
|
-
});
|
|
7
|
+
import { isTelemetryNested, runWithTelemetryContext, } from "./telemetry-context";
|
|
19
8
|
/**
|
|
20
9
|
* Helper function to extract cursor from pagination data
|
|
21
10
|
* Supports JSON:API style `links.next` URLs by extracting the `offset` parameter.
|
|
@@ -65,19 +54,32 @@ export function createFunction(coreFn, schema, telemetry) {
|
|
|
65
54
|
// Create a named function using dynamic property access
|
|
66
55
|
const namedFunctions = {
|
|
67
56
|
[functionName]: async function (options) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
57
|
+
return runWithTelemetryContext(async () => {
|
|
58
|
+
const startTime = Date.now();
|
|
59
|
+
const normalizedOptions = (options ?? {});
|
|
60
|
+
const isNested = isTelemetryNested();
|
|
61
|
+
try {
|
|
62
|
+
if (schema) {
|
|
63
|
+
const validatedOptions = validateOptions(schema, normalizedOptions);
|
|
64
|
+
const result = await coreFn({
|
|
65
|
+
...normalizedOptions,
|
|
66
|
+
...validatedOptions,
|
|
67
|
+
});
|
|
68
|
+
if (!isNested && telemetry?.onMethodCalled) {
|
|
69
|
+
const argumentCount = Object.keys(normalizedOptions).length;
|
|
70
|
+
telemetry.onMethodCalled({
|
|
71
|
+
methodName: functionName,
|
|
72
|
+
durationMs: Date.now() - startTime,
|
|
73
|
+
success: true,
|
|
74
|
+
argumentCount,
|
|
75
|
+
isPaginated: false,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
const result = await coreFn(normalizedOptions);
|
|
79
81
|
if (!isNested && telemetry?.onMethodCalled) {
|
|
80
|
-
const argumentCount = Object.keys(normalizedOptions).
|
|
82
|
+
const argumentCount = Object.keys(normalizedOptions).length;
|
|
81
83
|
telemetry.onMethodCalled({
|
|
82
84
|
methodName: functionName,
|
|
83
85
|
durationMs: Date.now() - startTime,
|
|
@@ -88,34 +90,22 @@ export function createFunction(coreFn, schema, telemetry) {
|
|
|
88
90
|
}
|
|
89
91
|
return result;
|
|
90
92
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const normalizedError = normalizeError(error);
|
|
106
|
-
if (!isNested && telemetry?.onMethodCalled) {
|
|
107
|
-
const argumentCount = Object.keys(normalizedOptions).filter((k) => k !== "_telemetry").length;
|
|
108
|
-
telemetry.onMethodCalled({
|
|
109
|
-
methodName: functionName,
|
|
110
|
-
durationMs: Date.now() - startTime,
|
|
111
|
-
success: false,
|
|
112
|
-
error: normalizedError,
|
|
113
|
-
argumentCount,
|
|
114
|
-
isPaginated: false,
|
|
115
|
-
});
|
|
93
|
+
catch (error) {
|
|
94
|
+
const normalizedError = normalizeError(error);
|
|
95
|
+
if (!isNested && telemetry?.onMethodCalled) {
|
|
96
|
+
const argumentCount = Object.keys(normalizedOptions).length;
|
|
97
|
+
telemetry.onMethodCalled({
|
|
98
|
+
methodName: functionName,
|
|
99
|
+
durationMs: Date.now() - startTime,
|
|
100
|
+
success: false,
|
|
101
|
+
error: normalizedError,
|
|
102
|
+
argumentCount,
|
|
103
|
+
isPaginated: false,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
throw normalizedError;
|
|
116
107
|
}
|
|
117
|
-
|
|
118
|
-
}
|
|
108
|
+
});
|
|
119
109
|
},
|
|
120
110
|
};
|
|
121
111
|
return namedFunctions[functionName];
|
|
@@ -172,81 +162,83 @@ export function createPaginatedFunction(coreFn, schema, telemetry, explicitFunct
|
|
|
172
162
|
// Create the main paginated function
|
|
173
163
|
const namedFunctions = {
|
|
174
164
|
[functionName]: function (options) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return result.value;
|
|
197
|
-
});
|
|
198
|
-
// Emit telemetry after first page completes
|
|
199
|
-
if (!isNested && telemetry?.onMethodCalled) {
|
|
200
|
-
firstPagePromise
|
|
201
|
-
.then(() => {
|
|
202
|
-
const argumentCount = Object.keys(normalizedOptions).filter((k) => k !== "_telemetry").length;
|
|
203
|
-
telemetry.onMethodCalled({
|
|
204
|
-
methodName: functionName,
|
|
205
|
-
durationMs: Date.now() - startTime,
|
|
206
|
-
success: true,
|
|
207
|
-
argumentCount,
|
|
208
|
-
isPaginated: true,
|
|
209
|
-
});
|
|
210
|
-
})
|
|
211
|
-
.catch((error) => {
|
|
212
|
-
const argumentCount = Object.keys(normalizedOptions).filter((k) => k !== "_telemetry").length;
|
|
213
|
-
telemetry.onMethodCalled({
|
|
214
|
-
methodName: functionName,
|
|
215
|
-
durationMs: Date.now() - startTime,
|
|
216
|
-
success: false,
|
|
217
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
218
|
-
argumentCount,
|
|
219
|
-
isPaginated: true,
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
// Add Symbol.asyncIterator to make the promise iterable
|
|
224
|
-
return Object.assign(firstPagePromise, {
|
|
225
|
-
[Symbol.asyncIterator]: async function* () {
|
|
226
|
-
// Yield the first page (await the promise)
|
|
227
|
-
yield await firstPagePromise;
|
|
228
|
-
// Yield the rest of the pages
|
|
229
|
-
for await (const page of iterator) {
|
|
230
|
-
yield page;
|
|
165
|
+
return runWithTelemetryContext(() => {
|
|
166
|
+
const startTime = Date.now();
|
|
167
|
+
// Convert undefined options to empty object
|
|
168
|
+
const normalizedOptions = (options ?? {});
|
|
169
|
+
const isNested = isTelemetryNested();
|
|
170
|
+
// Validate options if schema provided (validated fields take precedence)
|
|
171
|
+
const validatedOptions = {
|
|
172
|
+
...normalizedOptions,
|
|
173
|
+
...(validator ? validator(normalizedOptions) : normalizedOptions),
|
|
174
|
+
};
|
|
175
|
+
// Set default pageSize if not provided
|
|
176
|
+
const pageSize = validatedOptions.pageSize || 100;
|
|
177
|
+
const optimizedOptions = {
|
|
178
|
+
...validatedOptions,
|
|
179
|
+
pageSize,
|
|
180
|
+
};
|
|
181
|
+
const iterator = paginate(pageFunction, optimizedOptions);
|
|
182
|
+
// Get a promise for the first iteration
|
|
183
|
+
const firstPagePromise = iterator.next().then((result) => {
|
|
184
|
+
if (result.done) {
|
|
185
|
+
throw new Error("Paginate should always iterate at least once");
|
|
231
186
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
187
|
+
return result.value;
|
|
188
|
+
});
|
|
189
|
+
// Emit telemetry after first page completes
|
|
190
|
+
if (!isNested && telemetry?.onMethodCalled) {
|
|
191
|
+
firstPagePromise
|
|
192
|
+
.then(() => {
|
|
193
|
+
const argumentCount = Object.keys(normalizedOptions).length;
|
|
194
|
+
telemetry.onMethodCalled({
|
|
195
|
+
methodName: functionName,
|
|
196
|
+
durationMs: Date.now() - startTime,
|
|
197
|
+
success: true,
|
|
198
|
+
argumentCount,
|
|
199
|
+
isPaginated: true,
|
|
200
|
+
});
|
|
201
|
+
})
|
|
202
|
+
.catch((error) => {
|
|
203
|
+
const argumentCount = Object.keys(normalizedOptions).length;
|
|
204
|
+
telemetry.onMethodCalled({
|
|
205
|
+
methodName: functionName,
|
|
206
|
+
durationMs: Date.now() - startTime,
|
|
207
|
+
success: false,
|
|
208
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
209
|
+
argumentCount,
|
|
210
|
+
isPaginated: true,
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
// Add Symbol.asyncIterator to make the promise iterable
|
|
215
|
+
return Object.assign(firstPagePromise, {
|
|
216
|
+
[Symbol.asyncIterator]: async function* () {
|
|
217
|
+
// Yield the first page (await the promise)
|
|
218
|
+
yield await firstPagePromise;
|
|
219
|
+
// Yield the rest of the pages
|
|
220
|
+
for await (const page of iterator) {
|
|
221
|
+
yield page;
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
items: function () {
|
|
225
|
+
return {
|
|
226
|
+
[Symbol.asyncIterator]: async function* () {
|
|
227
|
+
// Yield the first page items
|
|
228
|
+
const firstPage = await firstPagePromise;
|
|
229
|
+
for (const item of firstPage.data) {
|
|
244
230
|
yield item;
|
|
245
231
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
232
|
+
// Yield items from the rest of the pages
|
|
233
|
+
for await (const page of iterator) {
|
|
234
|
+
for (const item of page.data) {
|
|
235
|
+
yield item;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
},
|
|
241
|
+
});
|
|
250
242
|
});
|
|
251
243
|
},
|
|
252
244
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
2
|
import { createFunction, createPaginatedFunction } from "./function-utils";
|
|
3
3
|
import { ZapierValidationError, ZapierUnknownError } from "../types/errors";
|
|
4
4
|
import { ALL_ITEMS, listTestItems } from "./pagination-utils.test";
|
|
5
|
+
import { isTelemetryNested } from "./telemetry-context";
|
|
5
6
|
describe("createFunction", () => {
|
|
6
7
|
it("should return result from core function", async () => {
|
|
7
8
|
const mockCoreFn = async (options) => {
|
|
@@ -108,3 +109,80 @@ describe("createPaginatedFunction", () => {
|
|
|
108
109
|
expect(items).toEqual(ALL_ITEMS.slice(0, 7));
|
|
109
110
|
});
|
|
110
111
|
});
|
|
112
|
+
describe("telemetry nesting via AsyncLocalStorage", () => {
|
|
113
|
+
it("emits telemetry for a top-level createFunction call", async () => {
|
|
114
|
+
const onMethodCalled = vi.fn();
|
|
115
|
+
const telemetry = { onMethodCalled };
|
|
116
|
+
async function myMethod(options) {
|
|
117
|
+
return options.value * 2;
|
|
118
|
+
}
|
|
119
|
+
const wrapped = createFunction(myMethod, undefined, telemetry);
|
|
120
|
+
await wrapped({ value: 5 });
|
|
121
|
+
expect(onMethodCalled).toHaveBeenCalledOnce();
|
|
122
|
+
expect(onMethodCalled).toHaveBeenCalledWith(expect.objectContaining({
|
|
123
|
+
methodName: "myMethod",
|
|
124
|
+
success: true,
|
|
125
|
+
isPaginated: false,
|
|
126
|
+
}));
|
|
127
|
+
});
|
|
128
|
+
it("emits telemetry for a top-level createPaginatedFunction call", async () => {
|
|
129
|
+
const onMethodCalled = vi.fn();
|
|
130
|
+
const wrapped = createPaginatedFunction(listTestItems, undefined, { onMethodCalled }, "topLevelList");
|
|
131
|
+
await wrapped({ pageSize: 3 });
|
|
132
|
+
expect(onMethodCalled).toHaveBeenCalledOnce();
|
|
133
|
+
expect(onMethodCalled).toHaveBeenCalledWith(expect.objectContaining({
|
|
134
|
+
methodName: "topLevelList",
|
|
135
|
+
success: true,
|
|
136
|
+
isPaginated: true,
|
|
137
|
+
}));
|
|
138
|
+
});
|
|
139
|
+
it("suppresses telemetry when createFunction calls another createFunction", async () => {
|
|
140
|
+
const outerTelemetry = vi.fn();
|
|
141
|
+
const innerTelemetry = vi.fn();
|
|
142
|
+
async function innerMethod(_options) {
|
|
143
|
+
return "inner";
|
|
144
|
+
}
|
|
145
|
+
const wrappedInner = createFunction(innerMethod, undefined, {
|
|
146
|
+
onMethodCalled: innerTelemetry,
|
|
147
|
+
});
|
|
148
|
+
async function outerMethod(_options) {
|
|
149
|
+
return wrappedInner({});
|
|
150
|
+
}
|
|
151
|
+
const wrappedOuter = createFunction(outerMethod, undefined, {
|
|
152
|
+
onMethodCalled: outerTelemetry,
|
|
153
|
+
});
|
|
154
|
+
await wrappedOuter({});
|
|
155
|
+
expect(outerTelemetry).toHaveBeenCalledOnce();
|
|
156
|
+
expect(innerTelemetry).not.toHaveBeenCalled();
|
|
157
|
+
});
|
|
158
|
+
it("suppresses telemetry for nested createPaginatedFunction calls", async () => {
|
|
159
|
+
const outerTelemetry = vi.fn();
|
|
160
|
+
const innerTelemetry = vi.fn();
|
|
161
|
+
const wrappedInner = createPaginatedFunction(listTestItems, undefined, { onMethodCalled: innerTelemetry }, "innerList");
|
|
162
|
+
async function outerMethod(_options) {
|
|
163
|
+
const result = await wrappedInner({ pageSize: 3 });
|
|
164
|
+
return result.data;
|
|
165
|
+
}
|
|
166
|
+
const wrappedOuter = createFunction(outerMethod, undefined, {
|
|
167
|
+
onMethodCalled: outerTelemetry,
|
|
168
|
+
});
|
|
169
|
+
await wrappedOuter({});
|
|
170
|
+
expect(outerTelemetry).toHaveBeenCalledOnce();
|
|
171
|
+
expect(innerTelemetry).not.toHaveBeenCalled();
|
|
172
|
+
});
|
|
173
|
+
it("context is restored after nested call completes", async () => {
|
|
174
|
+
expect(isTelemetryNested()).toBe(false);
|
|
175
|
+
async function inner(_options) {
|
|
176
|
+
return "inner";
|
|
177
|
+
}
|
|
178
|
+
const wrappedInner = createFunction(inner);
|
|
179
|
+
async function outer(_options) {
|
|
180
|
+
await wrappedInner({});
|
|
181
|
+
expect(isTelemetryNested()).toBe(false);
|
|
182
|
+
return "outer";
|
|
183
|
+
}
|
|
184
|
+
const wrappedOuter = createFunction(outer);
|
|
185
|
+
await wrappedOuter({});
|
|
186
|
+
expect(isTelemetryNested()).toBe(false);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry-context.d.ts","sourceRoot":"","sources":["../../src/utils/telemetry-context.ts"],"names":[],"mappings":"AAmBA,wBAAgB,iBAAiB,IAAI,OAAO,CAI3C;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAIzD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Intentional dynamic require — one-time environment detection for AsyncLocalStorage
|
|
2
|
+
// availability (e.g., unavailable in browsers). This is NOT lazy loading.
|
|
3
|
+
let telemetryStore = null;
|
|
4
|
+
try {
|
|
5
|
+
const mod = require("node:async_hooks");
|
|
6
|
+
telemetryStore = new mod.AsyncLocalStorage();
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
// In non-Node environments, telemetry is disabled entirely (isTelemetryNested
|
|
10
|
+
// always returns true) since we can't reliably deduplicate nested calls.
|
|
11
|
+
}
|
|
12
|
+
export function isTelemetryNested() {
|
|
13
|
+
if (!telemetryStore)
|
|
14
|
+
return true;
|
|
15
|
+
const store = telemetryStore.getStore();
|
|
16
|
+
return store !== undefined && store.depth > 0;
|
|
17
|
+
}
|
|
18
|
+
export function runWithTelemetryContext(fn) {
|
|
19
|
+
if (!telemetryStore)
|
|
20
|
+
return fn();
|
|
21
|
+
const currentDepth = telemetryStore.getStore()?.depth ?? -1;
|
|
22
|
+
return telemetryStore.run({ depth: currentDepth + 1 }, fn);
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry-context.test.d.ts","sourceRoot":"","sources":["../../src/utils/telemetry-context.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { isTelemetryNested, runWithTelemetryContext, } from "./telemetry-context";
|
|
3
|
+
describe("telemetry-context", () => {
|
|
4
|
+
describe("isTelemetryNested", () => {
|
|
5
|
+
it("returns false outside any context", () => {
|
|
6
|
+
expect(isTelemetryNested()).toBe(false);
|
|
7
|
+
});
|
|
8
|
+
it("returns false inside top-level runWithTelemetryContext (depth 0)", () => {
|
|
9
|
+
runWithTelemetryContext(() => {
|
|
10
|
+
expect(isTelemetryNested()).toBe(false);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
it("returns true inside nested runWithTelemetryContext (depth 1)", () => {
|
|
14
|
+
runWithTelemetryContext(() => {
|
|
15
|
+
runWithTelemetryContext(() => {
|
|
16
|
+
expect(isTelemetryNested()).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
it("returns true at deeper nesting levels", () => {
|
|
21
|
+
runWithTelemetryContext(() => {
|
|
22
|
+
runWithTelemetryContext(() => {
|
|
23
|
+
runWithTelemetryContext(() => {
|
|
24
|
+
expect(isTelemetryNested()).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it("restores context after exiting nested scope", () => {
|
|
30
|
+
runWithTelemetryContext(() => {
|
|
31
|
+
expect(isTelemetryNested()).toBe(false);
|
|
32
|
+
runWithTelemetryContext(() => {
|
|
33
|
+
expect(isTelemetryNested()).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
expect(isTelemetryNested()).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
expect(isTelemetryNested()).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe("async boundaries", () => {
|
|
41
|
+
it("works correctly across async/await boundaries", async () => {
|
|
42
|
+
await runWithTelemetryContext(async () => {
|
|
43
|
+
expect(isTelemetryNested()).toBe(false);
|
|
44
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
45
|
+
expect(isTelemetryNested()).toBe(false);
|
|
46
|
+
await runWithTelemetryContext(async () => {
|
|
47
|
+
expect(isTelemetryNested()).toBe(true);
|
|
48
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
49
|
+
expect(isTelemetryNested()).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
it("isolates parallel async calls from each other", async () => {
|
|
54
|
+
const results = [];
|
|
55
|
+
await runWithTelemetryContext(async () => {
|
|
56
|
+
const task1 = runWithTelemetryContext(async () => {
|
|
57
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
58
|
+
results.push({ id: "task1-inner", nested: isTelemetryNested() });
|
|
59
|
+
});
|
|
60
|
+
const task2 = (async () => {
|
|
61
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
62
|
+
results.push({ id: "task2-top", nested: isTelemetryNested() });
|
|
63
|
+
})();
|
|
64
|
+
await Promise.all([task1, task2]);
|
|
65
|
+
});
|
|
66
|
+
const task1Result = results.find((r) => r.id === "task1-inner");
|
|
67
|
+
const task2Result = results.find((r) => r.id === "task2-top");
|
|
68
|
+
expect(task1Result?.nested).toBe(true);
|
|
69
|
+
expect(task2Result?.nested).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("runWithTelemetryContext", () => {
|
|
73
|
+
it("returns the value from the wrapped function", () => {
|
|
74
|
+
const result = runWithTelemetryContext(() => 42);
|
|
75
|
+
expect(result).toBe(42);
|
|
76
|
+
});
|
|
77
|
+
it("returns async values from the wrapped function", async () => {
|
|
78
|
+
const result = await runWithTelemetryContext(async () => "hello");
|
|
79
|
+
expect(result).toBe("hello");
|
|
80
|
+
});
|
|
81
|
+
it("propagates errors from the wrapped function", () => {
|
|
82
|
+
expect(() => runWithTelemetryContext(() => {
|
|
83
|
+
throw new Error("test error");
|
|
84
|
+
})).toThrow("test error");
|
|
85
|
+
});
|
|
86
|
+
it("propagates async errors from the wrapped function", async () => {
|
|
87
|
+
await expect(runWithTelemetryContext(async () => {
|
|
88
|
+
throw new Error("async test error");
|
|
89
|
+
})).rejects.toThrow("async test error");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|