@revenexx/integrations-node-sdk 0.12.1 → 0.13.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/README.md CHANGED
@@ -11,7 +11,8 @@ packages and the workflow engine that runs them.
11
11
  ```
12
12
  integrations-node-sdk ← this package (types & helpers only)
13
13
  └── integrations-nodes-core ← implements INode, ships dist/manifest.json
14
- └── integrations-worker ← registers nodes, executes workflows
14
+ └── integrations-worker ← reads the manifest, executes workflows
15
+ (no SDK dependency — couples via manifestVersion)
15
16
  ```
16
17
 
17
18
  ## Installation
package/dist/cli.cjs CHANGED
@@ -24,9 +24,58 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/cli.ts
27
+ var fs2 = __toESM(require("fs"), 1);
28
+ var import_node_path2 = require("path");
29
+ var import_node_url = require("url");
30
+
31
+ // src/images.ts
27
32
  var fs = __toESM(require("fs"), 1);
28
33
  var import_node_path = require("path");
29
- var import_node_url = require("url");
34
+ function collectImageSources(manifest) {
35
+ const sources = /* @__PURE__ */ new Set();
36
+ for (const group of [manifest.nodes, manifest.credentials, manifest.templates]) {
37
+ for (const item of group ?? []) {
38
+ for (const image of item.images ?? []) {
39
+ if (typeof image.src === "string" && image.src.length > 0) {
40
+ sources.add(image.src);
41
+ }
42
+ }
43
+ }
44
+ }
45
+ return [...sources];
46
+ }
47
+ function copyImages(sources, outDir, rootDir) {
48
+ let copied = 0;
49
+ for (const src of sources) {
50
+ if (!isSafeRelativePath(src)) {
51
+ console.warn(` \u26A0 unsafe image path (absolute or escaping the package root), skipping: ${src}`);
52
+ continue;
53
+ }
54
+ const from = (0, import_node_path.resolve)(rootDir, src);
55
+ if (!fs.existsSync(from)) {
56
+ console.warn(` \u26A0 image not found, skipping: ${src}`);
57
+ continue;
58
+ }
59
+ if (!fs.statSync(from).isFile()) {
60
+ console.warn(` \u26A0 image path is not a file, skipping: ${src}`);
61
+ continue;
62
+ }
63
+ const to = (0, import_node_path.resolve)(outDir, src);
64
+ fs.mkdirSync((0, import_node_path.dirname)(to), { recursive: true });
65
+ fs.copyFileSync(from, to);
66
+ copied++;
67
+ }
68
+ if (sources.length > 0) {
69
+ console.log(` copied ${copied}/${sources.length} image(s) into dist/`);
70
+ }
71
+ }
72
+ function isSafeRelativePath(src) {
73
+ if ((0, import_node_path.isAbsolute)(src)) {
74
+ return false;
75
+ }
76
+ const normalized = (0, import_node_path.normalize)(src);
77
+ return normalized !== ".." && !normalized.startsWith(".." + import_node_path.sep);
78
+ }
30
79
 
31
80
  // src/extract.ts
