@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
@@ -17,7 +17,7 @@ import { mapConcurrent } from "../concurrency.js";
17
17
  import { messageOf } from "../errors.js";
18
18
  import { normalizeFieldKeys, pick } from "./canonical.js";
19
19
  import { isDescribeObject, isDescribeOperation } from "./context.js";
20
- import { get, item, pyEquals, pyIter, pyOr, truthy } from "./py.js";
20
+ import { get, item, pyEquals, pyIter, pyOr, pyTruthy } from "./py.js";
21
21
  /**
22
22
  * Registry `httpMethod` (e.g. `GETBYID`) → a guess at the `is resources`
23
23
  * operation name, for an entry whose canonical `operation.name` is empty.
@@ -40,10 +40,10 @@ export async function discoverOperationName(ctx, entry) {
40
40
  const connectorKey = item(item(entry, "connector"), "key");
41
41
  const objectName = item(operation, "objectName");
42
42
  const httpMethod = item(operation, "httpMethod");
43
- if (!truthy(connectorKey) || !truthy(objectName))
43
+ if (!pyTruthy(connectorKey) || !pyTruthy(objectName))
44
44
  return null;
45
45
  const objData = await isDescribeObject(ctx, connectorKey, objectName);
46
- if (!truthy(objData))
46
+ if (!pyTruthy(objData))
47
47
  return null;
48
48
  for (const op of pyIter(pyOr(pick(objData, "AvailableOperations", "availableOperations"), []))) {
49
49
  if (pyEquals(pick(op, "Method", "method"), httpMethod))
@@ -64,31 +64,31 @@ export function applyDescribe(entry, data) {
64
64
  const opData = pyOr(pick(data, "Operation", "operation"), {});
65
65
  // If the name was discovered via fallback, persist it now.
66
66
  const opResolvedName = pick(opData, "Name", "name");
67
- if (!truthy(item(operation, "name")) && truthy(opResolvedName))
67
+ if (!pyTruthy(item(operation, "name")) && pyTruthy(opResolvedName))
68
68
  operation.name = opResolvedName;
69
69
  // Path with placeholders (supersedes the simple `path`).
70
70
  const opPath = pick(opData, "Path", "path");
71
- if (truthy(opPath))
71
+ if (pyTruthy(opPath))
72
72
  operation.pathTemplate = opPath;
73
- if (!truthy(get(operation, "objectDisplayName"))) {
73
+ if (!pyTruthy(get(operation, "objectDisplayName"))) {
74
74
  const displayName = pick(data, "DisplayName", "displayName");
75
- if (truthy(displayName))
75
+ if (pyTruthy(displayName))
76
76
  operation.objectDisplayName = displayName;
77
77
  }
78
78
  const opCurated = pick(opData, "Curated", "curated");
79
- if (truthy(opCurated) && !truthy(get(display, "operationLabel")))
79
+ if (pyTruthy(opCurated) && !pyTruthy(get(display, "operationLabel")))
80
80
  display.operationLabel = opCurated;
81
81
  // Parameters (path / query) — raw shape, camelCase keys.
82
82
  const params = pyOr(pick(data, "Parameters", "parameters"), []);
83
- if (truthy(params))
83
+ if (pyTruthy(params))
84
84
  operation.parameters = normalizeFieldKeys(params);
85
85
  // Do not infer schema discovery from empty field lists: delete and file
86
86
  // operations legitimately expose neither. Connector metadata owns that flag.
87
87
  const requestFields = pyOr(pick(data, "RequestFields", "requestFields"), []);
88
88
  const responseFields = pyOr(pick(data, "ResponseFields", "responseFields"), []);
89
- if (truthy(requestFields))
89
+ if (pyTruthy(requestFields))
90
90
  e.inputSchema = { fields: normalizeFieldKeys(requestFields) };
91
- if (truthy(responseFields))
91
+ if (pyTruthy(responseFields))
92
92
  e.outputSchema = { fields: normalizeFieldKeys(responseFields) };
93
93
  return entry;
94
94
  }
@@ -111,16 +111,16 @@ export async function enrichEntry(ctx, entry) {
111
111
  const objectName = item(operation, "objectName");
112
112
  let opName = item(operation, "name");
113
113
  // Fallback: no operation name from the registry — discover via httpMethod.
114
- if (!truthy(opName)) {
114
+ if (!pyTruthy(opName)) {
115
115
  opName = pyOr(await discoverOperationName(ctx, entry), HTTP_METHOD_TO_OPERATION.get(item(operation, "httpMethod")) ?? null);
116
116
  }
117
- if (!truthy(opName))
117
+ if (!pyTruthy(opName))
118
118
  return entry; // nothing to do
119
119
  const failuresBefore = ctx.failedFieldDescribes.length;
120
120
  let data = await isDescribeOperation(ctx, connectorKey, objectName, opName);
121
- if (!truthy(data)) {
121
+ if (!pyTruthy(data)) {
122
122
  const alt = await discoverOperationName(ctx, entry);
123
- if (truthy(alt) && alt !== opName) {
123
+ if (pyTruthy(alt) && alt !== opName) {
124
124
  // The registry's verb did not name an operation the object has, so its
125
125
  // `-f` rejection is about a request that is now superseded: the CLI maps
126
126
  // `List` to GET, and Data Service `QueryEntityRecordsCurated` exposes
@@ -134,7 +134,7 @@ export async function enrichEntry(ctx, entry) {
134
134
  data = await isDescribeOperation(ctx, connectorKey, objectName, alt);
135
135
  }
136
136
  }
137
- if (!truthy(data))
137
+ if (!pyTruthy(data))
138
138
  return entry; // connector might be a stub or recently removed
139
139
  return applyDescribe(entry, data);
140
140
  }
@@ -159,9 +159,9 @@ export async function enrichAll(ctx, entries) {
159
159
  let pathTemplateAdded = 0;
160
160
  let schemaAdded = 0;
161
161
  for (const e of enriched) {
162
- if (truthy(get(get(e, "operation", {}), "pathTemplate")))
162
+ if (pyTruthy(get(get(e, "operation", {}), "pathTemplate")))
163
163
  pathTemplateAdded += 1;
164
- if (truthy(get(get(e, "inputSchema", {}), "fields")) || truthy(get(get(e, "outputSchema", {}), "fields")))
164
+ if (pyTruthy(get(get(e, "inputSchema", {}), "fields")) || pyTruthy(get(get(e, "outputSchema", {}), "fields")))
165
165
  schemaAdded += 1;
166
166
  }
167
167
  ctx.reporter.info(` pathTemplate added on ${pathTemplateAdded}/${entries.length}`);
@@ -22,7 +22,8 @@ import { failureReason } from "../uip/envelope.js";
22
22
  import { safeCacheName } from "../uip/response-cache.js";
23
23
  import { schemaCacheScope, writeCache } from "./context.js";
24
24
  import { objectsComeFromTheConnection } from "./generic-expand.js";
25
- import { codePointSlice, deepCopy, get, isRecord, item, pyIter, pyOr, truthy } from "./py.js";
25
+ import { codePointSlice, get, isPyDict, item, pyIter, pyOr, pyTruthy } from "./py.js";
26
+ import { jsonClone } from "../../core/json-file.js";
26
27
  /**
27
28
  * `Parameters[].type` on a `triggers objects` row routes the value into one of
28
29
  * three `--detail` buckets at emit time. Everything unrecognized is an event
@@ -87,7 +88,7 @@ async function runJson(client, argv, timeoutMs) {
87
88
  export async function triggersObjects(client, key, operation, connectionId, timeoutMs) {
88
89
  const payload = await runJson(client, isTriggersObjects(key, operation, connectionId), timeoutMs);
89
90
  let data = get(payload, "Data");
90
- if (isRecord(data))
91
+ if (isPyDict(data))
91
92
  data = get(data, "items");
92
93
  return Array.isArray(data) ? data : [];
93
94
  }
@@ -102,7 +103,7 @@ function lowerKey(k) {
102
103
  }
103
104
  /** `_camelize`: lower-case the first letter of every key, recursively. */
