@automate.ax/catalog 0.153.2 → 0.154.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,341 @@
1
+ import * as z from "zod/mini";
2
+ import { sentenceCase } from "./triggers/name.js";
3
+ const EMAIL_SCHEMA = z.email();
4
+ const URL_SCHEMA = z.url();
5
+ const DATE_SCHEMA = z.iso.date();
6
+ const DATETIME_SCHEMA = z.iso.datetime({ local: true });
7
+ const FIELD_NAME_SCHEMA = z
8
+ .string()
9
+ .check(z.trim(), z.minLength(1), z.maxLength(100));
10
+ const FIELD_LABEL_SCHEMA = z
11
+ .string()
12
+ .check(z.trim(), z.minLength(1), z.maxLength(255));
13
+ const FIELD_DESCRIPTION_SCHEMA = z.string().check(z.maxLength(2_000));
14
+ const FIELD_LENGTH_SCHEMA = z.int().check(z.nonnegative());
15
+ const FORM_FIELD_BASE_SCHEMA = {
16
+ description: z.optional(FIELD_DESCRIPTION_SCHEMA),
17
+ label: z.optional(FIELD_LABEL_SCHEMA),
18
+ name: FIELD_NAME_SCHEMA,
19
+ };
20
+ const FORM_TEXT_FIELD_SCHEMA = z.object({
21
+ ...FORM_FIELD_BASE_SCHEMA,
22
+ defaultValue: z.optional(z.string()),
23
+ maxLength: z.optional(FIELD_LENGTH_SCHEMA),
24
+ minLength: z.optional(FIELD_LENGTH_SCHEMA),
25
+ placeholder: z.optional(z.string()),
26
+ required: z.prefault(z.boolean(), true),
27
+ type: z.enum(["email", "phone", "text", "textarea", "url"]),
28
+ });
29
+ const FORM_NUMBER_FIELD_SCHEMA = z.object({
30
+ ...FORM_FIELD_BASE_SCHEMA,
31
+ defaultValue: z.optional(z.number()),
32
+ max: z.optional(z.number()),
33
+ min: z.optional(z.number()),
34
+ placeholder: z.optional(z.string()),
35
+ required: z.prefault(z.boolean(), true),
36
+ step: z.prefault(z.number().check(z.positive()), 1),
37
+ type: z.literal("number"),
38
+ });
39
+ const FORM_DATE_FIELD_SCHEMA = z.object({
40
+ ...FORM_FIELD_BASE_SCHEMA,
41
+ defaultValue: z.optional(z.iso.date()),
42
+ max: z.optional(z.iso.date()),
43
+ min: z.optional(z.iso.date()),
44
+ required: z.prefault(z.boolean(), true),
45
+ step: z.prefault(z.int().check(z.positive()), 1),
46
+ type: z.literal("date"),
47
+ });
48
+ const FORM_DATETIME_FIELD_SCHEMA = z.object({
49
+ ...FORM_FIELD_BASE_SCHEMA,
50
+ defaultValue: z.optional(z.iso.datetime({ local: true })),
51
+ max: z.optional(z.iso.datetime({ local: true })),
52
+ min: z.optional(z.iso.datetime({ local: true })),
53
+ required: z.prefault(z.boolean(), true),
54
+ step: z.prefault(z.number().check(z.positive()), 60),
55
+ type: z.literal("datetime"),
56
+ });
57
+ const FORM_CHOICE_FIELD_SCHEMA = {
58
+ ...FORM_FIELD_BASE_SCHEMA,
59
+ defaultValue: z.optional(FIELD_NAME_SCHEMA),
60
+ options: z
61
+ .array(z.object({
62
+ label: FIELD_LABEL_SCHEMA,
63
+ value: FIELD_NAME_SCHEMA,
64
+ }))
65
+ .check(z.minLength(1)),
66
+ placeholder: z.optional(z.string()),
67
+ required: z.prefault(z.boolean(), true),
68
+ };
69
+ const FORM_SELECT_FIELD_SCHEMA = z.object({
70
+ ...FORM_CHOICE_FIELD_SCHEMA,
71
+ type: z.literal("select"),
72
+ });
73
+ const FORM_RADIO_FIELD_SCHEMA = z.object({
74
+ ...FORM_CHOICE_FIELD_SCHEMA,
75
+ type: z.literal("radio"),
76
+ });
77
+ const FORM_MULTI_SELECT_FIELD_SCHEMA = z.object({
78
+ ...FORM_FIELD_BASE_SCHEMA,
79
+ defaultValue: z.optional(z.array(FIELD_NAME_SCHEMA)),
80
+ options: z
81
+ .array(z.object({
82
+ label: FIELD_LABEL_SCHEMA,
83
+ value: FIELD_NAME_SCHEMA,
84
+ }))
85
+ .check(z.minLength(1)),
86
+ placeholder: z.optional(z.string()),
87
+ required: z.prefault(z.boolean(), true),
88
+ type: z.literal("multi-select"),
89
+ });
90
+ const FORM_CHECKBOX_FIELD_SCHEMA = z.object({
91
+ ...FORM_FIELD_BASE_SCHEMA,
92
+ defaultValue: z.prefault(z.boolean(), false),
93
+ required: z.prefault(z.boolean(), false),
94
+ type: z.literal("checkbox"),
95
+ });
96
+ const FORM_FILE_FIELD_SCHEMA = z.object({
97
+ ...FORM_FIELD_BASE_SCHEMA,
98
+ required: z.prefault(z.boolean(), true),
99
+ type: z.literal("file"),
100
+ });
101
+ const FORM_FIELD_SCHEMA = z.pipe(z.discriminatedUnion("type", [
102
+ FORM_TEXT_FIELD_SCHEMA,
103
+ FORM_NUMBER_FIELD_SCHEMA,
104
+ FORM_DATE_FIELD_SCHEMA,
105
+ FORM_DATETIME_FIELD_SCHEMA,
106
+ FORM_SELECT_FIELD_SCHEMA,
107
+ FORM_RADIO_FIELD_SCHEMA,
108
+ FORM_MULTI_SELECT_FIELD_SCHEMA,
109
+ FORM_CHECKBOX_FIELD_SCHEMA,
110
+ FORM_FILE_FIELD_SCHEMA,
111
+ ]), z.transform((field) => ({
112
+ ...field,
113
+ label: field.label ??
114
+ `${sentenceCase(field.name)}${field.type === "checkbox" ? "?" : ""}`,
115
+ })));
116
+ export const FORM_FIELDS_SCHEMA = z.array(FORM_FIELD_SCHEMA).check(z.refine((fields) => new Set(fields.map((field) => field.name)).size === fields.length, "Form field names must be unique."), z.refine((fields) => fields.every(isFormFieldDefaultValid), "Form field defaults must satisfy their field constraints."));
117
+ /**
118
+ * Returns the configured fallback for a field without an assigned value.
119
+ *
120
+ * @param field - Field whose fallback should be resolved.
121
+ */
122
+ export function getFormFieldDefaultValue(field) {
123
+ if (field.type === "file")
124
+ return field.required === false ? null : undefined;
125
+ if (field.defaultValue !== undefined)
126
+ return field.defaultValue;
127
+ if (field.type === "checkbox")
128
+ return false;
129
+ if (field.type === "multi-select")
130
+ return [];
131
+ if (field.type === "number")
132
+ return field.required === false ? null : undefined;
133
+ return field.required === false ? "" : undefined;
134
+ }
135
+ /**
136
+ * Checks one decoded value against a dashboard field declaration.
137
+ *
138
+ * @param field - Field declaration whose constraints apply.
139
+ * @param value - Decoded value to validate.
140
+ */
141
+ export function isFormFieldValueValid(field, value) {
142
+ return getFormFieldValueSchema(field).safeParse(value).success;
143
+ }
144
+ /**
145
+ * Builds the shared validator for decoded field values.
146
+ *
147
+ * @param field - Field declaration whose constraints apply.
148
+ */
149
+ export function getFormFieldValueSchema(field) {
150
+ return z.custom((value) => getFormFieldError(field, value) === undefined, { error: (issue) => getFormFieldError(field, issue.input) });
151
+ }
152
+ /**
153
+ * Normalizes controlled or prompted values before validation.
154
+ *
155
+ * @param field - Field declaration.
156
+ * @param value - Raw control value.
157
+ */
158
+ export function normalizeFormFieldValue(field, value) {
159
+ if (field.type === "file") {
160
+ // Browsers submit an unnamed empty File when no file was selected.
161
+ return value instanceof File && !value.name && value.size === 0
162
+ ? null
163
+ : value;
164
+ }
165
+ if (typeof value === "string") {
166
+ const text = value.trim();
167
+ return field.type === "number" ? (text ? Number(text) : null) : text;
168
+ }
169
+ if (field.type === "multi-select" && Array.isArray(value)) {
170
+ return value
171
+ .map((entry) => typeof entry === "string" ? entry.trim() : entry)
172
+ .filter((entry) => entry !== "");
173
+ }
174
+ return value;
175
+ }
176
+ /**
177
+ * Reads and validates one submitted field, preserving uploaded File values.
178
+ *
179
+ * @param field - Field declaration.
180
+ * @param data - Submitted form data.
181
+ * @param name - Form name, including any UI namespace.
182
+ */
183
+ export function readFormFieldValue(field, data, name = field.name) {
184
+ // Preserve the transport value before applying field-specific normalization.
185
+ const raw = field.type === "checkbox"
186
+ ? data.has(name)
187
+ : field.type === "multi-select"
188
+ ? data.getAll(name)
189
+ : (data.get(name) ?? (field.type === "file" ? null : ""));
190
+ return getFormFieldValueSchema(field).safeParse(normalizeFormFieldValue(field, raw));
191
+ }
192
+ /**
193
+ * Validates configured form fields and ignores unconfigured entries.
194
+ *
195
+ * @param fields - Ordered field declarations.
196
+ * @param data - Submitted form data.
197
+ */
198
+ export function parseFormFields(fields, data) {
199
+ const results = fields.map((field) => ({
200
+ field,
201
+ result: readFormFieldValue(field, data),
202
+ }));
203
+ const issues = results.flatMap(({ field, result }) => result.success
204
+ ? []
205
+ : [
206
+ {
207
+ field: field.name,
208
+ message: result.error.issues[0].message,
209
+ },
210
+ ]);
211
+ if (issues.length > 0)
212
+ return { issues, success: false };
213
+ return {
214
+ data: Object.fromEntries(results.flatMap(({ field, result }) => result.success ? [[field.name, result.data]] : [])),
215
+ success: true,
216
+ };
217
+ }
218
+ /**
219
+ * Reports the first violated field constraint for every input surface.
220
+ *
221
+ * @param field - Field declaration.
222
+ * @param value - Decoded value.
223
+ */
224
+ function getFormFieldError(field, value) {
225
+ const label = field.label ?? sentenceCase(field.name);
226
+ const required = `${label} is required.`;
227
+ const invalid = `${label} is invalid.`;
228
+ if (field.type === "file") {
229
+ if (value === null)
230
+ return field.required === false ? undefined : required;
231
+ return value instanceof File ? undefined : `${label} must be a file.`;
232
+ }
233
+ if (field.type === "checkbox") {
234
+ if (typeof value !== "boolean")
235
+ return invalid;
236
+ return field.required === true && !value ? required : undefined;
237
+ }
238
+ if (field.type === "multi-select") {
239
+ if (!Array.isArray(value))
240
+ return invalid;
241
+ if (field.required !== false && value.length === 0)
242
+ return required;
243
+ return value.every((entry) => typeof entry === "string" &&
244
+ field.options.some((option) => option.value === entry))
245
+ ? undefined
246
+ : invalid;
247
+ }
248
+ if (field.type === "number") {
249
+ if (value === null)
250
+ return field.required === false ? undefined : required;
251
+ if (typeof value !== "number" || !Number.isFinite(value))
252
+ return `${label} must be a number.`;
253
+ if (field.min !== undefined && value < field.min)
254
+ return `${label} must be at least ${field.min}.`;
255
+ if (field.max !== undefined && value > field.max)
256
+ return `${label} must be at most ${field.max}.`;
257
+ const step = field.step ?? 1;
258
+ return isFormStepMismatch(value, field.min ?? field.defaultValue ?? 0, step)
259
+ ? `${label} must use increments of ${step}.`
260
+ : undefined;
261
+ }
262
+ if (typeof value !== "string")
263
+ return invalid;
264
+ const text = field.type === "radio" ||
265
+ field.type === "select" ||
266
+ field.type === "date" ||
267
+ field.type === "datetime"
268
+ ? value
269
+ : value.trim();
270
+ if (text === "")
271
+ return field.required === false ? undefined : required;
272
+ if (field.type === "radio" || field.type === "select") {
273
+ return field.options.some((option) => option.value === text)
274
+ ? undefined
275
+ : invalid;
276
+ }
277
+ if (field.type === "date" || field.type === "datetime") {
278
+ if (!(field.type === "date" ? DATE_SCHEMA : DATETIME_SCHEMA).safeParse(text)
279
+ .success)
280
+ return invalid;
281
+ const date = parseFormDateValue(field.type, text);
282
+ if (!Number.isFinite(date))
283
+ return invalid;
284
+ if (field.min !== undefined &&
285
+ date < parseFormDateValue(field.type, field.min))
286
+ return `${label} must be on or after ${field.min}.`;
287
+ if (field.max !== undefined &&
288
+ date > parseFormDateValue(field.type, field.max))
289
+ return `${label} must be on or before ${field.max}.`;
290
+ // HTML temporal steps are measured from this configured origin.
291
+ const base = parseFormDateValue(field.type, field.min ??
292
+ field.defaultValue ??
293
+ (field.type === "date" ? "1970-01-01" : "1970-01-01T00:00"));
294
+ // Compare timestamps using millisecond intervals.
295
+ const step = (field.step ?? (field.type === "date" ? 1 : 60)) *
296
+ (field.type === "date" ? 86_400_000 : 1_000);
297
+ return isFormStepMismatch(date, base, step)
298
+ ? `${label} does not match the configured step.`
299
+ : undefined;
300
+ }
301
+ if (field.minLength !== undefined && text.length < field.minLength)
302
+ return `${label} must contain at least ${field.minLength} characters.`;
303
+ if (field.maxLength !== undefined && text.length > field.maxLength)
304
+ return `${label} must contain at most ${field.maxLength} characters.`;
305
+ if (field.type === "email" && !EMAIL_SCHEMA.safeParse(text).success)
306
+ return `${label} must be an email address.`;
307
+ if (field.type === "url" && !URL_SCHEMA.safeParse(text).success)
308
+ return `${label} must be a URL.`;
309
+ }
310
+ /**
311
+ * Checks a configured default against the same constraints as submitted data.
312
+ *
313
+ * @param field - Field definition to validate.
314
+ */
315
+ function isFormFieldDefaultValid(field) {
316
+ if (field.type === "file" ||
317
+ field.type === "checkbox" ||
318
+ field.defaultValue === undefined)
319
+ return true;
320
+ return isFormFieldValueValid(field, field.defaultValue);
321
+ }
322
+ /**
323
+ * Checks whether a value falls outside an allowed step interval.
324
+ *
325
+ * @param value - Configured default value.
326
+ * @param base - Value from which step intervals begin.
327
+ * @param step - Allowed interval size.
328
+ */
329
+ function isFormStepMismatch(value, base, step) {
330
+ const intervals = (value - base) / step;
331
+ return Math.abs(intervals - Math.round(intervals)) > 1e-9;
332
+ }
333
+ /**
334
+ * Converts a validated local date or date-time string to a UTC number.
335
+ *
336
+ * @param type - Temporal field type.
337
+ * @param value - Local ISO value to convert.
338
+ */
339
+ function parseFormDateValue(type, value) {
340
+ return Date.parse(type === "date" ? `${value}T00:00:00Z` : `${value}Z`);
341
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { airtableService, anthropicService, apolloService, apifyService, asanaService, calendlyService, automateService, axiomService, brevoService, calcomService, browserbaseService, cerebrasService, chatgptService, cloudflareService, metaAdsService, closeService, closeBotService, clickUpService, codexService, cohereService, convexService, discordService, elevenLabsService, deepinfraService, deepseekService, firecrawlService, fireworksService, getIntegrationService, githubService, googleGenaiService, googleService, groqService, huggingfaceService, hubspotService, highLevelService, integrationCatalog, jobNimbusService, kitService, krispService, linearService, microsoftService, millionverifierService, mistralService, notionService, openaiService, onePasswordService, openrouterService, perplexityService, postgresService, redditService, resendService, scrapflyService, slackService, stripeService, tallyService, tidycalService, togetheraiService, trelloService, vercelAiGatewayService, vercelService, webflowService, whatsappService, xaiService, type IntegrationServiceId, } from "./catalog.js";
2
2
  export { defineIntegrationCatalog, defineIntegrationService, integrationScope, type IntegrationAccountConnectionOption, type IntegrationAccountRequirement, type IntegrationConnectionDefinition, type IntegrationConnectionNotice, type IntegrationFormField, type IntegrationScopeRequirement, type IntegrationServiceDefinition, type ScopeRequirementGroup, type TriggerAccountRequirement, type TriggerDefinition, type TriggerPresentation, type TriggerPresentationContext, type TriggerPresentationDetail, type TriggerScopePresentation, type TriggerPresentationValue, } from "./types.js";
3
- export { getDashboardRunEndpoint, getDashboardRunEndpointKey, getHttpTriggerEndpoint, getHttpTriggerEndpointKey, getMailhookAddress, getMailhookAddressKey, DASHBOARD_RUN_CONFIG_SCHEMA, DASHBOARD_RUN_FIELDS_SCHEMA, getDashboardRunFieldDefaultValue, isDashboardRunFieldValueValid, getTriggerDefinition, getTriggerName, getTriggerPresentation, isTriggerVisible, PLATFORM_EMAIL_DOMAIN, triggerCatalog, triggerDefinitions, type DashboardRunConfig, type DashboardRunEndpointOptions, type DashboardRunField, type DashboardRunFieldValue, type HttpTriggerEndpointOptions, type HttpTriggerScope, type MailhookAddressOptions, type MailhookScope, type TriggerType, } from "./triggers/index.js";
3
+ export { getDashboardRunEndpoint, getDashboardRunEndpointKey, getHttpTriggerEndpoint, getHttpTriggerEndpointKey, getMailhookAddress, getMailhookAddressKey, DASHBOARD_RUN_CONFIG_SCHEMA, getTriggerDefinition, getTriggerName, getTriggerPresentation, isTriggerVisible, PLATFORM_EMAIL_DOMAIN, triggerCatalog, triggerDefinitions, type DashboardRunConfig, type DashboardRunEndpointOptions, type HttpTriggerEndpointOptions, type HttpTriggerScope, type MailhookAddressOptions, type MailhookScope, type TriggerType, } from "./triggers/index.js";
4
+ export { FORM_FIELDS_SCHEMA, getFormFieldDefaultValue, getFormFieldValueSchema, isFormFieldValueValid, normalizeFormFieldValue, readFormFieldValue, parseFormFields, type FormField, type FormFieldValue, type FormFieldValues, } from "./form-fields.js";
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { airtableService, anthropicService, apolloService, apifyService, asanaService, calendlyService, automateService, axiomService, brevoService, calcomService, browserbaseService, cerebrasService, chatgptService, cloudflareService, metaAdsService, closeService, closeBotService, clickUpService, codexService, cohereService, convexService, discordService, elevenLabsService, deepinfraService, deepseekService, firecrawlService, fireworksService, getIntegrationService, githubService, googleGenaiService, googleService, groqService, huggingfaceService, hubspotService, highLevelService, integrationCatalog, jobNimbusService, kitService, krispService, linearService, microsoftService, millionverifierService, mistralService, notionService, openaiService, onePasswordService, openrouterService, perplexityService, postgresService, redditService, resendService, scrapflyService, slackService, stripeService, tallyService, tidycalService, togetheraiService, trelloService, vercelAiGatewayService, vercelService, webflowService, whatsappService, xaiService, } from "./catalog.js";
2
2
  export { defineIntegrationCatalog, defineIntegrationService, integrationScope, } from "./types.js";
3
- export { getDashboardRunEndpoint, getDashboardRunEndpointKey, getHttpTriggerEndpoint, getHttpTriggerEndpointKey, getMailhookAddress, getMailhookAddressKey, DASHBOARD_RUN_CONFIG_SCHEMA, DASHBOARD_RUN_FIELDS_SCHEMA, getDashboardRunFieldDefaultValue, isDashboardRunFieldValueValid, getTriggerDefinition, getTriggerName, getTriggerPresentation, isTriggerVisible, PLATFORM_EMAIL_DOMAIN, triggerCatalog, triggerDefinitions, } from "./triggers/index.js";
3
+ export { getDashboardRunEndpoint, getDashboardRunEndpointKey, getHttpTriggerEndpoint, getHttpTriggerEndpointKey, getMailhookAddress, getMailhookAddressKey, DASHBOARD_RUN_CONFIG_SCHEMA, getTriggerDefinition, getTriggerName, getTriggerPresentation, isTriggerVisible, PLATFORM_EMAIL_DOMAIN, triggerCatalog, triggerDefinitions, } from "./triggers/index.js";
4
+ export { FORM_FIELDS_SCHEMA, getFormFieldDefaultValue, getFormFieldValueSchema, isFormFieldValueValid, normalizeFormFieldValue, readFormFieldValue, parseFormFields, } from "./form-fields.js";