@zapier/zapier-sdk 0.33.0 → 0.33.2

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 (128) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.cjs +2 -1
  3. package/dist/index.d.mts +9 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.mjs +2 -1
  7. package/dist/plugins/registry/index.d.ts.map +1 -1
  8. package/dist/plugins/registry/index.js +1 -0
  9. package/dist/types/sdk.d.ts +8 -0
  10. package/dist/types/sdk.d.ts.map +1 -1
  11. package/package.json +2 -2
  12. package/dist/api/auth.test.d.ts +0 -2
  13. package/dist/api/auth.test.d.ts.map +0 -1
  14. package/dist/api/auth.test.js +0 -220
  15. package/dist/api/client.test.d.ts +0 -2
  16. package/dist/api/client.test.d.ts.map +0 -1
  17. package/dist/api/client.test.js +0 -611
  18. package/dist/api/debug.test.d.ts +0 -2
  19. package/dist/api/debug.test.d.ts.map +0 -1
  20. package/dist/api/debug.test.js +0 -59
  21. package/dist/api/polling.test.d.ts +0 -2
  22. package/dist/api/polling.test.d.ts.map +0 -1
  23. package/dist/api/polling.test.js +0 -360
  24. package/dist/auth.test.d.ts +0 -2
  25. package/dist/auth.test.d.ts.map +0 -1
  26. package/dist/auth.test.js +0 -480
  27. package/dist/plugins/eventEmission/builders.test.d.ts +0 -2
  28. package/dist/plugins/eventEmission/builders.test.d.ts.map +0 -1
  29. package/dist/plugins/eventEmission/builders.test.js +0 -138
  30. package/dist/plugins/eventEmission/index.test.d.ts +0 -5
  31. package/dist/plugins/eventEmission/index.test.d.ts.map +0 -1
  32. package/dist/plugins/eventEmission/index.test.js +0 -712
  33. package/dist/plugins/eventEmission/transport.test.d.ts +0 -5
  34. package/dist/plugins/eventEmission/transport.test.d.ts.map +0 -1
  35. package/dist/plugins/eventEmission/transport.test.js +0 -164
  36. package/dist/plugins/fetch/index.test.d.ts +0 -2
  37. package/dist/plugins/fetch/index.test.d.ts.map +0 -1
  38. package/dist/plugins/fetch/index.test.js +0 -428
  39. package/dist/plugins/findFirstConnection/index.test.d.ts +0 -2
  40. package/dist/plugins/findFirstConnection/index.test.d.ts.map +0 -1
  41. package/dist/plugins/findFirstConnection/index.test.js +0 -177
  42. package/dist/plugins/findUniqueConnection/index.test.d.ts +0 -2
  43. package/dist/plugins/findUniqueConnection/index.test.d.ts.map +0 -1
  44. package/dist/plugins/findUniqueConnection/index.test.js +0 -159
  45. package/dist/plugins/getAction/index.test.d.ts +0 -2
  46. package/dist/plugins/getAction/index.test.d.ts.map +0 -1
  47. package/dist/plugins/getAction/index.test.js +0 -211
  48. package/dist/plugins/getApp/index.test.d.ts +0 -2
  49. package/dist/plugins/getApp/index.test.d.ts.map +0 -1
  50. package/dist/plugins/getApp/index.test.js +0 -157
  51. package/dist/plugins/getConnection/index.test.d.ts +0 -2
  52. package/dist/plugins/getConnection/index.test.d.ts.map +0 -1
  53. package/dist/plugins/getConnection/index.test.js +0 -124
  54. package/dist/plugins/getInputFieldsSchema/index.test.d.ts +0 -2
  55. package/dist/plugins/getInputFieldsSchema/index.test.d.ts.map +0 -1
  56. package/dist/plugins/getInputFieldsSchema/index.test.js +0 -291
  57. package/dist/plugins/listActions/index.test.d.ts +0 -2
  58. package/dist/plugins/listActions/index.test.d.ts.map +0 -1
  59. package/dist/plugins/listActions/index.test.js +0 -454
  60. package/dist/plugins/listApps/index.test.d.ts +0 -2
  61. package/dist/plugins/listApps/index.test.d.ts.map +0 -1
  62. package/dist/plugins/listApps/index.test.js +0 -124
  63. package/dist/plugins/listConnections/index.test.d.ts +0 -2
  64. package/dist/plugins/listConnections/index.test.d.ts.map +0 -1
  65. package/dist/plugins/listConnections/index.test.js +0 -920
  66. package/dist/plugins/listInputFieldChoices/index.test.d.ts +0 -2
  67. package/dist/plugins/listInputFieldChoices/index.test.d.ts.map +0 -1
  68. package/dist/plugins/listInputFieldChoices/index.test.js +0 -717
  69. package/dist/plugins/listInputFields/index.test.d.ts +0 -2
  70. package/dist/plugins/listInputFields/index.test.d.ts.map +0 -1
  71. package/dist/plugins/listInputFields/index.test.js +0 -359
  72. package/dist/plugins/manifest/index.test.d.ts +0 -2
  73. package/dist/plugins/manifest/index.test.d.ts.map +0 -1
  74. package/dist/plugins/manifest/index.test.js +0 -1179
  75. package/dist/plugins/request/index.test.d.ts +0 -2
  76. package/dist/plugins/request/index.test.d.ts.map +0 -1
  77. package/dist/plugins/request/index.test.js +0 -458
  78. package/dist/plugins/runAction/index.test.d.ts +0 -2
  79. package/dist/plugins/runAction/index.test.d.ts.map +0 -1
  80. package/dist/plugins/runAction/index.test.js +0 -350
  81. package/dist/resolvers/connectionId.test.d.ts +0 -2
  82. package/dist/resolvers/connectionId.test.d.ts.map +0 -1
  83. package/dist/resolvers/connectionId.test.js +0 -61
  84. package/dist/sdk.test.d.ts +0 -2
  85. package/dist/sdk.test.d.ts.map +0 -1
  86. package/dist/sdk.test.js +0 -260
  87. package/dist/types/domain.test.d.ts +0 -2
  88. package/dist/types/domain.test.d.ts.map +0 -1
  89. package/dist/types/domain.test.js +0 -39
  90. package/dist/utils/array-utils.test.d.ts +0 -2
  91. package/dist/utils/array-utils.test.d.ts.map +0 -1
  92. package/dist/utils/array-utils.test.js +0 -107
  93. package/dist/utils/batch-utils.test.d.ts +0 -2
  94. package/dist/utils/batch-utils.test.d.ts.map +0 -1
  95. package/dist/utils/batch-utils.test.js +0 -476
  96. package/dist/utils/domain-utils.test.d.ts +0 -2
  97. package/dist/utils/domain-utils.test.d.ts.map +0 -1
  98. package/dist/utils/domain-utils.test.js +0 -346
  99. package/dist/utils/file-utils.test.d.ts +0 -2
  100. package/dist/utils/file-utils.test.d.ts.map +0 -1
  101. package/dist/utils/file-utils.test.js +0 -51
  102. package/dist/utils/function-utils.test.d.ts +0 -2
  103. package/dist/utils/function-utils.test.d.ts.map +0 -1
  104. package/dist/utils/function-utils.test.js +0 -188
  105. package/dist/utils/id-utils.test.d.ts +0 -2
  106. package/dist/utils/id-utils.test.d.ts.map +0 -1
  107. package/dist/utils/id-utils.test.js +0 -22
  108. package/dist/utils/pagination-utils.test.d.ts +0 -17
  109. package/dist/utils/pagination-utils.test.d.ts.map +0 -1
  110. package/dist/utils/pagination-utils.test.js +0 -461
  111. package/dist/utils/retry-utils.test.d.ts +0 -2
  112. package/dist/utils/retry-utils.test.d.ts.map +0 -1
  113. package/dist/utils/retry-utils.test.js +0 -90
  114. package/dist/utils/string-utils.test.d.ts +0 -2
  115. package/dist/utils/string-utils.test.d.ts.map +0 -1
  116. package/dist/utils/string-utils.test.js +0 -59
  117. package/dist/utils/telemetry-context.test.d.ts +0 -2
  118. package/dist/utils/telemetry-context.test.d.ts.map +0 -1
  119. package/dist/utils/telemetry-context.test.js +0 -154
  120. package/dist/utils/telemetry-utils.test.d.ts +0 -2
  121. package/dist/utils/telemetry-utils.test.d.ts.map +0 -1
  122. package/dist/utils/telemetry-utils.test.js +0 -155
  123. package/dist/utils/url-utils.test.d.ts +0 -2
  124. package/dist/utils/url-utils.test.d.ts.map +0 -1
  125. package/dist/utils/url-utils.test.js +0 -103
  126. package/dist/utils/validation.test.d.ts +0 -2
  127. package/dist/utils/validation.test.d.ts.map +0 -1
  128. package/dist/utils/validation.test.js +0 -44
