@revenexx/integrations-node-sdk 1.10.0 → 1.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,47 @@
1
+ import type { INode, INodeDescription, INodeResult, IOutputPort } from './types.js';
2
+ /**
3
+ * A node's output ports are alternatives: exactly one fires. A node with a
4
+ * single port has nothing to choose between, so its result may leave `branch`
5
+ * out; a node with several must say which one it took, because the worker
6
+ * otherwise activates every outgoing connection (PO-751).
7
+ *
8
+ * A node counts as multi-port when EITHER holds:
9
+ * - more than one port is declared (an `error` port counts: `out` + `error` is two);
10
+ * - a port is marked `resolveOutputs`, because that set is only known while the
11
+ * author edits and may hold several.
12
+ *
13
+ * A single port is one way out however it is declared — of kind `branch`, or named
14
+ * from a setting by `sourceFromConfig` (one name, with its `fallback`).
15
+ */
16
+ export declare function isMultiPort(outputs: readonly IOutputPort[] | undefined): boolean;
17
+ /** A result that names its branch — what every multi-port node returns. */
18
+ export interface IBranchedNodeResult extends INodeResult {
19
+ branch: string;
20
+ }
21
+ /**
22
+ * Declaration problems of a node's output ports that the manifest build can see.
23
+ * Returns one sentence per problem, empty when there is none.
24
+ *
25
+ * A node with several ports must give each fixed port a unique `name`, since `branch`
26
+ * names one of them, and a generated port's `fallback` name must not repeat a fixed
27
+ * one. (Whether `execute` really returns a `branch` cannot be read from a declaration
28
+ * — see {@link assertBranchNamed}.)
29
+ */
30
+ export declare function outputProblems(description: INodeDescription): string[];
31
+ /**
32
+ * The warnings `rvnxx-nodes manifest` prints for a package's nodes: every
33
+ * {@link outputProblems} finding, worded for the log. In 1.x they are warnings and the
34
+ * build continues; the next major turns them into errors.
35
+ */
36
+ export declare function outputWarnings(nodes: readonly INode[]): string[];
37
+ /**
38
+ * Check a result against the node's declaration. For a node's own tests:
39
+ * `assertBranchNamed(node, await node.execute(ctx, inputs), config)`.
40
+ *
41
+ * Throws when a multi-port node returned no `branch`, and when the branch is none of
42
+ * the names the node can produce: a fixed name is always accepted; beyond that, a
43
+ * `resolveOutputs` port accepts any name (it is unknown here), and a `sourceFromConfig`
44
+ * port accepts its fallback name and — when `config` is passed — the value it holds
45
+ * (with no `config`, any name). A single-port node may leave `branch` out.
46
+ */
47
+ export declare function assertBranchNamed(node: INode | INodeDescription, result: INodeResult, config?: Record<string, unknown>): void;
@@ -0,0 +1,122 @@
1
+ // src/branches.ts
2
+ function isMultiPort(outputs) {
3
+ if (!outputs) return false;
4
+ return outputs.length > 1 || outputs.some((p) => p.resolveOutputs === true);
5
+ }
6
+ var isGenerated = (p) => p.sourceFromConfig !== void 0 || p.resolveOutputs === true;
7
+ function outputProblems(description) {
8
+ const outputs = description.outputs ?? [];
9
+ if (!isMultiPort(outputs)) return [];
10
+ const problems = [];
11
+ const seen = /* @__PURE__ */ new Set();
12
+ for (const [i, port] of outputs.entries()) {
13
+ if (isGenerated(port)) continue;
14
+ if (port.name === void 0 || port.name === "") {
15
+ problems.push(
16
+ `${description.slug}: output ${i + 1} has no name, so a branch cannot choose it \u2014 a node with several outputs must name each one.`
17
+ );
18
+ } else if (seen.has(port.name)) {
19
+ problems.push(`${description.slug}: output name "${port.name}" is declared twice.`);
20
+ } else {
21
+ seen.add(port.name);
22
+ }
23
+ }
24
+ for (const port of outputs) {
25
+ const fb = port.fallback?.name;
26
+ if (isGenerated(port) && fb !== void 0 && seen.has(fb)) {
27
+ problems.push(`${description.slug}: the fallback name "${fb}" is also the name of a fixed output, so a branch "${fb}" is ambiguous.`);
28
+ }
29
+ }
30
+ return problems;
31
+ }
32
+ function outputWarnings(nodes) {
33
+ return nodes.flatMap((n) => outputProblems(n.description));
34
+ }
35
+ function assertBranchNamed(node, result, config) {
36
+ const description = "execute" in node ? node.description : node;
37
+ const outputs = description.outputs ?? [];
38
+ if (!isMultiPort(outputs)) return;
39
+ const branch = result.branch;
40
+ if (typeof branch !== "string" || branch === "") {
41
+ throw new Error(
42
+ `${description.slug} has several outputs, so its result must name the branch it took; without it every output fires.`
43
+ );
44
+ }
45
+ if (outputs.some((p) => !isGenerated(p) && p.name === branch)) return;
46
+ const producible = outputs.some((p) => {
47
+ if (p.resolveOutputs === true) return true;
48
+ if (p.sourceFromConfig === void 0) return false;
49
+ if (p.fallback?.name === branch) return true;
50
+ return config === void 0 || config[p.sourceFromConfig] === branch;
51
+ });
52
+ if (!producible) {
53
+ const names = outputs.map((p) => p.name ?? p.fallback?.name).filter((n) => n);
54
+ throw new Error(
55
+ `${description.slug} returned branch "${branch}", which none of its outputs can produce (${names.join(", ")}).`
56
+ );
57
+ }
58
+ }
59
+
60
+ // src/extract.ts
61
+ function extractManifest(node) {
62
+ return node.description;
63
+ }
64
+ function extractManifests(nodes) {
65
+ return nodes.map(extractManifest);
66
+ }
67
+ function extractCredentialManifest(credential) {
68
+ return credential.description;
69
+ }
70
+ function extractCredentialManifests(credentials) {
71
+ return credentials.map(extractCredentialManifest);
72
+ }
73
+
74
+ // src/manifest.ts
75
+ var MANIFEST_VERSION = "v0-draft";
76
+ var PACKAGE_ICON = /^lucide:[a-z0-9]+(-[a-z0-9]+)*$/;
77
+ var PHP_TRIM = /^[ \t\n\r\0\v]+|[ \t\n\r\0\v]+$/g;
78
+ function packageIconProblem(icon) {
79
+ if (icon === void 0 || PACKAGE_ICON.test(icon)) return void 0;
80
+ return `package.json "revenexx.icon" must be a Lucide icon name such as "lucide:bell", got "${icon}".`;
81
+ }
82
+ function parsePackageMeta(raw) {
83
+ const obj = raw && typeof raw === "object" ? raw : {};
84
+ const str = (v) => typeof v === "string" ? v.trim() : "";
85
+ const revenexx = obj.revenexx && typeof obj.revenexx === "object" ? obj.revenexx : {};
86
+ const displayName = str(revenexx.displayName);
87
+ const icon = typeof revenexx.icon === "string" ? revenexx.icon.replace(PHP_TRIM, "") : "";
88
+ return {
89
+ name: str(obj.name),
90
+ version: str(obj.version),
91
+ displayName: displayName !== "" ? displayName : void 0,
92
+ icon: icon !== "" ? icon : void 0
93
+ };
94
+ }
95
+ function buildManifest(nodes, credentials = [], templates = []) {
96
+ const manifest = {
97
+ manifestVersion: MANIFEST_VERSION,
98
+ nodes: extractManifests(nodes)
99
+ };
100
+ if (credentials.length > 0) {
101
+ manifest.credentials = extractCredentialManifests(credentials);
102
+ }
103
+ if (templates.length > 0) {
104
+ manifest.templates = templates;
105
+ }
106
+ return manifest;
107
+ }
108
+
109
+ export {
110
+ isMultiPort,
111
+ outputProblems,
112
+ outputWarnings,
113
+ assertBranchNamed,
114
+ extractManifest,
115
+ extractManifests,
116
+ extractCredentialManifest,
117
+ extractCredentialManifests,
118
+ MANIFEST_VERSION,
119
+ packageIconProblem,
120
+ parsePackageMeta,
121
+ buildManifest
122
+ };
package/dist/cli.cjs CHANGED
@@ -28,6 +28,41 @@ var fs2 = __toESM(require("fs"), 1);
28
28
  var import_node_path2 = require("path");
