@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.
- package/CHANGELOG.md +7 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +7 -2
- package/src/api/auth.ts +0 -28
- package/src/api/client.ts +0 -491
- package/src/api/debug.test.ts +0 -76
- package/src/api/debug.ts +0 -154
- package/src/api/index.ts +0 -90
- package/src/api/polling.test.ts +0 -405
- package/src/api/polling.ts +0 -253
- package/src/api/schemas.ts +0 -465
- package/src/api/types.ts +0 -152
- package/src/auth.ts +0 -72
- package/src/constants.ts +0 -16
- package/src/index.ts +0 -111
- package/src/plugins/api/index.ts +0 -43
- package/src/plugins/apps/index.ts +0 -203
- package/src/plugins/apps/schemas.ts +0 -64
- package/src/plugins/eventEmission/builders.ts +0 -115
- package/src/plugins/eventEmission/index.test.ts +0 -169
- package/src/plugins/eventEmission/index.ts +0 -294
- package/src/plugins/eventEmission/transport.test.ts +0 -214
- package/src/plugins/eventEmission/transport.ts +0 -135
- package/src/plugins/eventEmission/types.ts +0 -58
- package/src/plugins/eventEmission/utils.ts +0 -121
- package/src/plugins/fetch/index.ts +0 -83
- package/src/plugins/fetch/schemas.ts +0 -37
- package/src/plugins/findFirstAuthentication/index.test.ts +0 -209
- package/src/plugins/findFirstAuthentication/index.ts +0 -68
- package/src/plugins/findFirstAuthentication/schemas.ts +0 -47
- package/src/plugins/findUniqueAuthentication/index.test.ts +0 -197
- package/src/plugins/findUniqueAuthentication/index.ts +0 -77
- package/src/plugins/findUniqueAuthentication/schemas.ts +0 -49
- package/src/plugins/getAction/index.test.ts +0 -239
- package/src/plugins/getAction/index.ts +0 -75
- package/src/plugins/getAction/schemas.ts +0 -41
- package/src/plugins/getApp/index.test.ts +0 -181
- package/src/plugins/getApp/index.ts +0 -60
- package/src/plugins/getApp/schemas.ts +0 -33
- package/src/plugins/getAuthentication/index.test.ts +0 -294
- package/src/plugins/getAuthentication/index.ts +0 -95
- package/src/plugins/getAuthentication/schemas.ts +0 -38
- package/src/plugins/getProfile/index.ts +0 -60
- package/src/plugins/getProfile/schemas.ts +0 -24
- package/src/plugins/listActions/index.test.ts +0 -526
- package/src/plugins/listActions/index.ts +0 -132
- package/src/plugins/listActions/schemas.ts +0 -55
- package/src/plugins/listApps/index.test.ts +0 -378
- package/src/plugins/listApps/index.ts +0 -159
- package/src/plugins/listApps/schemas.ts +0 -41
- package/src/plugins/listAuthentications/index.test.ts +0 -739
- package/src/plugins/listAuthentications/index.ts +0 -152
- package/src/plugins/listAuthentications/schemas.ts +0 -77
- package/src/plugins/listInputFieldChoices/index.test.ts +0 -653
- package/src/plugins/listInputFieldChoices/index.ts +0 -173
- package/src/plugins/listInputFieldChoices/schemas.ts +0 -125
- package/src/plugins/listInputFields/index.test.ts +0 -439
- package/src/plugins/listInputFields/index.ts +0 -294
- package/src/plugins/listInputFields/schemas.ts +0 -68
- package/src/plugins/manifest/index.test.ts +0 -776
- package/src/plugins/manifest/index.ts +0 -461
- package/src/plugins/manifest/schemas.ts +0 -60
- package/src/plugins/registry/index.ts +0 -160
- package/src/plugins/request/index.test.ts +0 -333
- package/src/plugins/request/index.ts +0 -105
- package/src/plugins/request/schemas.ts +0 -69
- package/src/plugins/runAction/index.test.ts +0 -388
- package/src/plugins/runAction/index.ts +0 -215
- package/src/plugins/runAction/schemas.ts +0 -60
- package/src/resolvers/actionKey.ts +0 -37
- package/src/resolvers/actionType.ts +0 -34
- package/src/resolvers/appKey.ts +0 -7
- package/src/resolvers/authenticationId.ts +0 -54
- package/src/resolvers/index.ts +0 -11
- package/src/resolvers/inputFieldKey.ts +0 -70
- package/src/resolvers/inputs.ts +0 -69
- package/src/schemas/Action.ts +0 -52
- package/src/schemas/App.ts +0 -45
- package/src/schemas/Auth.ts +0 -59
- package/src/schemas/Field.ts +0 -169
- package/src/schemas/Run.ts +0 -40
- package/src/schemas/UserProfile.ts +0 -60
- package/src/sdk.test.ts +0 -212
- package/src/sdk.ts +0 -178
- package/src/types/domain.test.ts +0 -50
- package/src/types/domain.ts +0 -66
- package/src/types/errors.ts +0 -278
- package/src/types/events.ts +0 -43
- package/src/types/functions.ts +0 -28
- package/src/types/optional-zapier-sdk-cli-login.d.ts +0 -37
- package/src/types/plugin.ts +0 -125
- package/src/types/properties.ts +0 -80
- package/src/types/sdk.ts +0 -111
- package/src/types/telemetry-events.ts +0 -85
- package/src/utils/array-utils.test.ts +0 -131
- package/src/utils/array-utils.ts +0 -41
- package/src/utils/domain-utils.test.ts +0 -433
- package/src/utils/domain-utils.ts +0 -267
- package/src/utils/file-utils.test.ts +0 -73
- package/src/utils/file-utils.ts +0 -94
- package/src/utils/function-utils.test.ts +0 -141
- package/src/utils/function-utils.ts +0 -245
- package/src/utils/pagination-utils.test.ts +0 -620
- package/src/utils/pagination-utils.ts +0 -242
- package/src/utils/schema-utils.ts +0 -207
- package/src/utils/string-utils.test.ts +0 -45
- package/src/utils/string-utils.ts +0 -54
- package/src/utils/validation.test.ts +0 -51
- package/src/utils/validation.ts +0 -44
- package/tsconfig.build.json +0 -18
- package/tsconfig.json +0 -20
- package/tsconfig.tsbuildinfo +0 -1
- 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
|
-
});
|
package/src/utils/validation.ts
DELETED
|
@@ -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
|
-
};
|
package/tsconfig.build.json
DELETED
|
@@ -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
|
-
}
|