@revenexx/integrations-node-sdk 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,467 @@
1
+ type LocalizedString = string | Record<string, string>;
2
+ type DataType = 'any' | 'object' | 'array' | 'string' | 'number' | 'boolean';
3
+ type OutputKind = 'default' | 'branch' | 'error';
4
+ type NodeCategory = 'trigger' | 'action' | 'transform' | 'control' | 'io';
5
+ type ConfigType = 'string' | 'number' | 'boolean' | 'select' | 'multiselect' | 'object' | 'array' | 'expression' | 'secret-ref' | 'credentials-ref';
6
+ interface IInputPort {
7
+ dataType: DataType;
8
+ required?: boolean;
9
+ description?: LocalizedString;
10
+ }
11
+ interface IOutputField {
12
+ dataType: DataType;
13
+ description?: LocalizedString;
14
+ }
15
+ interface IOutputPort {
16
+ kind: OutputKind;
17
+ dataType: DataType;
18
+ name?: string;
19
+ label?: LocalizedString;
20
+ description?: LocalizedString;
21
+ fields?: Record<string, IOutputField>;
22
+ sourceFromConfig?: string;
23
+ fallback?: {
24
+ name: string;
25
+ label?: LocalizedString;
26
+ description?: LocalizedString;
27
+ };
28
+ }
29
+ interface IConfigOption {
30
+ value: string | number | boolean;
31
+ label: LocalizedString;
32
+ }
33
+ interface IConfigValidation {
34
+ pattern?: string;
35
+ min?: number;
36
+ max?: number;
37
+ minLength?: number;
38
+ maxLength?: number;
39
+ }
40
+ interface IConfigFieldBase {
41
+ key: string;
42
+ label: LocalizedString;
43
+ type: ConfigType;
44
+ description?: LocalizedString;
45
+ required?: boolean;
46
+ default?: unknown;
47
+ placeholder?: LocalizedString;
48
+ expressionAllowed?: boolean;
49
+ multiline?: boolean;
50
+ validation?: IConfigValidation;
51
+ options?: IConfigOption[];
52
+ /**
53
+ * Only meaningful when `type === 'credentials-ref'`: the namespaced slug(s) of
54
+ * the credential type(s) this field accepts (e.g. `revenexx:smtp`). The editor
55
+ * lists tenant credential instances of these type(s); the blob stores the
56
+ * chosen instance UUID. Pass a single string when only one type is accepted,
57
+ * or an array to let the field accept instances of any of several types (e.g.
58
+ * a node that works with both an OAuth and an API-token credential).
59
+ */
60
+ credentialType?: string | string[];
61
+ }
62
+ interface IConfigField extends IConfigFieldBase {
63
+ properties?: IConfigFieldBase[];
64
+ items?: IConfigFieldBase;
65
+ }
66
+ interface INodeDescription {
67
+ slug: string;
68
+ version: string;
69
+ category: NodeCategory;
70
+ name: LocalizedString;
71
+ description?: LocalizedString;
72
+ icon?: string;
73
+ inputs: Record<string, IInputPort>;
74
+ outputs: IOutputPort[];
75
+ config?: IConfigField[];
76
+ }
77
+ interface INodeContext {
78
+ signal: AbortSignal;
79
+ logger: {
80
+ info(message: string, meta?: Record<string, unknown>): void;
81
+ warn(message: string, meta?: Record<string, unknown>): void;
82
+ error(message: string, meta?: Record<string, unknown>): void;
83
+ };
84
+ secrets: {
85
+ get(key: string): Promise<string>;
86
+ };
87
+ /**
88
+ * Resolve the access data of a credential instance the node references via a
89
+ * `credentials-ref` config field. The runtime fulfils this from the
90
+ * credentials broker at execution time (inside the Temporal activity), so the
91
+ * returned value — e.g. a short-lived access token — is always current and
92
+ * never persisted into workflow history.
93
+ */
94
+ credentials: {
95
+ get(credentialsId: string): Promise<Record<string, unknown>>;
96
+ };
97
+ }
98
+ interface INodeResult {
99
+ outputs: Record<string, unknown>;
100
+ branch?: string;
101
+ }
102
+ interface INode {
103
+ description: INodeDescription;
104
+ execute(ctx: INodeContext, inputs: Record<string, unknown>): Promise<INodeResult>;
105
+ }
106
+ /**
107
+ * Optional capability interface for nodes that iterate over a collection.
108
+ * Implement this alongside {@link INode} to signal to the worker that this
109
+ * node drives iteration — the worker will call {@link extractItems} instead
110
+ * of relying on slug-based detection.
111
+ *
112
+ * This interface is the designated dispatch point for future child-workflow
113
+ * execution: once per-iteration isolation is needed, the worker can swap the
114
+ * inline loop for `executeChild` calls without any changes to the node or SDK.
115
+ */
116
+ interface INodeWithIteration {
117
+ /**
118
+ * Extracts the array of items to iterate over from the resolved inputs and
119
+ * config. Must be pure and synchronous — it runs inside a Temporal Activity.
120
+ */
121
+ extractItems(inputs: Record<string, unknown>, config: Record<string, unknown>): unknown[];
122
+ }
123
+ /**
124
+ * Type guard that checks whether a node implements {@link INodeWithIteration}.
125
+ */
126
+ declare function isNodeWithIteration(node: INode): node is INode & INodeWithIteration;
127
+ /**
128
+ * The authentication strategy a credential type uses. Determines which SDK
129
+ * base class a concrete credential extends and how the broker resolves it.
130
+ */
131
+ type CredentialAuthKind = 'static' | 'api-key' | 'basic' | 'oauth2-client-credentials' | 'oauth2-authcode';
132
+ /**
133
+ * Field types for the connection parameters a credential type declares. A
134
+ * superset-subset of {@link ConfigType}: scalars plus `secret` for values that
135
+ * must be masked in the UI and never returned in plaintext by the public API.
136
+ */
137
+ type CredentialFieldType = 'string' | 'number' | 'boolean' | 'select' | 'secret';
138
+ interface ICredentialField {
139
+ key: string;
140
+ label: LocalizedString;
141
+ type: CredentialFieldType;
142
+ description?: LocalizedString;
143
+ required?: boolean;
144
+ default?: unknown;
145
+ placeholder?: LocalizedString;
146
+ validation?: IConfigValidation;
147
+ options?: IConfigOption[];
148
+ }
149
+ /**
150
+ * The published contract of a credential type: how the editor renders its
151
+ * config form and which auth strategy the broker applies. The imperative
152
+ * `test`/`resolve` logic lives in the bundled {@link ICredential} code, not in
153
+ * this description.
154
+ */
155
+ interface ICredentialDescription {
156
+ /** Stable namespaced identifier `<namespace>:<slug>` (e.g. `revenexx:smtp`). */
157
+ slug: string;
158
+ version: string;
159
+ name: LocalizedString;
160
+ description?: LocalizedString;
161
+ icon?: string;
162
+ authKind: CredentialAuthKind;
163
+ fields: ICredentialField[];
164
+ }
165
+ /**
166
+ * Runtime context handed to a credential's `test`/`resolve`. Runs in the
167
+ * credentials broker (a Node side-container), never in workflow code.
168
+ */
169
+ interface ICredentialContext {
170
+ signal: AbortSignal;
171
+ logger: {
172
+ info(message: string, meta?: Record<string, unknown>): void;
173
+ warn(message: string, meta?: Record<string, unknown>): void;
174
+ error(message: string, meta?: Record<string, unknown>): void;
175
+ };
176
+ /**
177
+ * Persist updated durable credentials (e.g. a rotated `refresh_token`) back
178
+ * to storage. The broker wires this to `PATCH /v1/internal/credentials/{id}/durable-creds`.
179
+ * Absent during pre-save tests where no instance exists yet.
180
+ */
181
+ persistDurableCreds?(durableCreds: Record<string, unknown>): Promise<void>;
182
+ }
183
+ interface ICredentialTestResult {
184
+ ok: boolean;
185
+ message?: string;
186
+ }
187
+ interface ICredentialResolveResult {
188
+ /** The access data handed to the node (e.g. `{ host, port, user, password }` or `{ accessToken }`). */
189
+ credentials: Record<string, unknown>;
190
+ /** ISO-8601 expiry of the resolved access data; absent for non-expiring (static) credentials. */
191
+ expiresAt?: string;
192
+ }
193
+ /**
194
+ * A credential type implementation. `config` is the validated, user-entered
195
+ * field map; `durableCreds` holds system-managed long-lived secrets (e.g. a
196
+ * `refresh_token` obtained via 3-legged OAuth) and is `null` until they exist.
197
+ */
198
+ interface ICredential {
199
+ description: ICredentialDescription;
200
+ test(ctx: ICredentialContext, config: Record<string, unknown>): Promise<ICredentialTestResult>;
201
+ resolve(ctx: ICredentialContext, config: Record<string, unknown>, durableCreds: Record<string, unknown> | null): Promise<ICredentialResolveResult>;
202
+ }
203
+ /**
204
+ * Optional capability for `oauth2-authcode` credentials that need the
205
+ * interactive (3-legged) consent dance. The broker exposes these via
206
+ * `/oauth/authorize-url` and `/oauth/exchange-code`; Laravel proxies them.
207
+ */
208
+ interface ICredentialOAuthAuthorize {
209
+ buildAuthorizeUrl(ctx: ICredentialContext, config: Record<string, unknown>, params: {
210
+ redirectUri: string;
211
+ state: string;
212
+ }): Promise<{
213
+ authorizeUrl: string;
214
+ codeVerifier?: string;
215
+ }>;
216
+ exchangeCode(ctx: ICredentialContext, config: Record<string, unknown>, params: {
217
+ code: string;
218
+ redirectUri: string;
219
+ codeVerifier?: string;
220
+ }): Promise<{
221
+ durableCreds: Record<string, unknown>;
222
+ }>;
223
+ }
224
+ /**
225
+ * Type guard for credentials that implement the 3-legged OAuth consent flow.
226
+ */
227
+ declare function isOAuthAuthorizeCredential(credential: ICredential): credential is ICredential & ICredentialOAuthAuthorize;
228
+ /** Difficulty level surfaced in the template gallery. */
229
+ type TemplateLevel = 'beginner' | 'intermediate' | 'advanced';
230
+ /** Trigger kind a template can ship; mirrors the server's `TriggerType`. */
231
+ type TemplateTriggerType = 'manual' | 'schedule' | 'webhook' | 'event';
232
+ /**
233
+ * A trigger a template instantiates alongside its workflow. The `handle` is a
234
+ * stable UUID the workflow blob's edges reference via `from.nodeId`. Per-type
235
+ * `config` shapes: `schedule` → `{ cron, timezone? }`, `webhook` →
236
+ * `{ method, secretRef?, public? }`, `event` → `{ subject }`, `manual` → none.
237
+ * The integrations server deep-validates `config` per `type` on publish.
238
+ */
239
+ interface ITemplateTrigger {
240
+ /** Stable UUID the blob's edges reference via `from.nodeId`. */
241
+ handle: string;
242
+ type: TemplateTriggerType;
243
+ name?: string;
244
+ config?: Record<string, unknown>;
245
+ active?: boolean;
246
+ }
247
+ /**
248
+ * A workflow template a node package publishes: a ready-made workflow blueprint
249
+ * the editor offers as a starting point. Unlike {@link INode}/{@link ICredential}
250
+ * a template carries no executable code — it is plain data, so a package exports
251
+ * its `ITemplateDescription`s directly (no class wrapper).
252
+ *
253
+ * The `definition` is a workflow blob authored against the workflow-blob grammar
254
+ * named by `blobVersion`; the integrations server validates it on publish.
255
+ */
256
+ interface ITemplateDescription {
257
+ /** Stable namespaced identifier `<namespace>:<slug>` (e.g. `revenexx:slack-to-crm`). */
258
+ slug: string;
259
+ version: string;
260
+ /** Free-form grouping label shown in the gallery (e.g. `sales`). */
261
+ category: string;
262
+ level: TemplateLevel;
263
+ name: LocalizedString;
264
+ /** One-line summary shown on the template card. */
265
+ shortDescription?: LocalizedString;
266
+ /** Long-form description in Markdown. */
267
+ description?: LocalizedString;
268
+ icon?: string;
269
+ /** Free-form industry tags (e.g. `any`, `medical`). */
270
+ industries?: string[];
271
+ /** Free-form vendor tags (e.g. `pipedrive`, `slack`). */
272
+ vendors?: string[];
273
+ /** Workflow-blob schema version `definition` targets (e.g. `v0-draft`). */
274
+ blobVersion: string;
275
+ /** The workflow blob instantiated when a user picks this template. */
276
+ definition: Record<string, unknown>;
277
+ /**
278
+ * Triggers instantiated alongside the workflow (their `handle`s are
279
+ * referenced by `definition`'s edges). Omit for a manual-only template.
280
+ */
281
+ triggers?: ITemplateTrigger[];
282
+ }
283
+
284
+ /**
285
+ * Reduce a {@link LocalizedString} to a single plain string.
286
+ *
287
+ * Every consumer (worker, UI, the Laravel side) repeats the same "is it a
288
+ * string or a localized map?" branch to render a name/label; this centralizes
289
+ * it so the rules are defined once:
290
+ *
291
+ * - a plain string is trimmed and returned (or `undefined` if blank);
292
+ * - a localized map prefers `fallbackLang` (default `en`), then the first
293
+ * non-empty value;
294
+ * - a missing, empty, or blank-only value yields `undefined`, so the caller
295
+ * can apply its own fallback.
296
+ */
297
+ declare function normalizeLocalized(value: LocalizedString | undefined | null, fallbackLang?: string): string | undefined;
298
+
299
+ /**
300
+ * Reduce an `IConfigField.credentialType` (a single slug or an array of slugs)
301
+ * to a normalized, deduplicated `string[]`.
302
+ *
303
+ * The union `string | string[]` exists so node authors can write the common
304
+ * single-type case as a bare string, but every consumer (the editor that
305
+ * lists tenant credential instances, the worker that resolves the chosen
306
+ * instance, the Laravel side) needs a list to iterate. This centralizes the
307
+ * "is it a string or an array?" branch — analogous to {@link normalizeLocalized}
308
+ * for `LocalizedString` — so the rules are defined once:
309
+ *
310
+ * - a single non-empty slug becomes a one-element array;
311
+ * - an array is trimmed of blank entries and deduplicated, preserving order;
312
+ * - `undefined`, an empty string, or an empty/all-blank array yields `[]`, so
313
+ * the caller can treat "accepts nothing" uniformly.
314
+ */
315
+ declare function normalizeCredentialType(value: string | string[] | undefined): string[];
316
+
317
+ declare function extractManifest(node: INode): INodeDescription;
318
+ declare function extractManifests(nodes: INode[]): INodeDescription[];
319
+ declare function extractCredentialManifest(credential: ICredential): ICredentialDescription;
320
+ declare function extractCredentialManifests(credentials: ICredential[]): ICredentialDescription[];
321
+
322
+ /**
323
+ * Envelope version expected by the integrations server-side
324
+ * `TarballInspector`. Earlier (pre-registry) builds emitted a bare array
325
+ * without any `manifestVersion` field; `v0-draft` is currently the only
326
+ * accepted value (see `SchemaServiceProvider::DOMAIN_NODE` in the
327
+ * integrations service). Keeping it versioned lets the registry evolve the
328
+ * shape without breaking older publishers.
329
+ */
330
+ declare const MANIFEST_VERSION = "v0-draft";
331
+ interface NodeManifest {
332
+ manifestVersion: typeof MANIFEST_VERSION;
333
+ nodes: INodeDescription[];
334
+ /**
335
+ * Credential types this package publishes. Optional and additive: packages
336
+ * that ship no credentials omit it (older publishers never set it).
337
+ */
338
+ credentials?: ICredentialDescription[];
339
+ /**
340
+ * Workflow templates this package publishes. Optional and additive: packages
341
+ * that ship no templates omit it. Templates are plain data (no executable
342
+ * code), so they are carried verbatim from the package's `TEMPLATES` export.
343
+ */
344
+ templates?: ITemplateDescription[];
345
+ }
346
+ /**
347
+ * Builds the manifest envelope from a package's `NODES` (and optional
348
+ * `CREDENTIALS` / `TEMPLATES`) exports. The result is what gets written to
349
+ * `dist/manifest.json` and uploaded to the registry inside the tarball.
350
+ */
351
+ declare function buildManifest(nodes: INode[], credentials?: ICredential[], templates?: ITemplateDescription[]): NodeManifest;
352
+
353
+ /**
354
+ * Reusable credential base classes. Concrete credential types (in node
355
+ * packages) extend one of these and only supply field mappings / endpoints —
356
+ * the auth strategy itself lives here so it is implemented once.
357
+ *
358
+ * All of this runs in the credentials broker (a Node side-container), never in
359
+ * workflow code, so wall-clock time and network I/O are fine.
360
+ */
361
+ type Config = Record<string, unknown>;
362
+ type DurableCreds = Record<string, unknown> | null;
363
+ interface OAuthTokenResponse {
364
+ access_token?: string;
365
+ refresh_token?: string;
366
+ expires_in?: number;
367
+ token_type?: string;
368
+ /**
369
+ * Provider-specific base URL the access token must target (e.g. Pipedrive
370
+ * returns the company-scoped `api_domain` on both the initial exchange and
371
+ * every refresh). Absent for providers that use a single global API host.
372
+ */
373
+ api_domain?: string;
374
+ [key: string]: unknown;
375
+ }
376
+ /**
377
+ * The base for every credential type. Stores the published description and
378
+ * provides a small HTTP helper. `resolve` is abstract; `test` defaults to a
379
+ * presence check and should be overridden where a real connection test exists.
380
+ */
381
+ declare abstract class BaseCredential implements ICredential {
382
+ abstract readonly description: ICredentialDescription;
383
+ abstract resolve(ctx: ICredentialContext, config: Config, durableCreds: DurableCreds): Promise<ICredentialResolveResult>;
384
+ test(_ctx: ICredentialContext, config: Config): Promise<ICredentialTestResult>;
385
+ /** POST `application/x-www-form-urlencoded` and parse a JSON token response. */
386
+ protected postForm(ctx: ICredentialContext, url: string, form: Record<string, string | undefined>, headers?: Record<string, string>): Promise<OAuthTokenResponse>;
387
+ }
388
+ /**
389
+ * "Simple value retrieval": the validated config fields ARE the access data
390
+ * handed to the node (e.g. SMTP host/port/user/password, SFTP host/key). No
391
+ * token, no expiry. Override `test` to add a real connection check.
392
+ */
393
+ declare abstract class SimpleValueCredential extends BaseCredential {
394
+ resolve(_ctx: ICredentialContext, config: Config, _durableCreds: DurableCreds): Promise<ICredentialResolveResult>;
395
+ }
396
+ /**
397
+ * API-key credential. By default exposes the configured key under
398
+ * `{ apiKey }`; override {@link apiKeyField}/{@link credentialShape} to map.
399
+ */
400
+ declare abstract class ApiKeyCredential extends BaseCredential {
401
+ protected apiKeyField(): string;
402
+ protected credentialShape(apiKey: string): Record<string, unknown>;
403
+ resolve(_ctx: ICredentialContext, config: Config, _durableCreds: DurableCreds): Promise<ICredentialResolveResult>;
404
+ }
405
+ /**
406
+ * HTTP Basic auth credential. Exposes `{ username, password }` by default.
407
+ */
408
+ declare abstract class BasicAuthCredential extends BaseCredential {
409
+ protected usernameField(): string;
410
+ protected passwordField(): string;
411
+ resolve(_ctx: ICredentialContext, config: Config, _durableCreds: DurableCreds): Promise<ICredentialResolveResult>;
412
+ }
413
+ /**
414
+ * OAuth2 client-credentials grant (service-to-service, no user, no
415
+ * refresh_token). Concrete types supply the token endpoint + client creds via
416
+ * the config field map. Robust to downtime: a fresh access token is minted on
417
+ * every resolve when the broker cache misses.
418
+ */
419
+ declare abstract class OAuth2ClientCredentialsCredential extends BaseCredential {
420
+ protected abstract tokenUrl(config: Config): string;
421
+ protected abstract clientId(config: Config): string;
422
+ protected abstract clientSecret(config: Config): string;
423
+ protected scope(_config: Config): string | undefined;
424
+ /** Map the raw token response to the node-facing credentials. */
425
+ protected credentialShape(token: OAuthTokenResponse): Record<string, unknown>;
426
+ resolve(ctx: ICredentialContext, config: Config, _durableCreds: DurableCreds): Promise<ICredentialResolveResult>;
427
+ test(ctx: ICredentialContext, config: Config): Promise<ICredentialTestResult>;
428
+ }
429
+ /**
430
+ * OAuth2 authorization-code grant (3-legged, interactive consent). Implements
431
+ * the consent dance (with PKCE by default), code exchange, and refresh-token
432
+ * based resolution. The long-lived `refresh_token` is stored in durableCreds;
433
+ * if the provider rotates it, the new one is persisted via the context.
434
+ */
435
+ declare abstract class OAuth2AuthCodeCredential extends BaseCredential implements ICredentialOAuthAuthorize {
436
+ protected abstract authorizeUrl(config: Config): string;
437
+ protected abstract tokenUrl(config: Config): string;
438
+ protected abstract clientId(config: Config): string;
439
+ protected abstract clientSecret(config: Config): string;
440
+ protected scope(_config: Config): string | undefined;
441
+ protected usePkce(): boolean;
442
+ protected credentialShape(token: OAuthTokenResponse): Record<string, unknown>;
443
+ buildAuthorizeUrl(_ctx: ICredentialContext, config: Config, params: {
444
+ redirectUri: string;
445
+ state: string;
446
+ }): Promise<{
447
+ authorizeUrl: string;
448
+ codeVerifier?: string;
449
+ }>;
450
+ exchangeCode(ctx: ICredentialContext, config: Config, params: {
451
+ code: string;
452
+ redirectUri: string;
453
+ codeVerifier?: string;
454
+ }): Promise<{
455
+ durableCreds: Record<string, unknown>;
456
+ }>;
457
+ resolve(ctx: ICredentialContext, config: Config, durableCreds: DurableCreds): Promise<ICredentialResolveResult>;
458
+ test(_ctx: ICredentialContext, config: Config): Promise<ICredentialTestResult>;
459
+ }
460
+
461
+ declare class NodeError extends Error {
462
+ readonly code: string;
463
+ readonly meta?: Record<string, unknown>;
464
+ constructor(code: string, message: string, meta?: Record<string, unknown>);
465
+ }
466
+
467
+ export { ApiKeyCredential, BaseCredential, BasicAuthCredential, type ConfigType, type CredentialAuthKind, type CredentialFieldType, type DataType, type IConfigField, type IConfigFieldBase, type IConfigOption, type IConfigValidation, type ICredential, type ICredentialContext, type ICredentialDescription, type ICredentialField, type ICredentialOAuthAuthorize, type ICredentialResolveResult, type ICredentialTestResult, type IInputPort, type INode, type INodeContext, type INodeDescription, type INodeResult, type INodeWithIteration, type IOutputField, type IOutputPort, type ITemplateDescription, type ITemplateTrigger, type LocalizedString, MANIFEST_VERSION, type NodeCategory, NodeError, type NodeManifest, OAuth2AuthCodeCredential, OAuth2ClientCredentialsCredential, type OAuthTokenResponse, type OutputKind, SimpleValueCredential, type TemplateLevel, type TemplateTriggerType, buildManifest, extractCredentialManifest, extractCredentialManifests, extractManifest, extractManifests, isNodeWithIteration, isOAuthAuthorizeCredential, normalizeCredentialType, normalizeLocalized };