29
29
  var import_node_url = require("url");
30
30
 
31
+ // src/branches.ts
32
+ function isMultiPort(outputs) {
33
+ if (!outputs) return false;
34
+ return outputs.length > 1 || outputs.some((p) => p.resolveOutputs === true);
35
+ }
36
+ var isGenerated = (p) => p.sourceFromConfig !== void 0 || p.resolveOutputs === true;
37
+ function outputProblems(description) {
38
+ const outputs = description.outputs ?? [];
39
+ if (!isMultiPort(outputs)) return [];
40
+ const problems = [];
41
+ const seen = /* @__PURE__ */ new Set();
42
+ for (const [i, port] of outputs.entries()) {
43
+ if (isGenerated(port)) continue;
44
+ if (port.name === void 0 || port.name === "") {
45
+ problems.push(
46
+ `${description.slug}: output ${i + 1} has no name, so a branch cannot choose it \u2014 a node with several outputs must name each one.`
47
+ );
48
+ } else if (seen.has(port.name)) {
49
+ problems.push(`${description.slug}: output name "${port.name}" is declared twice.`);
50
+ } else {
51
+ seen.add(port.name);
52
+ }
53
+ }
54
+ for (const port of outputs) {
55
+ const fb = port.fallback?.name;
56
+ if (isGenerated(port) && fb !== void 0 && seen.has(fb)) {
57
+ problems.push(`${description.slug}: the fallback name "${fb}" is also the name of a fixed output, so a branch "${fb}" is ambiguous.`);
58
+ }
59
+ }
60
+ return problems;
61
+ }
62
+ function outputWarnings(nodes) {
63
+ return nodes.flatMap((n) => outputProblems(n.description));
64
+ }
65
+
31
66
  // src/images.ts
