@hostwebhook/node-types 1.61.0 → 1.63.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.
- package/dist/connections.js +3 -2
- package/dist/credentials.js +39 -0
- package/dist/dispatch.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -2
- package/dist/registry.js +10 -0
- package/dist/shopify-operations.d.ts +95 -0
- package/dist/shopify-operations.js +263 -0
- package/dist/types.d.ts +1 -1
- package/dist/ui.js +2 -0
- package/package.json +1 -1
package/dist/connections.js
CHANGED
|
@@ -18,7 +18,7 @@ exports.isNodeType = isNodeType;
|
|
|
18
18
|
const PROCESSING_OUTPUTS = [
|
|
19
19
|
'webhook', 'router', 'filter', 'transform', 'cache', 'code', 'rateLimiter',
|
|
20
20
|
'aggregator', 'conditional', 'delay', 'schemaValidator', 'split', 'markdown', 'fileTransform', 'limit',
|
|
21
|
-
'emailAction', 'gmailAction', 'httpAction', 'mongoAction', 'postgresAction', 'notificationAction', 'sheetsAction', 'calendarAction', 'docsAction', 'driveAction', 'firecrawlAction', 'telegramAction', 'whatsappAction', 'discordAction', 'slackAction', 'googleContactsAction', 'googleAnalyticsAction', 'notionAction', 'vectorStore', 'rssAction', 'socialMediaAction', 'mailchimpAction', 'loop',
|
|
21
|
+
'emailAction', 'gmailAction', 'httpAction', 'mongoAction', 'postgresAction', 'notificationAction', 'sheetsAction', 'calendarAction', 'docsAction', 'driveAction', 'firecrawlAction', 'telegramAction', 'whatsappAction', 'discordAction', 'slackAction', 'googleContactsAction', 'googleAnalyticsAction', 'notionAction', 'vectorStore', 'rssAction', 'socialMediaAction', 'mailchimpAction', 'shopifyAction', 'loop',
|
|
22
22
|
];
|
|
23
23
|
/** Standard processing input sources */
|
|
24
24
|
const STANDARD_INPUTS = [
|
|
@@ -26,7 +26,7 @@ const STANDARD_INPUTS = [
|
|
|
26
26
|
'cache', 'code', 'rateLimiter', 'aggregator', 'conditional', 'delay', 'schemaValidator', 'split', 'loop', 'markdown', 'fileTransform', 'limit',
|
|
27
27
|
];
|
|
28
28
|
/** Action nodes that can chain (non-terminal) */
|
|
29
|
-
const CHAINABLE_ACTIONS = ['httpAction', 'mongoAction', 'postgresAction', 'sheetsAction', 'calendarAction', 'docsAction', 'driveAction', 'firecrawlAction', 'telegramAction', 'whatsappAction', 'discordAction', 'slackAction', 'googleContactsAction', 'googleAnalyticsAction', 'notionAction', 'vectorStore', 'rssAction', 'socialMediaAction', 'mailchimpAction'];
|
|
29
|
+
const CHAINABLE_ACTIONS = ['httpAction', 'mongoAction', 'postgresAction', 'sheetsAction', 'calendarAction', 'docsAction', 'driveAction', 'firecrawlAction', 'telegramAction', 'whatsappAction', 'discordAction', 'slackAction', 'googleContactsAction', 'googleAnalyticsAction', 'notionAction', 'vectorStore', 'rssAction', 'socialMediaAction', 'mailchimpAction', 'shopifyAction'];
|
|
30
30
|
/** Full input list for action nodes (standard + chainable actions) */
|
|
31
31
|
const ACTION_INPUTS = [...STANDARD_INPUTS, ...CHAINABLE_ACTIONS];
|
|
32
32
|
exports.NODE_CONNECTIONS = {
|
|
@@ -78,6 +78,7 @@ exports.NODE_CONNECTIONS = {
|
|
|
78
78
|
discordAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
79
79
|
slackAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
80
80
|
mailchimpAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
81
|
+
shopifyAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
81
82
|
googleContactsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
82
83
|
googleAnalyticsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
83
84
|
notionAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
package/dist/credentials.js
CHANGED
|
@@ -74,6 +74,45 @@ exports.CREDENTIAL_TYPES = [
|
|
|
74
74
|
// autorización al pulsar Submit; la revisión de 5-10 días es para salir en
|
|
75
75
|
// su galería, o sea para que te encuentren, no para que funcione.
|
|
76
76
|
{ type: 'notion_oauth' },
|
|
77
|
+
// El OAuth de HostWebhook contra Mailchimp.
|
|
78
|
+
//
|
|
79
|
+
// Faltaba, y el síntoma no señalaba aquí. La api ya tenía el flujo entero
|
|
80
|
+
// —`mailchimp-oauth.service.ts`, su controlador, su DTO y sus presets— y el
|
|
81
|
+
// usuario autorizaba bien; lo que moría era el `create` de la credencial:
|
|
82
|
+
//
|
|
83
|
+
// ERROR [CredentialsController] Mailchimp OAuth callback failed:
|
|
84
|
+
// Credential validation failed: type: `mailchimp_oauth2` is not a valid
|
|
85
|
+
// enum value for path `type`.
|
|
86
|
+
//
|
|
87
|
+
// Porque de esta lista salen las DOS validaciones que ve ese documento: el
|
|
88
|
+
// `enum` de Mongoose en `credential.entity.ts` y el `@IsIn` del DTO. La api
|
|
89
|
+
// declaraba el string por su cuenta (`MAILCHIMP_CREDENTIAL_TYPE`) sin
|
|
90
|
+
// anotarlo `: CredentialType`, así que TypeScript lo dio por bueno y sólo
|
|
91
|
+
// reventó en producción, contra un cliente que no podía conectar su cuenta.
|
|
92
|
+
//
|
|
93
|
+
// Lleva `_oauth2` y no `_oauth` —al revés que `notion_oauth`— porque ése es
|
|
94
|
+
// el string que la api ya guarda y que el dashboard ya filtra en
|
|
95
|
+
// `components/triggers/registry.tsx` (`scopeFilter: "mailchimp_oauth2"`).
|
|
96
|
+
// Renombrarlo ahora rompería las credenciales que hayan llegado a existir:
|
|
97
|
+
// el `type` es la clave por la que los nodos las referencian.
|
|
98
|
+
{ type: 'mailchimp_oauth2' },
|
|
99
|
+
// El OAuth de HostWebhook contra Shopify.
|
|
100
|
+
//
|
|
101
|
+
// Se anota aquí ANTES de escribir el servicio, precisamente por lo que
|
|
102
|
+
// cuenta el bloque de Mailchimp de arriba: la api puede tener el flujo
|
|
103
|
+
// entero y bien, y aun así el `create` de la credencial muere con un enum
|
|
104
|
+
// de Mongoose que TypeScript no ve. Es el mismo agujero, y ya sabemos que
|
|
105
|
+
// no avisa al compilar.
|
|
106
|
+
//
|
|
107
|
+
// Lleva `_oauth2` por coherencia con `mailchimp_oauth2`, y porque el
|
|
108
|
+
// `scopeFilter` del dashboard filtra por esta cadena exacta.
|
|
109
|
+
//
|
|
110
|
+
// Una nota sobre lo que este tipo GUARDA, que aquí no se ve: el dominio de
|
|
111
|
+
// la tienda (`{shop}.myshopify.com`) viaja DENTRO del blob cifrado, igual
|
|
112
|
+
// que el `dc` de Mailchimp. Es parte del host de toda llamada y de la URL
|
|
113
|
+
// de autorización, así que una credencial de Shopify sin su dominio es
|
|
114
|
+
// inservible — no un detalle de presentación.
|
|
115
|
+
{ type: 'shopify_oauth2' },
|
|
77
116
|
];
|
|
78
117
|
/**
|
|
79
118
|
* Plain string array — what the Mongoose `enum` field and the
|
package/dist/dispatch.js
CHANGED
|
@@ -53,6 +53,7 @@ exports.NODE_DISPATCH = {
|
|
|
53
53
|
whatsappAction: { service: 'whatsappActionsService', collection: 'whatsappactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
54
54
|
discordAction: { service: 'discordActionsService', collection: 'discordactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
55
55
|
mailchimpAction: { service: 'mailchimpActionsService', collection: 'mailchimpactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
56
|
+
shopifyAction: { service: 'shopifyActionsService', collection: 'shopifyactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
56
57
|
slackAction: { service: 'slackActionsService', collection: 'slackactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
57
58
|
googleContactsAction: { service: 'googleContactsActionsService', collection: 'googlecontactsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
58
59
|
googleAnalyticsAction: { service: 'googleAnalyticsActionsService', collection: 'googleanalyticsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export { DISCORD_OPERATIONS, DISCORD_OPERATION_SPECS, isDiscordOperation, } from
|
|
|
26
26
|
export type { DiscordOperation, DiscordParamSpec, DiscordOperationSpec, } from './discord-operations';
|
|
27
27
|
export { MAILCHIMP_OPERATIONS, MAILCHIMP_OPERATION_SPECS, MAILCHIMP_CONTACT_STATUSES, isMailchimpOperation, } from './mailchimp-operations';
|
|
28
28
|
export type { MailchimpOperation, MailchimpContactStatus, MailchimpParamSpec, MailchimpOperationSpec, } from './mailchimp-operations';
|
|
29
|
+
export { SHOPIFY_OPERATIONS, SHOPIFY_OPERATION_SPECS, SHOPIFY_TAGGABLE_RESOURCES, SHOPIFY_SEARCHABLE_RESOURCES, isShopifyOperation, } from './shopify-operations';
|
|
30
|
+
export type { ShopifyOperation, ShopifyTaggableResource, ShopifySearchableResource, ShopifyParamSpec, ShopifyOperationSpec, } from './shopify-operations';
|
|
29
31
|
export { SLACK_OPERATIONS, SLACK_OPERATION_SPECS, isSlackOperation, } from './slack-operations';
|
|
30
32
|
export type { SlackOperation, SlackParamSpec, SlackOperationSpec, } from './slack-operations';
|
|
31
33
|
export { SHEETS_OPERATIONS, SHEETS_OPERATION_SPECS, isSheetsOperation, } from './sheets-operations';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DRIVE_OPERATION_SPECS = exports.DRIVE_OPERATIONS = exports.GOOGLE_CALENDAR_TOOLKIT_BY_TOOL_NAME = exports.GOOGLE_CALENDAR_TOOLKIT_SPECS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATION_SPECS = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.resolveGmailSendFields = exports.GMAIL_SEND_LEGACY_FIELDS = exports.NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME = exports.GMAIL_TOOLKIT_BY_TOOL_NAME = exports.NATIVE_EMAIL_TOOLKIT_SPECS = exports.GMAIL_SEND_AND_WAIT_TOOL_SPEC = exports.GMAIL_ALL_TOOLKIT_SPECS = exports.GMAIL_TOOLKIT_SPECS = exports.GMAIL_TOOLKIT_OPERATIONS = exports.GMAIL_DROPDOWN_OPERATIONS = exports.GMAIL_OPERATION_GROUPS = exports.GMAIL_OPERATION_SPECS = exports.GMAIL_OPERATIONS = exports.versionCatalogErrors = exports.fieldsLost = exports.fieldsLostBetween = exports.currentVersion = exports.versionSpec = exports.versionsOf = exports.isVersioned = 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.
|
|
5
|
-
exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.getModelLabel = exports.getDefaultModel = exports.getModelsFor = exports.MODEL_CONTEXT_WINDOWS = exports.LLM_MODELS = exports.LLM_PROVIDERS = exports.DOCS_TOOLKIT_DEFAULTABLE = exports.DOCS_TOOLKIT_BY_TOOL_NAME = exports.DOCS_TOOLKIT_SPECS = exports.isDocsOperation = void 0;
|
|
4
|
+
exports.isPostgresOperation = exports.POSTGRES_OPERATION_SPECS = exports.POSTGRES_MODES = exports.POSTGRES_OPERATIONS = exports.isNotionOperation = exports.NOTION_DROPDOWN_OPERATIONS = exports.NOTION_OPERATION_SPECS = exports.NOTION_OPERATIONS = exports.isGoogleAnalyticsOperation = exports.GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS = exports.GOOGLE_ANALYTICS_OPERATION_SPECS = exports.GOOGLE_ANALYTICS_OPERATIONS = 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.SHEETS_TOOLKIT_DEFAULTABLE = exports.SHEETS_TOOLKIT_BY_TOOL_NAME = exports.SHEETS_TOOLKIT_SPECS = exports.isSheetsOperation = exports.SHEETS_OPERATION_SPECS = exports.SHEETS_OPERATIONS = exports.isSlackOperation = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isShopifyOperation = exports.SHOPIFY_SEARCHABLE_RESOURCES = exports.SHOPIFY_TAGGABLE_RESOURCES = exports.SHOPIFY_OPERATION_SPECS = exports.SHOPIFY_OPERATIONS = exports.isMailchimpOperation = exports.MAILCHIMP_CONTACT_STATUSES = exports.MAILCHIMP_OPERATION_SPECS = exports.MAILCHIMP_OPERATIONS = exports.isDiscordOperation = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = exports.TELEGRAM_TOOLKIT_SPECS = exports.isTelegramOperation = exports.TELEGRAM_OPERATION_SPECS = exports.TELEGRAM_OPERATIONS = exports.DRIVE_TOOLKIT_BY_TOOL_NAME = exports.DRIVE_TOOLKIT_SPECS = exports.isDriveOperation = void 0;
|
|
5
|
+
exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.getModelLabel = exports.getDefaultModel = exports.getModelsFor = exports.MODEL_CONTEXT_WINDOWS = exports.LLM_MODELS = exports.LLM_PROVIDERS = exports.DOCS_TOOLKIT_DEFAULTABLE = exports.DOCS_TOOLKIT_BY_TOOL_NAME = exports.DOCS_TOOLKIT_SPECS = exports.isDocsOperation = exports.DOCS_OPERATION_SPECS = exports.DOCS_OPERATIONS = exports.isMongoOperation = exports.MONGO_OPERATION_SPECS = exports.MONGO_OPERATIONS = void 0;
|
|
6
6
|
var types_1 = require("./types");
|
|
7
7
|
Object.defineProperty(exports, "singleMeta", { enumerable: true, get: function () { return types_1.singleMeta; } });
|
|
8
8
|
Object.defineProperty(exports, "iterableMeta", { enumerable: true, get: function () { return types_1.iterableMeta; } });
|
|
@@ -92,6 +92,12 @@ Object.defineProperty(exports, "MAILCHIMP_OPERATIONS", { enumerable: true, get:
|
|
|
92
92
|
Object.defineProperty(exports, "MAILCHIMP_OPERATION_SPECS", { enumerable: true, get: function () { return mailchimp_operations_1.MAILCHIMP_OPERATION_SPECS; } });
|
|
93
93
|
Object.defineProperty(exports, "MAILCHIMP_CONTACT_STATUSES", { enumerable: true, get: function () { return mailchimp_operations_1.MAILCHIMP_CONTACT_STATUSES; } });
|
|
94
94
|
Object.defineProperty(exports, "isMailchimpOperation", { enumerable: true, get: function () { return mailchimp_operations_1.isMailchimpOperation; } });
|
|
95
|
+
var shopify_operations_1 = require("./shopify-operations");
|
|
96
|
+
Object.defineProperty(exports, "SHOPIFY_OPERATIONS", { enumerable: true, get: function () { return shopify_operations_1.SHOPIFY_OPERATIONS; } });
|
|
97
|
+
Object.defineProperty(exports, "SHOPIFY_OPERATION_SPECS", { enumerable: true, get: function () { return shopify_operations_1.SHOPIFY_OPERATION_SPECS; } });
|
|
98
|
+
Object.defineProperty(exports, "SHOPIFY_TAGGABLE_RESOURCES", { enumerable: true, get: function () { return shopify_operations_1.SHOPIFY_TAGGABLE_RESOURCES; } });
|
|
99
|
+
Object.defineProperty(exports, "SHOPIFY_SEARCHABLE_RESOURCES", { enumerable: true, get: function () { return shopify_operations_1.SHOPIFY_SEARCHABLE_RESOURCES; } });
|
|
100
|
+
Object.defineProperty(exports, "isShopifyOperation", { enumerable: true, get: function () { return shopify_operations_1.isShopifyOperation; } });
|
|
95
101
|
var slack_operations_1 = require("./slack-operations");
|
|
96
102
|
Object.defineProperty(exports, "SLACK_OPERATIONS", { enumerable: true, get: function () { return slack_operations_1.SLACK_OPERATIONS; } });
|
|
97
103
|
Object.defineProperty(exports, "SLACK_OPERATION_SPECS", { enumerable: true, get: function () { return slack_operations_1.SLACK_OPERATION_SPECS; } });
|
package/dist/registry.js
CHANGED
|
@@ -273,6 +273,16 @@ exports.NODE_REGISTRY = {
|
|
|
273
273
|
// light canvas. The brand value (#ffe01b) reads as a highlighter there.
|
|
274
274
|
color: '#c9a227', testable: true,
|
|
275
275
|
},
|
|
276
|
+
shopifyAction: {
|
|
277
|
+
type: 'shopifyAction', prefix: 'shp', label: 'Shopify',
|
|
278
|
+
group: 'Actions', detailPath: '/dashboard/shopify-actions', apiPath: '/shopify-actions',
|
|
279
|
+
stateKey: 'shopifyActions', allStateKey: 'allShopifyActions',
|
|
280
|
+
// Shopify's green, abierto un punto. El de marca (#008060) es más oscuro
|
|
281
|
+
// que cualquier otro color de este registro y se hunde contra el lienzo
|
|
282
|
+
// en modo oscuro; éste sigue leyéndose como Shopify y aguanta en los dos
|
|
283
|
+
// temas. Mismo criterio que el amarillo de Mailchimp, al revés.
|
|
284
|
+
color: '#0a9e70', testable: true,
|
|
285
|
+
},
|
|
276
286
|
telegramAction: {
|
|
277
287
|
type: 'telegramAction', prefix: 'tg', label: 'Telegram Action',
|
|
278
288
|
group: 'Actions', detailPath: '/dashboard/telegram-actions', apiPath: '/telegram-actions',
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shopify Admin API operations — single source of truth across the api, the
|
|
3
|
+
* dashboard and downstream consumers (MCP server).
|
|
4
|
+
*
|
|
5
|
+
* ## Four, out of several hundred
|
|
6
|
+
*
|
|
7
|
+
* The Admin API has hundreds of mutations. This node exposes four, and the
|
|
8
|
+
* choice is argued in `api/docs/ADR-0007-shopify.md`: a menu of everything is
|
|
9
|
+
* how a node becomes unusable, and these four cover what people actually
|
|
10
|
+
* automate — put a customer in the store, tag something, sync stock, and read
|
|
11
|
+
* back what the store already knows.
|
|
12
|
+
*
|
|
13
|
+
* Product creation and order fulfilment were considered and left out of v1.
|
|
14
|
+
* A product means variants, media and prices; a fulfilment means a location, a
|
|
15
|
+
* fulfilment service and a state machine. Each is a configuration surface of
|
|
16
|
+
* its own, not a field.
|
|
17
|
+
*
|
|
18
|
+
* ## GraphQL only, and the version is not ours to drift on
|
|
19
|
+
*
|
|
20
|
+
* REST went legacy in October 2024 and public apps have been GraphQL-only
|
|
21
|
+
* since April 2025, so there is no REST path here to fall back to. Every
|
|
22
|
+
* `apiRoute` below is a GraphQL mutation or query name, checkable against
|
|
23
|
+
* `shopify.dev/docs/api/admin-graphql`. The API version lives in ONE constant
|
|
24
|
+
* on the api side (`SHOPIFY_API_VERSION`) — never interpolated per call site.
|
|
25
|
+
*
|
|
26
|
+
* ## What was verified, and what was not (2026-08-25)
|
|
27
|
+
*
|
|
28
|
+
* Checked against the Admin GraphQL reference, not from memory:
|
|
29
|
+
*
|
|
30
|
+
* - `tagsAdd(id: ID!, tags: [String!]!)` and `tagsRemove` with the same
|
|
31
|
+
* shape. Both accept Order, DraftOrder, Customer, Product, Article and
|
|
32
|
+
* DiscountNode.
|
|
33
|
+
* - `customerCreate(input: CustomerInput!)` and `customerUpdate(input:
|
|
34
|
+
* CustomerInput!)` — for the update, **the id goes inside the input**, not
|
|
35
|
+
* as a sibling argument.
|
|
36
|
+
* - `inventoryAdjustQuantities(input: InventoryAdjustQuantitiesInput!)`, whose
|
|
37
|
+
* input requires `reason`, `name` and a `changes[]` of
|
|
38
|
+
* `{ delta, inventoryItemId, locationId }`.
|
|
39
|
+
*
|
|
40
|
+
* ⚠️ **NOT verified, and deliberately left loose below** — do not turn either
|
|
41
|
+
* into a hard-coded dropdown until someone reads the enumeration:
|
|
42
|
+
*
|
|
43
|
+
* - The full vocabulary of `reason`. Only `"correction"` is documented by
|
|
44
|
+
* example.
|
|
45
|
+
* - The full vocabulary of `name`. Only `"available"` is documented by
|
|
46
|
+
* example; the docs mention `on_hand` moving as a consequence, which is not
|
|
47
|
+
* the same as it being a valid input.
|
|
48
|
+
* - The exact search syntax for finding a customer by email
|
|
49
|
+
* (`customers(query: "email:…")`). It is the obvious shape and almost
|
|
50
|
+
* certainly right, which is exactly why it is worth confirming rather than
|
|
51
|
+
* assuming.
|
|
52
|
+
*
|
|
53
|
+
* ⚠️ **`@idempotent` is mandatory.** As of API version 2026-04,
|
|
54
|
+
* `inventoryAdjustQuantities` must be sent with the `@idempotent` directive
|
|
55
|
+
* and an idempotency key. A stock adjustment is the one operation here where a
|
|
56
|
+
* silent retry is a real inventory error, so this is not boilerplate.
|
|
57
|
+
*/
|
|
58
|
+
export declare const SHOPIFY_OPERATIONS: readonly ["upsertCustomer", "setTags", "adjustInventory", "findRecords"];
|
|
59
|
+
export type ShopifyOperation = (typeof SHOPIFY_OPERATIONS)[number];
|
|
60
|
+
/** Type guard — for DTOs and AI tool calls, where the input is untrusted. */
|
|
61
|
+
export declare function isShopifyOperation(value: unknown): value is ShopifyOperation;
|
|
62
|
+
/**
|
|
63
|
+
* The resources `tagsAdd` / `tagsRemove` accept. Taken from the mutation
|
|
64
|
+
* reference — this list is the API's, not a subset we chose.
|
|
65
|
+
*
|
|
66
|
+
* `article` and `discountNode` are left out of the node's own picker on
|
|
67
|
+
* purpose: neither is something a HostWebhook flow tags in practice, and every
|
|
68
|
+
* extra option is a field the user has to read past.
|
|
69
|
+
*/
|
|
70
|
+
export declare const SHOPIFY_TAGGABLE_RESOURCES: readonly ["order", "customer", "product", "draftOrder"];
|
|
71
|
+
export type ShopifyTaggableResource = (typeof SHOPIFY_TAGGABLE_RESOURCES)[number];
|
|
72
|
+
/** What `findRecords` can read back. */
|
|
73
|
+
export declare const SHOPIFY_SEARCHABLE_RESOURCES: readonly ["orders", "customers", "products"];
|
|
74
|
+
export type ShopifySearchableResource = (typeof SHOPIFY_SEARCHABLE_RESOURCES)[number];
|
|
75
|
+
export interface ShopifyParamSpec {
|
|
76
|
+
/** Field key — also the property name on operationConfig. */
|
|
77
|
+
name: string;
|
|
78
|
+
label: string;
|
|
79
|
+
/** Param type, so the dashboard renders the right control. `location` is a
|
|
80
|
+
* picker backed by a live lookup of the store's locations; `gid` is a
|
|
81
|
+
* Shopify global id, which is almost always templated from the payload
|
|
82
|
+
* rather than typed. */
|
|
83
|
+
type: 'gid' | 'location' | 'email' | 'string' | 'number' | 'tags' | 'taggableResource' | 'searchableResource' | 'json' | 'boolean';
|
|
84
|
+
required?: boolean;
|
|
85
|
+
description: string;
|
|
86
|
+
placeholder?: string;
|
|
87
|
+
}
|
|
88
|
+
export interface ShopifyOperationSpec {
|
|
89
|
+
label: string;
|
|
90
|
+
description: string;
|
|
91
|
+
/** The GraphQL operation this maps to, so the mapping is checkable. */
|
|
92
|
+
apiRoute: string;
|
|
93
|
+
params: ShopifyParamSpec[];
|
|
94
|
+
}
|
|
95
|
+
export declare const SHOPIFY_OPERATION_SPECS: Record<ShopifyOperation, ShopifyOperationSpec>;
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shopify Admin API operations — single source of truth across the api, the
|
|
4
|
+
* dashboard and downstream consumers (MCP server).
|
|
5
|
+
*
|
|
6
|
+
* ## Four, out of several hundred
|
|
7
|
+
*
|
|
8
|
+
* The Admin API has hundreds of mutations. This node exposes four, and the
|
|
9
|
+
* choice is argued in `api/docs/ADR-0007-shopify.md`: a menu of everything is
|
|
10
|
+
* how a node becomes unusable, and these four cover what people actually
|
|
11
|
+
* automate — put a customer in the store, tag something, sync stock, and read
|
|
12
|
+
* back what the store already knows.
|
|
13
|
+
*
|
|
14
|
+
* Product creation and order fulfilment were considered and left out of v1.
|
|
15
|
+
* A product means variants, media and prices; a fulfilment means a location, a
|
|
16
|
+
* fulfilment service and a state machine. Each is a configuration surface of
|
|
17
|
+
* its own, not a field.
|
|
18
|
+
*
|
|
19
|
+
* ## GraphQL only, and the version is not ours to drift on
|
|
20
|
+
*
|
|
21
|
+
* REST went legacy in October 2024 and public apps have been GraphQL-only
|
|
22
|
+
* since April 2025, so there is no REST path here to fall back to. Every
|
|
23
|
+
* `apiRoute` below is a GraphQL mutation or query name, checkable against
|
|
24
|
+
* `shopify.dev/docs/api/admin-graphql`. The API version lives in ONE constant
|
|
25
|
+
* on the api side (`SHOPIFY_API_VERSION`) — never interpolated per call site.
|
|
26
|
+
*
|
|
27
|
+
* ## What was verified, and what was not (2026-08-25)
|
|
28
|
+
*
|
|
29
|
+
* Checked against the Admin GraphQL reference, not from memory:
|
|
30
|
+
*
|
|
31
|
+
* - `tagsAdd(id: ID!, tags: [String!]!)` and `tagsRemove` with the same
|
|
32
|
+
* shape. Both accept Order, DraftOrder, Customer, Product, Article and
|
|
33
|
+
* DiscountNode.
|
|
34
|
+
* - `customerCreate(input: CustomerInput!)` and `customerUpdate(input:
|
|
35
|
+
* CustomerInput!)` — for the update, **the id goes inside the input**, not
|
|
36
|
+
* as a sibling argument.
|
|
37
|
+
* - `inventoryAdjustQuantities(input: InventoryAdjustQuantitiesInput!)`, whose
|
|
38
|
+
* input requires `reason`, `name` and a `changes[]` of
|
|
39
|
+
* `{ delta, inventoryItemId, locationId }`.
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ **NOT verified, and deliberately left loose below** — do not turn either
|
|
42
|
+
* into a hard-coded dropdown until someone reads the enumeration:
|
|
43
|
+
*
|
|
44
|
+
* - The full vocabulary of `reason`. Only `"correction"` is documented by
|
|
45
|
+
* example.
|
|
46
|
+
* - The full vocabulary of `name`. Only `"available"` is documented by
|
|
47
|
+
* example; the docs mention `on_hand` moving as a consequence, which is not
|
|
48
|
+
* the same as it being a valid input.
|
|
49
|
+
* - The exact search syntax for finding a customer by email
|
|
50
|
+
* (`customers(query: "email:…")`). It is the obvious shape and almost
|
|
51
|
+
* certainly right, which is exactly why it is worth confirming rather than
|
|
52
|
+
* assuming.
|
|
53
|
+
*
|
|
54
|
+
* ⚠️ **`@idempotent` is mandatory.** As of API version 2026-04,
|
|
55
|
+
* `inventoryAdjustQuantities` must be sent with the `@idempotent` directive
|
|
56
|
+
* and an idempotency key. A stock adjustment is the one operation here where a
|
|
57
|
+
* silent retry is a real inventory error, so this is not boilerplate.
|
|
58
|
+
*/
|
|
59
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
60
|
+
exports.SHOPIFY_OPERATION_SPECS = exports.SHOPIFY_SEARCHABLE_RESOURCES = exports.SHOPIFY_TAGGABLE_RESOURCES = exports.SHOPIFY_OPERATIONS = void 0;
|
|
61
|
+
exports.isShopifyOperation = isShopifyOperation;
|
|
62
|
+
exports.SHOPIFY_OPERATIONS = [
|
|
63
|
+
'upsertCustomer',
|
|
64
|
+
'setTags',
|
|
65
|
+
'adjustInventory',
|
|
66
|
+
'findRecords',
|
|
67
|
+
];
|
|
68
|
+
/** Type guard — for DTOs and AI tool calls, where the input is untrusted. */
|
|
69
|
+
function isShopifyOperation(value) {
|
|
70
|
+
return (typeof value === 'string' &&
|
|
71
|
+
exports.SHOPIFY_OPERATIONS.includes(value));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The resources `tagsAdd` / `tagsRemove` accept. Taken from the mutation
|
|
75
|
+
* reference — this list is the API's, not a subset we chose.
|
|
76
|
+
*
|
|
77
|
+
* `article` and `discountNode` are left out of the node's own picker on
|
|
78
|
+
* purpose: neither is something a HostWebhook flow tags in practice, and every
|
|
79
|
+
* extra option is a field the user has to read past.
|
|
80
|
+
*/
|
|
81
|
+
exports.SHOPIFY_TAGGABLE_RESOURCES = [
|
|
82
|
+
'order',
|
|
83
|
+
'customer',
|
|
84
|
+
'product',
|
|
85
|
+
'draftOrder',
|
|
86
|
+
];
|
|
87
|
+
/** What `findRecords` can read back. */
|
|
88
|
+
exports.SHOPIFY_SEARCHABLE_RESOURCES = [
|
|
89
|
+
'orders',
|
|
90
|
+
'customers',
|
|
91
|
+
'products',
|
|
92
|
+
];
|
|
93
|
+
exports.SHOPIFY_OPERATION_SPECS = {
|
|
94
|
+
upsertCustomer: {
|
|
95
|
+
label: 'Add or update customer',
|
|
96
|
+
description: 'Look the customer up by email, then create them or update the fields you set.',
|
|
97
|
+
apiRoute: 'mutation customerCreate / customerUpdate (lookup by email first)',
|
|
98
|
+
params: [
|
|
99
|
+
{
|
|
100
|
+
name: 'email',
|
|
101
|
+
label: 'Email address',
|
|
102
|
+
type: 'email',
|
|
103
|
+
required: true,
|
|
104
|
+
description: 'Identifies the customer. This is what decides between creating and updating — there is no separate "id" to keep.',
|
|
105
|
+
placeholder: '{{payload.email}}',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'firstName',
|
|
109
|
+
label: 'First name',
|
|
110
|
+
type: 'string',
|
|
111
|
+
description: 'Left untouched on an existing customer if you leave it empty.',
|
|
112
|
+
placeholder: '{{payload.firstName}}',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'lastName',
|
|
116
|
+
label: 'Last name',
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: 'Left untouched on an existing customer if you leave it empty.',
|
|
119
|
+
placeholder: '{{payload.lastName}}',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'phone',
|
|
123
|
+
label: 'Phone',
|
|
124
|
+
type: 'string',
|
|
125
|
+
description: 'Shopify wants it in E.164 (+52…). A malformed number fails the whole mutation, not just the field.',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'tags',
|
|
129
|
+
label: 'Tags',
|
|
130
|
+
type: 'tags',
|
|
131
|
+
description: 'Tags to set on the customer. Note these REPLACE the customer tags — to add without losing the existing ones, use the tag operation instead.',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'note',
|
|
135
|
+
label: 'Note',
|
|
136
|
+
type: 'string',
|
|
137
|
+
description: 'Internal note, visible to staff in the Shopify admin.',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: 'emailMarketingConsent',
|
|
141
|
+
label: 'Email marketing consent',
|
|
142
|
+
type: 'json',
|
|
143
|
+
description: 'Leave this EMPTY unless the customer actually consented. Omitting it leaves their consent exactly as it was; writing a subscribed state for someone who never opted in is a legal problem, not a technical one. Shape: {"marketingState":"SUBSCRIBED","marketingOptInLevel":"SINGLE_OPT_IN"}.',
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
setTags: {
|
|
148
|
+
label: 'Add or remove tags',
|
|
149
|
+
description: 'Tag an order, a customer or a product. Adding and removing are separate calls, so one node does one of the two.',
|
|
150
|
+
apiRoute: 'mutation tagsAdd / tagsRemove',
|
|
151
|
+
params: [
|
|
152
|
+
{
|
|
153
|
+
name: 'resource',
|
|
154
|
+
label: 'What to tag',
|
|
155
|
+
type: 'taggableResource',
|
|
156
|
+
required: true,
|
|
157
|
+
description: 'Shopify tags many resource types through the same mutation; this only picks which id you are passing.',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'resourceId',
|
|
161
|
+
label: 'Resource id',
|
|
162
|
+
type: 'gid',
|
|
163
|
+
required: true,
|
|
164
|
+
description: 'The Shopify global id, e.g. gid://shopify/Order/1234. A Shopify trigger gives you this directly in the payload.',
|
|
165
|
+
placeholder: '{{payload.admin_graphql_api_id}}',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'tags',
|
|
169
|
+
label: 'Tags',
|
|
170
|
+
type: 'tags',
|
|
171
|
+
required: true,
|
|
172
|
+
description: 'One or more tags.',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'remove',
|
|
176
|
+
label: 'Remove instead of add',
|
|
177
|
+
type: 'boolean',
|
|
178
|
+
description: 'Off adds the tags (tagsAdd); on removes them (tagsRemove). Unlike Mailchimp, Shopify cannot do both in one call.',
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
adjustInventory: {
|
|
183
|
+
label: 'Adjust inventory',
|
|
184
|
+
description: 'Move stock by a delta at one location. For the case where the real stock lives in another system.',
|
|
185
|
+
apiRoute: 'mutation inventoryAdjustQuantities',
|
|
186
|
+
params: [
|
|
187
|
+
{
|
|
188
|
+
name: 'inventoryItemId',
|
|
189
|
+
label: 'Inventory item id',
|
|
190
|
+
type: 'gid',
|
|
191
|
+
required: true,
|
|
192
|
+
description: 'gid://shopify/InventoryItem/1234. This is the inventory item, NOT the product or the variant — a variant points at one.',
|
|
193
|
+
placeholder: '{{payload.inventory_item_id}}',
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: 'locationId',
|
|
197
|
+
label: 'Location',
|
|
198
|
+
type: 'location',
|
|
199
|
+
required: true,
|
|
200
|
+
description: 'Which location the stock moves at. A store with one location still has to name it.',
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: 'delta',
|
|
204
|
+
label: 'Change by',
|
|
205
|
+
type: 'number',
|
|
206
|
+
required: true,
|
|
207
|
+
description: 'A DELTA, not a total. 5 adds five units and -5 removes five. Sending the absolute stock level here is the mistake that silently multiplies inventory.',
|
|
208
|
+
placeholder: '{{payload.delta}}',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: 'name',
|
|
212
|
+
label: 'Quantity type',
|
|
213
|
+
type: 'string',
|
|
214
|
+
required: true,
|
|
215
|
+
description: 'Which quantity is being changed. "available" is the documented value; check the reference before using another.',
|
|
216
|
+
placeholder: 'available',
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: 'reason',
|
|
220
|
+
label: 'Reason',
|
|
221
|
+
type: 'string',
|
|
222
|
+
required: true,
|
|
223
|
+
description: 'Why the stock moved — it lands in the store\'s inventory history. "correction" is the documented value; check the reference before using another.',
|
|
224
|
+
placeholder: 'correction',
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: 'referenceDocumentUri',
|
|
228
|
+
label: 'Reference',
|
|
229
|
+
type: 'string',
|
|
230
|
+
description: 'Optional audit-trail link back to whatever caused the change, so someone reading Shopify\'s inventory history can find it.',
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
findRecords: {
|
|
235
|
+
label: 'Find orders, customers or products',
|
|
236
|
+
description: 'Read back what the store knows, to enrich a payload that came from somewhere else.',
|
|
237
|
+
apiRoute: 'query orders / customers / products (with search syntax)',
|
|
238
|
+
params: [
|
|
239
|
+
{
|
|
240
|
+
name: 'resource',
|
|
241
|
+
label: 'What to look for',
|
|
242
|
+
type: 'searchableResource',
|
|
243
|
+
required: true,
|
|
244
|
+
description: 'Which collection to search.',
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: 'query',
|
|
248
|
+
label: 'Search',
|
|
249
|
+
type: 'string',
|
|
250
|
+
required: true,
|
|
251
|
+
description: 'Shopify search syntax, e.g. email:someone@example.com or financial_status:paid. Empty would return the newest records, which is rarely what anyone means.',
|
|
252
|
+
placeholder: 'email:{{payload.email}}',
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'first',
|
|
256
|
+
label: 'How many',
|
|
257
|
+
type: 'number',
|
|
258
|
+
description: 'Defaults to 1, which is the enrichment case. Asking for many costs more against the query-cost budget, not more calls.',
|
|
259
|
+
placeholder: '1',
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
},
|
|
263
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** All valid node type identifiers */
|
|
2
|
-
export type NodeType = 'webhook' | 'scheduledWorkflow' | 'chatTrigger' | 'trigger' | 'voiceAgent' | 'filter' | 'transform' | 'schemaValidator' | 'conditional' | 'delay' | 'rateLimiter' | 'aggregator' | 'cache' | 'code' | 'ai' | 'merge' | 'approval' | 'split' | 'loop' | 'markdown' | 'fileTransform' | 'limit' | 'router' | 'emailAction' | 'gmailAction' | 'httpAction' | 'mongoAction' | 'postgresAction' | 'notificationAction' | 'sheetsAction' | 'calendarAction' | 'docsAction' | 'driveAction' | 'firecrawlAction' | 'telegramAction' | 'whatsappAction' | 'discordAction' | 'slackAction' | 'googleContactsAction' | 'googleAnalyticsAction' | 'notionAction' | 'vectorStore' | 'rssAction' | 'socialMediaAction' | 'mailchimpAction' | 'stickyNote';
|
|
2
|
+
export type NodeType = 'webhook' | 'scheduledWorkflow' | 'chatTrigger' | 'trigger' | 'voiceAgent' | 'filter' | 'transform' | 'schemaValidator' | 'conditional' | 'delay' | 'rateLimiter' | 'aggregator' | 'cache' | 'code' | 'ai' | 'merge' | 'approval' | 'split' | 'loop' | 'markdown' | 'fileTransform' | 'limit' | 'router' | 'emailAction' | 'gmailAction' | 'httpAction' | 'mongoAction' | 'postgresAction' | 'notificationAction' | 'sheetsAction' | 'calendarAction' | 'docsAction' | 'driveAction' | 'firecrawlAction' | 'telegramAction' | 'whatsappAction' | 'discordAction' | 'slackAction' | 'googleContactsAction' | 'googleAnalyticsAction' | 'notionAction' | 'vectorStore' | 'rssAction' | 'socialMediaAction' | 'mailchimpAction' | 'shopifyAction' | 'stickyNote';
|
|
3
3
|
/** Node role in the pipeline */
|
|
4
4
|
export type NodeRole = 'source' | 'processing' | 'flowControl' | 'routing' | 'action' | 'monitoring';
|
|
5
5
|
/** Handle positions on the canvas node */
|
package/dist/ui.js
CHANGED
|
@@ -76,6 +76,7 @@ exports.NODE_UI = {
|
|
|
76
76
|
whatsappAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'whatsapp' } },
|
|
77
77
|
discordAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'discord' } },
|
|
78
78
|
mailchimpAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'mailchimp' } },
|
|
79
|
+
shopifyAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'shopify' } },
|
|
79
80
|
slackAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'slack' } },
|
|
80
81
|
googleContactsAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true } },
|
|
81
82
|
googleAnalyticsAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true } },
|
|
@@ -100,6 +101,7 @@ exports.PREFIX_TO_TYPE = [
|
|
|
100
101
|
{ prefix: 'dc-', type: 'discordAction', canvasType: 'discordAction' },
|
|
101
102
|
{ prefix: 'sl-', type: 'slackAction', canvasType: 'slackAction' },
|
|
102
103
|
{ prefix: 'mc-', type: 'mailchimpAction', canvasType: 'mailchimpAction' },
|
|
104
|
+
{ prefix: 'shp-', type: 'shopifyAction', canvasType: 'shopifyAction' },
|
|
103
105
|
{ prefix: 'gc-', type: 'googleContactsAction', canvasType: 'googleContactsAction' },
|
|
104
106
|
{ prefix: 'gaa-', type: 'googleAnalyticsAction', canvasType: 'googleAnalyticsAction' },
|
|
105
107
|
{ prefix: 'ntn-', type: 'notionAction', canvasType: 'notionAction' },
|
package/package.json
CHANGED