@zapier/zapier-sdk 0.13.6 → 0.13.8

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 (155) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/api/client.d.ts.map +1 -1
  3. package/dist/api/client.js +5 -5
  4. package/dist/api/client.test.d.ts +2 -0
  5. package/dist/api/client.test.d.ts.map +1 -0
  6. package/dist/api/client.test.js +80 -0
  7. package/dist/api/index.d.ts +1 -0
  8. package/dist/api/index.d.ts.map +1 -1
  9. package/dist/api/index.js +3 -1
  10. package/dist/api/schemas.d.ts +20 -20
  11. package/dist/api/types.d.ts +2 -0
  12. package/dist/api/types.d.ts.map +1 -1
  13. package/dist/auth.d.ts +3 -0
  14. package/dist/auth.d.ts.map +1 -1
  15. package/dist/auth.test.d.ts +2 -0
  16. package/dist/auth.test.d.ts.map +1 -0
  17. package/dist/auth.test.js +102 -0
  18. package/dist/constants.d.ts +4 -4
  19. package/dist/constants.d.ts.map +1 -1
  20. package/dist/constants.js +4 -4
  21. package/dist/index.cjs +89 -21
  22. package/dist/index.d.mts +21 -1
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2 -0
  26. package/dist/index.mjs +88 -22
  27. package/dist/plugins/api/index.d.ts.map +1 -1
  28. package/dist/plugins/api/index.js +4 -1
  29. package/dist/plugins/eventEmission/index.d.ts +2 -0
  30. package/dist/plugins/eventEmission/index.d.ts.map +1 -1
  31. package/dist/plugins/eventEmission/index.js +35 -9
  32. package/dist/plugins/eventEmission/index.test.js +100 -0
  33. package/dist/schemas/Action.d.ts +2 -2
  34. package/dist/schemas/Auth.d.ts +4 -4
  35. package/dist/schemas/Field.d.ts +10 -10
  36. package/dist/sdk.test.js +121 -1
  37. package/dist/types/sdk.d.ts +3 -0
  38. package/dist/types/sdk.d.ts.map +1 -1
  39. package/dist/utils/url-utils.d.ts +19 -0
  40. package/dist/utils/url-utils.d.ts.map +1 -0
  41. package/dist/utils/url-utils.js +62 -0
  42. package/dist/utils/url-utils.test.d.ts +2 -0
  43. package/dist/utils/url-utils.test.d.ts.map +1 -0
  44. package/dist/utils/url-utils.test.js +103 -0
  45. package/package.json +8 -3
  46. package/src/api/auth.ts +0 -28
  47. package/src/api/client.ts +0 -491
  48. package/src/api/debug.test.ts +0 -76
  49. package/src/api/debug.ts +0 -154
  50. package/src/api/index.ts +0 -90
  51. package/src/api/polling.test.ts +0 -405
  52. package/src/api/polling.ts +0 -253
  53. package/src/api/schemas.ts +0 -465
  54. package/src/api/types.ts +0 -152
  55. package/src/auth.ts +0 -72
  56. package/src/constants.ts +0 -16
  57. package/src/index.ts +0 -111
  58. package/src/plugins/api/index.ts +0 -43
  59. package/src/plugins/apps/index.ts +0 -203
  60. package/src/plugins/apps/schemas.ts +0 -64
  61. package/src/plugins/eventEmission/builders.ts +0 -115
  62. package/src/plugins/eventEmission/index.test.ts +0 -169
  63. package/src/plugins/eventEmission/index.ts +0 -294
  64. package/src/plugins/eventEmission/transport.test.ts +0 -214
  65. package/src/plugins/eventEmission/transport.ts +0 -135
  66. package/src/plugins/eventEmission/types.ts +0 -58
  67. package/src/plugins/eventEmission/utils.ts +0 -121
  68. package/src/plugins/fetch/index.ts +0 -83
  69. package/src/plugins/fetch/schemas.ts +0 -37
  70. package/src/plugins/findFirstAuthentication/index.test.ts +0 -209
  71. package/src/plugins/findFirstAuthentication/index.ts +0 -68
  72. package/src/plugins/findFirstAuthentication/schemas.ts +0 -47
  73. package/src/plugins/findUniqueAuthentication/index.test.ts +0 -197
  74. package/src/plugins/findUniqueAuthentication/index.ts +0 -77
  75. package/src/plugins/findUniqueAuthentication/schemas.ts +0 -49
  76. package/src/plugins/getAction/index.test.ts +0 -239
  77. package/src/plugins/getAction/index.ts +0 -75
  78. package/src/plugins/getAction/schemas.ts +0 -41
  79. package/src/plugins/getApp/index.test.ts +0 -181
  80. package/src/plugins/getApp/index.ts +0 -60
  81. package/src/plugins/getApp/schemas.ts +0 -33
  82. package/src/plugins/getAuthentication/index.test.ts +0 -294
  83. package/src/plugins/getAuthentication/index.ts +0 -95
  84. package/src/plugins/getAuthentication/schemas.ts +0 -38
  85. package/src/plugins/getProfile/index.ts +0 -60
  86. package/src/plugins/getProfile/schemas.ts +0 -24
  87. package/src/plugins/listActions/index.test.ts +0 -526
  88. package/src/plugins/listActions/index.ts +0 -132
  89. package/src/plugins/listActions/schemas.ts +0 -55
  90. package/src/plugins/listApps/index.test.ts +0 -378
  91. package/src/plugins/listApps/index.ts +0 -159
  92. package/src/plugins/listApps/schemas.ts +0 -41
  93. package/src/plugins/listAuthentications/index.test.ts +0 -739
  94. package/src/plugins/listAuthentications/index.ts +0 -152
  95. package/src/plugins/listAuthentications/schemas.ts +0 -77
  96. package/src/plugins/listInputFieldChoices/index.test.ts +0 -653
  97. package/src/plugins/listInputFieldChoices/index.ts +0 -173
  98. package/src/plugins/listInputFieldChoices/schemas.ts +0 -125
  99. package/src/plugins/listInputFields/index.test.ts +0 -439
  100. package/src/plugins/listInputFields/index.ts +0 -294
  101. package/src/plugins/listInputFields/schemas.ts +0 -68
  102. package/src/plugins/manifest/index.test.ts +0 -776
  103. package/src/plugins/manifest/index.ts +0 -461
  104. package/src/plugins/manifest/schemas.ts +0 -60
  105. package/src/plugins/registry/index.ts +0 -160
  106. package/src/plugins/request/index.test.ts +0 -333
  107. package/src/plugins/request/index.ts +0 -105
  108. package/src/plugins/request/schemas.ts +0 -69
  109. package/src/plugins/runAction/index.test.ts +0 -388
  110. package/src/plugins/runAction/index.ts +0 -215
  111. package/src/plugins/runAction/schemas.ts +0 -60
  112. package/src/resolvers/actionKey.ts +0 -37
  113. package/src/resolvers/actionType.ts +0 -34
  114. package/src/resolvers/appKey.ts +0 -7
  115. package/src/resolvers/authenticationId.ts +0 -54
  116. package/src/resolvers/index.ts +0 -11
  117. package/src/resolvers/inputFieldKey.ts +0 -70
  118. package/src/resolvers/inputs.ts +0 -69
  119. package/src/schemas/Action.ts +0 -52
  120. package/src/schemas/App.ts +0 -45
  121. package/src/schemas/Auth.ts +0 -59
  122. package/src/schemas/Field.ts +0 -169
  123. package/src/schemas/Run.ts +0 -40
  124. package/src/schemas/UserProfile.ts +0 -60
  125. package/src/sdk.test.ts +0 -212
  126. package/src/sdk.ts +0 -178
  127. package/src/types/domain.test.ts +0 -50
  128. package/src/types/domain.ts +0 -66
  129. package/src/types/errors.ts +0 -278
  130. package/src/types/events.ts +0 -43
  131. package/src/types/functions.ts +0 -28
  132. package/src/types/optional-zapier-sdk-cli-login.d.ts +0 -37
  133. package/src/types/plugin.ts +0 -125
  134. package/src/types/properties.ts +0 -80
  135. package/src/types/sdk.ts +0 -111
  136. package/src/types/telemetry-events.ts +0 -85
  137. package/src/utils/array-utils.test.ts +0 -131
  138. package/src/utils/array-utils.ts +0 -41
  139. package/src/utils/domain-utils.test.ts +0 -433
  140. package/src/utils/domain-utils.ts +0 -267
  141. package/src/utils/file-utils.test.ts +0 -73
  142. package/src/utils/file-utils.ts +0 -94
  143. package/src/utils/function-utils.test.ts +0 -141
  144. package/src/utils/function-utils.ts +0 -245
  145. package/src/utils/pagination-utils.test.ts +0 -620
  146. package/src/utils/pagination-utils.ts +0 -242
  147. package/src/utils/schema-utils.ts +0 -207
  148. package/src/utils/string-utils.test.ts +0 -45
  149. package/src/utils/string-utils.ts +0 -54
  150. package/src/utils/validation.test.ts +0 -51
  151. package/src/utils/validation.ts +0 -44
  152. package/tsconfig.build.json +0 -18
  153. package/tsconfig.json +0 -20
  154. package/tsconfig.tsbuildinfo +0 -1
  155. package/tsup.config.ts +0 -23