32
67
  var fs = __toESM(require("fs"), 1);
33
68
  var import_node_path = require("path");
@@ -174,6 +209,9 @@ async function runManifest() {
174
209
  '\u26A0 package.json has no "revenexx.displayName" \u2014 the node palette will fall back to the raw package name.'
175
210
  );
176
211
  }
212
+ for (const w of outputWarnings(mod.NODES)) {
213
+ console.warn(`\u26A0 ${w} This will be an error in the next major version.`);
214
+ }
177
215
  const manifest = buildManifest(mod.NODES, credentials, templates);
178
216
  const outDir = (0, import_node_path2.resolve)(projectRoot, "dist");
179
217
  const outFile = (0, import_node_path2.resolve)(outDir, "manifest.json");
package/dist/cli.js CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  buildManifest,
4
+ outputWarnings,
4
5
  packageIconProblem,
5
6
  parsePackageMeta
6
- } from "./chunk-5BCAXRU3.js";
7
+ } from "./chunk-XO73F62E.js";
7
8
 
8
9
  // src/cli.ts
9
10
  import * as fs2 from "fs";
@@ -107,6 +108,9 @@ async function runManifest() {
107
108
  '\u26A0 package.json has no "revenexx.displayName" \u2014 the node palette will fall back to the raw package name.'
108
109
  );
109
110
  }