@@ -1,461 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { paginate, paginateMaxItems } from "./pagination-utils";
3
- // Test data set - simulating a large dataset with cursor-based pagination
4
- export const ALL_ITEMS = Array.from({ length: 25 }, (_, i) => ({
5
- id: i + 1,
6
- name: `Item ${i + 1}`,
7
- }));
8
- // Test page function that implements cursor-based pagination logic
9
- export async function listTestItems(options) {
10
- const { pageSize = 5, cursor, filter, maxItems } = options;
11
- // Cap pageSize at 10 to simulate real API limits
12
- let actualPageSize = Math.min(pageSize, 10);
13
- // If maxItems hint is provided, further limit the page size for optimization
14
- if (maxItems !== undefined && maxItems > 0) {
15
- actualPageSize = Math.min(actualPageSize, maxItems);
16
- }
17
- // Filter items if filter is provided
18
- let items = ALL_ITEMS;
19
- if (filter) {
20
- items = items.filter((item) => item.name.toLowerCase().includes(filter.toLowerCase()));
21
- }
22
- // Find starting position based on cursor
23
- let startIndex = 0;
24
- if (cursor) {
25
- // Cursor format: "cursor-{itemId}"
26
- const cursorId = parseInt(cursor.replace("cursor-", ""));
27
- startIndex = items.findIndex((item) => item.id > cursorId);
28
- if (startIndex === -1) {
29
- // No more items after cursor
30
- return { data: [], nextCursor: undefined };
31
- }
32
- }
33
- // Get page of items
34
- const pageItems = items.slice(startIndex, startIndex + actualPageSize);
35
- // Determine next cursor
36
- let nextCursor;
37
- if (startIndex + actualPageSize < items.length && pageItems.length > 0) {
38
- const lastItem = pageItems[pageItems.length - 1];
39
- nextCursor = `cursor-${lastItem.id}`;
40
- }
41
- return {
42
- data: pageItems,
43
- nextCursor,
44
- };
45
- }
46
- describe("paginate", () => {
47
- it("should paginate through multiple pages", async () => {
48
- const pageIterator = paginate(listTestItems, { pageSize: 3 });
49
- const pages = [];
50
- for await (const page of pageIterator) {
51
- pages.push(page);
52
- }
53
- // With 25 items and pageSize 3, should have 9 pages (8 full + 1 partial)
54
- expect(pages).toHaveLength(9);
55
- // Check first page
56
- expect(pages[0]).toEqual({
57
- data: ALL_ITEMS.slice(0, 3),
58
- nextCursor: "cursor-3",
59
- });
60
- // Check second page (cursor should be passed correctly)
61
- expect(pages[1]).toEqual({
62
- data: ALL_ITEMS.slice(3, 6),
63
- nextCursor: "cursor-6",
64
- });
65
- // Check last page (should have no nextCursor)
66
- expect(pages[8]).toEqual({
67
- data: ALL_ITEMS.slice(24, 25),
68
- nextCursor: undefined,
69
- });
70
- // Verify we got all 25 items across all pages
71
- const allItems = pages.flatMap((page) => page.data);
72
- expect(allItems).toHaveLength(25);
73
- });
74
- it("should cap pageSize at 10 when unbuffered", async () => {
75
- // Use a page size larger than 10, should get capped
76
- const pageIterator = paginateMaxItems(listTestItems, { pageSize: 50 });
77
- const pages = [];
78
- for await (const page of pageIterator) {
79
- pages.push(page);
80
- }
81
- // Should get 3 pages: 10+10+5=25 (pageSize capped at 10)
82
- expect(pages).toHaveLength(3);
83
- expect(pages[0].data).toHaveLength(10); // First page capped at 10
84
- expect(pages[1].data).toHaveLength(10); // Second page capped at 10
85
- expect(pages[2].data).toHaveLength(5); // Last page has remaining 5
86
- expect(pages[2].nextCursor).toBeUndefined(); // Last page has no nextCursor
87
- });
88
- it("should not cap pageSize when buffered", async () => {
89
- const pageIterator = paginate(listTestItems, { pageSize: 50 });
90
- for await (const page of pageIterator) {
91
- expect(page.data).toHaveLength(25);
92
- break;
93
- }
94
- });
95
- it("should not cap pageSize when buffered", async () => {
96
- const pageIterator = paginate(listTestItems, { pageSize: 15 });
97
- const pages = [];
98
- for await (const page of pageIterator) {
99
- pages.push(page);
100
- }
101
- expect(pages).toHaveLength(2);
102
- expect(pages[0].data).toHaveLength(15);
103
- expect(pages[1].data).toHaveLength(10);
104
- });
105
- it("should continue from pseudo cursor", async () => {
106
- const pageIterator = paginate(listTestItems, { pageSize: 15 });
107
- const pages = [];
108
- for await (const page of pageIterator) {
109
- pages.push(page);
110
- break;
111
- }
112
- const nextPageIterator = paginate(listTestItems, {
113
- pageSize: 15,
114
- cursor: pages[0].nextCursor,
115
- });
116
- for await (const page of nextPageIterator) {
117
- pages.push(page);
118
- }
119
- expect(pages).toHaveLength(2);
120
- expect(pages[0].data).toHaveLength(15);
121
- expect(pages[1].data).toHaveLength(10);
122
- });
123
- it("should handle filtered results", async () => {
124
- // Filter for items containing "1" (should match Item 1, Item 10-19, Item 21)
125
- const pageIterator = paginate(listTestItems, {
126
- pageSize: 4,
127
- filter: "1",
128
- });
129
- const pages = [];
130
- for await (const page of pageIterator) {
131
- pages.push(page);
132
- }
133
- // Should have multiple pages due to pageSize: 4
134
- expect(pages.length).toBeGreaterThan(1);
135
- // All items should contain "1"
136
- const allItems = pages.flatMap((page) => page.data);
137
- expect(allItems.every((item) => item.name.includes("1"))).toBe(true);
138
- // Should include items like Item 1, Item 10, Item 11, etc.
139
- expect(allItems).toContainEqual(ALL_ITEMS[0]); // Item 1
140
- expect(allItems).toContainEqual(ALL_ITEMS[9]); // Item 10
141
- expect(allItems).toContainEqual(ALL_ITEMS[20]); // Item 21
142
- });
143
- it("should handle empty results", async () => {
144
- // Filter that matches no items
145
- const pageIterator = paginate(listTestItems, {
146
- pageSize: 10,
147
- filter: "nonexistent",
148
- });
149
- const pages = [];
150
- for await (const page of pageIterator) {
151
- pages.push(page);
152
- }
153
- expect(pages).toHaveLength(1);
154
- expect(pages[0]).toEqual({
155
- data: [],
156
- nextCursor: undefined,
157
- });
158
- });
159
- it("should pass through errors from the page function", async () => {
160
- // Create a page function that throws an error
161
- const errorPageFunction = async () => {
162
- throw new Error("Page function failed");
163
- };
164
- const pageIterator = paginate(errorPageFunction, {});
165
- let reachedUnreachableCode = undefined;
166
- await expect(async () => {
167
- for await (const page of pageIterator) {
168
- // Should never reach here due to error
169
- reachedUnreachableCode = page;
170
- }
171
- }).rejects.toThrow("Page function failed");
172
- // Verify we never reached the unreachable code
173
- expect(reachedUnreachableCode).toBeUndefined();
174
- });
175
- it("should start pagination from provided cursor", async () => {
176
- // Test that pagination can start from a specific cursor position
177
- const pageIterator = paginate(listTestItems, {
178
- pageSize: 3,
179
- cursor: "cursor-5", // Start after item 5
180
- });
181
- const pages = [];
182
- for await (const page of pageIterator) {
183
- pages.push(page);
184
- if (pages.length >= 2)
185
- break; // Just test first 2 pages
186
- }
187
- expect(pages).toHaveLength(2);
188
- // First page should start after cursor-5 (from item 6)
189
- expect(pages[0].data[0]).toEqual(ALL_ITEMS[5]); // Item 6
190
- // Second page should continue properly
191
- expect(pages[1].data[0]).toEqual(ALL_ITEMS[8]); // Item 9
192
- });
193
- it("should allow breaking out of pagination early", async () => {
194
- // Test that users can break out of the iterator manually
195
- const pageIterator = paginate(listTestItems, { pageSize: 3 });
196
- const pages = [];
197
- for await (const page of pageIterator) {
198
- pages.push(page);
199
- // Break after getting 3 pages
200
- if (pages.length >= 3) {
201
- break;
202
- }
203
- }
204
- // Should have stopped at exactly 3 pages with 9 items (3+3+3=9)
205
- const allItems = pages.flatMap((page) => page.data);
206
- expect(allItems).toHaveLength(9);
207
- expect(pages).toHaveLength(3);
208
- // Should be first 9 items
209
- expect(allItems).toEqual(ALL_ITEMS.slice(0, 9));
210
- });
211
- describe("conditional types for optional options", () => {
212
- it("should work with function that takes no options", async () => {
213
- async function fetchWithNoOptions(_options) {
214
- return { data: [1, 2, 3] };
215
- }
216
- const pages = [];
217
- for await (const page of paginate(fetchWithNoOptions)) {
218
- pages.push(...page.data);
219
- }
220
- expect(pages).toEqual([1, 2, 3]);
221
- });
222
- it("should work with function that takes optional options", async () => {
223
- async function fetchWithOptionalOptions(options) {
224
- return { data: [`page-${options?.cursor || "1"}`] };
225
- }
226
- const pages = [];
227
- for await (const page of paginate(fetchWithOptionalOptions, {
228
- limit: 5,
229
- })) {
230
- pages.push(...page.data);
231
- }
232
- expect(pages).toEqual(["page-1"]);
233
- });
234
- it("should work with function that takes only cursor", async () => {
235
- async function fetchWithOnlyCursor(_options) {
236
- return { data: [true] };
237
- }
238
- const pages = [];
239
- for await (const page of paginate(fetchWithOnlyCursor, {
240
- cursor: "start",
241
- })) {
242
- pages.push(...page.data);
243
- }
244
- expect(pages).toEqual([true]);
245
- });
246
- it("should work when baseOptions is omitted entirely", async () => {
247
- async function fetchSimple(_options) {
248
- return { data: ["simple"] };
249
- }
250
- const pages = [];
251
- for await (const page of paginate(fetchSimple)) {
252
- pages.push(...page.data);
253
- }
254
- expect(pages).toEqual(["simple"]);
255
- });
256
- });
257
- describe("maxItems functionality", () => {
258
- it("should limit total items across pages", async () => {
259
- const items = [];
260
- for await (const page of paginate(listTestItems, {
261
- pageSize: 3,
262
- maxItems: 7,
263
- })) {
264
- items.push(...page.data);
265
- }
266
- expect(items).toHaveLength(7);
267
- expect(items).toEqual(ALL_ITEMS.slice(0, 7));
268
- });
269
- it("should truncate the final page when maxItems is exceeded", async () => {
270
- // Create a function that ignores maxItems hint to force truncation
271
- async function fetchIgnoringMaxItems(options) {
272
- return listTestItems({
273
- pageSize: options.pageSize,
274
- cursor: options.cursor,
275
- });
276
- }
277
- const pageData = [];
278
- for await (const page of paginate(fetchIgnoringMaxItems, {
279
- pageSize: 3,
280
- maxItems: 7,
281
- })) {
282
- pageData.push(page);
283
- }
284
- // Should have 3 pages: [3 items], [3 items], [1 item (truncated)]
285
- expect(pageData).toHaveLength(3);
286
- expect(pageData[0].data).toHaveLength(3);
287
- expect(pageData[1].data).toHaveLength(3);
288
- expect(pageData[2].data).toHaveLength(1); // Truncated page
289
- expect(pageData[2].nextCursor).toBeUndefined(); // Should clear cursor
290
- });
291
- it("should stop early when maxItems is exactly a page boundary", async () => {
292
- const items = [];
293
- for await (const page of paginate(listTestItems, {
294
- pageSize: 3,
295
- maxItems: 6,
296
- })) {
297
- items.push(...page.data);
298
- }
299
- expect(items).toHaveLength(6);
300
- expect(items).toEqual(ALL_ITEMS.slice(0, 6));
301
- });
302
- it("should handle maxItems of 0", async () => {
303
- const items = [];
304
- for await (const page of paginate(listTestItems, {
305
- pageSize: 3,
306
- maxItems: 0,
307
- })) {
308
- items.push(...page.data);
309
- }
310
- expect(items).toHaveLength(0);
311
- });
312
- it("should work normally when maxItems is larger than total items", async () => {
313
- const items = [];
314
- for await (const page of paginate(listTestItems, {
315
- pageSize: 3,
316
- maxItems: 100,
317
- })) {
318
- items.push(...page.data);
319
- }
320
- // Should get all items since limit is higher than total
321
- expect(items).toEqual(ALL_ITEMS);
322
- });
323
- it("should work with maxItems and other options combined", async () => {
324
- async function fetchWithLimit(options) {
325
- const start = parseInt(options.cursor || "0");
326
- let limit = options.limit;
327
- // Apply maxItems hint for optimization
328
- if (options.maxItems !== undefined && options.maxItems > 0) {
329
- limit = Math.min(limit, options.maxItems);
330
- }
331
- const items = ALL_ITEMS.slice(start, start + limit);
332
- const nextCursor = start + limit < ALL_ITEMS.length
333
- ? (start + limit).toString()
334
- : undefined;
335
- return { data: items, nextCursor };
336
- }
337
- const items = [];
338
- for await (const page of paginate(fetchWithLimit, {
339
- limit: 2,
340
- maxItems: 5,
341
- })) {
342
- items.push(...page.data);
343
- }
344
- expect(items).toHaveLength(5);
345
- expect(items).toEqual(ALL_ITEMS.slice(0, 5));
346
- });
347
- it("should preserve original page structure except for truncation", async () => {
348
- // Create a function that ignores maxItems hint to force truncation
349
- async function fetchIgnoringMaxItems(options) {
350
- const pageSize = options.pageSize || 3;
351
- const start = options.cursor
352
- ? parseInt(options.cursor.replace("cursor-", "")) + 1
353
- : 0;
354
- const items = ALL_ITEMS.slice(start, start + pageSize);
355
- const nextCursor = start + pageSize <= ALL_ITEMS.length
356
- ? `cursor-${ALL_ITEMS[start + pageSize - 1].id}`
357
- : undefined;
358
- return { data: items, nextCursor };
359
- }
360
- let pageCount = 0;
361
- for await (const page of paginate(fetchIgnoringMaxItems, {
362
- pageSize: 3,
363
- maxItems: 7,
364
- })) {
365
- pageCount++;
366
- // All pages should have the expected structure
367
- expect(page).toHaveProperty("data");
368
- expect(Array.isArray(page.data)).toBe(true);
369
- if (pageCount === 3) {
370
- // Final truncated page
371
- expect(page.data).toHaveLength(1);
372
- expect(page.nextCursor).toBeUndefined();
373
- }
374
- else {
375
- // Regular pages
376
- expect(page.data).toHaveLength(3);
377
- expect(typeof page.nextCursor).toBe("string");
378
- }
379
- }
380
- expect(pageCount).toBe(3);
381
- });
382
- it("should handle API that ignores pageSize and returns more items on first page", async () => {
383
- // Simulate an API that ignores pageSize and always returns 10 items per page
384
- async function apiIgnoresPageSize(options) {
385
- return listTestItems({
386
- cursor: options.cursor,
387
- pageSize: 10,
388
- });
389
- }
390
- const pages = [];
391
- // User requests pageSize: 3, but API returns 10 per page
392
- for await (const page of paginate(apiIgnoresPageSize, {
393
- pageSize: 3,
394
- })) {
395
- pages.push(page);
396
- // Only get first page to test the behavior
397
- break;
398
- }
399
- // Should get 1 page with only 3 items (paginate enforces pageSize limit)
400
- expect(pages).toHaveLength(1);
401
- expect(pages[0].data).toHaveLength(3);
402
- expect(pages[0].data).toEqual(ALL_ITEMS.slice(0, 3));
403
- // Cursor should still be present since there are more items available
404
- expect(pages[0].nextCursor).toBeDefined();
405
- });
406
- it("should truncate oversized API response to pageSize", async () => {
407
- // API always returns 10 items, but user wants pageSize: 3
408
- async function apiReturns10Items(options) {
409
- return listTestItems({
410
- cursor: options.cursor,
411
- pageSize: 10,
412
- });
413
- }
414
- const pages = [];
415
- for await (const page of paginate(apiReturns10Items, {
416
- pageSize: 3,
417
- })) {
418
- pages.push(page);
419
- }
420
- // Should truncate each 10-item API response to 3 items per page
421
- expect(pages).toHaveLength(9);
422
- expect(pages[0].data).toHaveLength(3);
423
- expect(pages[1].data).toHaveLength(3);
424
- expect(pages[8].data).toHaveLength(1);
425
- // Verify items are from different API calls
426
- expect(pages[0].data).toEqual(ALL_ITEMS.slice(0, 3)); // Items 1-3 from first API call
427
- expect(pages[1].data).toEqual(ALL_ITEMS.slice(3, 6)); // Items 11-13 from second API call
428
- expect(pages[8].data).toEqual(ALL_ITEMS.slice(24, 25)); // Items 21-23 from third API call
429
- // All pages should have nextCursor since API has more data
430
- expect(pages[0].nextCursor).toBe('["$$offset$$",3,null]');
431
- expect(pages[8].nextCursor).toBeUndefined();
432
- });
433
- it("should handle pageSize enforcement with maxItems limit", async () => {
434
- // API returns 10 items, user wants pageSize: 3, maxItems: 7
435
- async function apiReturns10Items(options) {
436
- return listTestItems({
437
- cursor: options.cursor,
438
- pageSize: 10,
439
- });
440
- }
441
- const pages = [];
442
- for await (const page of paginate(apiReturns10Items, {
443
- pageSize: 3,
444
- maxItems: 7,
445
- })) {
446
- pages.push(page);
447
- }
448
- // Should get: [3, 3, 1] items (total 7, respecting maxItems)
449
- expect(pages).toHaveLength(3);
450
- expect(pages[0].data).toHaveLength(3);
451
- expect(pages[1].data).toHaveLength(3);
452
- expect(pages[2].data).toHaveLength(1); // Truncated to respect maxItems
453
- // Last page should have no nextCursor (maxItems reached)
454
- expect(pages[2].nextCursor).toBeUndefined();
455
- // Verify total items
456
- const allItems = pages.flatMap((p) => p.data);
457
- expect(allItems).toHaveLength(7);
458
- expect(allItems).toEqual(ALL_ITEMS.slice(0, 7));
459
- });
460
- });
461
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=retry-utils.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"retry-utils.test.d.ts","sourceRoot":"","sources":["../../src/utils/retry-utils.test.ts"],"names":[],"mappings":""}
@@ -1,90 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { calculateErrorBackoffMs, MAX_CONSECUTIVE_ERRORS, BASE_ERROR_BACKOFF_MS, JITTER_FACTOR, } from "./retry-utils";
3
- describe("retry-utils", () => {
4
- describe("calculateErrorBackoffMs", () => {
5
- beforeEach(() => {
6
- // Seed random for consistent testing
7
- vi.spyOn(Math, "random");
8
- });
9
- afterEach(() => {
10
- vi.restoreAllMocks();
11
- });
12
- it("should return base interval with jitter when no errors", () => {
13
- vi.spyOn(Math, "random").mockReturnValue(0.5); // Middle of jitter range
14
- const baseInterval = 1000;
15
- const waitTime = calculateErrorBackoffMs(baseInterval, 0);
16
- // With 0.5 random and JITTER_FACTOR 0.5:
17
- // jitter = 0.5 * 0.5 * 1000 = 250ms
18
- // errorBackoff = 0 (no errors)
19
- // total = 1000 + 250 + 0 = 1250ms
20
- expect(waitTime).toBe(1250);
21
- });
22
- it("should add error backoff for consecutive errors", () => {
23
- vi.spyOn(Math, "random").mockReturnValue(0); // No jitter for simpler math
24
- const baseInterval = 1000;
25
- const errorCount = 2;
26
- const waitTime = calculateErrorBackoffMs(baseInterval, errorCount);
27
- // jitter = 0 (random is 0)
28
- // errorBackoff = BASE_ERROR_BACKOFF_MS * (errorCount / 2) = 1000 * (2/2) = 1000ms
29
- // total = 1000 + 0 + 1000 = 2000ms
30
- expect(waitTime).toBe(2000);
31
- });
32
- it("should cap error backoff at 2x base interval", () => {
33
- vi.spyOn(Math, "random").mockReturnValue(0); // No jitter
34
- const baseInterval = 1000;
35
- const errorCount = 10; // Large error count
36
- const waitTime = calculateErrorBackoffMs(baseInterval, errorCount);
37
- // errorBackoff would be 1000 * (10/2) = 5000ms
38
- // but it's capped at 2x base = 2000ms
39
- // total = 1000 + 0 + 2000 = 3000ms
40
- expect(waitTime).toBe(3000);
41
- });
42
- it("should add random jitter to prevent thundering herd", () => {
43
- const baseInterval = 1000;
44
- const results = new Set();
45
- // Run multiple times with real random to ensure jitter varies
46
- vi.spyOn(Math, "random").mockRestore(); // Use real random
47
- for (let i = 0; i < 10; i++) {
48
- const waitTime = calculateErrorBackoffMs(baseInterval, 0);
49
- results.add(waitTime);
50
- }
51
- // Should have multiple different values due to jitter
52
- expect(results.size).toBeGreaterThan(1);
53
- // All values should be in expected range
54
- // baseInterval (1000) + jitter (0 to JITTER_FACTOR * baseInterval)
55
- // = 1000 to 1500ms
56
- for (const result of results) {
57
- expect(result).toBeGreaterThanOrEqual(1000);
58
- expect(result).toBeLessThanOrEqual(1500);
59
- }
60
- });
61
- it("should combine jitter and error backoff", () => {
62
- vi.spyOn(Math, "random").mockReturnValue(0.5);
63
- const baseInterval = 1000;
64
- const errorCount = 2;
65
- const waitTime = calculateErrorBackoffMs(baseInterval, errorCount);
66
- // jitter = 0.5 * 0.5 * 1000 = 250ms
67
- // errorBackoff = 1000 * (2/2) = 1000ms
68
- // total = 1000 + 250 + 1000 = 2250ms
69
- expect(waitTime).toBe(2250);
70
- });
71
- it("should return integer wait time", () => {
72
- vi.spyOn(Math, "random").mockReturnValue(0.333);
73
- const baseInterval = 1000;
74
- const waitTime = calculateErrorBackoffMs(baseInterval, 1);
75
- // Result should be floored integer
76
- expect(Number.isInteger(waitTime)).toBe(true);
77
- });
78
- });
79
- describe("constants", () => {
80
- it("should export MAX_CONSECUTIVE_ERRORS", () => {
81
- expect(MAX_CONSECUTIVE_ERRORS).toBe(3);
82
- });
83
- it("should export BASE_ERROR_BACKOFF_MS", () => {
84
- expect(BASE_ERROR_BACKOFF_MS).toBe(1000);
85
- });
86
- it("should export JITTER_FACTOR", () => {
87
- expect(JITTER_FACTOR).toBe(0.5);
88
- });
89
- });
90
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=string-utils.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"string-utils.test.d.ts","sourceRoot":"","sources":["../../src/utils/string-utils.test.ts"],"names":[],"mappings":""}
@@ -1,59 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { toTitleCase, stripPageSuffix } from "./string-utils";
3
- describe("toTitleCase", () => {
4
- it("converts snake_case to title case", () => {
5
- expect(toTitleCase("first_name")).toBe("First Name");
6
- expect(toTitleCase("sender_settings")).toBe("Sender Settings");
7
- expect(toTitleCase("api_key_config")).toBe("Api Key Config");
8
- });
9
- it("converts camelCase to title case", () => {
10
- expect(toTitleCase("firstName")).toBe("First Name");
11
- expect(toTitleCase("senderSettings")).toBe("Sender Settings");
12
- expect(toTitleCase("apiKeyConfig")).toBe("Api Key Config");
13
- });
14
- it("converts kebab-case to title case", () => {
15
- expect(toTitleCase("first-name")).toBe("First Name");
16
- expect(toTitleCase("sender-settings")).toBe("Sender Settings");
17
- expect(toTitleCase("api-key-config")).toBe("Api Key Config");
18
- });
19
- it("handles mixed formats", () => {
20
- expect(toTitleCase("first_name-value")).toBe("First Name Value");
21
- expect(toTitleCase("sender_settings-config")).toBe("Sender Settings Config");
22
- });
23
- it("handles single words", () => {
24
- expect(toTitleCase("name")).toBe("Name");
25
- expect(toTitleCase("settings")).toBe("Settings");
26
- });
27
- it("handles multiple spaces and trims", () => {
28
- expect(toTitleCase(" first name ")).toBe("First Name");
29
- expect(toTitleCase("sender__settings")).toBe("Sender Settings");
30
- });
31
- it("handles empty and edge cases", () => {
32
- expect(toTitleCase("")).toBe("");
33
- expect(toTitleCase("a")).toBe("A");
34
- expect(toTitleCase("_")).toBe("");
35
- });
36
- });
37
- describe("stripPageSuffix", () => {
38
- it("strips Page suffix from function names", () => {
39
- expect(stripPageSuffix("listAppsPage")).toBe("listApps");
40
- expect(stripPageSuffix("listActionsPage")).toBe("listActions");
41
- expect(stripPageSuffix("runActionPage")).toBe("runAction");
42
- expect(stripPageSuffix("listAuthenticationsPage")).toBe("listAuthentications");
43
- });
44
- it("leaves names without Page suffix unchanged", () => {
45
- expect(stripPageSuffix("listApps")).toBe("listApps");
46
- expect(stripPageSuffix("getProfile")).toBe("getProfile");
47
- expect(stripPageSuffix("request")).toBe("request");
48
- });
49
- it("only strips Page from the end", () => {
50
- expect(stripPageSuffix("myPageFunction")).toBe("myPageFunction");
51
- expect(stripPageSuffix("PageList")).toBe("PageList");
52
- expect(stripPageSuffix("getPageData")).toBe("getPageData");
53
- });
54
- it("handles edge cases", () => {
55
- expect(stripPageSuffix("Page")).toBe("");
56
- expect(stripPageSuffix("")).toBe("");
57
- expect(stripPageSuffix("p")).toBe("p");
58
- });
59
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=telemetry-context.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"telemetry-context.test.d.ts","sourceRoot":"","sources":["../../src/utils/telemetry-context.test.ts"],"names":[],"mappings":""}