@@ -1,267 +0,0 @@
1
- /**
2
- * Domain utility functions for working with app identifiers and data normalization
3
- */
4
-
5
- import type { Action, Authentication, ImplementationMeta } from "../api/types";
6
- import type { ActionItem, AppItem, AuthenticationItem } from "../types/domain";
7
-
8
- /**
9
- * Splits a versioned key to extract the base key and version
10
- *
11
- * @param versionedKey - Versioned key in format "KeyName@version" (e.g., "SlackCLIAPI@1.21.1")
12
- * @returns Tuple of [baseKey, version] or [versionedKey, undefined] if no @ symbol
13
- *
14
- * @example
15
- * splitVersionedKey("SlackCLIAPI@1.21.1") // ["SlackCLIAPI", "1.21.1"]
16
- * splitVersionedKey("SomeAPI") // ["SomeAPI", undefined]
17
- */
18
- export function splitVersionedKey(
19
- versionedKey: string,
20
- ): [string, string | undefined] {
21
- const parts = versionedKey.split("@");
22
- if (parts.length >= 2) {
23
- const baseKey = parts[0];
24
- const version = parts.slice(1).join("@"); // Handle edge case of multiple @ symbols
25
- return [baseKey, version];
26
- }
27
- return [versionedKey, undefined];
28
- }
29
-
30
- /**
31
- * Converts a lightweight ImplementationMeta to a slimmed AppItem
32
- *
33
- * @param implementationMeta - Raw ImplementationMeta from API
34
- * @returns Normalized AppItem with essential fields only
35
- */
36
- export function normalizeImplementationMetaToAppItem(
37
- implementationMeta: ImplementationMeta,
38
- ): AppItem {
39
- const [selectedApi, appVersion] = splitVersionedKey(implementationMeta.id);
40
-
41
- const { id, name, ...restOfImplementationMeta } = implementationMeta;
42
-
43
- return {
44
- ...restOfImplementationMeta,
45
- title: name,
46
- key: selectedApi,
47
- implementation_id: id,
48
- version: appVersion,
49
- };
50
- }
51
-
52
- /**
53
- * Converts an API Authentication to an AuthenticationItem with normalized field names
54
- *
55
- * @param auth - Raw Authentication from API
56
- * @param options - Additional fields to include
57
- * @param options.app_key - selected_api from implementations endpoint
58
- * @param options.app_version - Version extracted from selected_api
59
- * @returns Normalized AuthenticationItem
60
- */
61
- export function normalizeAuthenticationItem(
62
- auth: Authentication,
63
- options: { app_key?: string; app_version?: string } = {},
64
- ): AuthenticationItem {
65
- // Extract app key and version from selected_api if not provided
66
- let appKey = options.app_key;
67
- let appVersion = options.app_version;
68
-
69
- if (auth.selected_api) {
70
- const [extractedAppKey, extractedVersion] = splitVersionedKey(
71
- auth.selected_api,
72
- );
73
-
74
- // Use extracted app key if not provided in options
75
- if (!appKey) {
76
- appKey = extractedAppKey;
77
- }
78
-
79
- // Use extracted version if not provided in options
80
- if (!appVersion) {
81
- appVersion = extractedVersion;
82
- }
83
- }
84
-
85
- const {
86
- selected_api: selectedApi,
87
- customuser_id: userId,
88
- ...restOfAuth
89
- } = auth;
90
-
91
- return {
92
- ...restOfAuth, // Pass through all other API response fields except selected_api
93
- implementation_id: selectedApi, // Rename selected_api to implementation_id
94
- title: auth.title || (auth as any).label || undefined, // Coerce title from label if missing
95
- is_expired: auth.is_stale, // Map is_stale to is_expired
96
- expired_at: auth.marked_stale_at, // Map marked_stale_at to expired_at
97
- app_key: appKey, // App key from implementations endpoint or parsed from selected_api
98
- app_version: appVersion, // Version from selected_api or provided
99
- user_id: userId, // Map customuser_id to user_id
100
- };
101
- }
102
-
103
- export function normalizeActionItem(action: Action): ActionItem {
104
- const { name, type, selected_api: selectedApi } = action;
105
-
106
- // Split version from selected_api
107
- const [appKey, appVersion] = selectedApi
108
- ? splitVersionedKey(selectedApi)
109
- : ["", undefined];
110
-
111
- return {
112
- // Only include the fields we want - explicitly list them
113
- id: action.id,
114
- key: action.key,
115
- description: action.description,
116
- is_important: action.is_important,
117
- is_hidden: action.is_hidden,
118
- // Transformed fields
119
- app_key: appKey,
120
- app_version: appVersion,
121
- action_type: type,
122
- title: name, // Map name to title
123
- type: "action",
124
- };
125
- }
126
-
127
- /**
128
- * Groups app keys by their type based on format patterns
129
- *
130
- * @param appKeys Array of app key strings to categorize
131
- * @returns Object with arrays of keys grouped by type: selectedApi (CLI APIs), slug (everything else)
132
- *
133
- * @example
134
- * groupVersionedAppKeysByType([
135
- * 'FormatterCLIAPI@1.0.0',
136
- * 'slack',
137
- * 'AnotherAPI'
138
- * ])
139
- * // Returns: {
140
- * // selectedApi: ['FormatterCLIAPI@1.0.0', 'AnotherAPI'],
141
- * // slug: ['slack']
142
- * // }
143
- */
144
- export function groupVersionedAppKeysByType(appKeys: string[]): {
145
- selectedApi: string[];
146
- slug: string[];
147
- } {
148
- const result = {
149
- selectedApi: [] as string[],
150
- slug: [] as string[],
151
- };
152
-
153
- // Use Sets to track seen keys for deduplication
154
- const seenSelectedApi = new Set<string>();
155
- const seenSlugs = new Set<string>();
156
-
157
- for (const key of appKeys) {
158
- const appLocator = toAppLocator(key);
159
-
160
- if (appLocator.slug) {
161
- // For slugs, we need to reconstruct the versioned slug
162
- const versionedSlug = appLocator.version
163
- ? `${appLocator.slug}@${appLocator.version}`
164
- : appLocator.slug;
165
- if (!seenSlugs.has(versionedSlug)) {
166
- seenSlugs.add(versionedSlug);
167
- result.slug.push(versionedSlug);
168
- }
169
- } else {
170
- // For implementation names (selectedApi)
171
- if (!seenSelectedApi.has(key)) {
172
- seenSelectedApi.add(key);
173
- result.selectedApi.push(key);
174
- }
175
- }
176
- }
177
-
178
- return result;
179
- }
180
-
181
- export function groupAppKeysByType(appKeys: string[]): {
182
- selectedApi: string[];
183
- slug: string[];
184
- } {
185
- const grouped = groupVersionedAppKeysByType(appKeys);
186
-
187
- // Extract base keys and dedupe by using Set
188
- return {
189
- selectedApi: [
190
- ...new Set(grouped.selectedApi.map((key) => key.split("@")[0])),
191
- ],
192
- slug: [...new Set(grouped.slug.map((key) => key.split("@")[0]))],
193
- };
194
- }
195
-
196
- export function isSlug(slug: string): boolean {
197
- return !!slug.match(/^[a-z0-9]+(?:-[a-z0-9]+)*$/);
198
- }
199
-
200
- export function isSnakeCasedSlug(slug: string): boolean {
201
- // Allow leading underscore for slugs starting with a number.
202
- if (slug.match(/^_[0-9]/)) {
203
- slug = slug.slice(1);
204
- }
205
-
206
- return !!slug.match(/^[a-z0-9]+(?:_[a-z0-9]+)*$/);
207
- }
208
-
209
- export function dashifySnakeCasedSlug(slug: string): string {
210
- // Only dashify if it's a valid snake_cased slug.
211
- if (!isSnakeCasedSlug(slug)) {
212
- return slug;
213
- }
214
-
215
- // Allow one leading underscore for slugs starting with a number.
216
- if (slug.startsWith("_")) {
217
- slug = slug.slice(1);
218
- }
219
-
220
- return slug.replace(/_/g, "-");
221
- }
222
-
223
- export interface AppLocator {
224
- lookupAppKey: string;
225
- slug?: string;
226
- implementationName?: string;
227
- version?: string;
228
- }
229
- export interface ResolvedAppLocator extends AppLocator {
230
- implementationName: string;
231
- }
232
-
233
- export function isUuid(appKey: string): boolean {
234
- return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
235
- appKey,
236
- );
237
- }
238
-
239
- export function toAppLocator(appKey: string): AppLocator {
240
- const [appKeyWithoutVersion, version] = splitVersionedKey(appKey);
241
- if (isUuid(appKeyWithoutVersion)) {
242
- throw new Error(
243
- `UUID app keys are not supported. Use app slug or implementation ID instead of: ${appKey}`,
244
- );
245
- }
246
- const slug = isSlug(appKeyWithoutVersion)
247
- ? appKeyWithoutVersion
248
- : isSnakeCasedSlug(appKeyWithoutVersion)
249
- ? dashifySnakeCasedSlug(appKeyWithoutVersion)
250
- : undefined;
251
- return {
252
- lookupAppKey: appKeyWithoutVersion,
253
- slug,
254
- implementationName: slug ? undefined : appKeyWithoutVersion,
255
- version,
256
- };
257
- }
258
-
259
- export function isResolvedAppLocator(
260
- appLocator: AppLocator,
261
- ): appLocator is ResolvedAppLocator {
262
- return !!appLocator.implementationName;
263
- }
264
-
265
- export function toImplementationId(appLocator: ResolvedAppLocator): string {
266
- return `${appLocator.implementationName}@${appLocator.version || "latest"}`;
267
- }
@@ -1,73 +0,0 @@
1
- import { describe, it, expect, vi } from "vitest";
2
- import { resolve, writeFile, readFile } from "./file-utils";
3
-
4
- // Mock the dynamic imports to simulate missing modules
5
- vi.mock("fs/promises", () => {
6
- throw new Error("Module not found");
7
- });
8
-
9
- vi.mock("path", () => {
10
- throw new Error("Module not found");
11
- });
12
-
13
- describe("file-utils", () => {
14
- describe("resolve", () => {
15
- it("should handle absolute paths", async () => {
16
- expect(await resolve("/absolute/path")).toBe("/absolute/path");
17
- });
18
-
19
- it("should handle relative paths with ./", async () => {
20
- expect(await resolve("./relative/path")).toBe("/relative/path");
21
- expect(await resolve("./relative/path", "/base")).toBe(
22
- "/base/relative/path",
23
- );
24
- expect(await resolve("./relative/path", "/base/")).toBe(
25
- "/base/relative/path",
26
- );
27
- });
28
-
29
- it("should handle parent directory paths with ../", async () => {
30
- expect(await resolve("../parent/path")).toBe("/parent/path");
31
- expect(await resolve("../../nested/parent/path")).toBe(
32
- "/nested/parent/path",
33
- );
34
- expect(await resolve("../parent/path", "/base")).toBe(
35
- "/base/parent/path",
36
- );
37
- });
38
-
39
- it("should handle paths without prefix", async () => {
40
- expect(await resolve("simple/path")).toBe("/simple/path");
41
- expect(await resolve("simple/path", "/base")).toBe("/base/simple/path");
42
- expect(await resolve("simple/path", "/base/")).toBe("/base/simple/path");
43
- });
44
- });
45
-
46
- describe("writeFile and readFile with in-memory filesystem", () => {
47
- it("should write and read files using in-memory filesystem when fs is not available", async () => {
48
- const filePath = "/test/file.txt";
49
- const content = "Hello, world!";
50
-
51
- await writeFile(filePath, content);
52
-
53
- const readContent = await readFile(filePath);
54
- expect(readContent).toBe(content);
55
- });
56
-
57
- it("should throw error when reading non-existent file", async () => {
58
- await expect(readFile("/non/existent/file.txt")).rejects.toThrow(
59
- "File not found: /non/existent/file.txt",
60
- );
61
- });
62
-
63
- it("should handle multiple files in in-memory filesystem", async () => {
64
- await writeFile("/file1.txt", "content1");
65
- await writeFile("/file2.txt", "content2");
66
- await writeFile("/dir/file3.txt", "content3");
67
-
68
- expect(await readFile("/file1.txt")).toBe("content1");
69
- expect(await readFile("/file2.txt")).toBe("content2");
70
- expect(await readFile("/dir/file3.txt")).toBe("content3");
71
- });
72
- });
73
- });
@@ -1,94 +0,0 @@
1
- // In-memory filesystem fallback for browser environments
2
- const inMemoryFiles: { [path: string]: string } = {};
3
-
4
- // Lazy-loaded modules
5
- let fsPromises: any = null;
6
- let pathModule: any = null;
7
-
8
- async function loadFsPromises() {
9
- if (fsPromises) return fsPromises;
10
-
11
- try {
12
- fsPromises = await import("fs/promises");
13
- return fsPromises;
14
- } catch {
15
- return null;
16
- }
17
- }
18
-
19
- async function loadPathModule() {
20
- if (pathModule) return pathModule;
21
-
22
- try {
23
- pathModule = await import("path");
24
- return pathModule;
25
- } catch {
26
- return null;
27
- }
28
- }
29
-
30
- export async function resolve(
31
- path: string,
32
- basePath: string = "/",
33
- ): Promise<string> {
34
- const pathModule = await loadPathModule();
35
-
36
- if (pathModule) {
37
- return pathModule.resolve(path);
38
- }
39
-
40
- // Simple fallback path resolution assuming root "/" location
41
- if (path.startsWith("/")) {
42
- return path;
43
- }
44
-
45
- if (path.startsWith("./")) {
46
- // Remove ./ prefix and join to base
47
- const cleanPath = path.slice(2);
48
- return basePath.endsWith("/")
49
- ? basePath + cleanPath
50
- : basePath + "/" + cleanPath;
51
- }
52
-
53
- if (path.startsWith("../")) {
54
- // Remove ../ prefix since we're already at root
55
- const cleanPath = path.replace(/^(\.\.\/)+/, "");
56
- return basePath.endsWith("/")
57
- ? basePath + cleanPath
58
- : basePath + "/" + cleanPath;
59
- }
60
-
61
- // No slash prefix, join to base
62
- return basePath.endsWith("/") ? basePath + path : basePath + "/" + path;
63
- }
64
-
65
- export async function writeFile(
66
- filePath: string,
67
- content: string,
68
- ): Promise<void> {
69
- const fs = await loadFsPromises();
70
-
71
- if (fs) {
72
- await fs.writeFile(filePath, content, "utf8");
73
- return;
74
- }
75
-
76
- // Fallback to in-memory filesystem
77
- inMemoryFiles[filePath] = content;
78
- }
79
-
80
- export async function readFile(filePath: string): Promise<string> {
81
- const fs = await loadFsPromises();
82
-
83
- if (fs) {
84
- return await fs.readFile(filePath, "utf8");
85
- }
86
-
87
- // Fallback to in-memory filesystem
88
- const content = inMemoryFiles[filePath];
89
- if (content !== undefined) {
90
- return content;
91
- }
92
-
93
- throw new Error(`File not found: ${filePath}`);
94
- }
@@ -1,141 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { createFunction, createPaginatedFunction } from "./function-utils";
3
- import { ZapierValidationError, ZapierUnknownError } from "../types/errors";
4
- import { ALL_ITEMS, listTestItems } from "./pagination-utils.test";
5
-
6
- describe("createFunction", () => {
7
- it("should return result from core function", async () => {
8
- const mockCoreFn = async (options: { name: string }) => {
9
- return `Hello, ${options.name}!`;
10
- };
11
-
12
- const wrappedFn = createFunction(mockCoreFn);
13
- const result = await wrappedFn({ name: "World" });
14
-
15
- expect(result).toBe("Hello, World!");
16
- });
17
- });
18
-
19
- describe.each([
20
- { name: "createFunction", createFn: createFunction },
21
- { name: "createPaginatedFunction", createFn: createPaginatedFunction },
22
- ])("$name", ({ createFn }) => {
23
- it("should preserve function name", async () => {
24
- async function testCoreFn(options: { value: number }) {
25
- return options.value * 2;
26
- }
27
-
28
- const wrappedFn = createFn(testCoreFn);
29
-
30
- expect(wrappedFn.name).toBe("testCoreFn");
31
- });
32
-
33
- it("should normalize and rethrow ZapierError instances", async () => {
34
- const mockError = new ZapierValidationError("Validation failed", {
35
- details: { field: "name" },
36
- });
37
- const mockCoreFn = async () => {
38
- throw mockError;
39
- };
40
-
41
- const wrappedFn = createFn(mockCoreFn);
42
-
43
- await expect(wrappedFn({})).rejects.toBe(mockError);
44
- });
45
-
46
- it("should normalize generic Error to ZapierUnknownError", async () => {
47
- const mockCoreFn = async () => {
48
- throw new Error("Something went wrong");
49
- };
50
-
51
- const wrappedFn = createFn(mockCoreFn);
52
-
53
- await expect(wrappedFn({})).rejects.toThrow(ZapierUnknownError);
54
- });
55
-
56
- it("should normalize non-Error values to ZapierUnknownError", async () => {
57
- const mockCoreFn = async () => {
58
- throw "String error";
59
- };
60
-
61
- const wrappedFn = createFn(mockCoreFn);
62
-
63
- await expect(wrappedFn({})).rejects.toThrow(ZapierUnknownError);
64
- });
65
- });
66
-
67
- describe("createPaginatedFunction", () => {
68
- it("should wrap non-array result in {data: [result]}", async () => {
69
- const mockCoreFn = async (options: { name: string }) => {
70
- return `Hello, ${options.name}!`;
71
- };
72
-
73
- const wrappedFn = createPaginatedFunction(mockCoreFn);
74
- const result = await wrappedFn({ name: "World" });
75
-
76
- expect(result).toEqual({ data: ["Hello, World!"] });
77
- });
78
-
79
- it("should wrap array result in {data: result}", async () => {
80
- const mockCoreFn = async (options: { count: number }) => {
81
- return Array.from({ length: options.count }, (_, i) => `Item ${i + 1}`);
82
- };
83
-
84
- const wrappedFn = createPaginatedFunction(mockCoreFn);
85
- const result = await wrappedFn({ count: 3 });
86
-
87
- expect(result).toEqual({ data: ["Item 1", "Item 2", "Item 3"] });
88
- });
89
-
90
- it("should return {data} object unchanged", async () => {
91
- const mockCoreFn = async (options: { items: string[] }) => {
92
- return { data: options.items };
93
- };
94
-
95
- const wrappedFn = createPaginatedFunction(mockCoreFn);
96
- const result = await wrappedFn({ items: ["A", "B", "C"] });
97
-
98
- expect(result).toEqual({ data: ["A", "B", "C"] });
99
- });
100
-
101
- it("should allow for await iteration over pages", async () => {
102
- const listItems = createPaginatedFunction(listTestItems);
103
-
104
- const pages = [];
105
- for await (const page of listItems({ pageSize: 3 })) {
106
- pages.push(page);
107
- if (pages.length >= 3) break;
108
- }
109
-
110
- // Should have collected 3 pages
111
- expect(pages).toHaveLength(3);
112
-
113
- // Check first page structure
114
- expect(pages[0]).toEqual({
115
- data: ALL_ITEMS.slice(0, 3),
116
- nextCursor: "cursor-3",
117
- });
118
-
119
- // Check second page
120
- expect(pages[1]).toEqual({
121
- data: ALL_ITEMS.slice(3, 6),
122
- nextCursor: "cursor-6",
123
- });
124
- });
125
-
126
- it("should allow for await iteration over individual items", async () => {
127
- const listItems = createPaginatedFunction(listTestItems);
128
-
129
- const items = [];
130
- for await (const item of listItems({ pageSize: 3 }).items()) {
131
- items.push(item);
132
- if (items.length >= 7) break; // Get first 7 items across pages
133
- }
134
-
135
- // Should have collected 7 individual items
136
- expect(items).toHaveLength(7);
137
-
138
- // Should be the first 7 items from ALL_ITEMS (flattened from pages)
139
- expect(items).toEqual(ALL_ITEMS.slice(0, 7));
140
- });
141
- });