@hostwebhook/node-types 1.55.3 → 1.57.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/dispatch.js +1 -0
- package/dist/google-analytics-operations.d.ts +99 -0
- package/dist/google-analytics-operations.js +226 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +18 -2
- package/dist/registry.d.ts +103 -0
- package/dist/registry.js +125 -0
- package/dist/types.d.ts +1 -1
- package/dist/ui.js +2 -0
- package/package.json +4 -2
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', 'vectorStore', 'rssAction', 'socialMediaAction', 'loop',
|
|
21
|
+
'emailAction', 'gmailAction', 'httpAction', 'mongoAction', 'postgresAction', 'notificationAction', 'sheetsAction', 'calendarAction', 'docsAction', 'driveAction', 'firecrawlAction', 'telegramAction', 'whatsappAction', 'discordAction', 'slackAction', 'googleContactsAction', 'googleAnalyticsAction', 'vectorStore', 'rssAction', 'socialMediaAction', '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', 'vectorStore', 'rssAction', 'socialMediaAction'];
|
|
29
|
+
const CHAINABLE_ACTIONS = ['httpAction', 'mongoAction', 'postgresAction', 'sheetsAction', 'calendarAction', 'docsAction', 'driveAction', 'firecrawlAction', 'telegramAction', 'whatsappAction', 'discordAction', 'slackAction', 'googleContactsAction', 'googleAnalyticsAction', 'vectorStore', 'rssAction', 'socialMediaAction'];
|
|
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
|
googleContactsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
81
|
+
googleAnalyticsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
81
82
|
vectorStore: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
82
83
|
rssAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
83
84
|
socialMediaAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
package/dist/dispatch.js
CHANGED
|
@@ -54,6 +54,7 @@ exports.NODE_DISPATCH = {
|
|
|
54
54
|
discordAction: { service: 'discordActionsService', collection: 'discordactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
55
55
|
slackAction: { service: 'slackActionsService', collection: 'slackactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
56
56
|
googleContactsAction: { service: 'googleContactsActionsService', collection: 'googlecontactsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
57
|
+
googleAnalyticsAction: { service: 'googleAnalyticsActionsService', collection: 'googleanalyticsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
57
58
|
rssAction: { service: 'rssActionsService', collection: 'rssactions', hasFilters: false, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
58
59
|
socialMediaAction: { service: 'socialMediaActionsService', collection: 'socialmediaactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
59
60
|
// ── Sources — start the flow themselves, not dispatched ──
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Analytics 4 operation enum — single source of truth across the API,
|
|
3
|
+
* Dashboard, and Message Broker. Used by:
|
|
4
|
+
* - googleAnalyticsAction entity / DTO (api): operation field + validation
|
|
5
|
+
* - dashboard GoogleAnalyticsAction operation type
|
|
6
|
+
* - MCP toolkit specs, si algún día el nodo gana modo toolkit
|
|
7
|
+
*
|
|
8
|
+
* Mismo patrón que `drive-operations.ts` y `sheets-operations.ts`.
|
|
9
|
+
*
|
|
10
|
+
* ── Por qué sólo dos operaciones ──
|
|
11
|
+
*
|
|
12
|
+
* GA4 tiene más superficie (`runPivotReport`, `batchRunReports`,
|
|
13
|
+
* `checkCompatibility`), pero declarar una op que el servicio todavía no
|
|
14
|
+
* implementa la pinta en el desplegable y la deja fallar al ejecutarse: el
|
|
15
|
+
* enum ES el contrato de la pantalla. Entran cuando entre su código.
|
|
16
|
+
*
|
|
17
|
+
* ── Lo que NO es una operación ──
|
|
18
|
+
*
|
|
19
|
+
* Listar propiedades y listar las dimensiones/métricas disponibles NO son
|
|
20
|
+
* ops del nodo: son endpoints del controlador, que es como Drive alimenta su
|
|
21
|
+
* `DriveFilePicker` (`@Get('files/:credentialId')`). Una op existe para
|
|
22
|
+
* producir payload en una corrida; esto otro es para rellenar el formulario.
|
|
23
|
+
*
|
|
24
|
+
* ── Decisiones propias de GA (anti-n8n) ──
|
|
25
|
+
*
|
|
26
|
+
* - La propiedad se ELIGE de una lista, no se teclea. `properties/123456789`
|
|
27
|
+
* y `G-XXXXXXX` se confunden todo el rato y el error de GA no lo aclara.
|
|
28
|
+
* - Las fechas admiten `NdaysAgo` / `yesterday` / `today` además de
|
|
29
|
+
* `YYYY-MM-DD`, que es lo que acepta la API, y son el sitio donde avisar
|
|
30
|
+
* de que el dato no está cerrado hasta pasadas ~48 h.
|
|
31
|
+
* - El filtro común (una dimensión, un valor) son tres campos; la
|
|
32
|
+
* `FilterExpression` cruda queda de escape en Advanced y gana si está.
|
|
33
|
+
*/
|
|
34
|
+
export declare const GOOGLE_ANALYTICS_OPERATIONS: readonly ["runReport", "runRealtimeReport"];
|
|
35
|
+
export type GoogleAnalyticsOperation = (typeof GOOGLE_ANALYTICS_OPERATIONS)[number];
|
|
36
|
+
/** Type guard — para validar entrada no fiable (DTOs, tool calls). */
|
|
37
|
+
export declare function isGoogleAnalyticsOperation(value: unknown): value is GoogleAnalyticsOperation;
|
|
38
|
+
export type GoogleAnalyticsParamType =
|
|
39
|
+
/** Selector vivo de propiedades GA4 (necesita credencial). */
|
|
40
|
+
'analyticsProperty'
|
|
41
|
+
/** Multi-selección de métricas, con las opciones vivas del metadata. */
|
|
42
|
+
| 'analyticsMetrics'
|
|
43
|
+
/** Multi-selección de dimensiones, igual que las métricas. */
|
|
44
|
+
| 'analyticsDimensions'
|
|
45
|
+
/** Texto con autocompletado de `{{payload.*}}`. */
|
|
46
|
+
| 'template'
|
|
47
|
+
/** `<select>` normal; las etiquetas no son los valores, `options` obliga. */
|
|
48
|
+
| 'select' | 'number'
|
|
49
|
+
/**
|
|
50
|
+
* `<select>` de `''` / `'true'` / `'false'` que se guarda como BOOLEANO, o
|
|
51
|
+
* no se guarda si es `''`. Mismo motivo que el `starred` de Drive: "— sin
|
|
52
|
+
* tocar —" tiene que significar que la clave no viaja.
|
|
53
|
+
*/
|
|
54
|
+
| 'booleanSelect'
|
|
55
|
+
/** Área de texto para JSON crudo (la FilterExpression de escape). */
|
|
56
|
+
| 'json';
|
|
57
|
+
export interface GoogleAnalyticsParamSpec {
|
|
58
|
+
/** La clave de `operationConfig`. Es lo que lee la api, no un nombre de UI. */
|
|
59
|
+
name: string;
|
|
60
|
+
label: string;
|
|
61
|
+
type: GoogleAnalyticsParamType;
|
|
62
|
+
required?: boolean;
|
|
63
|
+
/** Ayuda bajo el campo. Texto plano: el paquete no lleva React. */
|
|
64
|
+
description?: string;
|
|
65
|
+
placeholder?: string;
|
|
66
|
+
/** Prerrellenado cuando la config guardada no trae valor para esta clave. */
|
|
67
|
+
default?: string | number;
|
|
68
|
+
/** Obligatorio en `select` y `booleanSelect` — etiqueta y valor difieren. */
|
|
69
|
+
options?: ReadonlyArray<{
|
|
70
|
+
value: string;
|
|
71
|
+
label: string;
|
|
72
|
+
}>;
|
|
73
|
+
min?: number;
|
|
74
|
+
max?: number;
|
|
75
|
+
/** Se pinta en la pestaña "Advanced" en vez de en "Config". */
|
|
76
|
+
advanced?: boolean;
|
|
77
|
+
/** Sólo visible cuando un campo hermano vale uno de estos. */
|
|
78
|
+
showWhen?: {
|
|
79
|
+
field: string;
|
|
80
|
+
in: ReadonlyArray<string>;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export interface GoogleAnalyticsOperationSpec {
|
|
84
|
+
/** Etiqueta de la fila en el desplegable de operación. */
|
|
85
|
+
label: string;
|
|
86
|
+
/** Sub-línea bajo la etiqueta. */
|
|
87
|
+
description: string;
|
|
88
|
+
/** Método de la API al que mapea (developers.google.com/analytics/devguides/reporting/data/v1). */
|
|
89
|
+
apiMethod: string;
|
|
90
|
+
/** Esquema de parámetros. El orden ES el orden en pantalla. */
|
|
91
|
+
params: GoogleAnalyticsParamSpec[];
|
|
92
|
+
}
|
|
93
|
+
export declare const GOOGLE_ANALYTICS_OPERATION_SPECS: Record<GoogleAnalyticsOperation, GoogleAnalyticsOperationSpec>;
|
|
94
|
+
/**
|
|
95
|
+
* Las operaciones tal y como se listan en el desplegable. Hoy es el enum
|
|
96
|
+
* entero; existe para que añadir una op interna (sin fila propia) no obligue
|
|
97
|
+
* a que la página vuelva a decidir por su cuenta qué enseñar.
|
|
98
|
+
*/
|
|
99
|
+
export declare const GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS: readonly GoogleAnalyticsOperation[];
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS = exports.GOOGLE_ANALYTICS_OPERATION_SPECS = exports.GOOGLE_ANALYTICS_OPERATIONS = void 0;
|
|
4
|
+
exports.isGoogleAnalyticsOperation = isGoogleAnalyticsOperation;
|
|
5
|
+
/**
|
|
6
|
+
* Google Analytics 4 operation enum — single source of truth across the API,
|
|
7
|
+
* Dashboard, and Message Broker. Used by:
|
|
8
|
+
* - googleAnalyticsAction entity / DTO (api): operation field + validation
|
|
9
|
+
* - dashboard GoogleAnalyticsAction operation type
|
|
10
|
+
* - MCP toolkit specs, si algún día el nodo gana modo toolkit
|
|
11
|
+
*
|
|
12
|
+
* Mismo patrón que `drive-operations.ts` y `sheets-operations.ts`.
|
|
13
|
+
*
|
|
14
|
+
* ── Por qué sólo dos operaciones ──
|
|
15
|
+
*
|
|
16
|
+
* GA4 tiene más superficie (`runPivotReport`, `batchRunReports`,
|
|
17
|
+
* `checkCompatibility`), pero declarar una op que el servicio todavía no
|
|
18
|
+
* implementa la pinta en el desplegable y la deja fallar al ejecutarse: el
|
|
19
|
+
* enum ES el contrato de la pantalla. Entran cuando entre su código.
|
|
20
|
+
*
|
|
21
|
+
* ── Lo que NO es una operación ──
|
|
22
|
+
*
|
|
23
|
+
* Listar propiedades y listar las dimensiones/métricas disponibles NO son
|
|
24
|
+
* ops del nodo: son endpoints del controlador, que es como Drive alimenta su
|
|
25
|
+
* `DriveFilePicker` (`@Get('files/:credentialId')`). Una op existe para
|
|
26
|
+
* producir payload en una corrida; esto otro es para rellenar el formulario.
|
|
27
|
+
*
|
|
28
|
+
* ── Decisiones propias de GA (anti-n8n) ──
|
|
29
|
+
*
|
|
30
|
+
* - La propiedad se ELIGE de una lista, no se teclea. `properties/123456789`
|
|
31
|
+
* y `G-XXXXXXX` se confunden todo el rato y el error de GA no lo aclara.
|
|
32
|
+
* - Las fechas admiten `NdaysAgo` / `yesterday` / `today` además de
|
|
33
|
+
* `YYYY-MM-DD`, que es lo que acepta la API, y son el sitio donde avisar
|
|
34
|
+
* de que el dato no está cerrado hasta pasadas ~48 h.
|
|
35
|
+
* - El filtro común (una dimensión, un valor) son tres campos; la
|
|
36
|
+
* `FilterExpression` cruda queda de escape en Advanced y gana si está.
|
|
37
|
+
*/
|
|
38
|
+
exports.GOOGLE_ANALYTICS_OPERATIONS = [
|
|
39
|
+
'runReport', // informe estándar sobre un rango de fechas
|
|
40
|
+
'runRealtimeReport', // los últimos ~30 minutos, sin rango de fechas
|
|
41
|
+
];
|
|
42
|
+
/** Type guard — para validar entrada no fiable (DTOs, tool calls). */
|
|
43
|
+
function isGoogleAnalyticsOperation(value) {
|
|
44
|
+
return (typeof value === 'string' &&
|
|
45
|
+
exports.GOOGLE_ANALYTICS_OPERATIONS.includes(value));
|
|
46
|
+
}
|
|
47
|
+
/* Fábricas de los campos que se repiten, para que un cambio de copy no haya
|
|
48
|
+
que perseguirlo por las dos operaciones —y por las que vengan. */
|
|
49
|
+
const propertyId = () => ({
|
|
50
|
+
name: 'propertyId',
|
|
51
|
+
label: 'GA4 Property',
|
|
52
|
+
type: 'analyticsProperty',
|
|
53
|
+
required: true,
|
|
54
|
+
description: 'Pick a property from the connected account, or paste a templated value like {{payload.propertyId}}. This is the numeric property id (properties/123456789), not the G-XXXXXXX measurement id.',
|
|
55
|
+
});
|
|
56
|
+
const metrics = () => ({
|
|
57
|
+
name: 'metrics',
|
|
58
|
+
label: 'Metrics',
|
|
59
|
+
type: 'analyticsMetrics',
|
|
60
|
+
required: true,
|
|
61
|
+
description: 'What to measure — activeUsers, sessions, screenPageViews, conversions. At least one is required.',
|
|
62
|
+
});
|
|
63
|
+
const dimensions = () => ({
|
|
64
|
+
name: 'dimensions',
|
|
65
|
+
label: 'Dimensions',
|
|
66
|
+
type: 'analyticsDimensions',
|
|
67
|
+
description: 'How to break the metrics down — date, country, pagePath, sessionSource. Leave empty for a single total row.',
|
|
68
|
+
});
|
|
69
|
+
const limit = (max, def) => ({
|
|
70
|
+
name: 'limit',
|
|
71
|
+
label: 'Row limit',
|
|
72
|
+
type: 'number',
|
|
73
|
+
default: def,
|
|
74
|
+
min: 1,
|
|
75
|
+
max,
|
|
76
|
+
description: `Maximum rows to return. The API caps a single request at ${max.toLocaleString('en-US')}.`,
|
|
77
|
+
});
|
|
78
|
+
/** Los tipos de coincidencia de una `stringFilter` de GA4. */
|
|
79
|
+
const MATCH_TYPE_OPTIONS = [
|
|
80
|
+
{ value: 'EXACT', label: 'is exactly' },
|
|
81
|
+
{ value: 'CONTAINS', label: 'contains' },
|
|
82
|
+
{ value: 'BEGINS_WITH', label: 'begins with' },
|
|
83
|
+
{ value: 'ENDS_WITH', label: 'ends with' },
|
|
84
|
+
{ value: 'FULL_REGEXP', label: 'matches regex' },
|
|
85
|
+
];
|
|
86
|
+
/* El filtro simple: una dimensión, una comparación, un valor. Cubre el caso
|
|
87
|
+
que pide todo el mundo ("country es Spain") sin obligar a escribir una
|
|
88
|
+
FilterExpression anidada. Quien necesite más tiene `dimensionFilterJson`. */
|
|
89
|
+
const simpleFilter = () => [
|
|
90
|
+
{
|
|
91
|
+
name: 'filterDimension',
|
|
92
|
+
label: 'Filter by dimension',
|
|
93
|
+
type: 'analyticsDimensions',
|
|
94
|
+
description: 'Optional. Restrict the report to rows where this dimension matches the value below.',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'filterMatchType',
|
|
98
|
+
label: 'Match',
|
|
99
|
+
type: 'select',
|
|
100
|
+
default: 'EXACT',
|
|
101
|
+
options: MATCH_TYPE_OPTIONS,
|
|
102
|
+
showWhen: { field: 'filterDimension', in: ['*'] },
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'filterValue',
|
|
106
|
+
label: 'Value',
|
|
107
|
+
type: 'template',
|
|
108
|
+
placeholder: 'Spain',
|
|
109
|
+
description: 'Templates work — e.g. {{payload.country}}.',
|
|
110
|
+
showWhen: { field: 'filterDimension', in: ['*'] },
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
const dimensionFilterJson = () => ({
|
|
114
|
+
name: 'dimensionFilterJson',
|
|
115
|
+
label: 'Raw dimension filter (FilterExpression JSON)',
|
|
116
|
+
type: 'json',
|
|
117
|
+
advanced: true,
|
|
118
|
+
placeholder: '{"andGroup":{"expressions":[{"filter":{"fieldName":"country","stringFilter":{"value":"Spain"}}}]}}',
|
|
119
|
+
description: 'Escape hatch for AND / OR / NOT groups the simple filter above cannot express. When set, this WINS and the three simple filter fields are ignored.',
|
|
120
|
+
});
|
|
121
|
+
const orderBy = () => [
|
|
122
|
+
{
|
|
123
|
+
name: 'orderByField',
|
|
124
|
+
label: 'Sort by',
|
|
125
|
+
type: 'template',
|
|
126
|
+
advanced: true,
|
|
127
|
+
placeholder: 'activeUsers',
|
|
128
|
+
description: 'Metric or dimension name to sort on. Must be one of the metrics or dimensions selected above. Empty = the API default order.',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'orderByDesc',
|
|
132
|
+
label: 'Sort direction',
|
|
133
|
+
type: 'booleanSelect',
|
|
134
|
+
advanced: true,
|
|
135
|
+
options: [
|
|
136
|
+
{ value: '', label: '— API default —' },
|
|
137
|
+
{ value: 'true', label: 'Descending (highest first)' },
|
|
138
|
+
{ value: 'false', label: 'Ascending (lowest first)' },
|
|
139
|
+
],
|
|
140
|
+
showWhen: { field: 'orderByField', in: ['*'] },
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
exports.GOOGLE_ANALYTICS_OPERATION_SPECS = {
|
|
144
|
+
runReport: {
|
|
145
|
+
label: 'Run report',
|
|
146
|
+
description: 'Metrics over a date range, broken down by dimensions.',
|
|
147
|
+
apiMethod: 'properties.runReport',
|
|
148
|
+
params: [
|
|
149
|
+
propertyId(),
|
|
150
|
+
metrics(),
|
|
151
|
+
dimensions(),
|
|
152
|
+
{
|
|
153
|
+
name: 'startDate',
|
|
154
|
+
label: 'Start date',
|
|
155
|
+
type: 'template',
|
|
156
|
+
required: true,
|
|
157
|
+
default: '28daysAgo',
|
|
158
|
+
description: 'YYYY-MM-DD, or a relative value: today, yesterday, NdaysAgo. Note GA4 keeps refining recent data for up to 48h, so a window ending at "today" can change between two runs.',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'endDate',
|
|
162
|
+
label: 'End date',
|
|
163
|
+
type: 'template',
|
|
164
|
+
required: true,
|
|
165
|
+
default: 'today',
|
|
166
|
+
description: 'Same formats as the start date. Inclusive.',
|
|
167
|
+
},
|
|
168
|
+
...simpleFilter(),
|
|
169
|
+
limit(250000, 100),
|
|
170
|
+
{
|
|
171
|
+
name: 'offset',
|
|
172
|
+
label: 'Row offset',
|
|
173
|
+
type: 'number',
|
|
174
|
+
advanced: true,
|
|
175
|
+
default: 0,
|
|
176
|
+
min: 0,
|
|
177
|
+
description: 'Skip this many rows before returning. Use with the row limit to page through a large report.',
|
|
178
|
+
},
|
|
179
|
+
...orderBy(),
|
|
180
|
+
dimensionFilterJson(),
|
|
181
|
+
{
|
|
182
|
+
name: 'keepEmptyRows',
|
|
183
|
+
label: 'Keep empty rows',
|
|
184
|
+
type: 'booleanSelect',
|
|
185
|
+
advanced: true,
|
|
186
|
+
options: [
|
|
187
|
+
{ value: '', label: '— API default (drop them) —' },
|
|
188
|
+
{ value: 'true', label: 'Keep rows whose metrics are all zero' },
|
|
189
|
+
{ value: 'false', label: 'Drop rows whose metrics are all zero' },
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
runRealtimeReport: {
|
|
195
|
+
label: 'Run realtime report',
|
|
196
|
+
description: 'What is happening right now — the last 30 minutes.',
|
|
197
|
+
apiMethod: 'properties.runRealtimeReport',
|
|
198
|
+
params: [
|
|
199
|
+
propertyId(),
|
|
200
|
+
metrics(),
|
|
201
|
+
dimensions(),
|
|
202
|
+
{
|
|
203
|
+
name: 'minutesAgo',
|
|
204
|
+
label: 'Window',
|
|
205
|
+
type: 'select',
|
|
206
|
+
default: '29',
|
|
207
|
+
options: [
|
|
208
|
+
{ value: '4', label: 'Last 5 minutes' },
|
|
209
|
+
{ value: '14', label: 'Last 15 minutes' },
|
|
210
|
+
{ value: '29', label: 'Last 30 minutes' },
|
|
211
|
+
],
|
|
212
|
+
description: 'Realtime has no date range — it always looks back from now, and GA4 caps the lookback at 30 minutes.',
|
|
213
|
+
},
|
|
214
|
+
...simpleFilter(),
|
|
215
|
+
limit(100000, 100),
|
|
216
|
+
...orderBy(),
|
|
217
|
+
dimensionFilterJson(),
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Las operaciones tal y como se listan en el desplegable. Hoy es el enum
|
|
223
|
+
* entero; existe para que añadir una op interna (sin fila propia) no obligue
|
|
224
|
+
* a que la página vuelva a decidir por su cuenta qué enseñar.
|
|
225
|
+
*/
|
|
226
|
+
exports.GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS = exports.GOOGLE_ANALYTICS_OPERATIONS;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,9 @@ export { singleMeta, iterableMeta } from './types';
|
|
|
3
3
|
export { NODE_CONNECTIONS, canReceiveFrom, canReceiveFromNodes, canSendToNodes, isTerminal, isNodeType, ALL_NODE_TYPES, } from './connections';
|
|
4
4
|
export { NODE_UI, PREFIX_TO_TYPE, resolveNodeId, } from './ui';
|
|
5
5
|
export { NODE_DISPATCH, getAllNodeCollections, getNodeDispatchConfig, } from './dispatch';
|
|
6
|
-
export type { NodeRegistryEntry, NodeGroup } from './registry';
|
|
6
|
+
export type { NodeRegistryEntry, NodeGroup, NodeVersionSpec, VersionCatalog, } from './registry';
|
|
7
7
|
export { NODE_REGISTRY, getNodeRegistryEntry, NODE_DETAIL_PATHS, NODE_COLORS, NODE_STATE_KEYS, PREFIX_TO_NODE_TYPE, NODE_TYPE_TO_PREFIX, } from './registry';
|
|
8
|
+
export { isVersioned, versionsOf, versionSpec, currentVersion, fieldsLostBetween, fieldsLost, versionCatalogErrors, } from './registry';
|
|
8
9
|
export { GMAIL_OPERATIONS, GMAIL_OPERATION_SPECS, GMAIL_OPERATION_GROUPS, GMAIL_DROPDOWN_OPERATIONS, GMAIL_TOOLKIT_OPERATIONS, GMAIL_TOOLKIT_SPECS, GMAIL_ALL_TOOLKIT_SPECS, GMAIL_SEND_AND_WAIT_TOOL_SPEC, NATIVE_EMAIL_TOOLKIT_SPECS, GMAIL_TOOLKIT_BY_TOOL_NAME, NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME, GMAIL_SEND_LEGACY_FIELDS, resolveGmailSendFields, isGmailOperation, } from './gmail-operations';
|
|
9
10
|
export type { GmailOperation, GmailOperationGroup, GmailParamType, GmailParamSpec, GmailOperationSpec, GmailToolkitParameter, GmailToolkitSpec, GmailSendFieldSource, GmailSendFields, } from './gmail-operations';
|
|
10
11
|
export { GOOGLE_CALENDAR_OPERATIONS, GOOGLE_CALENDAR_OPERATION_SPECS, isGoogleCalendarOperation, } from './calendar-operations';
|
|
@@ -31,6 +32,8 @@ export { SHEETS_TOOLKIT_SPECS, SHEETS_TOOLKIT_BY_TOOL_NAME, SHEETS_TOOLKIT_DEFAU
|
|
|
31
32
|
export type { SheetsToolkitSpec, SheetsToolkitParameter, } from './sheets-toolkit';
|
|
32
33
|
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';
|
|
33
34
|
export type { GoogleContactsOperation, GoogleContactsParamSpec, GoogleContactsOperationSpec, GoogleContactsOperationGroup, } from './google-contacts-operations';
|
|
35
|
+
export { GOOGLE_ANALYTICS_OPERATIONS, GOOGLE_ANALYTICS_OPERATION_SPECS, GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS, isGoogleAnalyticsOperation, } from './google-analytics-operations';
|
|
36
|
+
export type { GoogleAnalyticsOperation, GoogleAnalyticsParamType, GoogleAnalyticsParamSpec, GoogleAnalyticsOperationSpec, } from './google-analytics-operations';
|
|
34
37
|
export { POSTGRES_OPERATIONS, POSTGRES_MODES, POSTGRES_OPERATION_SPECS, isPostgresOperation, } from './postgres-operations';
|
|
35
38
|
export type { PostgresOperation, PostgresMode, PostgresOperationSpec, } from './postgres-operations';
|
|
36
39
|
export { MONGO_OPERATIONS, MONGO_OPERATION_SPECS, isMongoOperation, } from './mongo-operations';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
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.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 = exports.isPostgresOperation = exports.POSTGRES_OPERATION_SPECS = exports.POSTGRES_MODES = exports.POSTGRES_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.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 = void 0;
|
|
5
6
|
var types_1 = require("./types");
|
|
6
7
|
Object.defineProperty(exports, "singleMeta", { enumerable: true, get: function () { return types_1.singleMeta; } });
|
|
7
8
|
Object.defineProperty(exports, "iterableMeta", { enumerable: true, get: function () { return types_1.iterableMeta; } });
|
|
@@ -32,6 +33,16 @@ Object.defineProperty(exports, "NODE_COLORS", { enumerable: true, get: function
|
|
|
32
33
|
Object.defineProperty(exports, "NODE_STATE_KEYS", { enumerable: true, get: function () { return registry_1.NODE_STATE_KEYS; } });
|
|
33
34
|
Object.defineProperty(exports, "PREFIX_TO_NODE_TYPE", { enumerable: true, get: function () { return registry_1.PREFIX_TO_NODE_TYPE; } });
|
|
34
35
|
Object.defineProperty(exports, "NODE_TYPE_TO_PREFIX", { enumerable: true, get: function () { return registry_1.NODE_TYPE_TO_PREFIX; } });
|
|
36
|
+
// ── Node version catalog (ALL repos) ──
|
|
37
|
+
// See docs/ADR-0002-node-versioning.md in the api repo.
|
|
38
|
+
var registry_2 = require("./registry");
|
|
39
|
+
Object.defineProperty(exports, "isVersioned", { enumerable: true, get: function () { return registry_2.isVersioned; } });
|
|
40
|
+
Object.defineProperty(exports, "versionsOf", { enumerable: true, get: function () { return registry_2.versionsOf; } });
|
|
41
|
+
Object.defineProperty(exports, "versionSpec", { enumerable: true, get: function () { return registry_2.versionSpec; } });
|
|
42
|
+
Object.defineProperty(exports, "currentVersion", { enumerable: true, get: function () { return registry_2.currentVersion; } });
|
|
43
|
+
Object.defineProperty(exports, "fieldsLostBetween", { enumerable: true, get: function () { return registry_2.fieldsLostBetween; } });
|
|
44
|
+
Object.defineProperty(exports, "fieldsLost", { enumerable: true, get: function () { return registry_2.fieldsLost; } });
|
|
45
|
+
Object.defineProperty(exports, "versionCatalogErrors", { enumerable: true, get: function () { return registry_2.versionCatalogErrors; } });
|
|
35
46
|
// ── Per-service operation enums (ALL repos) ──
|
|
36
47
|
var gmail_operations_1 = require("./gmail-operations");
|
|
37
48
|
Object.defineProperty(exports, "GMAIL_OPERATIONS", { enumerable: true, get: function () { return gmail_operations_1.GMAIL_OPERATIONS; } });
|
|
@@ -96,6 +107,11 @@ Object.defineProperty(exports, "GOOGLE_CONTACTS_OPERATION_SPECS", { enumerable:
|
|
|
96
107
|
Object.defineProperty(exports, "GOOGLE_CONTACTS_OPERATION_GROUPS", { enumerable: true, get: function () { return google_contacts_operations_1.GOOGLE_CONTACTS_OPERATION_GROUPS; } });
|
|
97
108
|
Object.defineProperty(exports, "GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS", { enumerable: true, get: function () { return google_contacts_operations_1.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS; } });
|
|
98
109
|
Object.defineProperty(exports, "isGoogleContactsOperation", { enumerable: true, get: function () { return google_contacts_operations_1.isGoogleContactsOperation; } });
|
|
110
|
+
var google_analytics_operations_1 = require("./google-analytics-operations");
|
|
111
|
+
Object.defineProperty(exports, "GOOGLE_ANALYTICS_OPERATIONS", { enumerable: true, get: function () { return google_analytics_operations_1.GOOGLE_ANALYTICS_OPERATIONS; } });
|
|
112
|
+
Object.defineProperty(exports, "GOOGLE_ANALYTICS_OPERATION_SPECS", { enumerable: true, get: function () { return google_analytics_operations_1.GOOGLE_ANALYTICS_OPERATION_SPECS; } });
|
|
113
|
+
Object.defineProperty(exports, "GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS", { enumerable: true, get: function () { return google_analytics_operations_1.GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS; } });
|
|
114
|
+
Object.defineProperty(exports, "isGoogleAnalyticsOperation", { enumerable: true, get: function () { return google_analytics_operations_1.isGoogleAnalyticsOperation; } });
|
|
99
115
|
var postgres_operations_1 = require("./postgres-operations");
|
|
100
116
|
Object.defineProperty(exports, "POSTGRES_OPERATIONS", { enumerable: true, get: function () { return postgres_operations_1.POSTGRES_OPERATIONS; } });
|
|
101
117
|
Object.defineProperty(exports, "POSTGRES_MODES", { enumerable: true, get: function () { return postgres_operations_1.POSTGRES_MODES; } });
|
package/dist/registry.d.ts
CHANGED
|
@@ -41,6 +41,51 @@ export interface NodeRegistryEntry {
|
|
|
41
41
|
* Predicate must be pure (no side effects) — called on every render.
|
|
42
42
|
*/
|
|
43
43
|
isToolOnly?: (entity: Record<string, unknown>) => boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The versions this node type ships, when it ships more than one.
|
|
46
|
+
*
|
|
47
|
+
* Absent means the type does not version, which is true of every type
|
|
48
|
+
* today. A node type only declares this the day its provider deprecates
|
|
49
|
+
* something and a second version has to live alongside the first.
|
|
50
|
+
*
|
|
51
|
+
* See `docs/ADR-0002-node-versioning.md` in the api repo.
|
|
52
|
+
*/
|
|
53
|
+
versions?: NodeVersionSpec[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* One version of a node type.
|
|
57
|
+
*
|
|
58
|
+
* Deliberately NOT a schema. The api's DTO stays the validator; all this
|
|
59
|
+
* needs to know is WHICH config fields a version understands, because that
|
|
60
|
+
* is what answers the only question the UI has to ask: switching from here
|
|
61
|
+
* to there, what does the user lose?
|
|
62
|
+
*/
|
|
63
|
+
export interface NodeVersionSpec {
|
|
64
|
+
/** The number the user sees. Unique within a type. */
|
|
65
|
+
v: number;
|
|
66
|
+
/** Shown next to the number in the picker, e.g. "Discord API v10". */
|
|
67
|
+
label?: string;
|
|
68
|
+
/**
|
|
69
|
+
* `current` is what a new node is born on — exactly one per type.
|
|
70
|
+
* `legacy` still runs but is on its way out. `beta` is opt-in.
|
|
71
|
+
*/
|
|
72
|
+
status: 'current' | 'legacy' | 'beta';
|
|
73
|
+
/**
|
|
74
|
+
* The config fields this version understands.
|
|
75
|
+
*
|
|
76
|
+
* Only fields the user configures. Identity and topology — name,
|
|
77
|
+
* inputNodes, outputNodes, isActive, canvas position — never belong
|
|
78
|
+
* here: they survive a version change untouched, and listing them
|
|
79
|
+
* would put them at risk of being unset.
|
|
80
|
+
*/
|
|
81
|
+
fields: string[];
|
|
82
|
+
/**
|
|
83
|
+
* Entering this version keeps nothing. For the case where "v2" is
|
|
84
|
+
* really a different integration wearing the same node's name.
|
|
85
|
+
*/
|
|
86
|
+
cleanBreak?: boolean;
|
|
87
|
+
/** Why it was retired, or what it brings. Shown in picker and dialog. */
|
|
88
|
+
note?: string;
|
|
44
89
|
}
|
|
45
90
|
export declare const NODE_REGISTRY: Record<NodeType, NodeRegistryEntry>;
|
|
46
91
|
/** Get registry entry by canonical type */
|
|
@@ -55,3 +100,61 @@ export declare const NODE_STATE_KEYS: Record<string, [string, string]>;
|
|
|
55
100
|
export declare const PREFIX_TO_NODE_TYPE: Record<string, NodeType>;
|
|
56
101
|
/** type → prefix */
|
|
57
102
|
export declare const NODE_TYPE_TO_PREFIX: Record<string, string>;
|
|
103
|
+
/**
|
|
104
|
+
* The catalog these helpers read. Every one of them takes it as an
|
|
105
|
+
* optional last argument defaulting to the real registry: consumers never
|
|
106
|
+
* pass it, and the tests can exercise a versioned type without waiting
|
|
107
|
+
* for a real one to exist.
|
|
108
|
+
*/
|
|
109
|
+
export type VersionCatalog = Record<string, Pick<NodeRegistryEntry, 'type' | 'versions'>>;
|
|
110
|
+
/** True when this type ships more than one version. False for every type today. */
|
|
111
|
+
export declare function isVersioned(type: string, catalog?: VersionCatalog): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Every declared version, newest first — the order a picker wants.
|
|
114
|
+
* Empty for a type that does not version.
|
|
115
|
+
*/
|
|
116
|
+
export declare function versionsOf(type: string, catalog?: VersionCatalog): NodeVersionSpec[];
|
|
117
|
+
/** One version's spec, or undefined when the type or the number is unknown. */
|
|
118
|
+
export declare function versionSpec(type: string, v: number, catalog?: VersionCatalog): NodeVersionSpec | undefined;
|
|
119
|
+
/**
|
|
120
|
+
* The version a new node of this type is born on.
|
|
121
|
+
*
|
|
122
|
+
* Returns 1 for a type that does not version, which is also how an entity
|
|
123
|
+
* with no `nodeVersion` is read. A node created before its type ever
|
|
124
|
+
* versioned was calling the original endpoints, so calling it version 1
|
|
125
|
+
* is not a convention — it is the truth.
|
|
126
|
+
*/
|
|
127
|
+
export declare function currentVersion(type: string, catalog?: VersionCatalog): number;
|
|
128
|
+
/**
|
|
129
|
+
* Config fields present in `from` that `to` does not declare — the exact
|
|
130
|
+
* list a confirmation dialog should name, and the exact list the server
|
|
131
|
+
* should unset.
|
|
132
|
+
*
|
|
133
|
+
* Returns `null` when the question cannot be answered: the type does not
|
|
134
|
+
* version, or one of the two versions is not declared. `null` is not the
|
|
135
|
+
* same as "nothing is lost", and callers must not treat it that way — a
|
|
136
|
+
* dialog should refuse to offer the switch, and the api should reject the
|
|
137
|
+
* request. Returning `null` rather than `[]` is what stops "I don't know"
|
|
138
|
+
* from being read as "nothing".
|
|
139
|
+
*
|
|
140
|
+
* This lives here rather than in each consumer because the dialog that
|
|
141
|
+
* ANNOUNCES the loss and the server that PERFORMS it have to give the
|
|
142
|
+
* same answer. Two implementations would agree right until the day they
|
|
143
|
+
* did not, and that day the dialog lies.
|
|
144
|
+
*/
|
|
145
|
+
export declare function fieldsLostBetween(type: string, from: number, to: number, catalog?: VersionCatalog): string[] | null;
|
|
146
|
+
/**
|
|
147
|
+
* The same answer for two specs already in hand — what `fieldsLostBetween`
|
|
148
|
+
* delegates to once it has resolved both, and what a caller that already
|
|
149
|
+
* holds them from `versionsOf()` can use without a second lookup.
|
|
150
|
+
*/
|
|
151
|
+
export declare function fieldsLost(from: NodeVersionSpec, to: NodeVersionSpec): string[];
|
|
152
|
+
/**
|
|
153
|
+
* Declaration mistakes in the version catalogs, as readable lines.
|
|
154
|
+
*
|
|
155
|
+
* A guard rather than a line in a document: what this codebase learned
|
|
156
|
+
* from fifteen nodes skipping a checklist point everyone had read is that
|
|
157
|
+
* the written rule prevented nothing and the guard did. Empty means every
|
|
158
|
+
* catalog is well-formed.
|
|
159
|
+
*/
|
|
160
|
+
export declare function versionCatalogErrors(catalog?: VersionCatalog): string[];
|
package/dist/registry.js
CHANGED
|
@@ -9,6 +9,13 @@
|
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.NODE_REGISTRY = void 0;
|
|
11
11
|
exports.getNodeRegistryEntry = getNodeRegistryEntry;
|
|
12
|
+
exports.isVersioned = isVersioned;
|
|
13
|
+
exports.versionsOf = versionsOf;
|
|
14
|
+
exports.versionSpec = versionSpec;
|
|
15
|
+
exports.currentVersion = currentVersion;
|
|
16
|
+
exports.fieldsLostBetween = fieldsLostBetween;
|
|
17
|
+
exports.fieldsLost = fieldsLost;
|
|
18
|
+
exports.versionCatalogErrors = versionCatalogErrors;
|
|
12
19
|
exports.NODE_REGISTRY = {
|
|
13
20
|
// ── Sources ──
|
|
14
21
|
webhook: {
|
|
@@ -320,6 +327,19 @@ exports.NODE_REGISTRY = {
|
|
|
320
327
|
// handles to keep manual + tool modes from being mixed.
|
|
321
328
|
isToolOnly: (e) => e.aiEnabled === true,
|
|
322
329
|
},
|
|
330
|
+
googleAnalyticsAction: {
|
|
331
|
+
type: 'googleAnalyticsAction', prefix: 'gaa', label: 'Google Analytics',
|
|
332
|
+
group: 'Actions', detailPath: '/dashboard/google-analytics-actions', apiPath: '/google-analytics-actions',
|
|
333
|
+
stateKey: 'googleAnalyticsActions', allStateKey: 'allGoogleAnalyticsActions',
|
|
334
|
+
// Naranja de marca de Google Analytics (#E37400). No reutiliza el
|
|
335
|
+
// #ff9933 de RSS ni el orange-500 de Tailwind que ya llevan tres nodos:
|
|
336
|
+
// en el minimapa lo unico que distingue a un nodo es el color.
|
|
337
|
+
color: '#E37400', testable: true,
|
|
338
|
+
/* Sin isToolOnly: la v1 no trae modo toolkit de IA. Cada toolkit vive
|
|
339
|
+
dos veces --specs del dashboard para el AI Node y specs de la api para
|
|
340
|
+
MCP, sin nada que las ate-- y esa duplicacion no se paga antes de ver
|
|
341
|
+
el nodo funcionando en pantalla. */
|
|
342
|
+
},
|
|
323
343
|
vectorStore: {
|
|
324
344
|
type: 'vectorStore', prefix: 'vs', label: 'Vector Store',
|
|
325
345
|
group: 'Actions', detailPath: '/dashboard/vector-store-nodes', apiPath: '/vector-store-nodes',
|
|
@@ -370,3 +390,108 @@ exports.NODE_STATE_KEYS = Object.fromEntries(Object.values(exports.NODE_REGISTRY
|
|
|
370
390
|
exports.PREFIX_TO_NODE_TYPE = Object.fromEntries(Object.values(exports.NODE_REGISTRY).map(n => [n.prefix, n.type]));
|
|
371
391
|
/** type → prefix */
|
|
372
392
|
exports.NODE_TYPE_TO_PREFIX = Object.fromEntries(Object.values(exports.NODE_REGISTRY).map(n => [n.type, n.prefix]));
|
|
393
|
+
function specsFor(type, catalog) {
|
|
394
|
+
return catalog[type]?.versions;
|
|
395
|
+
}
|
|
396
|
+
/** True when this type ships more than one version. False for every type today. */
|
|
397
|
+
function isVersioned(type, catalog = exports.NODE_REGISTRY) {
|
|
398
|
+
return (specsFor(type, catalog)?.length ?? 0) > 0;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Every declared version, newest first — the order a picker wants.
|
|
402
|
+
* Empty for a type that does not version.
|
|
403
|
+
*/
|
|
404
|
+
function versionsOf(type, catalog = exports.NODE_REGISTRY) {
|
|
405
|
+
const declared = specsFor(type, catalog);
|
|
406
|
+
if (!declared)
|
|
407
|
+
return [];
|
|
408
|
+
return [...declared].sort((a, b) => b.v - a.v);
|
|
409
|
+
}
|
|
410
|
+
/** One version's spec, or undefined when the type or the number is unknown. */
|
|
411
|
+
function versionSpec(type, v, catalog = exports.NODE_REGISTRY) {
|
|
412
|
+
return specsFor(type, catalog)?.find(s => s.v === v);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* The version a new node of this type is born on.
|
|
416
|
+
*
|
|
417
|
+
* Returns 1 for a type that does not version, which is also how an entity
|
|
418
|
+
* with no `nodeVersion` is read. A node created before its type ever
|
|
419
|
+
* versioned was calling the original endpoints, so calling it version 1
|
|
420
|
+
* is not a convention — it is the truth.
|
|
421
|
+
*/
|
|
422
|
+
function currentVersion(type, catalog = exports.NODE_REGISTRY) {
|
|
423
|
+
const current = specsFor(type, catalog)?.find(s => s.status === 'current');
|
|
424
|
+
return current?.v ?? 1;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Config fields present in `from` that `to` does not declare — the exact
|
|
428
|
+
* list a confirmation dialog should name, and the exact list the server
|
|
429
|
+
* should unset.
|
|
430
|
+
*
|
|
431
|
+
* Returns `null` when the question cannot be answered: the type does not
|
|
432
|
+
* version, or one of the two versions is not declared. `null` is not the
|
|
433
|
+
* same as "nothing is lost", and callers must not treat it that way — a
|
|
434
|
+
* dialog should refuse to offer the switch, and the api should reject the
|
|
435
|
+
* request. Returning `null` rather than `[]` is what stops "I don't know"
|
|
436
|
+
* from being read as "nothing".
|
|
437
|
+
*
|
|
438
|
+
* This lives here rather than in each consumer because the dialog that
|
|
439
|
+
* ANNOUNCES the loss and the server that PERFORMS it have to give the
|
|
440
|
+
* same answer. Two implementations would agree right until the day they
|
|
441
|
+
* did not, and that day the dialog lies.
|
|
442
|
+
*/
|
|
443
|
+
function fieldsLostBetween(type, from, to, catalog = exports.NODE_REGISTRY) {
|
|
444
|
+
const a = versionSpec(type, from, catalog);
|
|
445
|
+
const b = versionSpec(type, to, catalog);
|
|
446
|
+
if (!a || !b)
|
|
447
|
+
return null;
|
|
448
|
+
return fieldsLost(a, b);
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* The same answer for two specs already in hand — what `fieldsLostBetween`
|
|
452
|
+
* delegates to once it has resolved both, and what a caller that already
|
|
453
|
+
* holds them from `versionsOf()` can use without a second lookup.
|
|
454
|
+
*/
|
|
455
|
+
function fieldsLost(from, to) {
|
|
456
|
+
if (from.v === to.v)
|
|
457
|
+
return [];
|
|
458
|
+
if (to.cleanBreak)
|
|
459
|
+
return [...from.fields];
|
|
460
|
+
const kept = new Set(to.fields);
|
|
461
|
+
return from.fields.filter(f => !kept.has(f));
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Declaration mistakes in the version catalogs, as readable lines.
|
|
465
|
+
*
|
|
466
|
+
* A guard rather than a line in a document: what this codebase learned
|
|
467
|
+
* from fifteen nodes skipping a checklist point everyone had read is that
|
|
468
|
+
* the written rule prevented nothing and the guard did. Empty means every
|
|
469
|
+
* catalog is well-formed.
|
|
470
|
+
*/
|
|
471
|
+
function versionCatalogErrors(catalog = exports.NODE_REGISTRY) {
|
|
472
|
+
const errors = [];
|
|
473
|
+
for (const entry of Object.values(catalog)) {
|
|
474
|
+
const versions = entry.versions;
|
|
475
|
+
if (!versions)
|
|
476
|
+
continue;
|
|
477
|
+
if (versions.length === 0) {
|
|
478
|
+
errors.push(`${entry.type}: declares an empty versions array — omit the field instead`);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
const numbers = versions.map(s => s.v);
|
|
482
|
+
const repeatedNumbers = [...new Set(numbers.filter((n, i) => numbers.indexOf(n) !== i))];
|
|
483
|
+
if (repeatedNumbers.length)
|
|
484
|
+
errors.push(`${entry.type}: repeated version number ${repeatedNumbers.join(', ')}`);
|
|
485
|
+
for (const spec of versions) {
|
|
486
|
+
if (!Number.isInteger(spec.v) || spec.v < 1)
|
|
487
|
+
errors.push(`${entry.type}: version "${spec.v}" is not a positive integer`);
|
|
488
|
+
const repeatedFields = [...new Set(spec.fields.filter((f, i) => spec.fields.indexOf(f) !== i))];
|
|
489
|
+
if (repeatedFields.length)
|
|
490
|
+
errors.push(`${entry.type} v${spec.v}: repeated field ${repeatedFields.join(', ')}`);
|
|
491
|
+
}
|
|
492
|
+
const current = versions.filter(s => s.status === 'current');
|
|
493
|
+
if (current.length !== 1)
|
|
494
|
+
errors.push(`${entry.type}: needs exactly one version with status "current", found ${current.length}`);
|
|
495
|
+
}
|
|
496
|
+
return errors;
|
|
497
|
+
}
|
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' | 'vectorStore' | 'rssAction' | 'socialMediaAction' | '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' | 'vectorStore' | 'rssAction' | 'socialMediaAction' | '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
|
@@ -77,6 +77,7 @@ exports.NODE_UI = {
|
|
|
77
77
|
discordAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'discord' } },
|
|
78
78
|
slackAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'slack' } },
|
|
79
79
|
googleContactsAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true } },
|
|
80
|
+
googleAnalyticsAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true } },
|
|
80
81
|
vectorStore: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true } },
|
|
81
82
|
rssAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true } },
|
|
82
83
|
socialMediaAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true } },
|
|
@@ -97,6 +98,7 @@ exports.PREFIX_TO_TYPE = [
|
|
|
97
98
|
{ prefix: 'dc-', type: 'discordAction', canvasType: 'discordAction' },
|
|
98
99
|
{ prefix: 'sl-', type: 'slackAction', canvasType: 'slackAction' },
|
|
99
100
|
{ prefix: 'gc-', type: 'googleContactsAction', canvasType: 'googleContactsAction' },
|
|
101
|
+
{ prefix: 'gaa-', type: 'googleAnalyticsAction', canvasType: 'googleAnalyticsAction' },
|
|
100
102
|
{ prefix: 'ha-', type: 'httpAction', canvasType: 'httpAction' },
|
|
101
103
|
{ prefix: 'ma-', type: 'mongoAction', canvasType: 'mongoAction' },
|
|
102
104
|
{ prefix: 'pg-', type: 'postgresAction', canvasType: 'postgresAction' },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hostwebhook/node-types",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.57.0",
|
|
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",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
12
|
+
"test": "vitest run",
|
|
12
13
|
"prepublishOnly": "npm run build"
|
|
13
14
|
},
|
|
14
15
|
"keywords": [
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
],
|
|
19
20
|
"license": "MIT",
|
|
20
21
|
"devDependencies": {
|
|
21
|
-
"typescript": "^5.0.0"
|
|
22
|
+
"typescript": "^5.0.0",
|
|
23
|
+
"vitest": "^3.0.0"
|
|
22
24
|
}
|
|
23
25
|
}
|