@hostwebhook/node-types 1.52.6 → 1.52.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.
@@ -14,3 +14,80 @@ export declare const GOOGLE_CONTACTS_OPERATIONS_V2: readonly ["batchCreateContac
14
14
  export declare const GOOGLE_CONTACTS_OPERATIONS: readonly ["listContacts", "searchContacts", "getContact", "createContact", "updateContact", "deleteContact", "listContactGroups", "addContactToGroup", "removeContactFromGroup", "batchCreateContacts", "batchUpdateContacts", "batchDeleteContacts", "createContactGroup", "updateContactGroup", "deleteContactGroup"];
15
15
  export type GoogleContactsOperation = (typeof GOOGLE_CONTACTS_OPERATIONS)[number];
16
16
  export declare function isGoogleContactsOperation(value: unknown): value is GoogleContactsOperation;
17
+ /**
18
+ * Per-operation form schema, same idea as SLACK_OPERATION_SPECS and
19
+ * DISCORD_OPERATION_SPECS: the dashboard detail page renders it with one loop
20
+ * instead of a branch per operation.
21
+ *
22
+ * Unlike Slack's, this one drives the **form only**. The AI-toolkit / MCP tool
23
+ * schema for this node lives apart (dashboard
24
+ * `google-contacts-operations-schemas.ts`, api `mcp-servers/toolkit-specs.ts`),
25
+ * which is why `description` is optional here — a handful of fields have never
26
+ * shown help text and inventing copy for them is not this change's job.
27
+ */
28
+ export interface GoogleContactsParamSpec {
29
+ /** Field key — also the property name on operationConfig / payload. */
30
+ name: string;
31
+ /** UI label, shown as the section title. Carries any "(optional)" suffix. */
32
+ label: string;
33
+ /**
34
+ * Param type for form rendering.
35
+ *
36
+ * `string`, `text`, `number` and `boolean` mean the same as in
37
+ * SlackParamSpec. The rest are this node's own:
38
+ * - `csvList` — stored as a string **array**, edited as comma-separated
39
+ * text. Losing the array on save breaks the node at run time, so the
40
+ * renderer must join on the way in and split on the way out.
41
+ * - `json` — stored parsed when the text is valid JSON, and as the raw
42
+ * string when it is not, so a half-typed array survives a keystroke.
43
+ * - `plain` — a bare input, no templating. Same control Discord calls
44
+ * `emoji`, a name inherited from Slack that fits neither field here.
45
+ * Unifying the two is a coordinated rename, so for now both exist.
46
+ */
47
+ type: 'string' | 'text' | 'plain' | 'csvList' | 'json' | 'number' | 'boolean';
48
+ /** Required by the People API. Metadata — the form does not gate on it. */
49
+ required?: boolean;
50
+ /** Help text under the input. Absent where the form has never shown any. */
51
+ description?: string;
52
+ /** Hint shown inside the input. */
53
+ placeholder?: string;
54
+ /** Value to show when the stored one is undefined. */
55
+ default?: string | number | boolean;
56
+ /** `number` only — and enforced on change, not just by the input. */
57
+ min?: number;
58
+ max?: number;
59
+ /** `text` / `json` only — height of the textarea. */
60
+ rows?: number;
61
+ /** `boolean` only — the line beside the switch. */
62
+ switchLabel?: string;
63
+ }
64
+ /** Group headings of the operation dropdown, in display order. */
65
+ export declare const GOOGLE_CONTACTS_OPERATION_GROUPS: readonly ["Read", "Write", "Groups", "Bulk", "Group management"];
66
+ export type GoogleContactsOperationGroup = (typeof GOOGLE_CONTACTS_OPERATION_GROUPS)[number];
67
+ export interface GoogleContactsOperationSpec {
68
+ /** UI label for the operation picker. */
69
+ label: string;
70
+ /** Line under the picker, and the picker's own description. */
71
+ description: string;
72
+ /** googleapis call the operation maps to (people v1). */
73
+ apiMethod: string;
74
+ /** Which half of the OAuth scope the operation needs. */
75
+ scope: 'read' | 'write';
76
+ /** Group heading it sits under in the dropdown. */
77
+ group: GoogleContactsOperationGroup;
78
+ /** Parameter schema. Order matters for form rendering. */
79
+ params: GoogleContactsParamSpec[];
80
+ }
81
+ /**
82
+ * personFields the read operations request when nobody overrides them. Lived in
83
+ * the dashboard page until this schema landed; it belongs next to the field
84
+ * that defaults to it.
85
+ */
86
+ export declare const GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = "names,emailAddresses,phoneNumbers,addresses,organizations,memberships";
87
+ /**
88
+ * Key order below is the dropdown order **within each group** — most-common
89
+ * operation first, which is why Read starts at searchContacts and not at the
90
+ * enum's listContacts. The dashboard groups these entries by `group` and keeps
91
+ * this order, so moving a key moves the dropdown.
92
+ */
93
+ export declare const GOOGLE_CONTACTS_OPERATION_SPECS: Record<GoogleContactsOperation, GoogleContactsOperationSpec>;
@@ -11,7 +11,7 @@
11
11
  * version; the api-side DTO gates which tier its accept-list includes.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.GOOGLE_CONTACTS_OPERATIONS = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = void 0;
14
+ exports.GOOGLE_CONTACTS_OPERATION_SPECS = exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = exports.GOOGLE_CONTACTS_OPERATION_GROUPS = exports.GOOGLE_CONTACTS_OPERATIONS = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = void 0;
15
15
  exports.isGoogleContactsOperation = isGoogleContactsOperation;
16
16
  exports.GOOGLE_CONTACTS_OPERATIONS_V1 = [
17
17
  'listContacts',
@@ -40,3 +40,328 @@ function isGoogleContactsOperation(value) {
40
40
  return (typeof value === 'string' &&
41
41
  exports.GOOGLE_CONTACTS_OPERATIONS.includes(value));
42
42
  }
43
+ /** Group headings of the operation dropdown, in display order. */
44
+ exports.GOOGLE_CONTACTS_OPERATION_GROUPS = [
45
+ 'Read',
46
+ 'Write',
47
+ 'Groups',
48
+ 'Bulk',
49
+ 'Group management',
50
+ ];
51
+ /**
52
+ * personFields the read operations request when nobody overrides them. Lived in
53
+ * the dashboard page until this schema landed; it belongs next to the field
54
+ * that defaults to it.
55
+ */
56
+ exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = 'names,emailAddresses,phoneNumbers,addresses,organizations,memberships';
57
+ /* Fields several operations share. Each takes overrides because the same key
58
+ can mean different things: `resourceName` is a contact in five operations
59
+ and a contact group in two. */
60
+ const contactResourceName = (over = {}) => ({
61
+ name: 'resourceName',
62
+ label: 'Contact resourceName',
63
+ type: 'string',
64
+ required: true,
65
+ placeholder: 'people/c12345 OR {{payload.resourceName}}',
66
+ description: 'The People API id of the contact (e.g. people/c123456789012345). Search or list a contact upstream to discover it, then pass it through.',
67
+ ...over,
68
+ });
69
+ /** The **group** under its own key, for the two group-management operations. */
70
+ const groupResourceNameAsResourceName = (over = {}) => ({
71
+ name: 'resourceName',
72
+ label: 'Group resourceName',
73
+ type: 'string',
74
+ required: true,
75
+ placeholder: 'contactGroups/12345',
76
+ ...over,
77
+ });
78
+ /** The group alongside a contact, so it needs its own key. */
79
+ const groupResourceName = (over = {}) => ({
80
+ name: 'groupResourceName',
81
+ label: 'Group resourceName',
82
+ type: 'string',
83
+ required: true,
84
+ placeholder: 'contactGroups/12345 OR {{payload.groupId}}',
85
+ description: "The contactGroup's resourceName. Use listContactGroups upstream to discover it, or hardcode if you always target the same group.",
86
+ ...over,
87
+ });
88
+ const pageSize = () => ({
89
+ name: 'pageSize',
90
+ label: 'Page size',
91
+ type: 'number',
92
+ default: 100,
93
+ min: 1,
94
+ max: 1000,
95
+ description: 'How many results per page. Use a Loop node downstream to paginate further with `nextPageToken`.',
96
+ });
97
+ /* Shown last on the three read operations, pre-filled with the default rather
98
+ than merely placeholder-hinted — that is how it has always looked. */
99
+ const personFields = () => ({
100
+ name: 'personFields',
101
+ label: 'Person fields (advanced, optional)',
102
+ type: 'plain',
103
+ default: exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS,
104
+ placeholder: exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS,
105
+ description: 'Comma-separated People API fields to include in the response. Default covers the 95% of cases — only change when you need photos, biographies, or to slim the payload.',
106
+ });
107
+ /** The contact body, identical on create and update. */
108
+ const contactBody = () => [
109
+ {
110
+ name: 'fullName',
111
+ label: 'Full name',
112
+ type: 'string',
113
+ placeholder: '{{payload.name}} OR Ariel Pérez',
114
+ description: 'Auto-splits into given + family name. Override with the separate fields below if the heuristic mangles compound surnames.',
115
+ },
116
+ {
117
+ name: 'givenName',
118
+ label: 'Given name (optional)',
119
+ type: 'string',
120
+ placeholder: '{{payload.firstName}}',
121
+ },
122
+ {
123
+ name: 'familyName',
124
+ label: 'Family name (optional)',
125
+ type: 'string',
126
+ placeholder: '{{payload.lastName}}',
127
+ },
128
+ {
129
+ name: 'emailAddresses',
130
+ label: 'Email addresses (comma-separated)',
131
+ type: 'csvList',
132
+ placeholder: '{{payload.email}}',
133
+ },
134
+ {
135
+ name: 'phoneNumbers',
136
+ label: 'Phone numbers (comma-separated)',
137
+ type: 'csvList',
138
+ placeholder: '{{payload.phone}}',
139
+ },
140
+ {
141
+ name: 'notes',
142
+ label: 'Notes (optional)',
143
+ type: 'text',
144
+ rows: 3,
145
+ placeholder: 'VIP customer · {{payload.context}}',
146
+ },
147
+ ];
148
+ const BATCH_CONTACTS_TEMPLATING = 'Templating works inside string values ({{payload.x}}).';
149
+ /**
150
+ * Key order below is the dropdown order **within each group** — most-common
151
+ * operation first, which is why Read starts at searchContacts and not at the
152
+ * enum's listContacts. The dashboard groups these entries by `group` and keeps
153
+ * this order, so moving a key moves the dropdown.
154
+ */
155
+ exports.GOOGLE_CONTACTS_OPERATION_SPECS = {
156
+ // ── Read ───────────────────────────────────────────────────────────
157
+ searchContacts: {
158
+ label: 'Search contacts',
159
+ description: 'Free-text search by name, email, phone, or organization.',
160
+ apiMethod: 'people.searchContacts',
161
+ scope: 'read',
162
+ group: 'Read',
163
+ params: [
164
+ {
165
+ name: 'query',
166
+ label: 'Query',
167
+ type: 'string',
168
+ required: true,
169
+ placeholder: '{{payload.email}} OR Ariel OR @company.com',
170
+ description: 'Free-text. Matches against names, email addresses, phone numbers, and organizations.',
171
+ },
172
+ personFields(),
173
+ ],
174
+ },
175
+ listContacts: {
176
+ label: 'List contacts',
177
+ description: "Paginated list of every contact in the user's address book.",
178
+ apiMethod: 'people.connections.list',
179
+ scope: 'read',
180
+ group: 'Read',
181
+ params: [pageSize(), personFields()],
182
+ },
183
+ getContact: {
184
+ label: 'Get contact',
185
+ description: 'Fetch a single contact by its resourceName (e.g. people/c12345).',
186
+ apiMethod: 'people.get',
187
+ scope: 'read',
188
+ group: 'Read',
189
+ params: [contactResourceName(), personFields()],
190
+ },
191
+ // ── Write ──────────────────────────────────────────────────────────
192
+ createContact: {
193
+ label: 'Create contact',
194
+ description: 'Add a new contact. Pass fullName for auto split into given/family.',
195
+ apiMethod: 'people.createContact',
196
+ scope: 'write',
197
+ group: 'Write',
198
+ params: contactBody(),
199
+ },
200
+ updateContact: {
201
+ label: 'Update contact',
202
+ description: 'Update fields on an existing contact. Update mask is auto-derived.',
203
+ apiMethod: 'people.updateContact',
204
+ scope: 'write',
205
+ group: 'Write',
206
+ params: [contactResourceName(), ...contactBody()],
207
+ },
208
+ deleteContact: {
209
+ label: 'Delete contact',
210
+ description: 'Remove a contact by resourceName. Irreversible.',
211
+ apiMethod: 'people.deleteContact',
212
+ scope: 'write',
213
+ group: 'Write',
214
+ params: [contactResourceName()],
215
+ },
216
+ // ── Groups ─────────────────────────────────────────────────────────
217
+ listContactGroups: {
218
+ label: 'List groups',
219
+ description: 'List all contact groups (labels) the user has.',
220
+ apiMethod: 'contactGroups.list',
221
+ scope: 'read',
222
+ group: 'Groups',
223
+ params: [pageSize()],
224
+ },
225
+ addContactToGroup: {
226
+ label: 'Add to group',
227
+ description: 'Idempotent — no error if the contact is already in the group.',
228
+ apiMethod: 'contactGroups.members.modify',
229
+ scope: 'write',
230
+ group: 'Groups',
231
+ params: [contactResourceName(), groupResourceName()],
232
+ },
233
+ removeContactFromGroup: {
234
+ label: 'Remove from group',
235
+ description: "Idempotent — no error if the contact wasn't in the group.",
236
+ apiMethod: 'contactGroups.members.modify',
237
+ scope: 'write',
238
+ group: 'Groups',
239
+ params: [contactResourceName(), groupResourceName()],
240
+ },
241
+ // ── Bulk ───────────────────────────────────────────────────────────
242
+ batchCreateContacts: {
243
+ label: 'Batch create',
244
+ description: 'Bulk-create up to 200 contacts in one call. Pass a `contacts` array.',
245
+ apiMethod: 'people.batchCreateContacts',
246
+ scope: 'write',
247
+ group: 'Bulk',
248
+ params: [
249
+ {
250
+ name: 'contacts',
251
+ label: 'Contacts (JSON array)',
252
+ type: 'json',
253
+ required: true,
254
+ rows: 10,
255
+ placeholder: `[
256
+ { "fullName": "Ariel Pérez", "emailAddresses": ["a@b.c"] },
257
+ { "fullName": "Bob Smith", "phoneNumbers": ["+15551234"] }
258
+ ]`,
259
+ description: `Up to 200 contacts per call. Each entry uses the same shape as createContact (fullName / emailAddresses / phoneNumbers / notes). ${BATCH_CONTACTS_TEMPLATING}`,
260
+ },
261
+ ],
262
+ },
263
+ batchUpdateContacts: {
264
+ label: 'Batch update',
265
+ description: 'Bulk-update up to 200 contacts. Each entry needs a resourceName.',
266
+ apiMethod: 'people.batchUpdateContacts',
267
+ scope: 'write',
268
+ group: 'Bulk',
269
+ params: [
270
+ {
271
+ name: 'contacts',
272
+ label: 'Contacts (JSON array)',
273
+ type: 'json',
274
+ required: true,
275
+ rows: 10,
276
+ placeholder: `[
277
+ { "resourceName": "people/c12345", "fullName": "New Name" }
278
+ ]`,
279
+ description: `Up to 200 contacts per call. Each entry uses the same shape as updateContact — must include \`resourceName\`. ${BATCH_CONTACTS_TEMPLATING}`,
280
+ },
281
+ {
282
+ name: 'updateMask',
283
+ label: 'Update mask',
284
+ type: 'plain',
285
+ required: true,
286
+ placeholder: 'names,emailAddresses',
287
+ description: 'Comma-separated People API field paths that the bulk update is allowed to replace. Required.',
288
+ },
289
+ ],
290
+ },
291
+ batchDeleteContacts: {
292
+ label: 'Batch delete',
293
+ description: 'Bulk-delete up to 500 contacts by resourceName. Irreversible.',
294
+ apiMethod: 'people.batchDeleteContacts',
295
+ scope: 'write',
296
+ group: 'Bulk',
297
+ params: [
298
+ {
299
+ name: 'resourceNames',
300
+ label: 'Resource names (JSON array)',
301
+ type: 'json',
302
+ required: true,
303
+ rows: 6,
304
+ placeholder: `[
305
+ "people/c12345",
306
+ "people/c67890"
307
+ ]`,
308
+ description: 'Up to 500 resourceNames per call. Irreversible.',
309
+ },
310
+ ],
311
+ },
312
+ // ── Group management ───────────────────────────────────────────────
313
+ createContactGroup: {
314
+ label: 'Create group',
315
+ description: 'Create a new contact group (label).',
316
+ apiMethod: 'contactGroups.create',
317
+ scope: 'write',
318
+ group: 'Group management',
319
+ params: [
320
+ {
321
+ name: 'name',
322
+ label: 'Group name',
323
+ type: 'string',
324
+ required: true,
325
+ placeholder: '{{payload.groupName}} OR Customers',
326
+ description: 'Display name for the new group.',
327
+ },
328
+ ],
329
+ },
330
+ updateContactGroup: {
331
+ label: 'Rename group',
332
+ description: 'Rename an existing group.',
333
+ apiMethod: 'contactGroups.update',
334
+ scope: 'write',
335
+ group: 'Group management',
336
+ /* Name before resourceName: odd, but it is the order the page has always
337
+ rendered, and reordering is a separate decision. */
338
+ params: [
339
+ {
340
+ name: 'name',
341
+ label: 'Group name',
342
+ type: 'string',
343
+ required: true,
344
+ placeholder: '{{payload.groupName}} OR Customers',
345
+ description: 'New display name (renames the existing group).',
346
+ },
347
+ groupResourceNameAsResourceName(),
348
+ ],
349
+ },
350
+ deleteContactGroup: {
351
+ label: 'Delete group',
352
+ description: 'Delete a group. Optionally delete its contacts too.',
353
+ apiMethod: 'contactGroups.delete',
354
+ scope: 'write',
355
+ group: 'Group management',
356
+ params: [
357
+ groupResourceNameAsResourceName(),
358
+ {
359
+ name: 'deleteContacts',
360
+ label: 'Also delete contacts in the group?',
361
+ type: 'boolean',
362
+ switchLabel: 'Delete the contacts that are members of this group too.',
363
+ description: 'Off (default) — contacts stay, just lose this group membership. On — every contact in the group is also deleted. Irreversible.',
364
+ },
365
+ ],
366
+ },
367
+ };
package/dist/index.d.ts CHANGED
@@ -19,7 +19,9 @@ export { DISCORD_OPERATIONS, DISCORD_OPERATION_SPECS, isDiscordOperation, } from
19
19
  export type { DiscordOperation, DiscordParamSpec, DiscordOperationSpec, } from './discord-operations';
20
20
  export { SLACK_OPERATIONS, SLACK_OPERATION_SPECS, isSlackOperation, } from './slack-operations';
21
21
  export type { SlackOperation, SlackParamSpec, SlackOperationSpec, } from './slack-operations';
22
- export { GOOGLE_CONTACTS_OPERATIONS, GOOGLE_CONTACTS_OPERATIONS_V1, GOOGLE_CONTACTS_OPERATIONS_V2, isGoogleContactsOperation, } from './google-contacts-operations';
23
- export type { GoogleContactsOperation } from './google-contacts-operations';
22
+ export { SHEETS_OPERATIONS, SHEETS_OPERATION_SPECS, isSheetsOperation, } from './sheets-operations';
23
+ export type { SheetsOperation, SheetsParamSpec, SheetsOperationSpec, } from './sheets-operations';
24
+ export { GOOGLE_CONTACTS_OPERATIONS, GOOGLE_CONTACTS_OPERATIONS_V1, GOOGLE_CONTACTS_OPERATIONS_V2, GOOGLE_CONTACTS_OPERATION_SPECS, GOOGLE_CONTACTS_OPERATION_GROUPS, GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS, isGoogleContactsOperation, } from './google-contacts-operations';
25
+ export type { GoogleContactsOperation, GoogleContactsParamSpec, GoogleContactsOperationSpec, GoogleContactsOperationGroup, } from './google-contacts-operations';
24
26
  export type { CredentialTypeRegistration, CredentialType } from './credentials';
25
27
  export { CREDENTIAL_TYPES, CREDENTIAL_TYPE_VALUES, credentialTypeValues, getCredentialType, isCredentialType, } from './credentials';
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = exports.isSlackOperation = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isDiscordOperation = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.isTelegramOperation = exports.TELEGRAM_OPERATIONS = exports.isDriveOperation = exports.DRIVE_OPERATIONS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.GMAIL_OPERATIONS = exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.getNodeRegistryEntry = exports.NODE_REGISTRY = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.resolveNodeId = exports.PREFIX_TO_TYPE = exports.NODE_UI = exports.ALL_NODE_TYPES = exports.isNodeType = exports.isTerminal = exports.canSendToNodes = exports.canReceiveFromNodes = exports.canReceiveFrom = exports.NODE_CONNECTIONS = exports.iterableMeta = exports.singleMeta = void 0;
3
+ exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = exports.GOOGLE_CONTACTS_OPERATION_GROUPS = exports.GOOGLE_CONTACTS_OPERATION_SPECS = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = exports.isSheetsOperation = exports.SHEETS_OPERATION_SPECS = exports.SHEETS_OPERATIONS = exports.isSlackOperation = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isDiscordOperation = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.isTelegramOperation = exports.TELEGRAM_OPERATIONS = exports.isDriveOperation = exports.DRIVE_OPERATIONS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.GMAIL_OPERATIONS = exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.getNodeRegistryEntry = exports.NODE_REGISTRY = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.resolveNodeId = exports.PREFIX_TO_TYPE = exports.NODE_UI = exports.ALL_NODE_TYPES = exports.isNodeType = exports.isTerminal = exports.canSendToNodes = exports.canReceiveFromNodes = exports.canReceiveFrom = exports.NODE_CONNECTIONS = exports.iterableMeta = exports.singleMeta = void 0;
4
+ exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = void 0;
4
5
  var types_1 = require("./types");
5
6
  Object.defineProperty(exports, "singleMeta", { enumerable: true, get: function () { return types_1.singleMeta; } });
6
7
  Object.defineProperty(exports, "iterableMeta", { enumerable: true, get: function () { return types_1.iterableMeta; } });
@@ -55,10 +56,17 @@ var slack_operations_1 = require("./slack-operations");
55
56
  Object.defineProperty(exports, "SLACK_OPERATIONS", { enumerable: true, get: function () { return slack_operations_1.SLACK_OPERATIONS; } });
56
57
  Object.defineProperty(exports, "SLACK_OPERATION_SPECS", { enumerable: true, get: function () { return slack_operations_1.SLACK_OPERATION_SPECS; } });
57
58
  Object.defineProperty(exports, "isSlackOperation", { enumerable: true, get: function () { return slack_operations_1.isSlackOperation; } });
59
+ var sheets_operations_1 = require("./sheets-operations");
60
+ Object.defineProperty(exports, "SHEETS_OPERATIONS", { enumerable: true, get: function () { return sheets_operations_1.SHEETS_OPERATIONS; } });
61
+ Object.defineProperty(exports, "SHEETS_OPERATION_SPECS", { enumerable: true, get: function () { return sheets_operations_1.SHEETS_OPERATION_SPECS; } });
62
+ Object.defineProperty(exports, "isSheetsOperation", { enumerable: true, get: function () { return sheets_operations_1.isSheetsOperation; } });
58
63
  var google_contacts_operations_1 = require("./google-contacts-operations");
59
64
  Object.defineProperty(exports, "GOOGLE_CONTACTS_OPERATIONS", { enumerable: true, get: function () { return google_contacts_operations_1.GOOGLE_CONTACTS_OPERATIONS; } });
60
65
  Object.defineProperty(exports, "GOOGLE_CONTACTS_OPERATIONS_V1", { enumerable: true, get: function () { return google_contacts_operations_1.GOOGLE_CONTACTS_OPERATIONS_V1; } });
61
66
  Object.defineProperty(exports, "GOOGLE_CONTACTS_OPERATIONS_V2", { enumerable: true, get: function () { return google_contacts_operations_1.GOOGLE_CONTACTS_OPERATIONS_V2; } });
67
+ Object.defineProperty(exports, "GOOGLE_CONTACTS_OPERATION_SPECS", { enumerable: true, get: function () { return google_contacts_operations_1.GOOGLE_CONTACTS_OPERATION_SPECS; } });
68
+ Object.defineProperty(exports, "GOOGLE_CONTACTS_OPERATION_GROUPS", { enumerable: true, get: function () { return google_contacts_operations_1.GOOGLE_CONTACTS_OPERATION_GROUPS; } });
69
+ Object.defineProperty(exports, "GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS", { enumerable: true, get: function () { return google_contacts_operations_1.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS; } });
62
70
  Object.defineProperty(exports, "isGoogleContactsOperation", { enumerable: true, get: function () { return google_contacts_operations_1.isGoogleContactsOperation; } });
63
71
  var credentials_1 = require("./credentials");
64
72
  Object.defineProperty(exports, "CREDENTIAL_TYPES", { enumerable: true, get: function () { return credentials_1.CREDENTIAL_TYPES; } });
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Google Sheets operation enum + form schema — single source of truth across
3
+ * the API, Dashboard, and Message Broker. Used by:
4
+ * - sheetsAction entity / DTO (api): operation field + validation
5
+ * - dashboard SheetsAction detail page: the whole operation form
6
+ *
7
+ * The api already accepts these six; the dashboard's own `SheetsAction` type
8
+ * only listed four (it never learned about appendOrUpdateRow and
9
+ * createSpreadsheet), which is exactly the drift this enum removes.
10
+ */
11
+ export declare const SHEETS_OPERATIONS: readonly ["appendRow", "updateRow", "appendOrUpdateRow", "readRange", "getRows", "createSpreadsheet"];
12
+ export type SheetsOperation = (typeof SHEETS_OPERATIONS)[number];
13
+ /** Type guard — useful when validating untrusted input (DTOs, AI tool calls). */
14
+ export declare function isSheetsOperation(value: unknown): value is SheetsOperation;
15
+ /**
16
+ * Per-operation form schema, same idea as SLACK_OPERATION_SPECS and
17
+ * GOOGLE_CONTACTS_OPERATION_SPECS: the detail page renders it with one loop
18
+ * instead of a branch per operation.
19
+ *
20
+ * Two things make this node's schema different from the others, and both are
21
+ * deliberate:
22
+ *
23
+ * 1. **The values are top-level entity fields, not an `operationConfig`
24
+ * record.** `name` here is the property on the sheetsAction document
25
+ * (`spreadsheetId`, `sheetName`, …), and `columnMapping` is a complex field.
26
+ * A renderer copied from Contacts would write to the wrong place.
27
+ * 2. **Half the fields are discovered at run time.** The column list comes from
28
+ * the chosen sheet's header row, fetched live. So the schema names the
29
+ * *picker* rather than carrying options — the same trick Discord's
30
+ * `channel` / `guild` / `role` types use.
31
+ */
32
+ export interface SheetsParamSpec {
33
+ /** Field key — the property name on the sheetsAction document. */
34
+ name: string;
35
+ /** UI label. Empty string where the control has never shown one. */
36
+ label: string;
37
+ /**
38
+ * Param type for form rendering.
39
+ *
40
+ * - `string` — one-line input with `{{payload.x}}` templating.
41
+ * - `spreadsheet` — the spreadsheet picker **plus** the "or paste ID / URL"
42
+ * input that sits under it. They are one field with two ways in, not two
43
+ * fields, and the paste box extracts the id out of a full URL.
44
+ * - `sheetTab` — the tab picker for the chosen spreadsheet. Picking a tab
45
+ * clears `columnMapping`, because the headers just changed.
46
+ * - `headerColumn` — a dropdown of the sheet's live header row.
47
+ * - `columnMapping` — the header-to-template editor. It owns the three
48
+ * states of the header fetch (loading, loaded, failed), which is what
49
+ * used to be six copies of the same conditional in the page.
50
+ */
51
+ type: 'string' | 'spreadsheet' | 'sheetTab' | 'headerColumn' | 'columnMapping';
52
+ /** Required by the operation. Metadata — the form does not gate on it. */
53
+ required?: boolean;
54
+ /** Help text under the control. Absent where the form shows none. */
55
+ description?: string;
56
+ /** Hint shown inside the input. */
57
+ placeholder?: string;
58
+ /**
59
+ * Section this field is rendered inside. Fields without one share the first
60
+ * section with the operation picker; the mapping fields sit in a second
61
+ * section whose title changes per operation ("Columns to Append",
62
+ * "Update Row", "Match & Upsert").
63
+ */
64
+ section?: string;
65
+ /** `columnMapping` only — the line shown while no column has been added. */
66
+ emptyText?: string;
67
+ }
68
+ export interface SheetsOperationSpec {
69
+ /** UI label for the operation picker. */
70
+ label: string;
71
+ /** Sub-line under the label in the picker. */
72
+ description: string;
73
+ /** googleapis call the operation maps to (sheets v4). */
74
+ apiMethod: string;
75
+ /** Parameter schema. Order matters for form rendering. */
76
+ params: SheetsParamSpec[];
77
+ }
78
+ export declare const SHEETS_OPERATION_SPECS: Record<SheetsOperation, SheetsOperationSpec>;
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ /**
3
+ * Google Sheets operation enum + form schema — single source of truth across
4
+ * the API, Dashboard, and Message Broker. Used by:
5
+ * - sheetsAction entity / DTO (api): operation field + validation
6
+ * - dashboard SheetsAction detail page: the whole operation form
7
+ *
8
+ * The api already accepts these six; the dashboard's own `SheetsAction` type
9
+ * only listed four (it never learned about appendOrUpdateRow and
10
+ * createSpreadsheet), which is exactly the drift this enum removes.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.SHEETS_OPERATION_SPECS = exports.SHEETS_OPERATIONS = void 0;
14
+ exports.isSheetsOperation = isSheetsOperation;
15
+ exports.SHEETS_OPERATIONS = [
16
+ 'appendRow',
17
+ 'updateRow',
18
+ 'appendOrUpdateRow',
19
+ 'readRange',
20
+ 'getRows',
21
+ 'createSpreadsheet',
22
+ ];
23
+ /** Type guard — useful when validating untrusted input (DTOs, AI tool calls). */
24
+ function isSheetsOperation(value) {
25
+ return (typeof value === 'string' &&
26
+ exports.SHEETS_OPERATIONS.includes(value));
27
+ }
28
+ /* Fields most operations share. The spreadsheet and its tab are the same two
29
+ controls everywhere they appear — createSpreadsheet is the only operation
30
+ that shows neither, because there is no document yet to point at. */
31
+ const spreadsheet = () => ({
32
+ name: 'spreadsheetId',
33
+ label: 'Select Spreadsheet',
34
+ type: 'spreadsheet',
35
+ required: true,
36
+ });
37
+ const sheetTab = () => ({
38
+ name: 'sheetName',
39
+ label: 'Sheet Name',
40
+ type: 'sheetTab',
41
+ required: true,
42
+ });
43
+ /** The mapping editor, whose label, section and empty line change per op. */
44
+ const columnMapping = (over = {}) => ({
45
+ name: 'columnMapping',
46
+ label: 'Columns to Update',
47
+ type: 'columnMapping',
48
+ emptyText: 'No columns selected. Add columns to update.',
49
+ ...over,
50
+ });
51
+ /** Which row to touch: the column to search in, and the value to look for. */
52
+ const matchFields = (section) => [
53
+ {
54
+ name: 'matchColumn',
55
+ label: 'Match Column',
56
+ type: 'headerColumn',
57
+ required: true,
58
+ description: 'Select the column to find the row to update',
59
+ section,
60
+ },
61
+ {
62
+ /* No static placeholder or description: both are built from the chosen
63
+ column at render time ({{payload.<column>}} and 'Template to match
64
+ against "<column>"'), so the schema cannot carry them. */
65
+ name: 'matchValueTemplate',
66
+ label: 'Match Value',
67
+ type: 'string',
68
+ required: true,
69
+ section,
70
+ },
71
+ ];
72
+ exports.SHEETS_OPERATION_SPECS = {
73
+ appendRow: {
74
+ label: 'Append Row',
75
+ description: 'Add a new row at the bottom',
76
+ apiMethod: 'spreadsheets.values.batchUpdate',
77
+ params: [
78
+ spreadsheet(),
79
+ sheetTab(),
80
+ columnMapping({
81
+ /* The append editor has never carried a label of its own — the section
82
+ title above it already says "Columns to Append". */
83
+ label: '',
84
+ section: 'Columns to Append',
85
+ emptyText: 'No columns selected. Add columns to populate in the new row.',
86
+ }),
87
+ ],
88
+ },
89
+ updateRow: {
90
+ label: 'Update Row',
91
+ description: 'Update cells in a specific range',
92
+ apiMethod: 'spreadsheets.values.batchUpdate',
93
+ params: [
94
+ spreadsheet(),
95
+ sheetTab(),
96
+ ...matchFields('Update Row'),
97
+ columnMapping({ section: 'Update Row' }),
98
+ ],
99
+ },
100
+ appendOrUpdateRow: {
101
+ label: 'Append or Update',
102
+ description: 'Update if row exists, append if not',
103
+ apiMethod: 'spreadsheets.values.batchUpdate',
104
+ /* Same fields as updateRow — only the section title differs. The label
105
+ inside still reads "Columns to Update", which is what the page has always
106
+ shown. */
107
+ params: [
108
+ spreadsheet(),
109
+ sheetTab(),
110
+ ...matchFields('Match & Upsert'),
111
+ columnMapping({ section: 'Match & Upsert' }),
112
+ ],
113
+ },
114
+ readRange: {
115
+ label: 'Read Range',
116
+ description: 'Read cells from a specific range',
117
+ apiMethod: 'spreadsheets.values.get',
118
+ params: [
119
+ spreadsheet(),
120
+ sheetTab(),
121
+ {
122
+ name: 'rangeTemplate',
123
+ label: 'Range Template',
124
+ type: 'string',
125
+ required: true,
126
+ placeholder: 'e.g. A2:D100',
127
+ },
128
+ ],
129
+ },
130
+ getRows: {
131
+ label: 'Get Rows',
132
+ description: 'Read all rows with headers as keys',
133
+ apiMethod: 'spreadsheets.values.get',
134
+ params: [spreadsheet(), sheetTab()],
135
+ },
136
+ createSpreadsheet: {
137
+ label: 'Create Spreadsheet',
138
+ description: 'Create a new Google Sheets spreadsheet',
139
+ apiMethod: 'spreadsheets.create',
140
+ /* `sheetName` means the **new document's title** here, not a tab — the one
141
+ field in this node that changes meaning *and* control between
142
+ operations. The api uses it for both the file name and its first tab. */
143
+ params: [
144
+ {
145
+ name: 'sheetName',
146
+ label: 'Spreadsheet Title',
147
+ type: 'string',
148
+ required: true,
149
+ placeholder: 'My New Spreadsheet',
150
+ description: 'Name of the new spreadsheet. Supports {{payload.field}} templates.',
151
+ },
152
+ ],
153
+ },
154
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostwebhook/node-types",
3
- "version": "1.52.6",
3
+ "version": "1.52.8",
4
4
  "description": "Shared node type definitions, connection rules, and dispatch config for HostWebhook",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",