@zapier/zapier-sdk 0.13.6 → 0.13.7

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 (114) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.mjs +1 -1
  4. package/package.json +7 -2
  5. package/src/api/auth.ts +0 -28
  6. package/src/api/client.ts +0 -491
  7. package/src/api/debug.test.ts +0 -76
  8. package/src/api/debug.ts +0 -154
  9. package/src/api/index.ts +0 -90
  10. package/src/api/polling.test.ts +0 -405
  11. package/src/api/polling.ts +0 -253
  12. package/src/api/schemas.ts +0 -465
  13. package/src/api/types.ts +0 -152
  14. package/src/auth.ts +0 -72
  15. package/src/constants.ts +0 -16
  16. package/src/index.ts +0 -111
  17. package/src/plugins/api/index.ts +0 -43
  18. package/src/plugins/apps/index.ts +0 -203
  19. package/src/plugins/apps/schemas.ts +0 -64
  20. package/src/plugins/eventEmission/builders.ts +0 -115
  21. package/src/plugins/eventEmission/index.test.ts +0 -169
  22. package/src/plugins/eventEmission/index.ts +0 -294
  23. package/src/plugins/eventEmission/transport.test.ts +0 -214
  24. package/src/plugins/eventEmission/transport.ts +0 -135
  25. package/src/plugins/eventEmission/types.ts +0 -58
  26. package/src/plugins/eventEmission/utils.ts +0 -121
  27. package/src/plugins/fetch/index.ts +0 -83
  28. package/src/plugins/fetch/schemas.ts +0 -37
  29. package/src/plugins/findFirstAuthentication/index.test.ts +0 -209
  30. package/src/plugins/findFirstAuthentication/index.ts +0 -68
  31. package/src/plugins/findFirstAuthentication/schemas.ts +0 -47
  32. package/src/plugins/findUniqueAuthentication/index.test.ts +0 -197
  33. package/src/plugins/findUniqueAuthentication/index.ts +0 -77
  34. package/src/plugins/findUniqueAuthentication/schemas.ts +0 -49
  35. package/src/plugins/getAction/index.test.ts +0 -239
  36. package/src/plugins/getAction/index.ts +0 -75
  37. package/src/plugins/getAction/schemas.ts +0 -41
  38. package/src/plugins/getApp/index.test.ts +0 -181
  39. package/src/plugins/getApp/index.ts +0 -60
  40. package/src/plugins/getApp/schemas.ts +0 -33
  41. package/src/plugins/getAuthentication/index.test.ts +0 -294
  42. package/src/plugins/getAuthentication/index.ts +0 -95
  43. package/src/plugins/getAuthentication/schemas.ts +0 -38
  44. package/src/plugins/getProfile/index.ts +0 -60
  45. package/src/plugins/getProfile/schemas.ts +0 -24
  46. package/src/plugins/listActions/index.test.ts +0 -526
  47. package/src/plugins/listActions/index.ts +0 -132
  48. package/src/plugins/listActions/schemas.ts +0 -55
  49. package/src/plugins/listApps/index.test.ts +0 -378
  50. package/src/plugins/listApps/index.ts +0 -159
  51. package/src/plugins/listApps/schemas.ts +0 -41
  52. package/src/plugins/listAuthentications/index.test.ts +0 -739
  53. package/src/plugins/listAuthentications/index.ts +0 -152
  54. package/src/plugins/listAuthentications/schemas.ts +0 -77
  55. package/src/plugins/listInputFieldChoices/index.test.ts +0 -653
  56. package/src/plugins/listInputFieldChoices/index.ts +0 -173
  57. package/src/plugins/listInputFieldChoices/schemas.ts +0 -125
  58. package/src/plugins/listInputFields/index.test.ts +0 -439
  59. package/src/plugins/listInputFields/index.ts +0 -294
  60. package/src/plugins/listInputFields/schemas.ts +0 -68
  61. package/src/plugins/manifest/index.test.ts +0 -776
  62. package/src/plugins/manifest/index.ts +0 -461
  63. package/src/plugins/manifest/schemas.ts +0 -60
  64. package/src/plugins/registry/index.ts +0 -160
  65. package/src/plugins/request/index.test.ts +0 -333
  66. package/src/plugins/request/index.ts +0 -105
  67. package/src/plugins/request/schemas.ts +0 -69
  68. package/src/plugins/runAction/index.test.ts +0 -388
  69. package/src/plugins/runAction/index.ts +0 -215
  70. package/src/plugins/runAction/schemas.ts +0 -60
  71. package/src/resolvers/actionKey.ts +0 -37
  72. package/src/resolvers/actionType.ts +0 -34
  73. package/src/resolvers/appKey.ts +0 -7
  74. package/src/resolvers/authenticationId.ts +0 -54
  75. package/src/resolvers/index.ts +0 -11
  76. package/src/resolvers/inputFieldKey.ts +0 -70
  77. package/src/resolvers/inputs.ts +0 -69
  78. package/src/schemas/Action.ts +0 -52
  79. package/src/schemas/App.ts +0 -45
  80. package/src/schemas/Auth.ts +0 -59
  81. package/src/schemas/Field.ts +0 -169
  82. package/src/schemas/Run.ts +0 -40
  83. package/src/schemas/UserProfile.ts +0 -60
  84. package/src/sdk.test.ts +0 -212
  85. package/src/sdk.ts +0 -178
  86. package/src/types/domain.test.ts +0 -50
  87. package/src/types/domain.ts +0 -66
  88. package/src/types/errors.ts +0 -278
  89. package/src/types/events.ts +0 -43
  90. package/src/types/functions.ts +0 -28
  91. package/src/types/optional-zapier-sdk-cli-login.d.ts +0 -37
  92. package/src/types/plugin.ts +0 -125
  93. package/src/types/properties.ts +0 -80
  94. package/src/types/sdk.ts +0 -111
  95. package/src/types/telemetry-events.ts +0 -85
  96. package/src/utils/array-utils.test.ts +0 -131
  97. package/src/utils/array-utils.ts +0 -41
  98. package/src/utils/domain-utils.test.ts +0 -433
  99. package/src/utils/domain-utils.ts +0 -267
  100. package/src/utils/file-utils.test.ts +0 -73
  101. package/src/utils/file-utils.ts +0 -94
  102. package/src/utils/function-utils.test.ts +0 -141
  103. package/src/utils/function-utils.ts +0 -245
  104. package/src/utils/pagination-utils.test.ts +0 -620
  105. package/src/utils/pagination-utils.ts +0 -242
  106. package/src/utils/schema-utils.ts +0 -207
  107. package/src/utils/string-utils.test.ts +0 -45
  108. package/src/utils/string-utils.ts +0 -54
  109. package/src/utils/validation.test.ts +0 -51
  110. package/src/utils/validation.ts +0 -44
  111. package/tsconfig.build.json +0 -18
  112. package/tsconfig.json +0 -20
  113. package/tsconfig.tsbuildinfo +0 -1
  114. package/tsup.config.ts +0 -23
