@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.
Files changed (67) hide show
  1. package/dist/bindings-v2.js +15 -17
  2. package/dist/bpmn/bpmn-sdk.js +3 -2
  3. package/dist/bpmn/check.js +3 -2
  4. package/dist/bpmn/decompile.js +123 -137
  5. package/dist/bpmn/intsvc.js +7 -2
  6. package/dist/bpmn/serialize.js +1 -1
  7. package/dist/bpmn/typed-node.js +5 -4
  8. package/dist/case/check-cli.js +18 -4
  9. package/dist/case/check.d.ts +26 -1
  10. package/dist/case/check.js +80 -2
  11. package/dist/case/cli-library.d.ts +14 -0
  12. package/dist/case/cli-library.js +31 -0
  13. package/dist/case/compile-cli.js +12 -12
  14. package/dist/case/decompile.js +103 -109
  15. package/dist/case/entry-points.js +2 -1
  16. package/dist/case/merge.js +3 -5
  17. package/dist/case/serialize.js +1 -1
  18. package/dist/check.js +26 -10
  19. package/dist/cli/commands/case.js +10 -1
  20. package/dist/core/ceql.js +5 -2
  21. package/dist/core/connector-inputs.js +4 -4
  22. package/dist/core/event-checks.js +17 -2
  23. package/dist/core/event-filters.d.ts +38 -4
  24. package/dist/core/event-filters.js +63 -7
  25. package/dist/core/expr.d.ts +1 -1
  26. package/dist/core/expr.js +3 -3
  27. package/dist/core/json-file.d.ts +2 -0
  28. package/dist/core/json-file.js +4 -0
  29. package/dist/core/library.js +2 -1
  30. package/dist/core/node-envelope.d.ts +1 -0
  31. package/dist/core/node-envelope.js +1 -1
  32. package/dist/core/own-entry.d.ts +9 -0
  33. package/dist/core/own-entry.js +11 -0
  34. package/dist/core/ts-literal.d.ts +22 -0
  35. package/dist/core/ts-literal.js +37 -0
  36. package/dist/core/typed-raw-node.js +4 -3
  37. package/dist/decompile.js +238 -255
  38. package/dist/merge.js +10 -12
  39. package/dist/registry/compat/pystr.d.ts +2 -0
  40. package/dist/registry/compat/pystr.js +35 -0
  41. package/dist/registry/compat/pyvalue.d.ts +16 -0
  42. package/dist/registry/compat/pyvalue.js +35 -0
  43. package/dist/registry/generators/markdown/pyvalue.d.ts +1 -4
  44. package/dist/registry/generators/markdown/pyvalue.js +2 -30
  45. package/dist/registry/generators/markdown/sections/inputs.js +3 -2
  46. package/dist/registry/generators/type-tree.d.ts +1 -7
  47. package/dist/registry/generators/type-tree.js +5 -22
  48. package/dist/registry/library-build/build.js +6 -6
  49. package/dist/registry/library-build/canonical.js +25 -25
  50. package/dist/registry/library-build/context.js +5 -5
  51. package/dist/registry/library-build/enrich.js +18 -18
  52. package/dist/registry/library-build/events.js +25 -24
  53. package/dist/registry/library-build/generic-expand.js +21 -20
  54. package/dist/registry/library-build/plan.js +8 -8
  55. package/dist/registry/library-build/py.d.ts +3 -10
  56. package/dist/registry/library-build/py.js +9 -52
  57. package/dist/registry/library-build/schema-action.js +6 -6
  58. package/dist/registry/prepare/event.js +2 -2
  59. package/dist/registry/prepare/source-import.js +2 -9
  60. package/dist/registry/prepare/source-labels.js +2 -9
  61. package/dist/registry/prepare/support.d.ts +2 -6
  62. package/dist/registry/prepare/support.js +3 -58
  63. package/dist/registry/pull-cli.js +6 -14
  64. package/dist/registry/search-cli.js +2 -2
  65. package/dist/schema-version.js +0 -1
  66. package/dist/serialize.js +86 -76
  67. 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
