@hostwebhook/node-types 1.69.0 → 1.70.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.
Files changed (92) hide show
  1. package/dist/calendar-toolkit.d.ts +1 -1
  2. package/dist/connections.d.ts +1 -1
  3. package/dist/discord-operations.d.ts +1 -1
  4. package/dist/discord-operations.js +4 -4
  5. package/dist/discord-toolkit.d.ts +1 -1
  6. package/dist/discord-toolkit.js +3 -3
  7. package/dist/dispatch.d.ts +1 -1
  8. package/dist/docs-toolkit.d.ts +1 -1
  9. package/dist/drive-toolkit.d.ts +1 -1
  10. package/dist/esm/calendar-operations.d.ts +67 -0
  11. package/dist/esm/calendar-operations.js +176 -0
  12. package/dist/esm/calendar-toolkit.d.ts +50 -0
  13. package/dist/esm/calendar-toolkit.js +152 -0
  14. package/dist/esm/capacidad-de-credencial.d.ts +75 -0
  15. package/dist/esm/capacidad-de-credencial.js +68 -0
  16. package/dist/esm/connections.d.ts +17 -0
  17. package/dist/esm/connections.js +106 -0
  18. package/dist/esm/credentials.d.ts +144 -0
  19. package/dist/esm/credentials.js +199 -0
  20. package/dist/esm/discord-operations.d.ts +104 -0
  21. package/dist/esm/discord-operations.js +407 -0
  22. package/dist/esm/discord-toolkit.d.ts +64 -0
  23. package/dist/esm/discord-toolkit.js +260 -0
  24. package/dist/esm/dispatch.d.ts +16 -0
  25. package/dist/esm/dispatch.js +76 -0
  26. package/dist/esm/docs-operations.d.ts +99 -0
  27. package/dist/esm/docs-operations.js +127 -0
  28. package/dist/esm/docs-toolkit.d.ts +42 -0
  29. package/dist/esm/docs-toolkit.js +97 -0
  30. package/dist/esm/drive-operations.d.ts +103 -0
  31. package/dist/esm/drive-operations.js +319 -0
  32. package/dist/esm/drive-toolkit.d.ts +54 -0
  33. package/dist/esm/drive-toolkit.js +177 -0
  34. package/dist/esm/github-operations.d.ts +147 -0
  35. package/dist/esm/github-operations.js +551 -0
  36. package/dist/esm/gmail-operations.d.ts +190 -0
  37. package/dist/esm/gmail-operations.js +560 -0
  38. package/dist/esm/google-analytics-operations.d.ts +99 -0
  39. package/dist/esm/google-analytics-operations.js +222 -0
  40. package/dist/esm/google-contacts-operations.d.ts +93 -0
  41. package/dist/esm/google-contacts-operations.js +363 -0
  42. package/dist/esm/index.d.ts +66 -0
  43. package/dist/esm/index.js +50 -0
  44. package/dist/esm/jira-operations.d.ts +164 -0
  45. package/dist/esm/jira-operations.js +470 -0
  46. package/dist/esm/llm-models.d.ts +101 -0
  47. package/dist/esm/llm-models.js +230 -0
  48. package/dist/esm/mailchimp-operations.d.ts +56 -0
  49. package/dist/esm/mailchimp-operations.js +155 -0
  50. package/dist/esm/mongo-operations.d.ts +61 -0
  51. package/dist/esm/mongo-operations.js +120 -0
  52. package/dist/esm/notion-operations.d.ts +125 -0
  53. package/dist/esm/notion-operations.js +511 -0
  54. package/dist/esm/openrouter.d.ts +142 -0
  55. package/dist/esm/openrouter.js +99 -0
  56. package/dist/esm/package.json +4 -0
  57. package/dist/esm/postgres-operations.d.ts +60 -0
  58. package/dist/esm/postgres-operations.js +75 -0
  59. package/dist/esm/registry.d.ts +160 -0
  60. package/dist/esm/registry.js +555 -0
  61. package/dist/esm/sheets-operations.d.ts +78 -0
  62. package/dist/esm/sheets-operations.js +150 -0
  63. package/dist/esm/sheets-toolkit.d.ts +49 -0
  64. package/dist/esm/sheets-toolkit.js +127 -0
  65. package/dist/esm/shopify-operations.d.ts +95 -0
  66. package/dist/esm/shopify-operations.js +259 -0
  67. package/dist/esm/slack-operations.d.ts +81 -0
  68. package/dist/esm/slack-operations.js +244 -0
  69. package/dist/esm/slack-toolkit.d.ts +60 -0
  70. package/dist/esm/slack-toolkit.js +235 -0
  71. package/dist/esm/telegram-operations.d.ts +104 -0
  72. package/dist/esm/telegram-operations.js +351 -0
  73. package/dist/esm/telegram-toolkit.d.ts +52 -0
  74. package/dist/esm/telegram-toolkit.js +183 -0
  75. package/dist/esm/types.d.ts +149 -0
  76. package/dist/esm/types.js +8 -0
  77. package/dist/esm/ui.d.ts +13 -0
  78. package/dist/esm/ui.js +150 -0
  79. package/dist/esm/whatsapp-operations.d.ts +23 -0
  80. package/dist/esm/whatsapp-operations.js +36 -0
  81. package/dist/index.d.ts +66 -66
  82. package/dist/index.js +185 -185
  83. package/dist/openrouter.d.ts +1 -1
  84. package/dist/registry.d.ts +1 -1
  85. package/dist/sheets-toolkit.d.ts +1 -1
  86. package/dist/slack-operations.d.ts +1 -1
  87. package/dist/slack-operations.js +4 -4
  88. package/dist/slack-toolkit.d.ts +1 -1
  89. package/dist/slack-toolkit.js +3 -3
  90. package/dist/telegram-toolkit.d.ts +1 -1
  91. package/dist/ui.d.ts +1 -1
  92. package/package.json +21 -3
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Canonical LLM provider + model catalog. Single source of truth for
3
+ * every place that names a model, used by:
4
+ *
5
+ * - dashboard pickers: AI nodes, LLM credentials, voice agents
6
+ * - api defaults: the AI node's `model` @Prop, the cheap model the
7
+ * tool generator and the fact extractor pick per provider
8
+ * - api summarization: context windows drive the chunk size
9
+ *
10
+ * It lived in `dashboard/lib/llm-models.ts` until 2026-08-18, with a
11
+ * comment arguing model strings were "dashboard chrome only". They were
12
+ * not: the api had its own hardcoded copies and nothing synced them. The
13
+ * picker offered `claude-haiku-4-5` while the api minted nodes on
14
+ * `claude-sonnet-4-20250514`, retired two months earlier. Same class of
15
+ * bug `credentials.ts` exists to kill — both sides import one list now.
16
+ *
17
+ * NOT here: the models HostWebhook runs on its own key (see the api's
18
+ * `common/llm-models.ts`) and the voice-platform catalogs. Retell and
19
+ * Vapi accept their own spellings — Retell says `claude-5-sonnet`, Vapi
20
+ * demands the dated Haiku id — so those live with their backends and
21
+ * follow the platform's docs, not the provider's.
22
+ *
23
+ * Last verified against provider docs: 2026-08-18
24
+ * - Anthropic: https://platform.claude.com/docs/en/about-claude/models/overview
25
+ * - OpenAI: https://developers.openai.com/api/docs/models
26
+ * - Google: https://ai.google.dev/gemini-api/docs/models
27
+ * - Groq: https://console.groq.com/docs/models
28
+ *
29
+ * NOT verified against docs, and on purpose: OpenRouter. Its catalog is not a
30
+ * list anybody types — see `LLM_MODELS.openrouter` and `openrouter.ts`.
31
+ */
32
+ export const LLM_PROVIDERS = [
33
+ { value: 'anthropic', label: 'Anthropic' },
34
+ { value: 'openai', label: 'OpenAI' },
35
+ { value: 'google', label: 'Google' },
36
+ { value: 'groq', label: 'Groq' },
37
+ { value: 'openrouter', label: 'OpenRouter' },
38
+ ];
39
+ export const LLM_MODELS = {
40
+ // ── Anthropic ──────────────────────────────────────────────────────────
41
+ // Current tier (Aug 2026): Fable 5, Opus 5, Sonnet 5, Haiku 4.5. The 4.x
42
+ // Opus/Sonnet line is still served but sits under "legacy" in the docs.
43
+ // Haiku 4.5 stays recommended: the only current-tier model in the
44
+ // $1/MTok bracket, so new nodes keep their cost profile.
45
+ // claude-sonnet-4-20250514 + claude-opus-4-20250514 retired 2026-06-15.
46
+ // claude-mythos-5 is omitted on purpose — invitation-only (Glasswing).
47
+ anthropic: [
48
+ { value: 'claude-haiku-4-5', label: 'Claude Haiku 4.5', recommended: true },
49
+ { value: 'claude-sonnet-5', label: 'Claude Sonnet 5' },
50
+ { value: 'claude-opus-5', label: 'Claude Opus 5' },
51
+ { value: 'claude-fable-5', label: 'Claude Fable 5' },
52
+ { value: 'claude-opus-4-8', label: 'Claude Opus 4.8 (legacy)' },
53
+ { value: 'claude-opus-4-7', label: 'Claude Opus 4.7 (legacy)' },
54
+ { value: 'claude-opus-4-6', label: 'Claude Opus 4.6 (legacy)' },
55
+ { value: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6 (legacy)' },
56
+ ],
57
+ // ── OpenAI ─────────────────────────────────────────────────────────────
58
+ // GPT-5.6 (Sol / Terra / Luna) is the current frontier family, released
59
+ // 2026-07-09. Luna is the cheap tier and the recommended default. 5.5 /
60
+ // 5.4-mini / 4o are still served but no longer listed as frontier, so
61
+ // they are kept only so existing nodes keep a friendly label.
62
+ // o1 removed — shut down 2025-07-28.
63
+ openai: [
64
+ { value: 'gpt-5.6-luna', label: 'GPT-5.6 Luna', recommended: true },
65
+ { value: 'gpt-5.6-terra', label: 'GPT-5.6 Terra' },
66
+ { value: 'gpt-5.6-sol', label: 'GPT-5.6 Sol' },
67
+ { value: 'gpt-5.5', label: 'GPT-5.5 (legacy)' },
68
+ { value: 'gpt-5.4-mini', label: 'GPT-5.4 mini (legacy)' },
69
+ { value: 'gpt-4o', label: 'GPT-4o (legacy)' },
70
+ { value: 'gpt-4o-mini', label: 'GPT-4o mini (legacy)' },
71
+ ],
72
+ // ── Google Gemini ──────────────────────────────────────────────────────
73
+ // The 3.x line went stable: 3.7/3.6/3.5 Flash and 3.1 Flash Lite no
74
+ // longer carry a -preview suffix. 3.5 Flash Lite is the cheapest current
75
+ // tier and the recommended default.
76
+ // gemini-3.1-flash-lite-preview and gemini-3-pro-preview were shut down —
77
+ // the first one used to be our default, so nodes still on it fail until
78
+ // the user repicks.
79
+ google: [
80
+ {
81
+ value: 'gemini-3.5-flash-lite',
82
+ label: 'Gemini 3.5 Flash Lite',
83
+ recommended: true,
84
+ },
85
+ { value: 'gemini-3.7-flash', label: 'Gemini 3.7 Flash' },
86
+ { value: 'gemini-3.6-flash', label: 'Gemini 3.6 Flash' },
87
+ { value: 'gemini-3.5-flash', label: 'Gemini 3.5 Flash' },
88
+ { value: 'gemini-3.1-flash-lite', label: 'Gemini 3.1 Flash Lite' },
89
+ { value: 'gemini-3.1-pro-preview', label: 'Gemini 3.1 Pro (preview)' },
90
+ { value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash (legacy)' },
91
+ { value: 'gemini-2.5-flash-lite', label: 'Gemini 2.5 Flash Lite (legacy)' },
92
+ { value: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro (legacy)' },
93
+ ],
94
+ // ── Groq ───────────────────────────────────────────────────────────────
95
+ // llama-3.1-8b-instant + llama-3.3-70b-versatile were retired 2026-08-16
96
+ // for free/developer tiers — removed. The first one was our recommended
97
+ // default, so nodes still on it fail until the user repicks.
98
+ // Groq's own replacements: 8b-instant → gpt-oss-20b, 70b-versatile →
99
+ // gpt-oss-120b or qwen3.6-27b.
100
+ groq: [
101
+ { value: 'openai/gpt-oss-20b', label: 'GPT OSS 20B', recommended: true },
102
+ { value: 'openai/gpt-oss-120b', label: 'GPT OSS 120B' },
103
+ { value: 'groq/compound', label: 'Compound' },
104
+ { value: 'groq/compound-mini', label: 'Compound Mini' },
105
+ { value: 'qwen/qwen3.6-27b', label: 'Qwen 3.6 27B (preview)' },
106
+ ],
107
+ // ── OpenRouter ─────────────────────────────────────────────────────────
108
+ // EMPTY ON PURPOSE. Not a gap, not a TODO — read this before filling it.
109
+ //
110
+ // Every other provider above is a lab with a dozen models, each with a
111
+ // price we look up in `MODEL_PRICING` over in platform-contracts. OpenRouter
112
+ // is a router: ~425 models from everybody else, and the set moves on its own
113
+ // — models appear, get deprecated and change price without anyone here
114
+ // touching a file. Typing them out has three separate costs:
115
+ //
116
+ // 1. It goes stale silently. That is the exact failure this whole file
117
+ // exists to kill (the picker offering a model the api had retired),
118
+ // only this time with 425 chances to happen instead of eight.
119
+ // 2. The price would have to be hand-copied into `MODEL_PRICING` too —
120
+ // 425 rows of a number the API already hands us with each model. A
121
+ // wrong row there does not fail loudly: it makes a dollar spending cap
122
+ // cut in the wrong place, or never.
123
+ // 3. Nobody wants a 425-entry dropdown anyway. The dashboard has to
124
+ // search/filter it live regardless of what we ship here.
125
+ //
126
+ // So the catalog is FETCHED: `GET https://openrouter.ai/api/v1/models` is
127
+ // open, needs no key, and each entry carries id, name, context_length AND
128
+ // its own pricing. `openrouter.ts` types that payload and maps it to the
129
+ // very same `LlmModelOption` the pickers already render; the pricing half
130
+ // is `tarifaDeOpenRouter` in `@hostwebhook/platform-contracts`, which turns
131
+ // it into a `ModelPricing` you hand to `calculateCost(..., tarifa)`.
132
+ //
133
+ // Consequence, stated out loud because it is the surprising part:
134
+ // `getDefaultModel('openrouter')` is `''`. There is no honest default among
135
+ // 425 models we do not curate — any pick here would be a guess that can be
136
+ // deprecated out from under us — so choosing a model is REQUIRED when the
137
+ // provider is OpenRouter. See `getDefaultModel` below.
138
+ openrouter: [],
139
+ };
140
+ /**
141
+ * Effective context window per model, in TOKENS. The api sizes
142
+ * summarization chunks from this; a miss falls back to a conservative
143
+ * 32k window, which still produces a summary — just chopped into far
144
+ * more map calls than needed. That silence is why this table went four
145
+ * months without a single current model in it.
146
+ *
147
+ * Includes ids the pickers no longer offer: the AI node's model field is
148
+ * a free-text datalist, so a node may hold one by hand.
149
+ *
150
+ * ⚠️ NOT here: OpenRouter's ids. Same reason its models are not in
151
+ * `LLM_MODELS` — its API publishes `context_length` per model, so the number
152
+ * travels with the model instead of being copied 425 times into this table.
153
+ * Whoever chunks for an OpenRouter model reads it from the fetched entry
154
+ * (`ModeloDeOpenRouter.context_length`); a lookup here misses and falls back
155
+ * to the conservative 32k, which still summarizes, just in more calls.
156
+ */
157
+ export const MODEL_CONTEXT_WINDOWS = {
158
+ // Anthropic — the 4.6 generation onward is 1M; Haiku and the 4.5 line
159
+ // stay at 200k. Retired ids left out: claude-sonnet-4-20250514 and
160
+ // claude-opus-4-1 (both gone 2026-06-15).
161
+ 'claude-fable-5': 1000000,
162
+ 'claude-opus-5': 1000000,
163
+ 'claude-sonnet-5': 1000000,
164
+ 'claude-haiku-4-5': 200000,
165
+ 'claude-opus-4-8': 1000000,
166
+ 'claude-opus-4-7': 1000000,
167
+ 'claude-opus-4-6': 1000000,
168
+ 'claude-sonnet-4-6': 1000000,
169
+ 'claude-sonnet-4-5': 200000,
170
+ 'claude-opus-4-5': 200000,
171
+ // OpenAI — the GPT-5.6 family is 1.05M. 'gpt-5.6' is the documented
172
+ // alias for Sol, so it needs its own key: the prefix fallback in the
173
+ // api stems on three dash-segments and would not reach 'gpt-5.6-sol'.
174
+ 'gpt-5.6': 1050000,
175
+ 'gpt-5.6-sol': 1050000,
176
+ 'gpt-5.6-terra': 1050000,
177
+ 'gpt-5.6-luna': 1050000,
178
+ 'gpt-4o': 128000,
179
+ 'gpt-4o-mini': 128000,
180
+ 'gpt-4-turbo': 128000,
181
+ 'gpt-4.1': 1000000,
182
+ // Google — the whole Gemini 3.x line is 1M. gemini-2.0-flash left out
183
+ // (shut down). 2.5 Pro is its real 1M input limit, not the 2M the api
184
+ // used to claim — that produced chunks the model would reject.
185
+ 'gemini-3.7-flash': 1000000,
186
+ 'gemini-3.6-flash': 1000000,
187
+ 'gemini-3.5-flash': 1000000,
188
+ 'gemini-3.5-flash-lite': 1000000,
189
+ 'gemini-3.1-flash-lite': 1000000,
190
+ 'gemini-3.1-pro-preview': 1000000,
191
+ 'gemini-2.5-pro': 1000000,
192
+ 'gemini-2.5-flash': 1000000,
193
+ 'gemini-2.5-flash-lite': 1000000,
194
+ };
195
+ /**
196
+ * The model catalog for a provider. Accepts both the raw provider name
197
+ * (`"anthropic"`) and the `llm_*` credential type prefix
198
+ * (`"llm_anthropic"`) so call sites that key off credential type can
199
+ * pass the type directly without stripping.
200
+ */
201
+ export function getModelsFor(provider) {
202
+ const key = provider.replace(/^llm_/, '');
203
+ return LLM_MODELS[key] ?? [];
204
+ }
205
+ /**
206
+ * The suggested default model for a provider, or `''` for one we do not
207
+ * catalog (Ollama runs whatever the user pulled, so it has no entry —
208
+ * callers fall back to the node's own model there).
209
+ *
210
+ * ⚠️ `'openrouter'` is the second `''`, and unlike Ollama it IS a provider in
211
+ * `LLM_PROVIDERS`, so callers that assumed "every provider in the list has a
212
+ * default" now have a case to handle. It is deliberate: its catalog is live
213
+ * (see `LLM_MODELS.openrouter`) and no static pick would survive. Treat `''`
214
+ * as "the user must choose", not as "fall back to something" — a node born
215
+ * with an empty model must not be sent to the API as-is.
216
+ */
217
+ export function getDefaultModel(provider) {
218
+ const models = getModelsFor(provider);
219
+ const recommended = models.find((m) => m.recommended);
220
+ return recommended?.value ?? models[0]?.value ?? '';
221
+ }
222
+ /** The friendly label for a model value, or the value itself. */
223
+ export function getModelLabel(value) {
224
+ for (const models of Object.values(LLM_MODELS)) {
225
+ const found = models.find((m) => m.value === value);
226
+ if (found?.label)
227
+ return found.label;
228
+ }
229
+ return value;
230
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Mailchimp Marketing API operations — single source of truth across the
3
+ * api, the dashboard and downstream consumers (MCP server).
4
+ *
5
+ * ## Three, out of one hundred and eighty-one
6
+ *
7
+ * The Marketing API has 181 routes. This node exposes three, and the choice
8
+ * is argued in `api/docs/ADR-0003-mailchimp.md`: a menu of 181 is how a node
9
+ * becomes unusable, and these three cover what people actually automate —
10
+ * put someone in a list, tag them, drop them into a journey.
11
+ *
12
+ * Campaign creation and sending were considered and left out of v1: a
13
+ * campaign needs a template, a segment and a schedule, which is a
14
+ * configuration surface of its own and not something anyone drives from a
15
+ * webhook on day one.
16
+ *
17
+ * ## Every field here exists in Mailchimp's OpenAPI schema
18
+ *
19
+ * Taken from `api.mailchimp.com/schema/3.0/Swagger.json`, not from memory.
20
+ * If you add a parameter, check it there first — an invented field is
21
+ * accepted, ignored, and then read back by the next maintainer as if it did
22
+ * something.
23
+ */
24
+ export declare const MAILCHIMP_OPERATIONS: readonly ["upsertContact", "setTags", "triggerJourney"];
25
+ export type MailchimpOperation = (typeof MAILCHIMP_OPERATIONS)[number];
26
+ /** Type guard — for DTOs and AI tool calls, where the input is untrusted. */
27
+ export declare function isMailchimpOperation(value: unknown): value is MailchimpOperation;
28
+ /**
29
+ * Contact status. `pending` is the default everywhere on purpose.
30
+ *
31
+ * Writing `subscribed` for someone who never consented is a legal problem,
32
+ * not a technical one — and a default of `subscribed` is exactly what makes
33
+ * it easy. `pending` sends Mailchimp's confirmation email, which is the
34
+ * behaviour someone should have to opt *out* of.
35
+ */
36
+ export declare const MAILCHIMP_CONTACT_STATUSES: readonly ["pending", "subscribed", "unsubscribed", "cleaned", "transactional"];
37
+ export type MailchimpContactStatus = (typeof MAILCHIMP_CONTACT_STATUSES)[number];
38
+ export interface MailchimpParamSpec {
39
+ /** Field key — also the property name on operationConfig. */
40
+ name: string;
41
+ label: string;
42
+ /** Param type, so the dashboard renders the right control. `audience`
43
+ * and `journeyStep` are pickers backed by live Mailchimp lookups. */
44
+ type: 'audience' | 'journeyStep' | 'email' | 'string' | 'status' | 'tags' | 'json' | 'boolean';
45
+ required?: boolean;
46
+ description: string;
47
+ placeholder?: string;
48
+ }
49
+ export interface MailchimpOperationSpec {
50
+ label: string;
51
+ description: string;
52
+ /** The Marketing API route this maps to, so the mapping is checkable. */
53
+ apiRoute: string;
54
+ params: MailchimpParamSpec[];
55
+ }
56
+ export declare const MAILCHIMP_OPERATION_SPECS: Record<MailchimpOperation, MailchimpOperationSpec>;
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Mailchimp Marketing API operations — single source of truth across the
3
+ * api, the dashboard and downstream consumers (MCP server).
4
+ *
5
+ * ## Three, out of one hundred and eighty-one
6
+ *
7
+ * The Marketing API has 181 routes. This node exposes three, and the choice
8
+ * is argued in `api/docs/ADR-0003-mailchimp.md`: a menu of 181 is how a node
9
+ * becomes unusable, and these three cover what people actually automate —
10
+ * put someone in a list, tag them, drop them into a journey.
11
+ *
12
+ * Campaign creation and sending were considered and left out of v1: a
13
+ * campaign needs a template, a segment and a schedule, which is a
14
+ * configuration surface of its own and not something anyone drives from a
15
+ * webhook on day one.
16
+ *
17
+ * ## Every field here exists in Mailchimp's OpenAPI schema
18
+ *
19
+ * Taken from `api.mailchimp.com/schema/3.0/Swagger.json`, not from memory.
20
+ * If you add a parameter, check it there first — an invented field is
21
+ * accepted, ignored, and then read back by the next maintainer as if it did
22
+ * something.
23
+ */
24
+ export const MAILCHIMP_OPERATIONS = [
25
+ 'upsertContact',
26
+ 'setTags',
27
+ 'triggerJourney',
28
+ ];
29
+ /** Type guard — for DTOs and AI tool calls, where the input is untrusted. */
30
+ export function isMailchimpOperation(value) {
31
+ return (typeof value === 'string' &&
32
+ MAILCHIMP_OPERATIONS.includes(value));
33
+ }
34
+ /**
35
+ * Contact status. `pending` is the default everywhere on purpose.
36
+ *
37
+ * Writing `subscribed` for someone who never consented is a legal problem,
38
+ * not a technical one — and a default of `subscribed` is exactly what makes
39
+ * it easy. `pending` sends Mailchimp's confirmation email, which is the
40
+ * behaviour someone should have to opt *out* of.
41
+ */
42
+ export const MAILCHIMP_CONTACT_STATUSES = [
43
+ 'pending',
44
+ 'subscribed',
45
+ 'unsubscribed',
46
+ 'cleaned',
47
+ 'transactional',
48
+ ];
49
+ export const MAILCHIMP_OPERATION_SPECS = {
50
+ upsertContact: {
51
+ label: 'Add or update contact',
52
+ description: 'Add a contact to an audience, or update them if the email is already there.',
53
+ apiRoute: 'PUT /lists/{list_id}/members/{subscriber_hash}',
54
+ params: [
55
+ {
56
+ name: 'audienceId',
57
+ label: 'Audience',
58
+ type: 'audience',
59
+ required: true,
60
+ description: 'The audience the contact belongs to.',
61
+ },
62
+ {
63
+ name: 'email',
64
+ label: 'Email address',
65
+ type: 'email',
66
+ required: true,
67
+ description: 'The contact. Mailchimp accepts the address itself here, so no hashing is needed.',
68
+ placeholder: '{{payload.email}}',
69
+ },
70
+ {
71
+ name: 'statusIfNew',
72
+ label: 'Status if new',
73
+ type: 'status',
74
+ required: true,
75
+ description: 'Only applies when the contact does not exist yet. An existing contact keeps the status it already had.',
76
+ },
77
+ {
78
+ name: 'mergeFields',
79
+ label: 'Merge fields',
80
+ type: 'json',
81
+ description: 'Mailchimp merge tags, e.g. {"FNAME":"Ada","LNAME":"Lovelace"}. The tags are defined per audience.',
82
+ placeholder: '{\n "FNAME": "{{payload.firstName}}"\n}',
83
+ },
84
+ {
85
+ name: 'language',
86
+ label: 'Language',
87
+ type: 'string',
88
+ description: 'Two-letter language code, e.g. "es". Optional.',
89
+ },
90
+ {
91
+ name: 'vip',
92
+ label: 'Mark as VIP',
93
+ type: 'boolean',
94
+ description: 'Mailchimp caps VIP contacts at 5,000 per audience.',
95
+ },
96
+ ],
97
+ },
98
+ setTags: {
99
+ label: 'Add or remove tags',
100
+ description: 'Set tags on a contact. Adding and removing happen in the same call.',
101
+ apiRoute: 'POST /lists/{list_id}/members/{subscriber_hash}/tags',
102
+ params: [
103
+ {
104
+ name: 'audienceId',
105
+ label: 'Audience',
106
+ type: 'audience',
107
+ required: true,
108
+ description: 'Tags are scoped to an audience, not to the account.',
109
+ },
110
+ {
111
+ name: 'email',
112
+ label: 'Email address',
113
+ type: 'email',
114
+ required: true,
115
+ description: 'The contact to tag.',
116
+ placeholder: '{{payload.email}}',
117
+ },
118
+ {
119
+ name: 'tags',
120
+ label: 'Tags',
121
+ type: 'tags',
122
+ required: true,
123
+ description: 'Each tag is set active (add) or inactive (remove). One call can do both.',
124
+ },
125
+ {
126
+ name: 'isSyncing',
127
+ label: 'Skip automations',
128
+ type: 'boolean',
129
+ description: 'When on, automations that watch these tags do NOT fire. Use it for a bulk backfill you do not want to trigger emails.',
130
+ },
131
+ ],
132
+ },
133
+ triggerJourney: {
134
+ label: 'Add contact to a journey',
135
+ description: 'Drop a contact into a Customer Journey at a specific step.',
136
+ apiRoute: 'POST /customer-journeys/journeys/{journey_id}/steps/{step_id}/actions/trigger',
137
+ params: [
138
+ {
139
+ name: 'journeyStep',
140
+ label: 'Journey step',
141
+ type: 'journeyStep',
142
+ required: true,
143
+ description: 'The journey and the step inside it. The journey must already be running in Mailchimp.',
144
+ },
145
+ {
146
+ name: 'email',
147
+ label: 'Email address',
148
+ type: 'email',
149
+ required: true,
150
+ description: 'The contact to enrol. They must already exist in the journey audience — this operation does not create them.',
151
+ placeholder: '{{payload.email}}',
152
+ },
153
+ ],
154
+ },
155
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * MongoDB operation enum + esquema del formulario — una sola copia para la api,
3
+ * el Dashboard y el broker.
4
+ *
5
+ * ── Lo que quita ──
6
+ * La lista estaba escrita a mano **cinco veces sólo en la api** (tres en el DTO,
7
+ * dos en la entidad) y otras tantas en el dashboard. Y de los siete valores,
8
+ * el lienzo sólo conocía **cinco**: `findAll` y `findOneAndUpdate` no estaban en
9
+ * sus mapas de etiqueta y color, así que se pintaban grises y con el nombre
10
+ * crudo en camelCase mientras el panel sí les daba azul y ámbar.
11
+ *
12
+ * ── Lo fácil de Mongo ──
13
+ * Al contrario que Drive o Calendar, aquí **no hay capa de renombrado**: el
14
+ * `name` de cada param es la prop de la entidad tal cual, y el `apiUpdate` de la
15
+ * página es un passthrough de una línea. Es la forma de guardado más simple de
16
+ * la tanda.
17
+ */
18
+ export declare const MONGO_OPERATIONS: readonly ["insertOne", "findOne", "findAll", "updateOne", "findOneAndUpdate", "deleteOne", "aggregate"];
19
+ export type MongoOperation = (typeof MONGO_OPERATIONS)[number];
20
+ /** Type guard — útil al validar entrada que no controlas (DTOs, tool calls). */
21
+ export declare function isMongoOperation(value: unknown): value is MongoOperation;
22
+ export type MongoParamType =
23
+ /** El `TemplateEditor` de JSON, con autocompletado de `{{payload.*}}`. */
24
+ 'jsonTemplate'
25
+ /** El componente `UpdateBuilder` (arma `$set`, `$inc`… sin escribir JSON). */
26
+ | 'updateBuilder'
27
+ /**
28
+ * Un `Switch` que se guarda **al instante** (`updateFieldInstant`) y se lee de
29
+ * la entidad, no de los locales. Se conserva así porque es como estaba.
30
+ */
31
+ | 'booleanInstant';
32
+ export interface MongoParamSpec {
33
+ /** La prop de la entidad. Sin renombrado: es también la clave del cable. */
34
+ name: string;
35
+ label: string;
36
+ type: MongoParamType;
37
+ /** Texto de ayuda. Llano: el paquete no lleva React. */
38
+ description?: string;
39
+ placeholder?: string;
40
+ /** Sólo para `jsonTemplate`: la altura del editor. */
41
+ minHeight?: number;
42
+ maxHeight?: number;
43
+ }
44
+ export interface MongoOperationSpec {
45
+ /** La del desplegable de la página de detalle: «Insert One». */
46
+ label: string;
47
+ /**
48
+ * La de la píldora estrecha del lienzo y del panel: «INSERT».
49
+ *
50
+ * **Son dos etiquetas a propósito**, como en Calendar: dos anchos distintos y
51
+ * las dos se ven. Antes había una TERCERA convención, porque el panel pintaba
52
+ * `operation.toUpperCase()` y decía «FINDONEANDUPDATE» donde el lienzo decía
53
+ * «UPDATE». Ahora los dos leen esta.
54
+ */
55
+ labelShort: string;
56
+ /** Sub-línea del desplegable. */
57
+ description: string;
58
+ /** Orden de pantalla. */
59
+ params: MongoParamSpec[];
60
+ }
61
+ export declare const MONGO_OPERATION_SPECS: Record<MongoOperation, MongoOperationSpec>;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * MongoDB operation enum + esquema del formulario — una sola copia para la api,
3
+ * el Dashboard y el broker.
4
+ *
5
+ * ── Lo que quita ──
6
+ * La lista estaba escrita a mano **cinco veces sólo en la api** (tres en el DTO,
7
+ * dos en la entidad) y otras tantas en el dashboard. Y de los siete valores,
8
+ * el lienzo sólo conocía **cinco**: `findAll` y `findOneAndUpdate` no estaban en
9
+ * sus mapas de etiqueta y color, así que se pintaban grises y con el nombre
10
+ * crudo en camelCase mientras el panel sí les daba azul y ámbar.
11
+ *
12
+ * ── Lo fácil de Mongo ──
13
+ * Al contrario que Drive o Calendar, aquí **no hay capa de renombrado**: el
14
+ * `name` de cada param es la prop de la entidad tal cual, y el `apiUpdate` de la
15
+ * página es un passthrough de una línea. Es la forma de guardado más simple de
16
+ * la tanda.
17
+ */
18
+ export const MONGO_OPERATIONS = [
19
+ 'insertOne', // inserta un documento
20
+ 'findOne', // devuelve el primero que casa
21
+ 'findAll', // devuelve todos los que casan (tope 1000)
22
+ 'updateOne', // actualiza el primero que casa
23
+ 'findOneAndUpdate', // actualiza y devuelve el documento
24
+ 'deleteOne', // borra el primero que casa
25
+ 'aggregate', // pipeline de agregación
26
+ ];
27
+ /** Type guard — útil al validar entrada que no controlas (DTOs, tool calls). */
28
+ export function isMongoOperation(value) {
29
+ return typeof value === 'string' && MONGO_OPERATIONS.includes(value);
30
+ }
31
+ /* Los campos que comparten varias operaciones. */
32
+ const queryTemplate = () => ({
33
+ name: 'queryTemplate',
34
+ label: 'Query Template',
35
+ type: 'jsonTemplate',
36
+ placeholder: '{"email": "{{payload.email}}"}',
37
+ minHeight: 100,
38
+ maxHeight: 200,
39
+ });
40
+ const updateTemplate = () => ({
41
+ name: 'updateTemplate',
42
+ label: 'Update Operations',
43
+ type: 'updateBuilder',
44
+ });
45
+ export const MONGO_OPERATION_SPECS = {
46
+ insertOne: {
47
+ label: 'Insert One',
48
+ labelShort: 'INSERT',
49
+ description: 'Insert a single document',
50
+ params: [
51
+ {
52
+ name: 'documentTemplate',
53
+ label: 'Document Template',
54
+ type: 'jsonTemplate',
55
+ placeholder: '{"name": "{{payload.name}}", "email": "{{payload.email}}"}',
56
+ minHeight: 100,
57
+ maxHeight: 200,
58
+ },
59
+ ],
60
+ },
61
+ findOne: {
62
+ label: 'Find One',
63
+ labelShort: 'FIND',
64
+ description: 'Find a single matching document',
65
+ params: [queryTemplate()],
66
+ },
67
+ findAll: {
68
+ label: 'Find All',
69
+ /* Antes no estaba en el mapa del lienzo y salía el crudo `findAll`. */
70
+ labelShort: 'FIND ALL',
71
+ description: 'Find all matching documents (limit 1000)',
72
+ params: [
73
+ /* El interruptor va ANTES del query, que es el orden en que se pintaba. */
74
+ {
75
+ name: 'includeCount',
76
+ label: 'Include Count',
77
+ type: 'booleanInstant',
78
+ description: 'Return total count of matching documents alongside results',
79
+ },
80
+ queryTemplate(),
81
+ ],
82
+ },
83
+ updateOne: {
84
+ label: 'Update One',
85
+ labelShort: 'UPDATE',
86
+ description: 'Update a single matching document',
87
+ params: [queryTemplate(), updateTemplate()],
88
+ },
89
+ findOneAndUpdate: {
90
+ label: 'Find & Update',
91
+ /* Tampoco estaba: salía el crudo `findOneAndUpdate` en el lienzo y
92
+ `FINDONEANDUPDATE` en el panel. */
93
+ labelShort: 'FIND & UPDATE',
94
+ description: 'Find, update, and return the document',
95
+ params: [queryTemplate(), updateTemplate()],
96
+ },
97
+ deleteOne: {
98
+ label: 'Delete One',
99
+ labelShort: 'DELETE',
100
+ description: 'Delete a single matching document',
101
+ params: [queryTemplate()],
102
+ },
103
+ aggregate: {
104
+ label: 'Aggregate',
105
+ labelShort: 'AGGREGATE',
106
+ description: 'Run aggregation pipeline ($match, $group, $sort...)',
107
+ params: [
108
+ {
109
+ /* Mismo campo que en las demás, con otro título y otro ejemplo: por eso
110
+ los params se declaran por operación y no en un registro global. */
111
+ name: 'queryTemplate',
112
+ label: 'Pipeline Template',
113
+ type: 'jsonTemplate',
114
+ placeholder: '[{"$match": {"status": "active"}}, {"$group": {"_id": "$category"}}]',
115
+ minHeight: 100,
116
+ maxHeight: 200,
117
+ },
118
+ ],
119
+ },
120
+ };