@@ -1,242 +0,0 @@
1
- const offsetCursorMarker = "$$offset$$";
2
-
3
- type TPageOptions<TOptions> = TOptions extends undefined
4
- ? { cursor?: string; maxItems?: number; pageSize?: number }
5
- : TOptions & { cursor?: string; maxItems?: number; pageSize?: number };
6
-
7
- function splitOffsetCursor(cursor?: string): [number, string | undefined] {
8
- if (!cursor) {
9
- return [0, cursor];
10
- }
11
-
12
- try {
13
- const parsedCursor = JSON.parse(cursor);
14
- if (!Array.isArray(parsedCursor)) {
15
- return [0, cursor];
16
- }
17
-
18
- const [marker, offset, currentCursor] = parsedCursor;
19
-
20
- if (marker !== offsetCursorMarker) {
21
- return [0, cursor];
22
- }
23
-
24
- if (typeof offset !== "number") {
25
- return [0, cursor];
26
- }
27
-
28
- return [offset, currentCursor];
29
- } catch {
30
- return [0, cursor];
31
- }
32
- }
33
-
34
- function createOffsetCursor(
35
- offset: number,
36
- currentCursor: string | undefined,
37
- ): string {
38
- return JSON.stringify([offsetCursorMarker, offset, currentCursor]);
39
- }
40
-
41
- export function createPrefixedCursor(
42
- prefix: string,
43
- cursor: string | undefined,
44
- ): string {
45
- if (!cursor) {
46
- return `${prefix}::`;
47
- }
48
- return `${prefix}::${cursor}`;
49
- }
50
-
51
- export function splitPrefixedCursor(
52
- cursor: string | undefined,
53
- prefixes?: string[],
54
- ): [string | undefined, string | undefined] {
55
- if (!cursor) {
56
- return [undefined, undefined];
57
- }
58
-
59
- const [prefix, ...rest] = cursor.split("::");
60
-
61
- if (prefixes && !prefixes.includes(prefix)) {
62
- return [undefined, cursor];
63
- }
64
-
65
- cursor = rest.join("::");
66
-
67
- if (!cursor) {
68
- return [prefix, undefined];
69
- }
70
-
71
- return [prefix, cursor];
72
- }
73
-
74
- /**
75
- * Utility for paginating through API endpoints that return cursor-based pages
76
- *
77
- * @param pageFunction - Function that fetches a single page with {data, nextCursor} structure
78
- * @param pageOptions - Options to pass to the page function (cursor will be managed automatically)
79
- * @returns Async iterator that yields pages
80
- */
81
- export async function* paginateMaxItems<
82
- TOptions,
83
- TPage extends { data: any[]; nextCursor?: string },
84
- >(
85
- pageFunction: (
86
- options: TOptions & {
87
- cursor?: string;
88
- maxItems?: number;
89
- pageSize?: number;
90
- },
91
- ) => Promise<TPage>,
92
- pageOptions?: TPageOptions<TOptions>,
93
- ): AsyncIterableIterator<TPage> {
94
- let cursor = pageOptions?.cursor;
95
- let totalItemsYielded = 0;
96
- const maxItems = pageOptions?.maxItems;
97
- const pageSize = pageOptions?.pageSize;
98
-
99
- do {
100
- const options = {
101
- ...(pageOptions || {}),
102
- cursor,
103
- pageSize:
104
- maxItems !== undefined && pageSize !== undefined
105
- ? Math.min(pageSize, maxItems)
106
- : pageSize,
107
- } as TOptions & { cursor?: string; maxItems?: number; pageSize?: number };
108
-
109
- const page = await pageFunction(options);
110
-
111
- if (maxItems !== undefined) {
112
- const remainingItems = maxItems - totalItemsYielded;
113
- if (page.data.length >= remainingItems) {
114
- const yieldedPage = {
115
- ...page,
116
- data: page.data.slice(0, remainingItems),
117
- nextCursor: undefined,
118
- };
119
- yield yieldedPage;
120
- break;
121
- }
122
- }
123
-
124
- yield page;
125
- totalItemsYielded += page.data.length;
126
-
127
- cursor = page.nextCursor;
128
- } while (cursor);
129
- }
130
-
131
- export async function* paginateBuffered<
132
- TOptions,
133
- TPage extends { data: any[]; nextCursor?: string },
134
- >(
135
- pageFunction: (
136
- options: TOptions & {
137
- cursor?: string;
138
- maxItems?: number;
139
- pageSize?: number;
140
- },
141
- ) => Promise<TPage>,
142
- pageOptions?: TPageOptions<TOptions>,
143
- ): AsyncIterableIterator<TPage> {
144
- const pageSize = pageOptions?.pageSize;
145
- const [cursorOffset, currentCursor] = splitOffsetCursor(pageOptions?.cursor);
146
- const options = {
147
- ...(pageOptions || {}),
148
- cursor: currentCursor,
149
- } as TPageOptions<TOptions>;
150
- const iterator = paginateMaxItems(pageFunction, options);
151
- let bufferedPages: TPage[] = [];
152
- let isFirstPage = true;
153
- let cursor: string | undefined;
154
- for await (let page of iterator) {
155
- if (isFirstPage) {
156
- isFirstPage = false;
157
- if (cursorOffset) {
158
- page = {
159
- ...page,
160
- data: page.data.slice(cursorOffset),
161
- };
162
- }
163
- }
164
- if (!pageSize) {
165
- yield page;
166
- cursor = page.nextCursor;
167
- continue;
168
- }
169
-
170
- const bufferedLength = bufferedPages.reduce(
171
- (acc, page) => acc + page.data.length,
172
- 0,
173
- );
174
-
175
- // If we don't have enough to fill a page, buffer this page.
176
- if (bufferedLength + page.data.length < pageSize) {
177
- bufferedPages.push(page);
178
- cursor = page.nextCursor;
179
- continue;
180
- }
181
-
182
- // Let's yield a page from our buffered pages.
183
- const bufferedItems = bufferedPages.map((p) => p.data).flat();
184
- const allItems = [...bufferedItems, ...page.data];
185
- const pageItems = allItems.slice(0, pageSize);
186
- const remainingItems = allItems.slice(pageItems.length);
187
-
188
- // No extra items to buffer, so we can just yield a normal page with a cursor to the next one.
189
- if (remainingItems.length === 0) {
190
- yield {
191
- ...page,
192
- data: pageItems,
193
- nextCursor: page.nextCursor,
194
- };
195
- bufferedPages = [];
196
- cursor = page.nextCursor;
197
- continue;
198
- }
199
-
200
- // Yield our items with a cursor to offset into this page.
201
- yield {
202
- ...page,
203
- data: pageItems,
204
- nextCursor: createOffsetCursor(
205
- page.data.length - remainingItems.length,
206
- cursor,
207
- ),
208
- };
209
-
210
- while (remainingItems.length > pageSize) {
211
- const pageItems = remainingItems.splice(0, pageSize);
212
- yield {
213
- ...page,
214
- data: pageItems,
215
- nextCursor: createOffsetCursor(
216
- page.data.length - remainingItems.length,
217
- cursor,
218
- ),
219
- };
220
- }
221
-
222
- bufferedPages = [
223
- {
224
- ...page,
225
- data: remainingItems,
226
- },
227
- ];
228
-
229
- cursor = page.nextCursor;
230
- }
231
-
232
- if (bufferedPages.length > 0) {
233
- const lastBufferedPage = bufferedPages.slice(-1)[0];
234
- const bufferedItems = bufferedPages.map((p) => p.data).flat();
235
- yield {
236
- ...lastBufferedPage,
237
- data: bufferedItems,
238
- };
239
- }
240
- }
241
-
242
- export const paginate = paginateBuffered;
@@ -1,207 +0,0 @@
1
- import { z } from "zod";
2
- import type { ZapierSdk } from "../types/sdk";
3
- import type { InputFieldItem } from "../schemas/Field";
4
-
5
- // ============================================================================
6
- // Format Metadata Types
7
- // ============================================================================
8
-
9
- export interface FormattedItem {
10
- title: string;
11
- id?: string;
12
- key?: string;
13
- keys?: string[];
14
- description?: string;
15
- data?: unknown; // Optional: if provided, CLI will use formatJsonOutput instead of details
16
- details: Array<{
17
- text: string;
18
- style: "normal" | "dim" | "accent" | "warning" | "success";
19
- }>;
20
- }
21
-
22
- export interface FormatMetadata<TItem = unknown> {
23
- format: (item: TItem) => FormattedItem;
24
- }
25
-
26
- // Helper function to add format metadata to schemas
27
- export function withFormatter<T extends z.ZodType>(
28
- schema: T,
29
- formatMeta: FormatMetadata<z.infer<T>>,
30
- ): T {
31
- // Store format metadata on the schema definition
32
- Object.assign(schema._def, {
33
- formatMeta: formatMeta,
34
- });
35
- return schema as T & {
36
- _def: T["_def"] & { formatMeta: FormatMetadata<z.infer<T>> };
37
- };
38
- }
39
-
40
- // Helper function to get output schema from an input schema
41
- export function getOutputSchema(inputSchema: z.ZodType): z.ZodType | undefined {
42
- return (inputSchema._def as { outputSchema?: z.ZodType }).outputSchema;
43
- }
44
-
45
- // Helper function to link input schemas to output schemas
46
- export function withOutputSchema<T extends z.ZodType>(
47
- inputSchema: T,
48
- outputSchema: z.ZodType,
49
- ): T & {
50
- _def: T["_def"] & { outputSchema: z.ZodType };
51
- } {
52
- // Store output schema reference on the input schema
53
- Object.assign(inputSchema._def, {
54
- outputSchema,
55
- });
56
- return inputSchema as T & {
57
- _def: T["_def"] & { outputSchema: z.ZodType };
58
- };
59
- }
60
-
61
- // ============================================================================
62
- // Resolution Metadata Types
63
- // ============================================================================
64
-
65
- export interface PromptConfig {
66
- type: "list";
67
- name: string;
68
- message: string;
69
- choices: Array<{
70
- name: string;
71
- value: unknown;
72
- }>;
73
- }
74
-
75
- export interface Resolver {
76
- type: string;
77
- depends?: readonly string[] | string[]; // Parameters this resolver depends on
78
- }
79
-
80
- export interface StaticResolver extends Resolver {
81
- type: "static";
82
- inputType?: "text" | "password" | "email";
83
- placeholder?: string;
84
- }
85
-
86
- export interface DynamicResolver<
87
- TItem = unknown,
88
- TParams = Record<string, unknown>,
89
- > extends Resolver {
90
- type: "dynamic";
91
- fetch: (sdk: ZapierSdk, resolvedParams: TParams) => Promise<TItem[]>; // Function to fetch data using SDK
92
- prompt: (items: TItem[], params: TParams) => PromptConfig;
93
- }
94
-
95
- export interface FieldsResolver<TParams = Record<string, unknown>>
96
- extends Resolver {
97
- type: "fields";
98
- fetch: (sdk: ZapierSdk, resolvedParams: TParams) => Promise<InputFieldItem[]>; // Function to fetch fields using SDK
99
- }
100
-
101
- export type ResolverMetadata<
102
- TItem = unknown,
103
- TParams = Record<string, unknown>,
104
- > = StaticResolver | DynamicResolver<TItem, TParams> | FieldsResolver<TParams>;
105
-
106
- // Wrapper interface for resolver metadata to allow future extensibility
107
- export interface ResolverConfig<
108
- TItem = unknown,
109
- TParams = Record<string, unknown>,
110
- > {
111
- resolver: ResolverMetadata<TItem, TParams>;
112
- // Future metadata types can be added here:
113
- // validation?: ValidationMetadata;
114
- // display?: DisplayMetadata;
115
- }
116
-
117
- // Helper function to add resolver metadata to schemas
118
- export function withResolver<
119
- T extends z.ZodType,
120
- TItem = unknown,
121
- TParams = Record<string, unknown>,
122
- >(schema: T, config: ResolverConfig<TItem, TParams>): T {
123
- // Store resolver metadata on the schema definition
124
- (
125
- schema._def as { resolverMeta?: ResolverConfig<TItem, TParams> }
126
- ).resolverMeta = config;
127
- return schema;
128
- }
129
-
130
- // ============================================================================
131
- // Schema Description Utilities
132
- // ============================================================================
133
-
134
- export function getSchemaDescription(schema: z.ZodSchema): string | undefined {
135
- return schema.description;
136
- }
137
-
138
- export function getFieldDescriptions(
139
- schema: z.ZodObject<z.ZodRawShape>,
140
- ): Record<string, string> {
141
- const descriptions: Record<string, string> = {};
142
- const shape = schema.shape;
143
-
144
- for (const [key, fieldSchema] of Object.entries(shape)) {
145
- if (fieldSchema instanceof z.ZodType && fieldSchema.description) {
146
- descriptions[key] = fieldSchema.description;
147
- }
148
- }
149
-
150
- return descriptions;
151
- }
152
-
153
- // ============================================================================
154
- // Positional Parameter Metadata
155
- // ============================================================================
156
-
157
- export interface PositionalMetadata {
158
- positionalMeta: {
159
- positional: true;
160
- };
161
- }
162
-
163
- // Helper function to mark a parameter as positional for CLI
164
- export function withPositional<T extends z.ZodType>(
165
- schema: T,
166
- ): T & {
167
- _def: T["_def"] & PositionalMetadata;
168
- } {
169
- // Store positional metadata on the schema definition
170
- Object.assign(schema._def, {
171
- positionalMeta: { positional: true },
172
- } satisfies PositionalMetadata);
173
- return schema as T & {
174
- _def: T["_def"] & PositionalMetadata;
175
- };
176
- }
177
-
178
- function schemaHasPositionalMeta<T extends z.ZodType>(
179
- schema: T,
180
- ): schema is T & {
181
- _def: T["_def"] & PositionalMetadata;
182
- } {
183
- return "positionalMeta" in schema._def;
184
- }
185
-
186
- // Helper function to check if a parameter should be positional
187
- export function isPositional(schema: z.ZodType): boolean {
188
- // Check the current schema first
189
- if (
190
- schemaHasPositionalMeta(schema) &&
191
- schema._def.positionalMeta?.positional
192
- ) {
193
- return true;
194
- }
195
-
196
- // If this is a ZodOptional, check the inner type
197
- if (schema instanceof z.ZodOptional) {
198
- return isPositional(schema._def.innerType);
199
- }
200
-
201
- // If this is a ZodDefault, check the inner type
202
- if (schema instanceof z.ZodDefault) {
203
- return isPositional(schema._def.innerType);
204
- }
205
-
206
- return false;
207
- }
@@ -1,45 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { toTitleCase } from "./string-utils";
3
-
4
- describe("toTitleCase", () => {
5
- it("converts snake_case to title case", () => {
6
- expect(toTitleCase("first_name")).toBe("First Name");
7
- expect(toTitleCase("sender_settings")).toBe("Sender Settings");
8
- expect(toTitleCase("api_key_config")).toBe("Api Key Config");
9
- });
10
-
11
- it("converts camelCase to title case", () => {
12
- expect(toTitleCase("firstName")).toBe("First Name");
13
- expect(toTitleCase("senderSettings")).toBe("Sender Settings");
14
- expect(toTitleCase("apiKeyConfig")).toBe("Api Key Config");
15
- });
16
-
17
- it("converts kebab-case to title case", () => {
18
- expect(toTitleCase("first-name")).toBe("First Name");
19
- expect(toTitleCase("sender-settings")).toBe("Sender Settings");
20
- expect(toTitleCase("api-key-config")).toBe("Api Key Config");
21
- });
22
-
23
- it("handles mixed formats", () => {
24
- expect(toTitleCase("first_name-value")).toBe("First Name Value");
25
- expect(toTitleCase("sender_settings-config")).toBe(
26
- "Sender Settings Config",
27
- );
28
- });
29
-
30
- it("handles single words", () => {
31
- expect(toTitleCase("name")).toBe("Name");
32
- expect(toTitleCase("settings")).toBe("Settings");
33
- });
34
-
35
- it("handles multiple spaces and trims", () => {
36
- expect(toTitleCase(" first name ")).toBe("First Name");
37
- expect(toTitleCase("sender__settings")).toBe("Sender Settings");
38
- });
39
-
40
- it("handles empty and edge cases", () => {
41
- expect(toTitleCase("")).toBe("");
42
- expect(toTitleCase("a")).toBe("A");
43
- expect(toTitleCase("_")).toBe("");
44
- });
45
- });
@@ -1,54 +0,0 @@
1
- /**
2
- * String utility functions for the Zapier SDK
3
- */
4
-
5
- /**
6
- * Converts a string to title case, handling various input formats:
7
- * - camelCase: "firstName" → "First Name"
8
- * - snake_case: "first_name" → "First Name"
9
- * - kebab-case: "first-name" → "First Name"
10
- * - mixed formats: "first_name-value" → "First Name Value"
11
- */
12
- export function toTitleCase(input: string): string {
13
- return (
14
- input
15
- // insert a space before capital letters (handles camelCase)
16
- .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
17
- // replace delimiters (underscore, dash, multiple spaces) with single space
18
- .replace(/[_\-]+/g, " ")
19
- .replace(/\s+/g, " ")
20
- .trim()
21
- // split and capitalize each word
22
- .split(" ")
23
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
24
- .join(" ")
25
- );
26
- }
27
-
28
- /**
29
- * Converts a string to snake_case, handling various input formats:
30
- * - camelCase: "firstName" → "first_name"
31
- * - kebab-case: "first-name" → "first_name"
32
- * - title case: "First Name" → "first_name"
33
- * - mixed formats: "first-Name Value" → "first_name_value"
34
- * - starts with number: "123abc" → "_123abc"
35
- */
36
- export function toSnakeCase(input: string): string {
37
- let result = input
38
- // insert underscore before capital letters (handles camelCase)
39
- .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
40
- // replace spaces and dashes with underscores
41
- .replace(/[\s\-]+/g, "_")
42
- // replace multiple underscores with single underscore
43
- .replace(/_+/g, "_")
44
- // remove leading/trailing underscores and convert to lowercase
45
- .replace(/^_|_$/g, "")
46
- .toLowerCase();
47
-
48
- // If the result starts with a number, prefix with underscore
49
- if (/^[0-9]/.test(result)) {
50
- result = "_" + result;
51
- }
52
-
53
- return result;
54
- }
@@ -1,51 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { z } from "zod";
3
- import { createValidator } from "./validation";
4
- import { ZapierValidationError } from "../types/errors";
5
-
6
- describe("validation utilities", () => {
7
- describe("createValidator", () => {
8
- it("should validate valid input", () => {
9
- const schema = z.object({
10
- name: z.string(),
11
- age: z.number(),
12
- });
13
- const validator = createValidator(schema);
14
-
15
- const result = validator({ name: "John", age: 30 });
16
- expect(result).toEqual({ name: "John", age: 30 });
17
- });
18
-
19
- it("should throw ZapierValidationError for invalid input", () => {
20
- const schema = z.object({
21
- name: z.string(),
22
- age: z.number(),
23
- });
24
- const validator = createValidator(schema);
25
-
26
- expect(() => {
27
- validator({ name: "John", age: "not-a-number" });
28
- }).toThrow(ZapierValidationError);
29
- });
30
-
31
- it("should include helpful error messages", () => {
32
- const schema = z.object({
33
- name: z.string(),
34
- age: z.number().min(0),
35
- });
36
- const validator = createValidator(schema);
37
-
38
- try {
39
- validator({ name: 123, age: -5 });
40
- } catch (error) {
41
- expect(error).toBeInstanceOf(ZapierValidationError);
42
- expect((error as ZapierValidationError).message).toContain("name:");
43
- expect((error as ZapierValidationError).message).toContain("age:");
44
- expect(
45
- ((error as ZapierValidationError).details as { zodErrors: unknown })
46
- ?.zodErrors,
47
- ).toBeDefined();
48
- }
49
- });
50
- });
51
- });
@@ -1,44 +0,0 @@
1
- import type { z } from "zod";
2
- import { ZapierValidationError } from "../types/errors";
3
-
4
- /**
5
- * Throws clean ZapierValidationError on validation failures
6
- */
7
- const validate = <TSchema extends z.ZodSchema>(
8
- schema: TSchema,
9
- input: unknown,
10
- ) => {
11
- const result = schema.safeParse(input);
12
-
13
- if (!result.success) {
14
- // Format Zod errors into a clean, user-friendly message
15
- const errorMessages = result.error.errors.map((error) => {
16
- const path = error.path.length > 0 ? error.path.join(".") : "input";
17
- return `${path}: ${error.message}`;
18
- });
19
-
20
- const message = `Validation failed:\n ${errorMessages.join("\n ")}`;
21
-
22
- throw new ZapierValidationError(message, {
23
- details: {
24
- zodErrors: result.error.errors,
25
- input,
26
- },
27
- });
28
- }
29
-
30
- return result.data;
31
- };
32
-
33
- export function createValidator<TSchema extends z.ZodSchema>(schema: TSchema) {
34
- return function validateFn(input: unknown): z.infer<TSchema> {
35
- return validate(schema, input);
36
- };
37
- }
38
-
39
- export const validateOptions = <TOptions, TSchemaOptions extends TOptions>(
40
- schema: z.ZodSchema<TSchemaOptions>,
41
- options: TOptions,
42
- ) => {
43
- return validate(schema, options);
44
- };
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ES2020",
5
- "declaration": true,
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "strict": true,
9
- "noUnusedLocals": true,
10
- "noUnusedParameters": true,
11
- "esModuleInterop": true,
12
- "skipLibCheck": true,
13
- "forceConsistentCasingInFileNames": true,
14
- "moduleResolution": "bundler"
15
- },
16
- "include": ["src/**/*"],
17
- "exclude": ["dist", "node_modules", "src/**/*.test.ts", "src/**/*.spec.ts"]
18
- }
package/tsconfig.json DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ES2020",
5
- "declaration": true,
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "strict": true,
9
- "noUnusedLocals": true,
10
- "noUnusedParameters": true,
11
- "esModuleInterop": true,
12
- "skipLibCheck": true,
13
- "forceConsistentCasingInFileNames": true,
14
- "moduleResolution": "bundler",
15
- "composite": true,
16
- "declarationMap": true
17
- },
18
- "include": ["src/**/*"],
19
- "exclude": ["dist", "node_modules"]
20
- }