32
81
  function extractManifest(node) {
@@ -65,8 +114,8 @@ function fail(message) {
65
114
  process.exit(1);
66
115
  }
67
116
  async function runManifest() {
68
- const distEntry = (0, import_node_path.resolve)(projectRoot, "dist", "index.js");
69
- if (!fs.existsSync(distEntry)) {
117
+ const distEntry = (0, import_node_path2.resolve)(projectRoot, "dist", "index.js");
118
+ if (!fs2.existsSync(distEntry)) {
70
119
  fail("dist/index.js is missing. Run the build (tsup) before `rvnxx-nodes manifest`.");
71
120
  }
72
121
  const mod = await import((0, import_node_url.pathToFileURL)(distEntry).href);
@@ -82,10 +131,10 @@ async function runManifest() {
82
131
  const credentials = mod.CREDENTIALS ?? [];
83
132
  const templates = mod.TEMPLATES ?? [];
84
133
  const manifest = buildManifest(mod.NODES, credentials, templates);
85
- const outDir = (0, import_node_path.resolve)(projectRoot, "dist");
86
- const outFile = (0, import_node_path.resolve)(outDir, "manifest.json");
87
- fs.mkdirSync(outDir, { recursive: true });
88
- fs.writeFileSync(outFile, JSON.stringify(manifest, null, 2), "utf-8");
134
+ const outDir = (0, import_node_path2.resolve)(projectRoot, "dist");
135
+ const outFile = (0, import_node_path2.resolve)(outDir, "manifest.json");
136
+ fs2.mkdirSync(outDir, { recursive: true });
137
+ fs2.writeFileSync(outFile, JSON.stringify(manifest, null, 2), "utf-8");
89
138
  const credentialCount = manifest.credentials?.length ?? 0;
90
139
  const templateCount = manifest.templates?.length ?? 0;
91
140
  console.log(
@@ -100,6 +149,7 @@ async function runManifest() {
100
149
  for (const t of manifest.templates ?? []) {
101
150
  console.log(` template ${t.slug}@${t.version} (${t.level})`);
102
151
  }
152
+ copyImages(collectImageSources(manifest), outDir, projectRoot);
103
153
  }
104
154
  async function main() {
105
155
  const command = process.argv[2];
package/dist/cli.js CHANGED
@@ -4,17 +4,68 @@ import {
4
4
  } from "./chunk-X5HI7WPI.js";
5
5
 
6
6
  // src/cli.ts
7
- import * as fs from "fs";
8
- import { resolve } from "path";
7
+ import * as fs2 from "fs";
8
+ import { resolve as resolve2 } from "path";
9
9
  import { pathToFileURL } from "url";
10
+
11
+ // src/images.ts
12
+ import * as fs from "fs";
13
+ import { dirname, isAbsolute, normalize, resolve, sep } from "path";
14
+ function collectImageSources(manifest) {
15
+ const sources = /* @__PURE__ */ new Set();
16
+ for (const group of [manifest.nodes, manifest.credentials, manifest.templates]) {
17
+ for (const item of group ?? []) {
18
+ for (const image of item.images ?? []) {
19
+ if (typeof image.src === "string" && image.src.length > 0) {
20
+ sources.add(image.src);
21
+ }
22
+ }
23
+ }
24
+ }
25
+ return [...sources];
26
+ }
27
+ function copyImages(sources, outDir, rootDir) {
28
+ let copied = 0;
29
+ for (const src of sources) {
30
+ if (!isSafeRelativePath(src)) {
31
+ console.warn(` \u26A0 unsafe image path (absolute or escaping the package root), skipping: ${src}`);
32
+ continue;
33
+ }
34
+ const from = resolve(rootDir, src);
35
+ if (!fs.existsSync(from)) {
36
+ console.warn(` \u26A0 image not found, skipping: ${src}`);
37
+ continue;
38
+ }
39
+ if (!fs.statSync(from).isFile()) {
40
+ console.warn(` \u26A0 image path is not a file, skipping: ${src}`);
41
+ continue;
42
+ }
43
+ const to = resolve(outDir, src);
44
+ fs.mkdirSync(dirname(to), { recursive: true });
45
+ fs.copyFileSync(from, to);
46
+ copied++;
47
+ }
48
+ if (sources.length > 0) {
49
+ console.log(` copied ${copied}/${sources.length} image(s) into dist/`);
50
+ }
51
+ }
52
+ function isSafeRelativePath(src) {
53
+ if (isAbsolute(src)) {
54
+ return false;
55
+ }
56
+ const normalized = normalize(src);
57
+ return normalized !== ".." && !normalized.startsWith(".." + sep);
58
+ }
59
+
60
+ // src/cli.ts
10
61
  var projectRoot = process.cwd();
11
62
  function fail(message) {
12
63
  console.error(`Error: ${message}`);
13
64
  process.exit(1);
14
65
  }
15
66
  async function runManifest() {
16
- const distEntry = resolve(projectRoot, "dist", "index.js");
17
- if (!fs.existsSync(distEntry)) {
67
+ const distEntry = resolve2(projectRoot, "dist", "index.js");
68
+ if (!fs2.existsSync(distEntry)) {
18
69
  fail("dist/index.js is missing. Run the build (tsup) before `rvnxx-nodes manifest`.");
19
70
  }
20
71
  const mod = await import(pathToFileURL(distEntry).href);
@@ -30,10 +81,10 @@ async function runManifest() {
30
81
  const credentials = mod.CREDENTIALS ?? [];
31
82
  const templates = mod.TEMPLATES ?? [];
32
83
  const manifest = buildManifest(mod.NODES, credentials, templates);
33
- const outDir = resolve(projectRoot, "dist");
34
- const outFile = resolve(outDir, "manifest.json");
35
- fs.mkdirSync(outDir, { recursive: true });
36
- fs.writeFileSync(outFile, JSON.stringify(manifest, null, 2), "utf-8");
84
+ const outDir = resolve2(projectRoot, "dist");
85
+ const outFile = resolve2(outDir, "manifest.json");
86
+ fs2.mkdirSync(outDir, { recursive: true });
87
+ fs2.writeFileSync(outFile, JSON.stringify(manifest, null, 2), "utf-8");
37
88
  const credentialCount = manifest.credentials?.length ?? 0;
38
89
  const templateCount = manifest.templates?.length ?? 0;
39
90
  console.log(
@@ -48,6 +99,7 @@ async function runManifest() {
48
99
  for (const t of manifest.templates ?? []) {
49
100
  console.log(` template ${t.slug}@${t.version} (${t.level})`);
50
101
  }
102
+ copyImages(collectImageSources(manifest), outDir, projectRoot);
51
103
  }
52
104
  async function main() {
53
105
  const command = process.argv[2];
package/dist/index.d.cts CHANGED
@@ -2,7 +2,25 @@ type LocalizedString = string | Record<string, string>;
2
2
  type DataType = 'any' | 'object' | 'array' | 'string' | 'number' | 'boolean';
3
3
  type OutputKind = 'default' | 'branch' | 'error';
4
4
  type NodeCategory = 'trigger' | 'action' | 'transform' | 'control' | 'io';
5
- type ConfigType = 'string' | 'number' | 'boolean' | 'select' | 'multiselect' | 'object' | 'array' | 'expression' | 'secret-ref' | 'credentials-ref';
5
+ type ConfigType = 'string' | 'number' | 'boolean' | 'select' | 'multiselect' | 'object' | 'array' | 'expression' | 'secret-ref' | 'credentials-ref' | 'dynamic-schema';
6
+ /**
7
+ * Semantic category of an image, driving how the registry/CDN organises and
8
+ * displays it.
9
+ */
10
+ type ImageCategory = 'screenshot' | 'logo' | 'banner' | 'icon' | 'other';
11
+ /**
12
+ * A single image a node, credential type, or template ships alongside its
13
+ * description (e.g. a screenshot, logo, or banner). The file is bundled into
14
+ * the package tarball and uploaded to the Revenexx CDN when the package is
15
+ * published.
16
+ */
17
+ interface IImage {
18
+ /** Path to the image file, relative to the package root (e.g. `images/screenshot.png`). */
19
+ src: string;
20
+ alt: LocalizedString;
21
+ title?: LocalizedString;
22
+ category: ImageCategory;
23
+ }
6
24
  interface IInputPort {
7
25
  dataType: DataType;
8
26
  required?: boolean;
@@ -20,6 +38,14 @@ interface IOutputPort {
20
38
  description?: LocalizedString;
21
39
  fields?: Record<string, IOutputField>;
22
40
  sourceFromConfig?: string;
41
+ /**
42
+ * Marks a generic port set resolved at author time by the node's
43
+ * `resolveOutputs` callback (PO-143), rather than a static `name` or a
44
+ * config-driven `sourceFromConfig`. Set at most one of `name`,
45
+ * `sourceFromConfig`, `resolveOutputs` — this is a server-validated
46
+ * constraint on publish, not enforced by this (intentionally flat) type.
47
+ */
48
+ resolveOutputs?: boolean;
23
49
  fallback?: {
24
50
  name: string;
25
51
  label?: LocalizedString;
@@ -49,6 +75,23 @@ interface IConfigFieldBase {
49
75
  multiline?: boolean;
50
76
  validation?: IConfigValidation;
51
77
  options?: IConfigOption[];
78
+ /**
79
+ * When true, a *known* field's `options` are resolved at author time via the
80
+ * node's `loadOptions` (a live/dependent dropdown) instead of a static
81
+ * `options[]`. Applies to `select` / `multiselect`; left unset for ordinary
82
+ * static fields.
83
+ *
84
+ * Independent of `type: 'dynamic-schema'`: that field type triggers
85
+ * `resolveConfigSchema` on its own and does NOT require `dynamic: true`.
86
+ */
87
+ dynamic?: boolean;
88
+ /**
89
+ * Config keys whose values drive this field's dynamic resolution. The editor
90
+ * re-resolves when one of them changes. A key listed here is
91
+ * *dependency-driving* and MUST be a literal (it may not set
92
+ * `expressionAllowed`), so its value is known at author time.
93
+ */
94
+ dependsOn?: string[];
52
95
  /**
53
96
  * Only meaningful when `type === 'credentials-ref'`: the namespaced slug(s) of
54
97
  * the credential type(s) this field accepts (e.g. `revenexx:smtp`). The editor
@@ -70,6 +113,8 @@ interface INodeDescription {
70
113
  name: LocalizedString;
71
114
  description?: LocalizedString;
72
115
  icon?: string;
116
+ /** Associated images (screenshots, logos, banners) shipped with the package. */
117
+ images?: IImage[];
73
118
  inputs: Record<string, IInputPort>;
74
119
  outputs: IOutputPort[];
75
120
  config?: IConfigField[];
@@ -99,18 +144,73 @@ interface INodeResult {
99
144
  outputs: Record<string, unknown>;
100
145
  branch?: string;
101
146
  }
147
+ /**
148
+ * Context handed to a node's *author-time* resolvers (`loadOptions`,
149
+ * `resolveConfigSchema`, `resolveOutputs`). These run in the node-runtime host
150
+ * (a Node side-container) while a user is editing the workflow — never in
151
+ * `execute`. The result is snapshotted into the workflow blob at save, so the
152
+ * runtime never calls these (PO-143).
153
+ */
154
+ interface INodeAuthorContext {
155
+ signal: AbortSignal;
156
+ logger: {
157
+ info(message: string, meta?: Record<string, unknown>): void;
158
+ warn(message: string, meta?: Record<string, unknown>): void;
159
+ error(message: string, meta?: Record<string, unknown>): void;
160
+ };
161
+ /** The user's current partial config values. */
162
+ config: Record<string, unknown>;
163
+ /**
164
+ * Lazily resolve a `secret-ref` config field's value (a key in the tenant
165
+ * secret store → a single string). Mirrors {@link INodeContext.secrets} so a
166
+ * resolver that authenticates via a secret-ref uses the same call it would in
167
+ * `execute`. Resolves against the internal secret endpoint; only the keys the
168
+ * resolver actually asks for are fetched.
169
+ */
170
+ secrets: {
171
+ get(key: string): Promise<string>;
172
+ };
173
+ /**
174
+ * Lazily resolve a `credentials-ref` instance's access data via the
175
+ * credentials broker (id → structured material, e.g. `{ accessToken }`).
176
+ * Mirrors {@link INodeContext.credentials}.
177
+ */
178
+ credentials: {
179
+ get(credentialsId: string): Promise<Record<string, unknown>>;
180
+ };
181
+ /** Preferred locale for resolved labels, when the caller supplies one. */
182
+ locale?: string;
183
+ }
102
184
  interface INode {
103
185
  description: INodeDescription;
104
186
  execute(ctx: INodeContext, inputs: Record<string, unknown>): Promise<INodeResult>;
187
+ /**
188
+ * Author-time: resolve the options of a `dynamic` field (a live/dependent
189
+ * dropdown). `fieldKey` is the config key being resolved. Optional — declare
190
+ * it only for nodes with `dynamic` `select`/`multiselect` fields.
191
+ */
192
+ loadOptions?(ctx: INodeAuthorContext, fieldKey: string): Promise<IConfigOption[]>;
193
+ /**
194
+ * Author-time: resolve the flat set of typed config fields that replaces a
195
+ * `type: 'dynamic-schema'` marker (e.g. an API's parameters once app + API
196
+ * are chosen). Returns fields in the same config-field grammar; the node is
197
+ * responsible for flattening any nested API shape into flat keys.
198
+ */
199
+ resolveConfigSchema?(ctx: INodeAuthorContext): Promise<IConfigField[]>;
200
+ /**
201
+ * Author-time: resolve a generic output-port set for an output marked
202
+ * `resolveOutputs` (e.g. ports derived from a connected resource's schema).
203
+ */
204
+ resolveOutputs?(ctx: INodeAuthorContext): Promise<IOutputPort[]>;
105
205
  }
106
206
  /**
107
207
  * 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
208
+ * Implement this alongside {@link INode} to signal to the runtime that this
209
+ * node drives iteration — the runtime will call {@link extractItems} instead
110
210
  * of relying on slug-based detection.
111
211
  *
112
212
  * This interface is the designated dispatch point for future child-workflow
113
- * execution: once per-iteration isolation is needed, the worker can swap the
213
+ * execution: once per-iteration isolation is needed, the runtime can swap the
114
214
  * inline loop for `executeChild` calls without any changes to the node or SDK.
115
215
  */
116
216
  interface INodeWithIteration {
@@ -159,6 +259,8 @@ interface ICredentialDescription {
159
259
  name: LocalizedString;
160
260
  description?: LocalizedString;
161
261
  icon?: string;
262
+ /** Associated images (screenshots, logos, banners) shipped with the package. */
263
+ images?: IImage[];
162
264
  authKind: CredentialAuthKind;
163
265
  fields: ICredentialField[];
164
266
  }
@@ -270,6 +372,8 @@ interface ITemplateDescription {
270
372
  industries?: string[];
271
373
  /** Free-form vendor tags (e.g. `pipedrive`, `slack`). */
272
374
  vendors?: string[];
375
+ /** Associated images (screenshots, logos, banners) shipped with the package. */
376
+ images?: IImage[];
273
377
  /** Workflow-blob schema version `definition` targets (e.g. `v0-draft`). */
274
378
  blobVersion: string;
275
379
  /** The workflow blob instantiated when a user picks this template. */
@@ -284,7 +388,7 @@ interface ITemplateDescription {
284
388
  /**
285
389
  * Reduce a {@link LocalizedString} to a single plain string.
286
390
  *
287
- * Every consumer (worker, UI, the Laravel side) repeats the same "is it a
391
+ * Every consumer (the UI, the Laravel side) repeats the same "is it a
288
392
  * string or a localized map?" branch to render a name/label; this centralizes
289
393
  * it so the rules are defined once:
290
394
  *
@@ -302,8 +406,8 @@ declare function normalizeLocalized(value: LocalizedString | undefined | null, f
302
406
  *
303
407
  * The union `string | string[]` exists so node authors can write the common
304
408
  * 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
409
+ * lists tenant credential instances, the Laravel side) needs a list to
410
+ * iterate. This centralizes the
307
411
  * "is it a string or an array?" branch — analogous to {@link normalizeLocalized}
308
412
  * for `LocalizedString` — so the rules are defined once:
309
413
  *
@@ -489,4 +593,4 @@ declare abstract class OAuth2AuthCodeCredential extends BaseCredential implement
489
593
  test(_ctx: ICredentialContext, config: Config): Promise<ICredentialTestResult>;
490
594
  }
491
595
 
492
- export { ApiKeyCredential, BaseCredential, BasicAuthCredential, type ConfigType, type CredentialAuthKind, type CredentialFieldType, DEFAULT_RETRY_ATTEMPTS, DEFAULT_RETRY_DELAY_MS, DEFAULT_TIMEOUT_MS, 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, MAX_RETRY_ATTEMPTS, MAX_TIMEOUT_MS, type NodeCategory, NodeError, type NodeManifest, OAuth2AuthCodeCredential, OAuth2ClientCredentialsCredential, type OAuthTokenResponse, type OutputKind, type SafeFetchOptions, type SafeFetchRetry, SimpleValueCredential, type TemplateLevel, type TemplateTriggerType, buildManifest, extractCredentialManifest, extractCredentialManifests, extractManifest, extractManifests, isNodeWithIteration, isOAuthAuthorizeCredential, normalizeCredentialType, normalizeLocalized, retryConfigFields, safeFetch, timeoutConfigField };
596
+ export { ApiKeyCredential, BaseCredential, BasicAuthCredential, type ConfigType, type CredentialAuthKind, type CredentialFieldType, DEFAULT_RETRY_ATTEMPTS, DEFAULT_RETRY_DELAY_MS, DEFAULT_TIMEOUT_MS, 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 IImage, type IInputPort, type INode, type INodeAuthorContext, type INodeContext, type INodeDescription, type INodeResult, type INodeWithIteration, type IOutputField, type IOutputPort, type ITemplateDescription, type ITemplateTrigger, type ImageCategory, type LocalizedString, MANIFEST_VERSION, MAX_RETRY_ATTEMPTS, MAX_TIMEOUT_MS, type NodeCategory, NodeError, type NodeManifest, OAuth2AuthCodeCredential, OAuth2ClientCredentialsCredential, type OAuthTokenResponse, type OutputKind, type SafeFetchOptions, type SafeFetchRetry, SimpleValueCredential, type TemplateLevel, type TemplateTriggerType, buildManifest, extractCredentialManifest, extractCredentialManifests, extractManifest, extractManifests, isNodeWithIteration, isOAuthAuthorizeCredential, normalizeCredentialType, normalizeLocalized, retryConfigFields, safeFetch, timeoutConfigField };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,25 @@ type LocalizedString = string | Record<string, string>;
2
2
  type DataType = 'any' | 'object' | 'array' | 'string' | 'number' | 'boolean';
3
3
  type OutputKind = 'default' | 'branch' | 'error';
4
4
  type NodeCategory = 'trigger' | 'action' | 'transform' | 'control' | 'io';
5
- type ConfigType = 'string' | 'number' | 'boolean' | 'select' | 'multiselect' | 'object' | 'array' | 'expression' | 'secret-ref' | 'credentials-ref';
5
+ type ConfigType = 'string' | 'number' | 'boolean' | 'select' | 'multiselect' | 'object' | 'array' | 'expression' | 'secret-ref' | 'credentials-ref' | 'dynamic-schema';
6
+ /**
7
+ * Semantic category of an image, driving how the registry/CDN organises and
8
+ * displays it.
9
+ */
10
+ type ImageCategory = 'screenshot' | 'logo' | 'banner' | 'icon' | 'other';
11
+ /**
12
+ * A single image a node, credential type, or template ships alongside its
13
+ * description (e.g. a screenshot, logo, or banner). The file is bundled into
14
+ * the package tarball and uploaded to the Revenexx CDN when the package is
15
+ * published.
16
+ */
17
+ interface IImage {
18
+ /** Path to the image file, relative to the package root (e.g. `images/screenshot.png`). */
19
+ src: string;
20
+ alt: LocalizedString;
21
+ title?: LocalizedString;
22
+ category: ImageCategory;
23
+ }
6
24
  interface IInputPort {
7
25
  dataType: DataType;
8
26
  required?: boolean;
@@ -20,6 +38,14 @@ interface IOutputPort {
20
38
  description?: LocalizedString;
21
39
  fields?: Record<string, IOutputField>;
22
40
  sourceFromConfig?: string;
41
+ /**
42
+ * Marks a generic port set resolved at author time by the node's
43
+ * `resolveOutputs` callback (PO-143), rather than a static `name` or a
44
+ * config-driven `sourceFromConfig`. Set at most one of `name`,
45
+ * `sourceFromConfig`, `resolveOutputs` — this is a server-validated
46
+ * constraint on publish, not enforced by this (intentionally flat) type.
47
+ */
48
+ resolveOutputs?: boolean;
23
49
  fallback?: {
24
50
  name: string;
25
51
  label?: LocalizedString;
@@ -49,6 +75,23 @@ interface IConfigFieldBase {
49
75
  multiline?: boolean;
50
76
  validation?: IConfigValidation;
51
77
  options?: IConfigOption[];
78
+ /**
79
+ * When true, a *known* field's `options` are resolved at author time via the
80
+ * node's `loadOptions` (a live/dependent dropdown) instead of a static
81
+ * `options[]`. Applies to `select` / `multiselect`; left unset for ordinary
82
+ * static fields.
83
+ *
84
+ * Independent of `type: 'dynamic-schema'`: that field type triggers
85
+ * `resolveConfigSchema` on its own and does NOT require `dynamic: true`.
86
+ */
87
+ dynamic?: boolean;
88
+ /**
89
+ * Config keys whose values drive this field's dynamic resolution. The editor
90
+ * re-resolves when one of them changes. A key listed here is
91
+ * *dependency-driving* and MUST be a literal (it may not set
92
+ * `expressionAllowed`), so its value is known at author time.
93
+ */
94
+ dependsOn?: string[];
52
95
  /**
53
96
  * Only meaningful when `type === 'credentials-ref'`: the namespaced slug(s) of
54
97
  * the credential type(s) this field accepts (e.g. `revenexx:smtp`). The editor
@@ -70,6 +113,8 @@ interface INodeDescription {
70
113
  name: LocalizedString;
71
114
  description?: LocalizedString;
72
115
  icon?: string;
116
+ /** Associated images (screenshots, logos, banners) shipped with the package. */
117
+ images?: IImage[];
73
118
  inputs: Record<string, IInputPort>;
74
119
  outputs: IOutputPort[];
75
120
  config?: IConfigField[];
@@ -99,18 +144,73 @@ interface INodeResult {
99
144
  outputs: Record<string, unknown>;
100
145
  branch?: string;
101
146
  }
147
+ /**
148
+ * Context handed to a node's *author-time* resolvers (`loadOptions`,
149
+ * `resolveConfigSchema`, `resolveOutputs`). These run in the node-runtime host
150
+ * (a Node side-container) while a user is editing the workflow — never in
151
+ * `execute`. The result is snapshotted into the workflow blob at save, so the
152
+ * runtime never calls these (PO-143).
153
+ */
154
+ interface INodeAuthorContext {
155
+ signal: AbortSignal;
156
+ logger: {
157
+ info(message: string, meta?: Record<string, unknown>): void;
158
+ warn(message: string, meta?: Record<string, unknown>): void;
159
+ error(message: string, meta?: Record<string, unknown>): void;
160
+ };
161
+ /** The user's current partial config values. */
162
+ config: Record<string, unknown>;
163
+ /**
164
+ * Lazily resolve a `secret-ref` config field's value (a key in the tenant
165
+ * secret store → a single string). Mirrors {@link INodeContext.secrets} so a
166
+ * resolver that authenticates via a secret-ref uses the same call it would in
167
+ * `execute`. Resolves against the internal secret endpoint; only the keys the
168
+ * resolver actually asks for are fetched.
169
+ */
170
+ secrets: {
171
+ get(key: string): Promise<string>;
172
+ };
173
+ /**
174
+ * Lazily resolve a `credentials-ref` instance's access data via the
175
+ * credentials broker (id → structured material, e.g. `{ accessToken }`).
176
+ * Mirrors {@link INodeContext.credentials}.
177
+ */
178
+ credentials: {
179
+ get(credentialsId: string): Promise<Record<string, unknown>>;
180
+ };
181
+ /** Preferred locale for resolved labels, when the caller supplies one. */
182
+ locale?: string;
183
+ }
102
184
  interface INode {
103
185
  description: INodeDescription;
104
186
  execute(ctx: INodeContext, inputs: Record<string, unknown>): Promise<INodeResult>;
187
+ /**
188
+ * Author-time: resolve the options of a `dynamic` field (a live/dependent
189
+ * dropdown). `fieldKey` is the config key being resolved. Optional — declare
190
+ * it only for nodes with `dynamic` `select`/`multiselect` fields.
191
+ */
192
+ loadOptions?(ctx: INodeAuthorContext, fieldKey: string): Promise<IConfigOption[]>;
193
+ /**
194
+ * Author-time: resolve the flat set of typed config fields that replaces a
195
+ * `type: 'dynamic-schema'` marker (e.g. an API's parameters once app + API
196
+ * are chosen). Returns fields in the same config-field grammar; the node is
197
+ * responsible for flattening any nested API shape into flat keys.
198
+ */
199
+ resolveConfigSchema?(ctx: INodeAuthorContext): Promise<IConfigField[]>;
200
+ /**
201
+ * Author-time: resolve a generic output-port set for an output marked
202
+ * `resolveOutputs` (e.g. ports derived from a connected resource's schema).
203
+ */
204
+ resolveOutputs?(ctx: INodeAuthorContext): Promise<IOutputPort[]>;
105
205
  }
106
206
  /**
107
207
  * 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
208
+ * Implement this alongside {@link INode} to signal to the runtime that this
209
+ * node drives iteration — the runtime will call {@link extractItems} instead
110
210
  * of relying on slug-based detection.
111
211
  *
112
212
  * This interface is the designated dispatch point for future child-workflow
113
- * execution: once per-iteration isolation is needed, the worker can swap the
213
+ * execution: once per-iteration isolation is needed, the runtime can swap the
114
214
  * inline loop for `executeChild` calls without any changes to the node or SDK.
115
215
  */
116
216
  interface INodeWithIteration {
@@ -159,6 +259,8 @@ interface ICredentialDescription {
159
259
  name: LocalizedString;
160
260
  description?: LocalizedString;
161
261
  icon?: string;
262
+ /** Associated images (screenshots, logos, banners) shipped with the package. */
263
+ images?: IImage[];
162
264
  authKind: CredentialAuthKind;
163
265
  fields: ICredentialField[];
164
266
  }
@@ -270,6 +372,8 @@ interface ITemplateDescription {
270
372
  industries?: string[];
271
373
  /** Free-form vendor tags (e.g. `pipedrive`, `slack`). */
272
374
  vendors?: string[];
375
+ /** Associated images (screenshots, logos, banners) shipped with the package. */
376
+ images?: IImage[];
273
377
  /** Workflow-blob schema version `definition` targets (e.g. `v0-draft`). */
274
378
  blobVersion: string;
275
379
  /** The workflow blob instantiated when a user picks this template. */
@@ -284,7 +388,7 @@ interface ITemplateDescription {
284
388
  /**
285
389
  * Reduce a {@link LocalizedString} to a single plain string.
286
390
  *
287
- * Every consumer (worker, UI, the Laravel side) repeats the same "is it a
391
+ * Every consumer (the UI, the Laravel side) repeats the same "is it a
288
392
  * string or a localized map?" branch to render a name/label; this centralizes
289
393
  * it so the rules are defined once:
290
394
  *
@@ -302,8 +406,8 @@ declare function normalizeLocalized(value: LocalizedString | undefined | null, f
302
406
  *
303
407
  * The union `string | string[]` exists so node authors can write the common
304
408
  * 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
409
+ * lists tenant credential instances, the Laravel side) needs a list to
410
+ * iterate. This centralizes the
307
411
  * "is it a string or an array?" branch — analogous to {@link normalizeLocalized}
308
412
  * for `LocalizedString` — so the rules are defined once:
309
413
  *
@@ -489,4 +593,4 @@ declare abstract class OAuth2AuthCodeCredential extends BaseCredential implement
489
593
  test(_ctx: ICredentialContext, config: Config): Promise<ICredentialTestResult>;
490
594
  }
491
595
 
492
- export { ApiKeyCredential, BaseCredential, BasicAuthCredential, type ConfigType, type CredentialAuthKind, type CredentialFieldType, DEFAULT_RETRY_ATTEMPTS, DEFAULT_RETRY_DELAY_MS, DEFAULT_TIMEOUT_MS, 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, MAX_RETRY_ATTEMPTS, MAX_TIMEOUT_MS, type NodeCategory, NodeError, type NodeManifest, OAuth2AuthCodeCredential, OAuth2ClientCredentialsCredential, type OAuthTokenResponse, type OutputKind, type SafeFetchOptions, type SafeFetchRetry, SimpleValueCredential, type TemplateLevel, type TemplateTriggerType, buildManifest, extractCredentialManifest, extractCredentialManifests, extractManifest, extractManifests, isNodeWithIteration, isOAuthAuthorizeCredential, normalizeCredentialType, normalizeLocalized, retryConfigFields, safeFetch, timeoutConfigField };
596
+ export { ApiKeyCredential, BaseCredential, BasicAuthCredential, type ConfigType, type CredentialAuthKind, type CredentialFieldType, DEFAULT_RETRY_ATTEMPTS, DEFAULT_RETRY_DELAY_MS, DEFAULT_TIMEOUT_MS, 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 IImage, type IInputPort, type INode, type INodeAuthorContext, type INodeContext, type INodeDescription, type INodeResult, type INodeWithIteration, type IOutputField, type IOutputPort, type ITemplateDescription, type ITemplateTrigger, type ImageCategory, type LocalizedString, MANIFEST_VERSION, MAX_RETRY_ATTEMPTS, MAX_TIMEOUT_MS, type NodeCategory, NodeError, type NodeManifest, OAuth2AuthCodeCredential, OAuth2ClientCredentialsCredential, type OAuthTokenResponse, type OutputKind, type SafeFetchOptions, type SafeFetchRetry, SimpleValueCredential, type TemplateLevel, type TemplateTriggerType, buildManifest, extractCredentialManifest, extractCredentialManifests, extractManifest, extractManifests, isNodeWithIteration, isOAuthAuthorizeCredential, normalizeCredentialType, normalizeLocalized, retryConfigFields, safeFetch, timeoutConfigField };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revenexx/integrations-node-sdk",
3
- "version": "0.12.1",
3
+ "version": "0.13.0",
4
4
  "description": "TypeScript interfaces and utilities for Revenexx integration nodes",
5
5
  "license": "MIT",
6
6
  "author": "revenexx GmbH",