111
+ for (const w of outputWarnings(mod.NODES)) {
112
+ console.warn(`\u26A0 ${w} This will be an error in the next major version.`);
113
+ }
110
114
  const manifest = buildManifest(mod.NODES, credentials, templates);
111
115
  const outDir = resolve2(projectRoot, "dist");
112
116
  const outFile = resolve2(outDir, "manifest.json");
package/dist/index.cjs CHANGED
@@ -50,6 +50,7 @@ __export(index_exports, {
50
50
  RetryableError: () => RetryableError,
51
51
  SimpleValueCredential: () => SimpleValueCredential,
52
52
  VALUELESS_OPERATORS: () => VALUELESS_OPERATORS,
53
+ assertBranchNamed: () => assertBranchNamed,
53
54
  assertPublicUrl: () => assertPublicUrl,
54
55
  backoffDelay: () => backoffDelay,
55
56
  buildManifest: () => buildManifest,
@@ -64,12 +65,15 @@ __export(index_exports, {
64
65
  headerCrossesOrigin: () => headerCrossesOrigin,
65
66
  httpErrorResult: () => httpErrorResult,
66
67
  isBlockedAddress: () => isBlockedAddress,
68
+ isMultiPort: () => isMultiPort,
67
69
  isNodeWithIteration: () => isNodeWithIteration,
68
70
  isOAuthAuthorizeCredential: () => isOAuthAuthorizeCredential,
69
71
  isOperator: () => isOperator,
70
72
  maxBytesConfigField: () => maxBytesConfigField,
71
73
  normalizeCredentialType: () => normalizeCredentialType,
72
74
  normalizeLocalized: () => normalizeLocalized,
75
+ outputProblems: () => outputProblems,
76
+ outputWarnings: () => outputWarnings,
73
77
  packageIconProblem: () => packageIconProblem,
74
78
  parsePackageMeta: () => parsePackageMeta,
75
79
  readArrayBuffer: () => readArrayBuffer,
@@ -294,6 +298,65 @@ function extractCredentialManifests(credentials) {
294
298
  return credentials.map(extractCredentialManifest);
295
299
  }
296
300
 
301
+ // src/branches.ts
302
+ function isMultiPort(outputs) {
303
+ if (!outputs) return false;
304
+ return outputs.length > 1 || outputs.some((p) => p.resolveOutputs === true);
305
+ }
306
+ var isGenerated = (p) => p.sourceFromConfig !== void 0 || p.resolveOutputs === true;
307
+ function outputProblems(description) {
308
+ const outputs = description.outputs ?? [];
309
+ if (!isMultiPort(outputs)) return [];
310
+ const problems = [];
311
+ const seen = /* @__PURE__ */ new Set();
312
+ for (const [i, port] of outputs.entries()) {
313
+ if (isGenerated(port)) continue;
314
+ if (port.name === void 0 || port.name === "") {
315
+ problems.push(
316
+ `${description.slug}: output ${i + 1} has no name, so a branch cannot choose it \u2014 a node with several outputs must name each one.`
317
+ );
318
+ } else if (seen.has(port.name)) {
319
+ problems.push(`${description.slug}: output name "${port.name}" is declared twice.`);
320
+ } else {
321
+ seen.add(port.name);
322
+ }
323
+ }
324
+ for (const port of outputs) {
325
+ const fb = port.fallback?.name;
326
+ if (isGenerated(port) && fb !== void 0 && seen.has(fb)) {
327
+ problems.push(`${description.slug}: the fallback name "${fb}" is also the name of a fixed output, so a branch "${fb}" is ambiguous.`);
328
+ }
329
+ }
330
+ return problems;
331
+ }
332
+ function outputWarnings(nodes) {
333
+ return nodes.flatMap((n) => outputProblems(n.description));
334
+ }
335
+ function assertBranchNamed(node, result, config) {
336
+ const description = "execute" in node ? node.description : node;
337
+ const outputs = description.outputs ?? [];
338
+ if (!isMultiPort(outputs)) return;
339
+ const branch = result.branch;
340
+ if (typeof branch !== "string" || branch === "") {
341
+ throw new Error(
342
+ `${description.slug} has several outputs, so its result must name the branch it took; without it every output fires.`
343
+ );
344
+ }
345
+ if (outputs.some((p) => !isGenerated(p) && p.name === branch)) return;
346
+ const producible = outputs.some((p) => {
347
+ if (p.resolveOutputs === true) return true;
348
+ if (p.sourceFromConfig === void 0) return false;
349
+ if (p.fallback?.name === branch) return true;
350
+ return config === void 0 || config[p.sourceFromConfig] === branch;
351
+ });
352
+ if (!producible) {
353
+ const names = outputs.map((p) => p.name ?? p.fallback?.name).filter((n) => n);
354
+ throw new Error(
355
+ `${description.slug} returned branch "${branch}", which none of its outputs can produce (${names.join(", ")}).`
356
+ );
357
+ }
358
+ }
359
+
297
360
  // src/ssrf.ts
298
361
  var import_node_diagnostics_channel = require("diagnostics_channel");
299
362
  var import_node_net = require("net");
@@ -1235,6 +1298,7 @@ function base64Url(buf) {
1235
1298
  RetryableError,
1236
1299
  SimpleValueCredential,
1237
1300
  VALUELESS_OPERATORS,
1301
+ assertBranchNamed,
1238
1302
  assertPublicUrl,
1239
1303
  backoffDelay,
1240
1304
  buildManifest,
@@ -1249,12 +1313,15 @@ function base64Url(buf) {
1249
1313
  headerCrossesOrigin,
1250
1314
  httpErrorResult,
1251
1315
  isBlockedAddress,
1316
+ isMultiPort,
1252
1317
  isNodeWithIteration,
1253
1318
  isOAuthAuthorizeCredential,
1254
1319
  isOperator,
1255
1320
  maxBytesConfigField,
1256
1321
  normalizeCredentialType,
1257
1322
  normalizeLocalized,
1323
+ outputProblems,
1324
+ outputWarnings,
1258
1325
  packageIconProblem,
1259
1326
  parsePackageMeta,
1260
1327
  readArrayBuffer,
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export * from './credentialType.js';
6
6
  export * from './errors.js';
7
7
  export * from './nodeErrors.js';
8
8
  export * from './extract.js';
9
+ export * from './branches.js';
9
10
  export * from './fetch.js';
10
11
  export { assertPublicUrl, isBlockedAddress } from './ssrf.js';
11
12
  export type { LookupAddress, LookupFn } from './ssrf.js';
package/dist/index.js CHANGED
@@ -1,13 +1,17 @@
1
1
  import {
2
2
  MANIFEST_VERSION,
3
+ assertBranchNamed,
3
4
  buildManifest,
4
5
  extractCredentialManifest,
5
6
  extractCredentialManifests,
6
7
  extractManifest,
7
8
  extractManifests,
9
+ isMultiPort,
10
+ outputProblems,
11
+ outputWarnings,
8
12
  packageIconProblem,
9
13
  parsePackageMeta
10
- } from "./chunk-5BCAXRU3.js";
14
+ } from "./chunk-XO73F62E.js";
11
15
 
12
16
  // src/types.ts
13
17
  function isNodeWithIteration(node) {
@@ -1107,6 +1111,7 @@ export {
1107
1111
  RetryableError,
1108
1112
  SimpleValueCredential,
1109
1113
  VALUELESS_OPERATORS,
1114
+ assertBranchNamed,
1110
1115
  assertPublicUrl,
1111
1116
  backoffDelay,
1112
1117
  buildManifest,
@@ -1121,12 +1126,15 @@ export {
1121
1126
  headerCrossesOrigin,
1122
1127
  httpErrorResult,
1123
1128
  isBlockedAddress,
1129
+ isMultiPort,
1124
1130
  isNodeWithIteration,
1125
1131
  isOAuthAuthorizeCredential,
1126
1132
  isOperator,
1127
1133
  maxBytesConfigField,
1128
1134
  normalizeCredentialType,
1129
1135
  normalizeLocalized,
1136
+ outputProblems,
1137
+ outputWarnings,
1130
1138
  packageIconProblem,
1131
1139
  parsePackageMeta,
1132
1140
  readArrayBuffer,
package/dist/types.d.ts CHANGED
@@ -368,6 +368,14 @@ export interface INodeState {
368
368
  }
369
369
  export interface INodeResult {
370
370
  outputs: Record<string, unknown>;
371
+ /**
372
+ * The output port the node took. Output ports are alternatives — exactly one
373
+ * fires — so a node with more than one output (an `error` port counts) must
374
+ * name it; only a node with a single output may omit it. The worker fails such
375
+ * a step from the integrations release that carries PO-751; until then a result
376
+ * without `branch` fires every output. See `isMultiPort` for what counts as
377
+ * several, and `assertBranchNamed` to check a result in the node's own tests.
378
+ */
371
379
  branch?: string;
372
380
  }
373
381
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revenexx/integrations-node-sdk",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "TypeScript interfaces and utilities for Revenexx integration nodes",
5
5
  "license": "MIT",
6
6
  "author": "revenexx GmbH",
@@ -1,59 +0,0 @@
1
- // src/extract.ts
2
- function extractManifest(node) {
3
- return node.description;
4
- }
5
- function extractManifests(nodes) {
6
- return nodes.map(extractManifest);
7
- }
8
- function extractCredentialManifest(credential) {
9
- return credential.description;
10
- }
11
- function extractCredentialManifests(credentials) {
12
- return credentials.map(extractCredentialManifest);
13
- }
14
-
15
- // src/manifest.ts
16
- var MANIFEST_VERSION = "v0-draft";
17
- var PACKAGE_ICON = /^lucide:[a-z0-9]+(-[a-z0-9]+)*$/;
18
- var PHP_TRIM = /^[ \t\n\r\0\v]+|[ \t\n\r\0\v]+$/g;
19
- function packageIconProblem(icon) {
20
- if (icon === void 0 || PACKAGE_ICON.test(icon)) return void 0;
21
- return `package.json "revenexx.icon" must be a Lucide icon name such as "lucide:bell", got "${icon}".`;
22
- }
23
- function parsePackageMeta(raw) {
24
- const obj = raw && typeof raw === "object" ? raw : {};
25
- const str = (v) => typeof v === "string" ? v.trim() : "";
26
- const revenexx = obj.revenexx && typeof obj.revenexx === "object" ? obj.revenexx : {};
27
- const displayName = str(revenexx.displayName);
28
- const icon = typeof revenexx.icon === "string" ? revenexx.icon.replace(PHP_TRIM, "") : "";
29
- return {
30
- name: str(obj.name),
31
- version: str(obj.version),
32
- displayName: displayName !== "" ? displayName : void 0,
33
- icon: icon !== "" ? icon : void 0
34
- };
35
- }
36
- function buildManifest(nodes, credentials = [], templates = []) {
37
- const manifest = {
38
- manifestVersion: MANIFEST_VERSION,
39
- nodes: extractManifests(nodes)
40
- };
41
- if (credentials.length > 0) {
42
- manifest.credentials = extractCredentialManifests(credentials);
43
- }
44
- if (templates.length > 0) {
45
- manifest.templates = templates;
46
- }
47
- return manifest;
48
- }
49
-
50
- export {
51
- extractManifest,
52
- extractManifests,
53
- extractCredentialManifest,
54
- extractCredentialManifests,
55
- MANIFEST_VERSION,
56
- packageIconProblem,
57
- parsePackageMeta,
58
- buildManifest
59
- };