@uipath/maestro-builder-sdk 6.17.6 → 6.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bindings-v2.js +15 -17
- package/dist/bpmn/bpmn-sdk.js +3 -2
- package/dist/bpmn/check.js +3 -2
- package/dist/bpmn/decompile.js +123 -137
- package/dist/bpmn/intsvc.js +7 -2
- package/dist/bpmn/serialize.js +1 -1
- package/dist/bpmn/typed-node.js +5 -4
- package/dist/case/check-cli.js +18 -4
- package/dist/case/check.d.ts +26 -1
- package/dist/case/check.js +80 -2
- package/dist/case/cli-library.d.ts +14 -0
- package/dist/case/cli-library.js +31 -0
- package/dist/case/compile-cli.js +12 -12
- package/dist/case/decompile.js +103 -109
- package/dist/case/entry-points.js +2 -1
- package/dist/case/merge.js +3 -5
- package/dist/case/serialize.js +1 -1
- package/dist/check.js +26 -10
- package/dist/cli/commands/case.js +10 -1
- package/dist/core/ceql.js +5 -2
- package/dist/core/connector-inputs.js +4 -4
- package/dist/core/event-checks.js +17 -2
- package/dist/core/event-filters.d.ts +38 -4
- package/dist/core/event-filters.js +63 -7
- package/dist/core/expr.d.ts +1 -1
- package/dist/core/expr.js +3 -3
- package/dist/core/json-file.d.ts +2 -0
- package/dist/core/json-file.js +4 -0
- package/dist/core/library.js +2 -1
- package/dist/core/node-envelope.d.ts +1 -0
- package/dist/core/node-envelope.js +1 -1
- package/dist/core/own-entry.d.ts +9 -0
- package/dist/core/own-entry.js +11 -0
- package/dist/core/ts-literal.d.ts +22 -0
- package/dist/core/ts-literal.js +37 -0
- package/dist/core/typed-raw-node.js +4 -3
- package/dist/decompile.js +238 -255
- package/dist/merge.js +10 -12
- package/dist/registry/compat/pystr.d.ts +2 -0
- package/dist/registry/compat/pystr.js +35 -0
- package/dist/registry/compat/pyvalue.d.ts +16 -0
- package/dist/registry/compat/pyvalue.js +35 -0
- package/dist/registry/generators/markdown/pyvalue.d.ts +1 -4
- package/dist/registry/generators/markdown/pyvalue.js +2 -30
- package/dist/registry/generators/markdown/sections/inputs.js +3 -2
- package/dist/registry/generators/type-tree.d.ts +1 -7
- package/dist/registry/generators/type-tree.js +5 -22
- package/dist/registry/library-build/build.js +6 -6
- package/dist/registry/library-build/canonical.js +25 -25
- package/dist/registry/library-build/context.js +5 -5
- package/dist/registry/library-build/enrich.js +18 -18
- package/dist/registry/library-build/events.js +25 -24
- package/dist/registry/library-build/generic-expand.js +21 -20
- package/dist/registry/library-build/plan.js +8 -8
- package/dist/registry/library-build/py.d.ts +3 -10
- package/dist/registry/library-build/py.js +9 -52
- package/dist/registry/library-build/schema-action.js +6 -6
- package/dist/registry/prepare/event.js +2 -2
- package/dist/registry/prepare/source-import.js +2 -9
- package/dist/registry/prepare/source-labels.js +2 -9
- package/dist/registry/prepare/support.d.ts +2 -6
- package/dist/registry/prepare/support.js +3 -58
- package/dist/registry/pull-cli.js +6 -14
- package/dist/registry/search-cli.js +2 -2
- package/dist/schema-version.js +0 -1
- package/dist/serialize.js +86 -76
- package/package.json +2 -2
package/dist/merge.js
CHANGED
|
@@ -64,9 +64,7 @@
|
|
|
64
64
|
* merge that stayed silent about it wrote a file it already knew would fail
|
|
65
65
|
* `validate`. Those are reported through {@link MergeOptions.warn}.
|
|
66
66
|
*/
|
|
67
|
-
|
|
68
|
-
return JSON.parse(JSON.stringify(v));
|
|
69
|
-
}
|
|
67
|
+
import { jsonClone } from './core/json-file.js';
|
|
70
68
|
function byId(nodes) {
|
|
71
69
|
return new Map((nodes ?? []).map((n) => [n.id, n]));
|
|
72
70
|
}
|
|
@@ -88,10 +86,10 @@ function nodeUnchanged(a, b) {
|
|
|
88
86
|
function withOriginalLayout(edited, orig) {
|
|
89
87
|
if (!orig || orig.ui === undefined)
|
|
90
88
|
return edited;
|
|
91
|
-
return { ...edited, ui:
|
|
89
|
+
return { ...edited, ui: jsonClone(orig.ui) };
|
|
92
90
|
}
|
|
93
91
|
export function mergeFlow(original, edited, options = {}) {
|
|
94
|
-
const result =
|
|
92
|
+
const result = jsonClone(original);
|
|
95
93
|
const origNodes = byId(original.nodes);
|
|
96
94
|
const baseNodes = byId(options.baseline?.nodes);
|
|
97
95
|
// ── Nodes: the edited graph decides which nodes exist and in what order; the
|
|
@@ -102,19 +100,19 @@ export function mergeFlow(original, edited, options = {}) {
|
|
|
102
100
|
if (options.baseline && orig && nodeUnchanged(e, baseNodes.get(e.id))) {
|
|
103
101
|
// Untouched by the user (equal to the faithful baseline) → keep the
|
|
104
102
|
// original node exactly, placeholder-safe.
|
|
105
|
-
finalNodes.push(
|
|
103
|
+
finalNodes.push(jsonClone(orig));
|
|
106
104
|
}
|
|
107
105
|
else {
|
|
108
106
|
// Added or edited → take the edited node, re-attaching original layout.
|
|
109
|
-
finalNodes.push(withOriginalLayout(
|
|
107
|
+
finalNodes.push(withOriginalLayout(jsonClone(e), orig));
|
|
110
108
|
}
|
|
111
109
|
}
|
|
112
110
|
result.nodes = finalNodes;
|
|
113
111
|
// Nodes present in the original but absent from `edited` were deleted by the
|
|
114
112
|
// user; they are simply not carried over.
|
|
115
113
|
// ── Variables & subflows: the edited flow is authoritative (the user's graph). ──
|
|
116
|
-
result.variables =
|
|
117
|
-
result.subflows =
|
|
114
|
+
result.variables = jsonClone(edited.variables);
|
|
115
|
+
result.subflows = jsonClone(edited.subflows ?? {});
|
|
118
116
|
const subflowScopes = Object.values(result.subflows);
|
|
119
117
|
// ── Edges: the edited graph's edges define the wiring, under the edited
|
|
120
118
|
// file's own ids. compile draws every edge id — root AND subflows — from
|
|
@@ -172,7 +170,7 @@ function withFileUniqueEdgeIds(edges, subflows) {
|
|
|
172
170
|
return edges.map((e) => {
|
|
173
171
|
const id = typeof e.id === 'string' && !taken.has(e.id) ? e.id : fresh();
|
|
174
172
|
taken.add(id);
|
|
175
|
-
return { ...
|
|
173
|
+
return { ...jsonClone(e), id };
|
|
176
174
|
});
|
|
177
175
|
}
|
|
178
176
|
/** The `nodeType@version` identity of a definition row or of a node's type pin. */
|
|
@@ -219,7 +217,7 @@ function mergeDefinitions(orig, edited, nodes, warn) {
|
|
|
219
217
|
+ `Recompile the edited source against an SDK that bundles ${key}, or align the node's typeVersion `
|
|
220
218
|
+ `with a definition one of the inputs does carry.`);
|
|
221
219
|
}
|
|
222
|
-
return [...out.values()].map((d) =>
|
|
220
|
+
return [...out.values()].map((d) => jsonClone(d));
|
|
223
221
|
}
|
|
224
222
|
function mergeBindings(orig, edited) {
|
|
225
223
|
const byId2 = new Map();
|
|
@@ -229,5 +227,5 @@ function mergeBindings(orig, edited) {
|
|
|
229
227
|
for (const b of orig ?? [])
|
|
230
228
|
if (b.id)
|
|
231
229
|
byId2.set(b.id, b); // original wins
|
|
232
|
-
return [...byId2.values()].map((b) =>
|
|
230
|
+
return [...byId2.values()].map((b) => jsonClone(b));
|
|
233
231
|
}
|
|
@@ -48,3 +48,5 @@ export declare function beforeLast(s: string, sep: string): string;
|
|
|
48
48
|
export declare function splitOnce(s: string, sep: string): [string, string | undefined];
|
|
49
49
|
/** `str.isdigit()` for ASCII digits, which is all the generators ever test. */
|
|
50
50
|
export declare function isAsciiDigits(s: string): boolean;
|
|
51
|
+
/** `repr(str)`: single quotes unless the text has a `'` and no `"`; escapes as CPython does. */
|
|
52
|
+
export declare function pyReprString(s: string): string;
|
|
@@ -219,3 +219,38 @@ export function splitOnce(s, sep) {
|
|
|
219
219
|
export function isAsciiDigits(s) {
|
|
220
220
|
return /^[0-9]+$/.test(s);
|
|
221
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* `str.isprintable()` is false for these categories (and true for the ASCII
|
|
224
|
+
* space, which is in Zs). `repr` escapes exactly the non-printable ones.
|
|
225
|
+
*/
|
|
226
|
+
const NOT_PRINTABLE = /[\p{Cc}\p{Cf}\p{Cs}\p{Co}\p{Cn}\p{Zl}\p{Zp}\p{Zs}]/u;
|
|
227
|
+
function hex(codePoint, width) {
|
|
228
|
+
return codePoint.toString(16).padStart(width, "0");
|
|
229
|
+
}
|
|
230
|
+
/** `repr(str)`: single quotes unless the text has a `'` and no `"`; escapes as CPython does. */
|
|
231
|
+
export function pyReprString(s) {
|
|
232
|
+
const quote = s.includes("'") && !s.includes('"') ? '"' : "'";
|
|
233
|
+
let out = quote;
|
|
234
|
+
for (const ch of s) {
|
|
235
|
+
if (ch === quote || ch === "\\")
|
|
236
|
+
out += `\\${ch}`;
|
|
237
|
+
else if (ch === "\n")
|
|
238
|
+
out += "\\n";
|
|
239
|
+
else if (ch === "\r")
|
|
240
|
+
out += "\\r";
|
|
241
|
+
else if (ch === "\t")
|
|
242
|
+
out += "\\t";
|
|
243
|
+
else if (ch !== " " && NOT_PRINTABLE.test(ch)) {
|
|
244
|
+
const codePoint = ch.codePointAt(0);
|
|
245
|
+
out +=
|
|
246
|
+
codePoint < 0x100
|
|
247
|
+
? `\\x${hex(codePoint, 2)}`
|
|
248
|
+
: codePoint < 0x10000
|
|
249
|
+
? `\\u${hex(codePoint, 4)}`
|
|
250
|
+
: `\\U${hex(codePoint, 8)}`;
|
|
251
|
+
}
|
|
252
|
+
else
|
|
253
|
+
out += ch;
|
|
254
|
+
}
|
|
255
|
+
return out + quote;
|
|
256
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python value semantics the registry ports lean on (`bool()`, `or`), spelled
|
|
3
|
+
* once; FB005 holds these names to this file.
|
|
4
|
+
*
|
|
5
|
+
* The prepare helpers, the library build and the generators each carried a copy,
|
|
6
|
+
* and they had drifted: `bigint` and `Set` were handled in some only, and `pyOr`
|
|
7
|
+
* had two signatures. On JSON values (all `JSON.parse` yields) every copy agreed,
|
|
8
|
+
* so the byte-locked outputs that lean on them are unchanged.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* `bool(value)`: `None`, `False`, `0`, `""` and empty containers are false;
|
|
12
|
+
* `NaN` is true, as in Python.
|
|
13
|
+
*/
|
|
14
|
+
export declare function pyTruthy(value: unknown): boolean;
|
|
15
|
+
/** `a or b or …`: the first truthy operand, else the last one. */
|
|
16
|
+
export declare function pyOr<T extends readonly unknown[]>(...values: T): T[number];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python value semantics the registry ports lean on (`bool()`, `or`), spelled
|
|
3
|
+
* once; FB005 holds these names to this file.
|
|
4
|
+
*
|
|
5
|
+
* The prepare helpers, the library build and the generators each carried a copy,
|
|
6
|
+
* and they had drifted: `bigint` and `Set` were handled in some only, and `pyOr`
|
|
7
|
+
* had two signatures. On JSON values (all `JSON.parse` yields) every copy agreed,
|
|
8
|
+
* so the byte-locked outputs that lean on them are unchanged.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* `bool(value)`: `None`, `False`, `0`, `""` and empty containers are false;
|
|
12
|
+
* `NaN` is true, as in Python.
|
|
13
|
+
*/
|
|
14
|
+
export function pyTruthy(value) {
|
|
15
|
+
if (value === null || value === undefined || value === false || value === "")
|
|
16
|
+
return false;
|
|
17
|
+
if (typeof value === "number")
|
|
18
|
+
return value !== 0; // NaN is truthy in Python
|
|
19
|
+
if (typeof value === "bigint")
|
|
20
|
+
return value !== 0n;
|
|
21
|
+
if (Array.isArray(value))
|
|
22
|
+
return value.length > 0;
|
|
23
|
+
if (value instanceof Map || value instanceof Set)
|
|
24
|
+
return value.size > 0;
|
|
25
|
+
if (typeof value === "object")
|
|
26
|
+
return Object.keys(value).length > 0;
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
/** `a or b or …`: the first truthy operand, else the last one. */
|
|
30
|
+
export function pyOr(...values) {
|
|
31
|
+
for (const value of values)
|
|
32
|
+
if (pyTruthy(value))
|
|
33
|
+
return value;
|
|
34
|
+
return values[values.length - 1];
|
|
35
|
+
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
+
export { pyTruthy, pyOr } from "../../compat/pyvalue.js";
|
|
1
2
|
export type Dict = Record<string, unknown>;
|
|
2
|
-
/** Python truthiness for JSON-shaped values: empty strings and containers are false, `NaN` is true. */
|
|
3
|
-
export declare function pyTruthy(v: unknown): boolean;
|
|
4
|
-
/** `a or b or …`: the first Python-truthy operand, else the last one. */
|
|
5
|
-
export declare function pyOr<T>(...values: T[]): T;
|
|
6
3
|
/** `isinstance(v, dict)`. */
|
|
7
4
|
export declare function isDict(v: unknown): v is Dict;
|
|
8
5
|
/** `(v or {})` where the Python goes on to call `.get` — a non-dict reads as empty. */
|
|
@@ -10,36 +10,8 @@
|
|
|
10
10
|
* default) and a `None` value (printed as `None`).
|
|
11
11
|
*/
|
|
12
12
|
import { pyStr, pyStripChars } from "../../compat/pystr.js";
|
|
13
|
-
|
|
14
|
-
export
|
|
15
|
-
if (v === null || v === undefined)
|
|
16
|
-
return false;
|
|
17
|
-
switch (typeof v) {
|
|
18
|
-
case "boolean":
|
|
19
|
-
return v;
|
|
20
|
-
case "number":
|
|
21
|
-
return v !== 0;
|
|
22
|
-
case "bigint":
|
|
23
|
-
return v !== 0n;
|
|
24
|
-
case "string":
|
|
25
|
-
return v.length > 0;
|
|
26
|
-
case "object":
|
|
27
|
-
if (Array.isArray(v))
|
|
28
|
-
return v.length > 0;
|
|
29
|
-
if (v instanceof Map || v instanceof Set)
|
|
30
|
-
return v.size > 0;
|
|
31
|
-
return Object.keys(v).length > 0;
|
|
32
|
-
default:
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
/** `a or b or …`: the first Python-truthy operand, else the last one. */
|
|
37
|
-
export function pyOr(...values) {
|
|
38
|
-
for (const v of values)
|
|
39
|
-
if (pyTruthy(v))
|
|
40
|
-
return v;
|
|
41
|
-
return values[values.length - 1];
|
|
42
|
-
}
|
|
13
|
+
import { pyOr } from "../../compat/pyvalue.js";
|
|
14
|
+
export { pyTruthy, pyOr } from "../../compat/pyvalue.js";
|
|
43
15
|
/** `isinstance(v, dict)`. */
|
|
44
16
|
export function isDict(v) {
|
|
45
17
|
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
@@ -13,6 +13,7 @@ import { pyStr, pyStrip } from "../../../compat/pystr.js";
|
|
|
13
13
|
import { isContainerPart } from "../../fields.js";
|
|
14
14
|
import { authoringField, enumValue, referenceCollection } from "../common.js";
|
|
15
15
|
import { dictOf, listOf, pyOr, pyTruthy } from "../pyvalue.js";
|
|
16
|
+
import { ownEntry } from "../../../../core/own-entry.js";
|
|
16
17
|
/** `operation.parameters[].type` → the wire slot, mirroring `core/library.ts`'s PARAM_LOCATIONS. */
|
|
17
18
|
const PARAM_LOCATIONS = { query: "query", path: "path", multipart: "multipart" };
|
|
18
19
|
/** A parameter's `dataType` → the scalar names `compile`'s literal type-check understands. */
|
|
@@ -28,7 +29,7 @@ const SCALARS = {
|
|
|
28
29
|
export function scalarType(dataType) {
|
|
29
30
|
if (typeof dataType !== "string")
|
|
30
31
|
return "?";
|
|
31
|
-
return
|
|
32
|
+
return ownEntry(SCALARS, dataType) ?? dataType;
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
35
|
* The op's QUERY, PATH and file MULTIPART parameters as body-shaped fields,
|
|
@@ -41,7 +42,7 @@ export function paramFields(j) {
|
|
|
41
42
|
const p = dictOf(raw);
|
|
42
43
|
const name = p.name;
|
|
43
44
|
const type = p.type;
|
|
44
|
-
const loc = typeof type === "string"
|
|
45
|
+
const loc = typeof type === "string" ? ownEntry(PARAM_LOCATIONS, type) : undefined;
|
|
45
46
|
if (typeof name !== "string" || !name || !loc)
|
|
46
47
|
continue;
|
|
47
48
|
if (isContainerPart(p))
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import type { LibraryField } from "../model/library.js";
|
|
2
|
+
export { pyTruthy } from "../compat/pyvalue.js";
|
|
2
3
|
/** Registry field `type` → TypeScript type; anything else is `unknown`. */
|
|
3
4
|
export declare const TYPE_MAP: Readonly<Record<string, string>>;
|
|
4
|
-
/**
|
|
5
|
-
* Python truthiness for a JSON-shaped value. JavaScript agrees on `null`,
|
|
6
|
-
* `false`, `0` and `""` but calls an empty array or object truthy; the Python
|
|
7
|
-
* generator tested `field.get("required")`, `runtime.get("requiresConnection")`
|
|
8
|
-
* and the like with a bare `if`, so the emitted bytes depend on this rule.
|
|
9
|
-
*/
|
|
10
|
-
export declare function pyTruthy(value: unknown): boolean;
|
|
11
5
|
/** `TYPE_MAP.get(str(field["type"] or "").lower(), "unknown")`. */
|
|
12
6
|
export declare function tsType(field: {
|
|
13
7
|
type?: unknown;
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { sortedStrings } from "../compat/compare.js";
|
|
14
14
|
import { pyStr } from "../compat/pystr.js";
|
|
15
|
+
import { pyTruthy } from "../compat/pyvalue.js";
|
|
15
16
|
import { pascal, singular } from "../naming.js";
|
|
17
|
+
import { ownEntry } from "../../core/own-entry.js";
|
|
18
|
+
export { pyTruthy } from "../compat/pyvalue.js";
|
|
16
19
|
/** Registry field `type` → TypeScript type; anything else is `unknown`. */
|
|
17
20
|
export const TYPE_MAP = {
|
|
18
21
|
string: "string",
|
|
@@ -28,34 +31,14 @@ export const TYPE_MAP = {
|
|
|
28
31
|
json: "unknown",
|
|
29
32
|
file: "string",
|
|
30
33
|
};
|
|
31
|
-
/**
|
|
32
|
-
* Python truthiness for a JSON-shaped value. JavaScript agrees on `null`,
|
|
33
|
-
* `false`, `0` and `""` but calls an empty array or object truthy; the Python
|
|
34
|
-
* generator tested `field.get("required")`, `runtime.get("requiresConnection")`
|
|
35
|
-
* and the like with a bare `if`, so the emitted bytes depend on this rule.
|
|
36
|
-
*/
|
|
37
|
-
export function pyTruthy(value) {
|
|
38
|
-
if (value === null || value === undefined || value === false || value === "")
|
|
39
|
-
return false;
|
|
40
|
-
if (typeof value === "number")
|
|
41
|
-
return value !== 0; // NaN is truthy in Python
|
|
42
|
-
if (typeof value === "bigint")
|
|
43
|
-
return value !== 0n;
|
|
44
|
-
if (Array.isArray(value))
|
|
45
|
-
return value.length > 0;
|
|
46
|
-
if (value instanceof Map || value instanceof Set)
|
|
47
|
-
return value.size > 0;
|
|
48
|
-
if (typeof value === "object")
|
|
49
|
-
return Object.keys(value).length > 0;
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
34
|
/** `str(value or "")`: the coercion the generator applied before comparing a type name. */
|
|
53
35
|
function strOrEmpty(value) {
|
|
54
36
|
return pyTruthy(value) ? pyStr(value) : "";
|
|
55
37
|
}
|
|
56
38
|
/** `TYPE_MAP.get(str(field["type"] or "").lower(), "unknown")`. */
|
|
57
39
|
export function tsType(field) {
|
|
58
|
-
|
|
40
|
+
const type = strOrEmpty(field.type).toLowerCase();
|
|
41
|
+
return ownEntry(TYPE_MAP, type) ?? "unknown";
|
|
59
42
|
}
|
|
60
43
|
/** One node in a tree reconstructed from flattened connector field paths. */
|
|
61
44
|
export class TypeNode {
|
|
@@ -22,7 +22,7 @@ import { enrichAll } from "./enrich.js";
|
|
|
22
22
|
import { resolveConnectorEvents } from "./events.js";
|
|
23
23
|
import { expandAllGenerics, sortExpandedEntries } from "./generic-expand.js";
|
|
24
24
|
import { orphanCheck, planLibrary, triggerRows } from "./plan.js";
|
|
25
|
-
import { get, item, pyIter, pyOr, pyReprStrList,
|
|
25
|
+
import { get, item, pyIter, pyOr, pyReprStrList, pyTruthy } from "./py.js";
|
|
26
26
|
import { writePlan, writeTriggerSideFile } from "./write.js";
|
|
27
27
|
/** The Python's default: beside the output directory. */
|
|
28
28
|
export function defaultTriggersOutput(outputDir) {
|
|
@@ -109,7 +109,7 @@ export async function fetchAll(ctx, nodeTypes) {
|
|
|
109
109
|
* sorted and deduplicated; `key → record`, `null` where the lookup failed.
|
|
110
110
|
*/
|
|
111
111
|
export async function fetchConnectorRecordsForKeys(ctx, keys) {
|
|
112
|
-
const sorted = sortedStrings(new Set([...keys].filter((key) =>
|
|
112
|
+
const sorted = sortedStrings(new Set([...keys].filter((key) => pyTruthy(key))));
|
|
113
113
|
ctx.reporter.info(` Looking up ${sorted.length} connector records...`);
|
|
114
114
|
let done = 0;
|
|
115
115
|
const records = await mapConcurrent(sorted, ctx.concurrency, async (key) => {
|
|
@@ -129,7 +129,7 @@ export async function fetchConnectorRecordsForKeys(ctx, keys) {
|
|
|
129
129
|
}
|
|
130
130
|
/** `fetch_connector_records`: one record per unique connector key in `entries`. */
|
|
131
131
|
export function fetchConnectorRecords(ctx, entries) {
|
|
132
|
-
const keys = entries.map((e) => item(item(e, "connector"), "key")).filter((key) =>
|
|
132
|
+
const keys = entries.map((e) => item(item(e, "connector"), "key")).filter((key) => pyTruthy(key));
|
|
133
133
|
return fetchConnectorRecordsForKeys(ctx, keys);
|
|
134
134
|
}
|
|
135
135
|
/** The whole generator, `main()` minus argument parsing and cache cleanup. */
|
|
@@ -213,7 +213,7 @@ export async function buildLibrary(options, ports) {
|
|
|
213
213
|
const name = cacheFiles.get(nt);
|
|
214
214
|
try {
|
|
215
215
|
const entry = extractCanonical(nodeOf(ctx.caches.registry.read(name)));
|
|
216
|
-
if (
|
|
216
|
+
if (pyTruthy(entry)) {
|
|
217
217
|
entries.push(entry);
|
|
218
218
|
v1defSources.set(item(entry, "nodeType"), name);
|
|
219
219
|
// `entry["operation"].get("activityType", "") or "(empty)"`, printed
|
|
@@ -247,10 +247,10 @@ export async function buildLibrary(options, ports) {
|
|
|
247
247
|
for (const e of entries) {
|
|
248
248
|
const key = item(item(e, "connector"), "key");
|
|
249
249
|
mergeConnectorRecord(e, connectorRecords.get(key) ?? null);
|
|
250
|
-
if (
|
|
250
|
+
if (pyTruthy(get(item(e, "connector"), "name")))
|
|
251
251
|
named += 1;
|
|
252
252
|
}
|
|
253
|
-
const schemaDynamic = entries.filter((e) =>
|
|
253
|
+
const schemaDynamic = entries.filter((e) => pyTruthy(get(item(e, "runtime"), "requiresConnectionForSchema"))).length;
|
|
254
254
|
info(` Named ${named}/${entries.length} entries from connector records`);
|
|
255
255
|
info(` schema-dynamic from connector capabilities: ${schemaDynamic}/${entries.length}`);
|
|
256
256
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { pyStr, pyStrip, lowerFirst, startsWithUpper } from "../compat/pystr.js";
|
|
14
14
|
import { sortedStrings } from "../compat/compare.js";
|
|
15
|
-
import { get,
|
|
15
|
+
import { get, isPyDict, pyIter, pyOr, partition, pyTruthy } from "./py.js";
|
|
16
16
|
import { schemaAction } from "./schema-action.js";
|
|
17
17
|
export const SCHEMA_VERSION = "1";
|
|
18
18
|
/** `(connectorKey, objectName)` as a Map key; `null`s stay distinct from `""`. */
|
|
@@ -84,7 +84,7 @@ export function applyCuratedOverrides(entries, overrides = CURATED_OP_OVERRIDE)
|
|
|
84
84
|
for (const e of entries) {
|
|
85
85
|
const key = curatedKey(get(pyOr(get(e, "connector", {}), {}), "key", ""), get(pyOr(get(e, "operation", {}), {}), "objectName", ""));
|
|
86
86
|
const override = overrides.get(key);
|
|
87
|
-
if (!
|
|
87
|
+
if (!pyTruthy(override))
|
|
88
88
|
continue;
|
|
89
89
|
const target = e;
|
|
90
90
|
for (const [section, fields] of Object.entries(override)) {
|
|
@@ -130,17 +130,17 @@ export function parameterMetadata(connectorMethodInfo) {
|
|
|
130
130
|
meta.displayName = get(p, "displayName");
|
|
131
131
|
if (get(p, "reference") !== null)
|
|
132
132
|
meta.reference = get(p, "reference");
|
|
133
|
-
if (
|
|
133
|
+
if (isPyDict(p) && "enum" in p) {
|
|
134
134
|
meta.enum = p.enum ?? null;
|
|
135
135
|
}
|
|
136
|
-
else if (
|
|
136
|
+
else if (pyTruthy(get(p, "enhancedEnum"))) {
|
|
137
137
|
const values = pyIter(get(p, "enhancedEnum"))
|
|
138
|
-
.filter((e) =>
|
|
138
|
+
.filter((e) => isPyDict(e) && get(e, "value") !== null)
|
|
139
139
|
.map((e) => get(e, "value"));
|
|
140
140
|
if (values.length > 0)
|
|
141
141
|
meta.enum = values;
|
|
142
142
|
}
|
|
143
|
-
if (
|
|
143
|
+
if (pyTruthy(meta))
|
|
144
144
|
out.set(name, meta);
|
|
145
145
|
}
|
|
146
146
|
return out;
|
|
@@ -156,7 +156,7 @@ export function nodeOf(payload) {
|
|
|
156
156
|
*/
|
|
157
157
|
export function extractCanonical(nodeData, ctx = {}) {
|
|
158
158
|
const node = nodeData;
|
|
159
|
-
if (!
|
|
159
|
+
if (!pyTruthy(node))
|
|
160
160
|
return undefined;
|
|
161
161
|
const nodeType = get(node, "nodeType", "");
|
|
162
162
|
const model = pyOr(get(node, "model", {}), {});
|
|
@@ -168,7 +168,7 @@ export function extractCanonical(nodeData, ctx = {}) {
|
|
|
168
168
|
for (const section of pyIter(pyOr(get(pyOr(get(node, "form", {}), {}), "sections", []), []))) {
|
|
169
169
|
for (const f of pyIter(pyOr(get(section, "fields", []), []))) {
|
|
170
170
|
const cp = get(pyOr(get(f, "componentProps"), {}), "connectorDetail");
|
|
171
|
-
if (
|
|
171
|
+
if (isPyDict(cp) && "configuration" in cp) {
|
|
172
172
|
connectorDetail = cp;
|
|
173
173
|
break;
|
|
174
174
|
}
|
|
@@ -176,13 +176,13 @@ export function extractCanonical(nodeData, ctx = {}) {
|
|
|
176
176
|
if (connectorDetail)
|
|
177
177
|
break;
|
|
178
178
|
}
|
|
179
|
-
if (!
|
|
179
|
+
if (!pyTruthy(connectorDetail))
|
|
180
180
|
return undefined;
|
|
181
|
-
if (!
|
|
181
|
+
if (!pyTruthy(get(connectorDetail, "isEnabled", false)))
|
|
182
182
|
return undefined;
|
|
183
183
|
if (get(connectorDetail, "targetPlatform") !== "CrossPlatform")
|
|
184
184
|
return undefined;
|
|
185
|
-
if (
|
|
185
|
+
if (pyTruthy(get(connectorDetail, "isExperimental", true)))
|
|
186
186
|
return undefined;
|
|
187
187
|
const configuration = get(connectorDetail, "configuration", "{}");
|
|
188
188
|
if (typeof configuration !== "string") {
|
|
@@ -210,9 +210,9 @@ export function extractCanonical(nodeData, ctx = {}) {
|
|
|
210
210
|
const paramMeta = parameterMetadata(cmi);
|
|
211
211
|
const declaredFields = pyOr(get(get(node, "inputDefinition", {}), "fields"), []);
|
|
212
212
|
const inputFields = [];
|
|
213
|
-
if (
|
|
213
|
+
if (pyTruthy(declaredFields)) {
|
|
214
214
|
for (const declared of pyIter(declaredFields)) {
|
|
215
|
-
if (!
|
|
215
|
+
if (!isPyDict(declared))
|
|
216
216
|
throw new TypeError("cannot convert dictionary update sequence element to a dict");
|
|
217
217
|
const merged = { ...declared };
|
|
218
218
|
const meta = paramMeta.get(get(merged, "name")) ?? {};
|
|
@@ -236,9 +236,9 @@ export function extractCanonical(nodeData, ctx = {}) {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
let outputFields = get(get(node, "outputDefinition", {}), "fields");
|
|
239
|
-
if (!
|
|
239
|
+
if (!pyTruthy(outputFields))
|
|
240
240
|
outputFields = get(get(node, "outputResponseDefinition", {}), "fields");
|
|
241
|
-
if (!
|
|
241
|
+
if (!pyTruthy(outputFields))
|
|
242
242
|
outputFields = [];
|
|
243
243
|
const display = pyOr(get(node, "display", {}), {});
|
|
244
244
|
const isTrigger = pyStr(activityType).endsWith("Trigger");
|
|
@@ -257,7 +257,7 @@ export function extractCanonical(nodeData, ctx = {}) {
|
|
|
257
257
|
// the platform stamps (`EMAIL_RECEIVED`), the key every trigger API takes.
|
|
258
258
|
if (isTrigger)
|
|
259
259
|
operation.eventOperation = pyOr(contextValue(context, "operation"), "");
|
|
260
|
-
if (isTrigger &&
|
|
260
|
+
if (isTrigger && pyTruthy(get(node, "eventMode")))
|
|
261
261
|
operation.eventMode = pyOr(get(node, "eventMode"), "");
|
|
262
262
|
const entry = {
|
|
263
263
|
schemaVersion: ctx.schemaVersion ?? SCHEMA_VERSION,
|
|
@@ -292,14 +292,14 @@ export function extractCanonical(nodeData, ctx = {}) {
|
|
|
292
292
|
// Some operations resolve their real field set from the VALUES of a few
|
|
293
293
|
// parent fields; record what a caller has to do about it.
|
|
294
294
|
const action = schemaAction(cmi, inputFields);
|
|
295
|
-
if (
|
|
295
|
+
if (pyTruthy(action))
|
|
296
296
|
operation.schemaAction = action;
|
|
297
297
|
const tableKey = curatedKey(get(config, "connectorKey", ""), get(config, "objectName", ""));
|
|
298
298
|
const dispatchStyle = (ctx.dispatchStyles ?? CURATED_DISPATCH_STYLE).get(tableKey);
|
|
299
|
-
if (
|
|
299
|
+
if (pyTruthy(dispatchStyle))
|
|
300
300
|
operation.dispatchStyle = dispatchStyle;
|
|
301
301
|
const paramGrammars = (ctx.paramGrammars ?? CURATED_PARAM_GRAMMARS).get(tableKey);
|
|
302
|
-
if (
|
|
302
|
+
if (pyTruthy(paramGrammars))
|
|
303
303
|
operation.paramGrammars = paramGrammars;
|
|
304
304
|
return entry;
|
|
305
305
|
}
|
|
@@ -316,7 +316,7 @@ export function asBool(value) {
|
|
|
316
316
|
}
|
|
317
317
|
/** Read the connector tier across CLI response casing variants. */
|
|
318
318
|
export function connectorTier(record) {
|
|
319
|
-
if (!
|
|
319
|
+
if (!isPyDict(record))
|
|
320
320
|
return null;
|
|
321
321
|
let value = get(record, "Tier");
|
|
322
322
|
if (value === null)
|
|
@@ -355,11 +355,11 @@ export function filterNodeTypesByTier(nodeTypes, connectorRecords, tier) {
|
|
|
355
355
|
}
|
|
356
356
|
/** `merge_connector_record`: stamp connector discovery metadata and its derived flag. */
|
|
357
357
|
export function mergeConnectorRecord(entry, record) {
|
|
358
|
-
if (!
|
|
358
|
+
if (!pyTruthy(record))
|
|
359
359
|
return entry;
|
|
360
360
|
const connector = entry.connector;
|
|
361
361
|
const name = get(record, "Name");
|
|
362
|
-
if (
|
|
362
|
+
if (pyTruthy(name))
|
|
363
363
|
connector.name = name;
|
|
364
364
|
const tier = connectorTier(record);
|
|
365
365
|
if (tier !== null)
|
|
@@ -372,7 +372,7 @@ export function mergeConnectorRecord(entry, record) {
|
|
|
372
372
|
hasCustomFieldDiscovery: asBool(get(record, "HasCustomFieldDiscovery")),
|
|
373
373
|
};
|
|
374
374
|
connector.discovery = discovery;
|
|
375
|
-
entry.runtime.requiresConnectionForSchema = Object.entries(discovery).some(([key, value]) => key !== "type" &&
|
|
375
|
+
entry.runtime.requiresConnectionForSchema = Object.entries(discovery).some(([key, value]) => key !== "type" && pyTruthy(value));
|
|
376
376
|
return entry;
|
|
377
377
|
}
|
|
378
378
|
// ---------------------------------------------------------------------------
|
|
@@ -489,7 +489,7 @@ export function toLowerCamel(s) {
|
|
|
489
489
|
* uses camelCase, so the on-disk schema stays stable across CLI versions.
|
|
490
490
|
*/
|
|
491
491
|
export function normalizeFieldKeys(value) {
|
|
492
|
-
if (
|
|
492
|
+
if (isPyDict(value)) {
|
|
493
493
|
const out = {};
|
|
494
494
|
for (const [k, v] of Object.entries(value))
|
|
495
495
|
out[toLowerCamel(k)] = normalizeFieldKeys(v);
|
|
@@ -501,7 +501,7 @@ export function normalizeFieldKeys(value) {
|
|
|
501
501
|
}
|
|
502
502
|
/** `_pick`: the first non-None value among `d[k]` for the given keys, else `null`. */
|
|
503
503
|
export function pick(d, ...keys) {
|
|
504
|
-
if (!
|
|
504
|
+
if (!isPyDict(d))
|
|
505
505
|
return null;
|
|
506
506
|
for (const k of keys) {
|
|
507
507
|
const v = d[k];
|
|
@@ -19,7 +19,7 @@ import { sortedByKeys } from "../compat/compare.js";
|
|
|
19
19
|
import { isConnectorsMetadata, isResourcesDescribeObject, isResourcesDescribeOperation, isResourcesList as isResourcesListArgv, registryGet as registryGetArgv, } from "../uip/commands.js";
|
|
20
20
|
import { CACHED_FAILURE, isCachedFailure, safeCacheName } from "../uip/response-cache.js";
|
|
21
21
|
import { failureReason, isNodeNotFound } from "../uip/envelope.js";
|
|
22
|
-
import { get,
|
|
22
|
+
import { get, isPyDict, pyOr, pyTruthy } from "./py.js";
|
|
23
23
|
export function createBuildContext(ports, scope = {}) {
|
|
24
24
|
const parentFields = scope.parentFields instanceof Map
|
|
25
25
|
? new Map(scope.parentFields)
|
|
@@ -111,7 +111,7 @@ function cacheNameOf(...parts) {
|
|
|
111
111
|
}
|
|
112
112
|
/** `payload.get("Result")` on something that is not a dict raises in Python. */
|
|
113
113
|
function envelope(payload) {
|
|
114
|
-
if (!
|
|
114
|
+
if (!isPyDict(payload))
|
|
115
115
|
throw new TypeError(`'${Array.isArray(payload) ? "list" : typeof payload}' object has no attribute 'get'`);
|
|
116
116
|
return payload;
|
|
117
117
|
}
|
|
@@ -174,7 +174,7 @@ export async function isDescribeOperation(ctx, connectorKey, objectName, operati
|
|
|
174
174
|
const cached = ctx.caches.is.read(name);
|
|
175
175
|
if (isCachedFailure(cached))
|
|
176
176
|
noteFailedFieldDescribe(ctx, connectorKey, objectName, operation, get(cached, "Message"));
|
|
177
|
-
return
|
|
177
|
+
return isPyDict(cached) && cached.Data !== undefined ? cached.Data : undefined;
|
|
178
178
|
}
|
|
179
179
|
const result = await runUip(ctx, isResourcesDescribeOperation(connectorKey, objectName, operation, ctx.connectionId, ctx.parentFields));
|
|
180
180
|
if (refused(result))
|
|
@@ -199,7 +199,7 @@ export async function isDescribeObject(ctx, connectorKey, objectName) {
|
|
|
199
199
|
const name = cacheNameOf(schemaCacheScope(ctx), connectorKey, objectName, "_operations");
|
|
200
200
|
if (ctx.caches.is.has(name)) {
|
|
201
201
|
const cached = ctx.caches.is.read(name);
|
|
202
|
-
return
|
|
202
|
+
return isPyDict(cached) && cached.Data !== undefined ? cached.Data : undefined;
|
|
203
203
|
}
|
|
204
204
|
const result = await runUip(ctx, isResourcesDescribeObject(connectorKey, objectName, ctx.connectionId));
|
|
205
205
|
if (refused(result))
|
|
@@ -233,7 +233,7 @@ export async function isResourcesList(ctx, connectorKey, options = {}) {
|
|
|
233
233
|
return Array.isArray(data) ? data : undefined;
|
|
234
234
|
}
|
|
235
235
|
const result = await runUip(ctx, isResourcesListArgv(connectorKey, {
|
|
236
|
-
...(
|
|
236
|
+
...(pyTruthy(operation) ? { operation } : {}),
|
|
237
237
|
...(options.refresh ? { refresh: true } : {}),
|
|
238
238
|
connectionId: ctx.connectionId,
|
|
239
239
|
}));
|