104
105
  export function camelize(value) {
105
- if (isRecord(value)) {
106
+ if (isPyDict(value)) {
106
107
  const out = {};
107
108
  for (const [k, v] of Object.entries(value))
108
109
  out[lowerKey(k)] = camelize(v);
@@ -122,23 +123,23 @@ function lowerTop(f) {
122
123
  /** `_is_hidden`: a hidden parameter that is safe to DROP — hidden and optional. */
123
124
  export function isHidden(low) {
124
125
  const raw = pyOr(get(low, "design"), {});
125
- if (!isRecord(raw))
126
+ if (!isPyDict(raw))
126
127
  return false;
127
128
  const design = camelize(raw);
128
- const hidden = truthy(pyOr(get(design, "isHidden"), get(design, "hidden")));
129
- return hidden && !truthy(get(low, "required"));
129
+ const hidden = pyTruthy(pyOr(get(design, "isHidden"), get(design, "hidden")));
130
+ return hidden && !pyTruthy(get(low, "required"));
130
131
  }
131
132
  /** `_keep_hidden_marker`: flag a kept parameter the designer hides (necessarily a required one). */
132
133
  export function keepHiddenMarker(low, field) {
133
134
  const design = camelize(pyOr(get(low, "design"), {}));
134
- if (isRecord(design) && truthy(pyOr(get(design, "isHidden"), get(design, "hidden"))))
135
+ if (isPyDict(design) && pyTruthy(pyOr(get(design, "isHidden"), get(design, "hidden"))))
135
136
  field.hidden = true;
136
137
  return field;
137
138
  }
138
139
  /** `(f.get("sortOrder") is None, f.get("sortOrder") or 0)`. */
139
140
  function sortOrderKey(f) {
140
141
  const so = get(f, "sortOrder");
141
- return [so === null ? 1 : 0, truthy(so) ? so : 0];
142
+ return [so === null ? 1 : 0, pyTruthy(so) ? so : 0];
142
143
  }
143
144
  /**
144
145
  * `_with_cascade`: mark each parameter that another one has to resolve before
@@ -152,7 +153,7 @@ export function withCascade(fields) {
152
153
  const explicit = pyIter(pyOr(get(reference, "dependsOn"), []));
153
154
  const own = get(field, "name");
154
155
  const depends = sortedStrings([...names]
155
- .filter((other) => truthy(other) && other !== own && (path.includes(`{${pyStr(other)}}`) || explicit.includes(other)))
156
+ .filter((other) => pyTruthy(other) && other !== own && (path.includes(`{${pyStr(other)}}`) || explicit.includes(other)))
156
157
  .map((other) => pyStr(other)));
157
158
  if (depends.length > 0) {
158
159
  field.dependsOn = depends;
@@ -175,11 +176,11 @@ export function withCascade(fields) {
175
176
  export function camelFields(fields) {
176
177
  const out = [];
177
178
  for (const f of pyIter(pyOr(fields, []))) {
178
- if (!isRecord(f))
179
+ if (!isPyDict(f))
179
180
  continue;
180
181
  const low = lowerTop(f);
181
182
  const name = get(low, "name");
182
- if (!truthy(name))
183
+ if (!pyTruthy(name))
183
184
  continue;
184
185
  if (isHidden(low))
185
186
  continue;
@@ -208,11 +209,11 @@ export function camelFields(fields) {
208
209
  export function normalizeBucketFields(parameters) {
209
210
  const out = [];
210
211
  for (const f of pyIter(pyOr(parameters, []))) {
211
- if (!isRecord(f))
212
+ if (!isPyDict(f))
212
213
  continue;
213
214
  const low = lowerTop(f);
214
215
  const name = get(low, "name");
215
- if (!truthy(name))
216
+ if (!pyTruthy(name))
216
217
  continue;
217
218
  if (isHidden(low))
218
219
  continue;
@@ -252,7 +253,7 @@ export function unionEventParameters(declared, objectParameters) {
252
253
  merged.set(field.name, field);
253
254
  continue;
254
255
  }
255
- existing.required = truthy(get(existing, "required")) || truthy(get(field, "required"));
256
+ existing.required = pyTruthy(get(existing, "required")) || pyTruthy(get(field, "required"));
256
257
  existing.bucket = field.bucket;
257
258
  for (const k of ["displayName", "description", "reference", "sortOrder"]) {
258
259
  if (get(field, k) !== null && get(existing, k) === null)
@@ -306,7 +307,7 @@ export function mergeDescribeIntoNode(node, describe, objectParameters = null, o
306
307
  const out = { ...node };
307
308
  out.eventParameters = { fields: unionEventParameters(get(describe, "EventParameters"), objectParameters) };
308
309
  out.filterFields = { fields: camelFields(get(describe, "FilterFields")) };
309
- if (truthy(get(describe, "OutputFields"))) {
310
+ if (pyTruthy(get(describe, "OutputFields"))) {
310
311
  out.outputResponseDefinition = { fields: camelFields(get(describe, "OutputFields")) };
311
312
  }
312
313
  Object.assign(out, resolveEventFacts(objectRow, describe));
@@ -322,8 +323,8 @@ export function eventFacts(node, objectRow) {
322
323
  const textBlocks = pyOr(get(pyOr(get(row, "Design"), {}), "TextBlocks"), get(pyOr(get(row, "design"), {}), "textBlocks"), []);
323
324
  return {
324
325
  eventMode: pyStr(pyOr(stated.eventMode ?? null, "polling")),
325
- byoaConnection: truthy(stated.byoaConnection ?? false),
326
- isWebhookUrlVisible: truthy(stated.isWebhookUrlVisible ?? false),
326
+ byoaConnection: pyTruthy(stated.byoaConnection ?? false),
327
+ isWebhookUrlVisible: pyTruthy(stated.isWebhookUrlVisible ?? false),
327
328
  textBlocks: pyIter(textBlocks).map(textBlockText),
328
329
  };
329
330
  }
@@ -338,7 +339,7 @@ export function eventFacts(node, objectRow) {
338
339
  * something plausible.
339
340
  */
340
341
  function textBlockText(block) {
341
- if (isRecord(block)) {
342
+ if (isPyDict(block)) {
342
343
  const text = pyOr(get(block, "Text"), get(block, "text"));
343
344
  if (typeof text === "string")
344
345
  return text;
@@ -432,7 +433,7 @@ export async function describeCuratedEvents(ctx, entries) {
432
433
  const operation = item(entry, "operation");
433
434
  const eventOperation = pyOr(get(operation, "eventOperation"), "");
434
435
  const objectName = pyOr(get(operation, "objectName"), "");
435
- if (!truthy(eventOperation) || !truthy(objectName))
436
+ if (!pyTruthy(eventOperation) || !pyTruthy(objectName))
436
437
  return [entry, null, null];
437
438
  const rows = await triggersObjectsCached(ctx, key, eventOperation);
438
439
  const obj = rows.find((r) => get(r, "Name") === objectName) ?? null;
@@ -440,7 +441,7 @@ export async function describeCuratedEvents(ctx, entries) {
440
441
  });
441
442
  let described = 0;
442
443
  for (const [entry, describe, obj] of results) {
443
- if (!truthy(describe))
444
+ if (!pyTruthy(describe))
444
445
  continue;
445
446
  applyEventSchema(entry, describe, obj);
446
447
  described += 1;
@@ -453,7 +454,7 @@ export async function describeCuratedEvents(ctx, entries) {
453
454
  * nodeType deliberately; the object rides on `operation.objectName`.
454
455
  */
455
456
  export function makeExpandedEventEntry(template, obj, describe) {
456
- const entry = deepCopy(template);
457
+ const entry = jsonClone(template);
457
458
  const objectName = pyOr(get(obj, "Name"), "");
458
459
  const objectDisplay = pyOr(get(obj, "DisplayName"), objectName);
459
460
  const operation = item(entry, "operation");
@@ -477,10 +478,10 @@ export async function expandGenericEvents(ctx, templates) {
477
478
  for (const template of templates) {
478
479
  const key = item(item(template, "connector"), "key");
479
480
  const operation = pyOr(get(item(template, "operation"), "eventOperation"), "");
480
- if (!truthy(operation))
481
+ if (!pyTruthy(operation))
481
482
  continue;
482
483
  for (const obj of await triggersObjectsCached(ctx, key, operation)) {
483
- if (truthy(get(obj, "Name")))
484
+ if (pyTruthy(get(obj, "Name")))
484
485
  work.push([template, key, operation, obj]);
485
486
  }
486
487
  }
@@ -508,7 +509,7 @@ export async function resolveConnectorEvents(ctx, entries) {
508
509
  // UNKNOWN is not "static": expand only where the connector positively
509
510
  // says its objects do not come from the connection.
510
511
  const discovery = get(pyOr(get(e, "connector"), {}), "discovery");
511
- if (!truthy(discovery)) {
512
+ if (!pyTruthy(discovery)) {
512
513
  ctx.reporter.warn(` WARN: ${item(e, "nodeType")} not expanded — no connector discovery metadata, so whether its ` +
513
514
  "objects come from the connection is unknown");
514
515
  }
@@ -20,7 +20,8 @@ import { mapConcurrent } from "../concurrency.js";
20
20
  import { messageOf } from "../errors.js";
21
21
  import { isHttpPassthrough, normalizeFieldKeys, pick } from "./canonical.js";
22
22
  import { isDescribeObject, isDescribeOperation, isResourcesList } from "./context.js";
23
- import { deepCopy, get, item, pyIter, pyOr, truthy } from "./py.js";
23
+ import { get, item, pyIter, pyOr, pyTruthy } from "./py.js";
24
+ import { jsonClone } from "../../core/json-file.js";
24
25
  /**
25
26
  * `_generic_templates_by_op`: bucket Generic CRUD entries by IS operation
26
27
  * name per connector. Generic CRUD actions carry an `operation` verb
@@ -37,7 +38,7 @@ export function genericTemplatesByOp(genericEntries) {
37
38
  if (!opName)
38
39
  continue;
39
40
  const key = item(item(e, "connector"), "key");
40
- if (!truthy(key))
41
+ if (!pyTruthy(key))
41
42
  continue;
42
43
  let byOp = out.get(key);
43
44
  if (!byOp) {
@@ -56,7 +57,7 @@ export function genericTemplatesByOp(genericEntries) {
56
57
  * discovery flag is copied, never inferred from field counts.
57
58
  */
58
59
  export function makeExpandedEntry(template, obj, op, opData) {
59
- const entry = deepCopy(template);
60
+ const entry = jsonClone(template);
60
61
  const templateOperation = item(template, "operation");
61
62
  const objectName = pyOr(get(obj, "Name"), "");
62
63
  const objectDisplay = pyOr(get(obj, "DisplayName"), objectName);
@@ -75,16 +76,16 @@ export function makeExpandedEntry(template, obj, op, opData) {
75
76
  operation.sourceNodeType = item(template, "nodeType");
76
77
  const display = item(entry, "display");
77
78
  display.label = pyStrip(`${pyStr(opName)} ${pyStr(objectDisplay)}`);
78
- if (truthy(opDescription))
79
+ if (pyTruthy(opDescription))
79
80
  display.description = opDescription;
80
81
  // Pull schema + path template from the per-operation describe payload.
81
- if (truthy(opData)) {
82
+ if (pyTruthy(opData)) {
82
83
  const opInner = pyOr(pick(opData, "Operation", "operation"), {});
83
84
  const opInnerPath = pick(opInner, "Path", "path");
84
- if (truthy(opInnerPath))
85
+ if (pyTruthy(opInnerPath))
85
86
  operation.pathTemplate = opInnerPath;
86
87
  const params = pyOr(pick(opData, "Parameters", "parameters"), []);
87
- if (truthy(params))
88
+ if (pyTruthy(params))
88
89
  operation.parameters = normalizeFieldKeys(params);
89
90
  const requestFields = pyOr(pick(opData, "RequestFields", "requestFields"), []);
90
91
  const responseFields = pyOr(pick(opData, "ResponseFields", "responseFields"), []);
@@ -102,7 +103,7 @@ export function uniqueByName(objects) {
102
103
  const unique = new Map();
103
104
  for (const obj of pyIter(pyOr(objects, []))) {
104
105
  const objectName = get(obj, "Name");
105
- if (truthy(objectName) && !unique.has(objectName))
106
+ if (pyTruthy(objectName) && !unique.has(objectName))
106
107
  unique.set(objectName, obj);
107
108
  }
108
109
  return unique;
@@ -117,7 +118,7 @@ export async function listGenericObjects(ctx, connectorKey, templatesByOp, objec
117
118
  const operationFilter = templatesByOp.size === 1 ? [...templatesByOp.keys()][0] : null;
118
119
  const objects = await isResourcesList(ctx, connectorKey, { operation: operationFilter });
119
120
  let unique = uniqueByName(objects);
120
- if (truthy(objectNames) && !objectNames.every((name) => unique.has(name))) {
121
+ if (pyTruthy(objectNames) && !objectNames.every((name) => unique.has(name))) {
121
122
  const refreshed = uniqueByName(await isResourcesList(ctx, connectorKey, { operation: operationFilter, refresh: true }));
122
123
  // Keep the cached answer when the refresh failed outright, so a network
123
124
  // blip does not turn a working lookup into "not found".
@@ -141,24 +142,24 @@ export async function expandGenericForConnector(ctx, connectorKey, templatesByOp
141
142
  const out = [];
142
143
  for (const obj of objects) {
143
144
  const objectName = get(obj, "Name");
144
- if (!truthy(objectName))
145
+ if (!pyTruthy(objectName))
145
146
  continue;
146
147
  if (objectNames !== undefined && !objectNames.includes(objectName))
147
148
  continue;
148
149
  const objData = await isDescribeObject(ctx, connectorKey, objectName);
149
- if (!truthy(objData))
150
+ if (!pyTruthy(objData))
150
151
  continue;
151
152
  const available = pyOr(pick(objData, "AvailableOperations", "availableOperations"), []);
152
153
  for (const op of pyIter(available)) {
153
154
  const opName = pick(op, "Name", "name");
154
- if (!truthy(opName))
155
+ if (!pyTruthy(opName))
155
156
  continue;
156
157
  const pair = JSON.stringify([objectName, opName]);
157
158
  if (seenPairs.has(pair))
158
159
  continue;
159
160
  seenPairs.add(pair);
160
161
  const template = templatesByOp.get(opName);
161
- if (!truthy(template))
162
+ if (!pyTruthy(template))
162
163
  continue;
163
164
  const opData = await isDescribeOperation(ctx, connectorKey, objectName, opName);
164
165
  out.push(makeExpandedEntry(template, obj, op, opData));
@@ -172,7 +173,7 @@ export async function expandGenericForConnector(ctx, connectorKey, templatesByOp
172
173
  */
173
174
  export function objectsComeFromTheConnection(entry) {
174
175
  const discovery = pyOr(get(pyOr(get(entry, "connector"), {}), "discovery"), {});
175
- return truthy(pyOr(get(discovery, "hasObjectDiscovery"), get(discovery, "hasCustomObjectDiscovery")));
176
+ return pyTruthy(pyOr(get(discovery, "hasObjectDiscovery"), get(discovery, "hasCustomObjectDiscovery")));
176
177
  }
177
178
  /**
178
179
  * `as_discovery_entry`: recast a Generic CRUD template as the discovery entry
@@ -184,7 +185,7 @@ export function asDiscoveryEntry(entry) {
184
185
  operation.objectFromDiscovery = true;
185
186
  operation.objectName = "";
186
187
  const name = pyOr(get(operation, "name"), "");
187
- if (truthy(name)) {
188
+ if (pyTruthy(name)) {
188
189
  item(entry, "display").label = name === "List" ? "List Records" : `${pyStr(name)} Record`;
189
190
  }
190
191
  return entry;
@@ -197,7 +198,7 @@ export function asDiscoveryEntry(entry) {
197
198
  export function disambiguateDiscoveryLabels(entries) {
198
199
  const groups = new Map();
199
200
  for (const entry of entries) {
200
- if (!truthy(get(pyOr(get(entry, "operation"), {}), "objectFromDiscovery")))
201
+ if (!pyTruthy(get(pyOr(get(entry, "operation"), {}), "objectFromDiscovery")))
201
202
  continue;
202
203
  const key = JSON.stringify([
203
204
  get(pyOr(get(entry, "connector"), {}), "key"),
@@ -314,16 +315,16 @@ export async function expandAllGenerics(ctx, entries) {
314
315
  objData = undefined;
315
316
  ctx.reporter.warn(` WARN: object describe failed for ${k}/${pyStr(item(obj, "Name"))}: ${messageOf(exc)}`);
316
317
  }
317
- if (truthy(objData)) {
318
+ if (pyTruthy(objData)) {
318
319
  const available = pyOr(pick(objData, "AvailableOperations", "availableOperations"), []);
319
320
  const seenOpNames = new Set();
320
321
  for (const op of pyIter(available)) {
321
322
  const opName = pick(op, "Name", "name");
322
- if (!truthy(opName) || seenOpNames.has(opName))
323
+ if (!pyTruthy(opName) || seenOpNames.has(opName))
323
324
  continue;
324
325
  seenOpNames.add(opName);
325
326
  const template = templates.get(k).get(opName);
326
- if (truthy(template))
327
+ if (pyTruthy(template))
327
328
  found.push([k, obj, op, template]);
328
329
  }
329
330
  }
@@ -356,7 +357,7 @@ export async function expandAllGenerics(ctx, entries) {
356
357
  return entry;
357
358
  });
358
359
  expanded.push(...results);
359
- const schemaDynamic = expanded.filter((e) => truthy(get(item(e, "runtime"), "requiresConnectionForSchema"))).length;
360
+ const schemaDynamic = expanded.filter((e) => pyTruthy(get(item(e, "runtime"), "requiresConnectionForSchema"))).length;
360
361
  ctx.reporter.info(` expanded ${expanded.length} entries from ${genericCrud.length} Generic CRUD actions`);
361
362
  ctx.reporter.info(` schema-dynamic among expanded: ${schemaDynamic}/${expanded.length}`);
362
363
  return [curatedAndPassthrough, expanded];
@@ -12,7 +12,7 @@ import { sortedByKeys } from "../compat/compare.js";
12
12
  import { pyStr } from "../compat/pystr.js";
13
13
  import { messageOf } from "../errors.js";
14
14
  import { SCHEMA_VERSION, actionIdFromNodeType } from "./canonical.js";
15
- import { get, isRecord, item, pyEquals, pyIter, pyOr, truthy } from "./py.js";
15
+ import { get, isPyDict, item, pyEquals, pyIter, pyOr, pyTruthy } from "./py.js";
16
16
  export { actionIdFromNodeType };
17
17
  /** `_safe_path_segment`: sanitize a string for use as one path segment. */
18
18
  export function safePathSegment(s) {
@@ -42,7 +42,7 @@ export function entryRelpath(entry) {
42
42
  return `${connectorKey}/generic/${obj}/${op}@${version}`;
43
43
  }
44
44
  const actionId = actionIdFromNodeType(item(entry, "nodeType"));
45
- if (get(operation, "activityType") === "GenericTrigger" && truthy(get(operation, "objectName"))) {
45
+ if (get(operation, "activityType") === "GenericTrigger" && pyTruthy(get(operation, "objectName"))) {
46
46
  const obj = safePathSegment(pyStr(item(operation, "objectName")));
47
47
  return `${connectorKey}/events/${obj}/${actionId}@${version}`;
48
48
  }
@@ -55,7 +55,7 @@ export function entryRelpath(entry) {
55
55
  * connector definition when absent (`setdefault`: a present `null` stays).
56
56
  */
57
57
  export function buildV1Definition(nodeData) {
58
- if (!isRecord(nodeData))
58
+ if (!isPyDict(nodeData))
59
59
  throw new TypeError(`'${nodeData === null ? "NoneType" : typeof nodeData}' object has no attribute 'items'`);
60
60
  const keep = {};
61
61
  for (const [k, v] of Object.entries(nodeData)) {
@@ -150,18 +150,18 @@ export function planLibrary(entries, loadNode, opts = {}) {
150
150
  let v1def;
151
151
  try {
152
152
  const nodeData = typeof lookupKey === "string" ? loadNode(lookupKey) : undefined;
153
- if (truthy(nodeData)) {
153
+ if (pyTruthy(nodeData)) {
154
154
  v1def = buildV1Definition(nodeData);
155
155
  // The entry's fields are the node's own plus whatever the parameters
156
156
  // filled in — a superset of a verbatim projection — so prefer them
157
157
  // whenever there are any. An expanded Generic node stamps even an
158
158
  // empty list: its template's fields belong to the dispatch node.
159
159
  const resolvedFields = pyOr(get(get(entry, "inputSchema", {}), "fields"), []);
160
- if (expanded || truthy(resolvedFields)) {
160
+ if (expanded || pyTruthy(resolvedFields)) {
161
161
  if (!("inputDefinition" in v1def))
162
162
  v1def.inputDefinition = {};
163
163
  const inputDefinition = v1def.inputDefinition;
164
- if (!isRecord(inputDefinition)) {
164
+ if (!isPyDict(inputDefinition)) {
165
165
  throw new TypeError(`'${inputDefinition === null ? "NoneType" : typeof inputDefinition}' object does not support item assignment`);
166
166
  }
167
167
  // Stamp onto a COPY. `buildV1Definition` is shallow, so this block is
@@ -177,7 +177,7 @@ export function planLibrary(entries, loadNode, opts = {}) {
177
177
  // A per-object EVENT variant carries its own subscription vocabulary,
178
178
  // and this sidecar is where the SDK reads one.
179
179
  const eventSchema = pyOr(get(entry, "eventSchema"), {});
180
- if (truthy(eventSchema)) {
180
+ if (pyTruthy(eventSchema)) {
181
181
  v1def.eventParameters = { fields: pyOr(get(eventSchema, "eventParameters"), []) };
182
182
  v1def.filterFields = { fields: pyOr(get(eventSchema, "filterFields"), []) };
183
183
  for (const fact of ["eventMode", "byoaConnection", "isWebhookUrlVisible"]) {
@@ -207,7 +207,7 @@ export function planLibrary(entries, loadNode, opts = {}) {
207
207
  };
208
208
  // `registry search` reads only this index; emitted only when set, so every
209
209
  // other row stays byte-identical.
210
- if (truthy(get(operation, "objectFromDiscovery")))
210
+ if (pyTruthy(get(operation, "objectFromDiscovery")))
211
211
  row.objectFromDiscovery = true;
212
212
  index.push(row);
213
213
  }
@@ -8,13 +8,10 @@
8
8
  * to `dict.get`, and two dicts compare equal in any key order. The port spells
9
9
  * each rule once here so every call site can read like the line it replaces.
10
10
  */
11
+ export { pyTruthy, pyOr } from "../compat/pyvalue.js";
11
12
  export type Dict = Record<string, unknown>;
12
- /** A plain JSON object: what `json.load` calls a dict. */
13
- export declare function isRecord(value: unknown): value is Dict;
14
- /** `bool(value)` for a JSON-shaped value. */
15
- export declare function truthy(value: unknown): boolean;
16
- /** `a or b or …`: the first truthy operand, else the last one. */
17
- export declare function pyOr<T>(...values: T[]): T;
13
+ /** A plain JSON object: what `json.load` calls a dict (Map excluded — unlike core/node-envelope.ts's isRecord). */
14
+ export declare function isPyDict(value: unknown): value is Dict;
18
15
  /**
19
16
  * `d.get(key, default)`. A present key answers its value even when that value
20
17
  * is `null`; an absent key answers `dflt`, which is `null` (Python's `None`)
@@ -33,10 +30,6 @@ export declare function pyIter(value: unknown): unknown[];
33
30
  * order; lists element by element; `True == 1` as in Python.
34
31
  */
35
32
  export declare function pyEquals(a: unknown, b: unknown): boolean;
36
- /** `json.loads(json.dumps(value))`: the generator's deep copy, JSON-shaped values only. */
37
- export declare function deepCopy<T>(value: T): T;
38
- /** `repr(str)`: single quotes unless the text holds one and no double quote. */
39
- export declare function pyReprStr(s: string): string;
40
33
  /** `repr(list_of_str)`: `['a', 'b']`. */
41
34
  export declare function pyReprStrList(items: readonly string[]): string;
42
35
  /** `s[start:end]` by code point. */
@@ -8,31 +8,12 @@
8
8
  * to `dict.get`, and two dicts compare equal in any key order. The port spells
9
9
  * each rule once here so every call site can read like the line it replaces.
10
10
  */
11
- /** A plain JSON object: what `json.load` calls a dict. */
12
- export function isRecord(value) {
11
+ import { pyReprString } from "../compat/pystr.js";
12
+ export { pyTruthy, pyOr } from "../compat/pyvalue.js";
13
+ /** A plain JSON object: what `json.load` calls a dict (Map excluded — unlike core/node-envelope.ts's isRecord). */
14
+ export function isPyDict(value) {
13
15
  return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Map);
14
16
  }
15
- /** `bool(value)` for a JSON-shaped value. */
16
- export function truthy(value) {
17
- if (value === null || value === undefined || value === false || value === "")
18
- return false;
19
- if (typeof value === "number")
20
- return value !== 0;
21
- if (Array.isArray(value))
22
- return value.length > 0;
23
- if (value instanceof Map)
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 (truthy(value))
33
- return value;
34
- return values[values.length - 1];
35
- }
36
17
  /**
37
18
  * Does the dict itself hold `key`? — `key in d` for a Python dict.
38
19
  *
@@ -58,7 +39,7 @@ function hasKey(d, key) {
58
39
  * Calling `.get` on a non-dict raises in Python, and does here.
59
40
  */
60
41
  export function get(d, key, dflt = null) {
61
- if (!isRecord(d)) {
42
+ if (!isPyDict(d)) {
62
43
  throw new TypeError(`'${pyTypeName(d)}' object has no attribute 'get'`);
63
44
  }
64
45
  if (!hasKey(d, key))
@@ -68,7 +49,7 @@ export function get(d, key, dflt = null) {
68
49
  }
69
50
  /** `d[key]`: raises on a missing key the way Python's `KeyError` does. */
70
51
  export function item(d, key) {
71
- if (!isRecord(d)) {
52
+ if (!isPyDict(d)) {
72
53
  throw new TypeError(`'${pyTypeName(d)}' object is not subscriptable`);
73
54
  }
74
55
  if (!hasKey(d, key) || d[key] === undefined)
@@ -83,7 +64,7 @@ export function pyIter(value) {
83
64
  return [...value];
84
65
  if (value instanceof Map)
85
66
  return [...value.keys()];
86
- if (isRecord(value))
67
+ if (isPyDict(value))
87
68
  return Object.keys(value);
88
69
  throw new TypeError(`'${pyTypeName(value)}' object is not iterable`);
89
70
  }
@@ -116,7 +97,7 @@ export function pyEquals(a, b) {
116
97
  return false;
117
98
  return a.every((item, i) => pyEquals(item, b[i]));
118
99
  }
119
- if (isRecord(a) && isRecord(b)) {
100
+ if (isPyDict(a) && isPyDict(b)) {
120
101
  const keysA = Object.keys(a).filter((k) => a[k] !== undefined);
121
102
  const keysB = Object.keys(b).filter((k) => b[k] !== undefined);
122
103
  if (keysA.length !== keysB.length)
@@ -128,33 +109,9 @@ export function pyEquals(a, b) {
128
109
  }
129
110
  return false;
130
111
  }
131
- /** `json.loads(json.dumps(value))`: the generator's deep copy, JSON-shaped values only. */
132
- export function deepCopy(value) {
133
- return JSON.parse(JSON.stringify(value));
134
- }
135
- /** `repr(str)`: single quotes unless the text holds one and no double quote. */
136
- export function pyReprStr(s) {
137
- const quote = s.includes("'") && !s.includes('"') ? '"' : "'";
138
- let out = quote;
139
- for (const ch of s) {
140
- if (ch === "\\")
141
- out += "\\\\";
142
- else if (ch === quote)
143
- out += `\\${quote}`;
144
- else if (ch === "\n")
145
- out += "\\n";
146
- else if (ch === "\r")
147
- out += "\\r";
148
- else if (ch === "\t")
149
- out += "\\t";
150
- else
151
- out += ch;
152
- }
153
- return out + quote;
154
- }
155
112
  /** `repr(list_of_str)`: `['a', 'b']`. */
156
113
  export function pyReprStrList(items) {
157
- return `[${items.map(pyReprStr).join(", ")}]`;
114
+ return `[${items.map(pyReprString).join(", ")}]`;
158
115
  }
159
116
  /** `s[start:end]` by code point. */
160
117
  export function codePointSlice(s, start, end) {