- function clone(v) {
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: clone(orig.ui) };
89
+ return { ...edited, ui: jsonClone(orig.ui) };
92
90
  }
93
91
  export function mergeFlow(original, edited, options = {}) {
94
- const result = clone(original);
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(clone(orig));
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(clone(e), orig));
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 = clone(edited.variables);
117
- result.subflows = clone(edited.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 { ...clone(e), id };
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) => clone(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) => clone(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
- /** Python truthiness for JSON-shaped values: empty strings and containers are false, `NaN` is true. */
14
- export function pyTruthy(v) {
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 Object.hasOwn(SCALARS, dataType) ? SCALARS[dataType] : dataType;
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" && Object.hasOwn(PARAM_LOCATIONS, type) ? PARAM_LOCATIONS[type] : undefined;
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
- return TYPE_MAP[strOrEmpty(field.type).toLowerCase()] ?? "unknown";
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, truthy } from "./py.js";
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) => truthy(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) => truthy(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 (truthy(entry)) {
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 (truthy(get(item(e, "connector"), "name")))
250
+ if (pyTruthy(get(item(e, "connector"), "name")))
251
251
  named += 1;
252
252
  }
253
- const schemaDynamic = entries.filter((e) => truthy(get(item(e, "runtime"), "requiresConnectionForSchema"))).length;
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, isRecord, pyIter, pyOr, partition, truthy } from "./py.js";
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 (!truthy(override))
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 (isRecord(p) && "enum" in p) {
133
+ if (isPyDict(p) && "enum" in p) {
134
134
  meta.enum = p.enum ?? null;
135
135
  }
136
- else if (truthy(get(p, "enhancedEnum"))) {
136
+ else if (pyTruthy(get(p, "enhancedEnum"))) {
137
137
  const values = pyIter(get(p, "enhancedEnum"))
138
- .filter((e) => isRecord(e) && get(e, "value") !== null)
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 (truthy(meta))
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 (!truthy(node))
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 (isRecord(cp) && "configuration" in cp) {
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 (!truthy(connectorDetail))
179
+ if (!pyTruthy(connectorDetail))
180
180
  return undefined;
181
- if (!truthy(get(connectorDetail, "isEnabled", false)))
181
+ if (!pyTruthy(get(connectorDetail, "isEnabled", false)))
182
182
  return undefined;
183
183
  if (get(connectorDetail, "targetPlatform") !== "CrossPlatform")
184
184
  return undefined;
185
- if (truthy(get(connectorDetail, "isExperimental", true)))
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 (truthy(declaredFields)) {
213
+ if (pyTruthy(declaredFields)) {
214
214
  for (const declared of pyIter(declaredFields)) {
215
- if (!isRecord(declared))
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 (!truthy(outputFields))
239
+ if (!pyTruthy(outputFields))
240
240
  outputFields = get(get(node, "outputResponseDefinition", {}), "fields");
241
- if (!truthy(outputFields))
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 && truthy(get(node, "eventMode")))
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 (truthy(action))
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 (truthy(dispatchStyle))
299
+ if (pyTruthy(dispatchStyle))
300
300
  operation.dispatchStyle = dispatchStyle;
301
301
  const paramGrammars = (ctx.paramGrammars ?? CURATED_PARAM_GRAMMARS).get(tableKey);
302
- if (truthy(paramGrammars))
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 (!isRecord(record))
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 (!truthy(record))
358
+ if (!pyTruthy(record))
359
359
  return entry;
360
360
  const connector = entry.connector;
361
361
  const name = get(record, "Name");
362
- if (truthy(name))
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" && truthy(value));
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 (isRecord(value)) {
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 (!isRecord(d))
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, isRecord, pyOr, truthy } from "./py.js";
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 (!isRecord(payload))
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 isRecord(cached) && cached.Data !== undefined ? cached.Data : undefined;
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 isRecord(cached) && cached.Data !== undefined ? cached.Data : undefined;
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
- ...(truthy(operation) ? { operation } : {}),
236
+ ...(pyTruthy(operation) ? { operation } : {}),
237
237
  ...(options.refresh ? { refresh: true } : {}),
238
238
  connectionId: ctx.connectionId,
239
239
  }));