@uipath/maestro-builder-sdk 6.4.0 → 6.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/dist/cli/commands/registry.js +68 -12
  2. package/dist/registry/archive/entry-policy.d.ts +36 -0
  3. package/dist/registry/archive/entry-policy.js +60 -0
  4. package/dist/registry/archive/extract.d.ts +31 -0
  5. package/dist/registry/archive/extract.js +92 -0
  6. package/dist/registry/archive/zip-reader.d.ts +38 -0
  7. package/dist/registry/archive/zip-reader.js +256 -0
  8. package/dist/registry/codecs/pyjson.d.ts +62 -0
  9. package/dist/registry/codecs/pyjson.js +208 -0
  10. package/dist/registry/compat/compare.d.ts +18 -0
  11. package/dist/registry/compat/compare.js +32 -0
  12. package/dist/registry/compat/paths.d.ts +4 -0
  13. package/dist/registry/compat/paths.js +17 -0
  14. package/dist/registry/compat/pystr.d.ts +50 -0
  15. package/dist/registry/compat/pystr.js +221 -0
  16. package/dist/registry/concurrency.d.ts +10 -0
  17. package/dist/registry/concurrency.js +34 -0
  18. package/dist/registry/curated-overlay.d.ts +36 -4
  19. package/dist/registry/curated-overlay.js +87 -14
  20. package/dist/registry/errors.d.ts +26 -0
  21. package/dist/registry/errors.js +38 -0
  22. package/dist/registry/extractor.d.ts +9 -10
  23. package/dist/registry/extractor.js +5 -15
  24. package/dist/registry/files.d.ts +26 -0
  25. package/dist/registry/files.js +160 -0
  26. package/dist/registry/generators/descriptors.d.ts +81 -0
  27. package/dist/registry/generators/descriptors.js +405 -0
  28. package/dist/registry/generators/fields.d.ts +46 -0
  29. package/dist/registry/generators/fields.js +111 -0
  30. package/dist/registry/generators/markdown/common.d.ts +21 -0
  31. package/dist/registry/generators/markdown/common.js +55 -0
  32. package/dist/registry/generators/markdown/index.d.ts +33 -0
  33. package/dist/registry/generators/markdown/index.js +37 -0
  34. package/dist/registry/generators/markdown/page.d.ts +8 -0
  35. package/dist/registry/generators/markdown/page.js +52 -0
  36. package/dist/registry/generators/markdown/pyvalue.d.ts +34 -0
  37. package/dist/registry/generators/markdown/pyvalue.js +106 -0
  38. package/dist/registry/generators/markdown/sections/choosing-object.d.ts +2 -0
  39. package/dist/registry/generators/markdown/sections/choosing-object.js +52 -0
  40. package/dist/registry/generators/markdown/sections/example.d.ts +22 -0
  41. package/dist/registry/generators/markdown/sections/example.js +295 -0
  42. package/dist/registry/generators/markdown/sections/filtering.d.ts +2 -0
  43. package/dist/registry/generators/markdown/sections/filtering.js +76 -0
  44. package/dist/registry/generators/markdown/sections/header.d.ts +2 -0
  45. package/dist/registry/generators/markdown/sections/header.js +37 -0
  46. package/dist/registry/generators/markdown/sections/inputs.d.ts +30 -0
  47. package/dist/registry/generators/markdown/sections/inputs.js +151 -0
  48. package/dist/registry/generators/markdown/sections/lookups.d.ts +12 -0
  49. package/dist/registry/generators/markdown/sections/lookups.js +150 -0
  50. package/dist/registry/generators/markdown/sections/outputs.d.ts +3 -0
  51. package/dist/registry/generators/markdown/sections/outputs.js +9 -0
  52. package/dist/registry/generators/markdown/sections/resolving-fields.d.ts +2 -0
  53. package/dist/registry/generators/markdown/sections/resolving-fields.js +139 -0
  54. package/dist/registry/generators/markdown/sections/schema-dynamic.d.ts +2 -0
  55. package/dist/registry/generators/markdown/sections/schema-dynamic.js +86 -0
  56. package/dist/registry/generators/materialize.d.ts +30 -0
  57. package/dist/registry/generators/materialize.js +132 -0
  58. package/dist/registry/generators/md-symbols.d.ts +22 -0
  59. package/dist/registry/generators/md-symbols.js +58 -0
  60. package/dist/registry/generators/triggers.d.ts +15 -0
  61. package/dist/registry/generators/triggers.js +85 -0
  62. package/dist/registry/generators/type-registry.d.ts +50 -0
  63. package/dist/registry/generators/type-registry.js +154 -0
  64. package/dist/registry/generators/type-tree.d.ts +65 -0
  65. package/dist/registry/generators/type-tree.js +179 -0
  66. package/dist/registry/library-build/build.d.ts +57 -0
  67. package/dist/registry/library-build/build.js +286 -0
  68. package/dist/registry/library-build/canonical.d.ts +119 -0
  69. package/dist/registry/library-build/canonical.js +512 -0
  70. package/dist/registry/library-build/cli.d.ts +45 -0
  71. package/dist/registry/library-build/cli.js +182 -0
  72. package/dist/registry/library-build/context.d.ts +107 -0
  73. package/dist/registry/library-build/context.js +267 -0
  74. package/dist/registry/library-build/enrich.d.ts +36 -0
  75. package/dist/registry/library-build/enrich.js +158 -0
  76. package/dist/registry/library-build/events.d.ts +108 -0
  77. package/dist/registry/library-build/events.js +476 -0
  78. package/dist/registry/library-build/generic-expand.d.ts +77 -0
  79. package/dist/registry/library-build/generic-expand.js +363 -0
  80. package/dist/registry/library-build/plan.d.ts +71 -0
  81. package/dist/registry/library-build/plan.js +270 -0
  82. package/dist/registry/library-build/py.d.ts +45 -0
  83. package/dist/registry/library-build/py.js +167 -0
  84. package/dist/registry/library-build/schema-action.d.ts +38 -0
  85. package/dist/registry/library-build/schema-action.js +107 -0
  86. package/dist/registry/library-build/write.d.ts +6 -0
  87. package/dist/registry/library-build/write.js +26 -0
  88. package/dist/registry/library-reader.d.ts +20 -0
  89. package/dist/registry/library-reader.js +50 -0
  90. package/dist/registry/lookup-specs.d.ts +55 -0
  91. package/dist/registry/lookup-specs.js +140 -0
  92. package/dist/registry/materialize-cli.js +28 -14
  93. package/dist/registry/materializer.d.ts +25 -3
  94. package/dist/registry/materializer.js +45 -29
  95. package/dist/registry/model/library.d.ts +254 -0
  96. package/dist/registry/model/library.js +23 -0
  97. package/dist/registry/naming.d.ts +54 -0
  98. package/dist/registry/naming.js +142 -0
  99. package/dist/registry/orphans.d.ts +4 -0
  100. package/dist/registry/orphans.js +86 -0
  101. package/dist/registry/prepare/actions.d.ts +26 -0
  102. package/dist/registry/prepare/actions.js +94 -0
  103. package/dist/registry/prepare/connections.d.ts +181 -0
  104. package/dist/registry/prepare/connections.js +399 -0
  105. package/dist/registry/prepare/event.d.ts +96 -0
  106. package/dist/registry/prepare/event.js +268 -0
  107. package/dist/registry/prepare/handoff.d.ts +21 -0
  108. package/dist/registry/prepare/handoff.js +91 -0
  109. package/dist/registry/prepare/ledger.d.ts +104 -0
  110. package/dist/registry/prepare/ledger.js +216 -0
  111. package/dist/registry/prepare/messages.d.ts +190 -0
  112. package/dist/registry/prepare/messages.js +472 -0
  113. package/dist/registry/prepare/objects.d.ts +130 -0
  114. package/dist/registry/prepare/objects.js +318 -0
  115. package/dist/registry/prepare/overlay.d.ts +149 -0
  116. package/dist/registry/prepare/overlay.js +487 -0
  117. package/dist/registry/prepare/parents.d.ts +41 -0
  118. package/dist/registry/prepare/parents.js +122 -0
  119. package/dist/registry/prepare/pipeline.d.ts +91 -0
  120. package/dist/registry/prepare/pipeline.js +376 -0
  121. package/dist/registry/prepare/regenerate.d.ts +9 -0
  122. package/dist/registry/prepare/regenerate.js +50 -0
  123. package/dist/registry/prepare/resolve.d.ts +199 -0
  124. package/dist/registry/prepare/resolve.js +470 -0
  125. package/dist/registry/prepare/retry.d.ts +83 -0
  126. package/dist/registry/prepare/retry.js +109 -0
  127. package/dist/registry/prepare/source-import.d.ts +54 -0
  128. package/dist/registry/prepare/source-import.js +192 -0
  129. package/dist/registry/prepare/support.d.ts +41 -0
  130. package/dist/registry/prepare/support.js +178 -0
  131. package/dist/registry/prepare-cli.d.ts +16 -5
  132. package/dist/registry/prepare-cli.js +60 -53
  133. package/dist/registry/pull-cli.js +8 -7
  134. package/dist/registry/reporter.d.ts +26 -0
  135. package/dist/registry/reporter.js +41 -0
  136. package/dist/registry/uip/client.d.ts +99 -0
  137. package/dist/registry/uip/client.js +226 -0
  138. package/dist/registry/uip/commands.d.ts +40 -0
  139. package/dist/registry/uip/commands.js +80 -0
  140. package/dist/registry/uip/envelope.d.ts +37 -0
  141. package/dist/registry/uip/envelope.js +84 -0
  142. package/dist/registry/uip/response-cache.d.ts +28 -0
  143. package/dist/registry/uip/response-cache.js +57 -0
  144. package/lib/README.md +10 -8
  145. package/package.json +3 -3
  146. package/dist/generators/_connections.py +0 -331
  147. package/dist/generators/_events.py +0 -355
  148. package/dist/generators/_lookups.py +0 -114
  149. package/dist/generators/_naming.py +0 -185
  150. package/dist/generators/_objects.py +0 -299
  151. package/dist/generators/_resolve.py +0 -319
  152. package/dist/generators/convert_library_to_md.py +0 -1441
  153. package/dist/generators/extract_library.py +0 -125
  154. package/dist/generators/generate_connectors_ts.py +0 -936
  155. package/dist/generators/generate_library_json.py +0 -2527
  156. package/dist/generators/generate_triggers_ts.py +0 -110
  157. package/dist/generators/materialize_registry.py +0 -155
  158. package/dist/generators/prepare_connector.py +0 -1860
  159. package/dist/registry/python.d.ts +0 -26
  160. package/dist/registry/python.js +0 -46
@@ -0,0 +1,476 @@
1
+ /**
2
+ * Connector EVENTS — triggers and mid-flow waits — from `_events.py` and the
3
+ * event half of `generate_library_json.py`.
4
+ *
5
+ * Events have their own twin pair of CLI calls: `uip is triggers objects`
6
+ * answers PER EVENT OBJECT (the catalog a generic event ranges over, and per
7
+ * object the `Parameters` it contributes), `uip is triggers describe` answers
8
+ * PER (event, object) (`EventParameters`, `FilterFields`, `OutputFields`).
9
+ * The complete input set is the UNION of the two, required if EITHER side says
10
+ * required — a field can appear in one and not the other, so neither call alone
11
+ * is trusted.
12
+ *
13
+ * Key order in every field record is the Python's dict order, because these
14
+ * fields are written into the library and the sidecar with `json.dump`.
15
+ */
16
+ import { sortedStrings } from "../compat/compare.js";
17
+ import { lowerFirst, pyStr, pyStrip, startsWithUpper } from "../compat/pystr.js";
18
+ import { mapConcurrent } from "../concurrency.js";
19
+ import { Refusal } from "../errors.js";
20
+ import { isTriggersDescribe, isTriggersObjects } from "../uip/commands.js";
21
+ import { safeCacheName } from "../uip/response-cache.js";
22
+ import { schemaCacheScope, writeCache } from "./context.js";
23
+ import { objectsComeFromTheConnection } from "./generic-expand.js";
24
+ import { codePointSlice, deepCopy, get, isRecord, item, pyIter, pyOr, truthy } from "./py.js";
25
+ /**
26
+ * `Parameters[].type` on a `triggers objects` row routes the value into one of
27
+ * three `--detail` buckets at emit time. Everything unrecognized is an event
28
+ * parameter — the designer's own fallback. A `Map`, not an object, because
29
+ * `_BUCKETS.get(…)` on a dict answers `None` for `"constructor"` where a plain
30
+ * object would answer `Object`'s constructor function.
31
+ */
32
+ const BUCKETS = new Map([
33
+ ["query", "query"],
34
+ ["path", "path"],
35
+ ]);
36
+ /** A trigger metadata call failed, with the reason and the way forward. */
37
+ export class EventDiscoveryError extends Refusal {
38
+ refused;
39
+ constructor(message,
40
+ /**
41
+ * True when the call was never made — the client refused it (`--offline`).
42
+ * Such a failure is not an answer from the tenant and must not be cached
43
+ * as one; see {@link triggersObjectsCached}.
44
+ */
45
+ refused = false) {
46
+ super(message, "EVENT_DISCOVERY");
47
+ this.refused = refused;
48
+ this.name = "EventDiscoveryError";
49
+ }
50
+ }
51
+ /** `str(value)` for a message, tolerating the shapes `pyStr` refuses. */
52
+ function pyFormat(value) {
53
+ try {
54
+ return pyStr(value);
55
+ }
56
+ catch {
57
+ return JSON.stringify(value);
58
+ }
59
+ }
60
+ /** `_run_json`: the whole of stdout as one `Result: Success` envelope, or an {@link EventDiscoveryError}. */
61
+ async function runJson(client, argv, timeoutMs) {
62
+ const result = await client.run(argv, timeoutMs === undefined ? undefined : { timeoutMs });
63
+ const head = `\`${argv.slice(0, 5).join(" ")} …\``;
64
+ let payload;
65
+ try {
66
+ payload = JSON.parse(result.stdout);
67
+ }
68
+ catch {
69
+ const text = codePointSlice(pyStrip(result.stderr || result.stdout), 0, 300);
70
+ throw new EventDiscoveryError(`${head} did not answer JSON: ${text || "no output"}`, result.refused === true);
71
+ }
72
+ if (get(payload, "Result") !== "Success") {
73
+ // `refused` is carried here too, although the refusing client in this tree
74
+ // answers with empty stdout and so always fails the parse above. The rule
75
+ // the cache depends on is "a call that never ran is never remembered", and
76
+ // that rule should not rest on what a client happens to put on stdout.
77
+ throw new EventDiscoveryError(`${head} failed: ${pyFormat(pyOr(get(payload, "Message"), get(payload, "Result")))}`, result.refused === true);
78
+ }
79
+ return payload;
80
+ }
81
+ /**
82
+ * `triggers_objects`: the event objects for `(connector, operation)`, PascalCase
83
+ * rows. `connectionId` is optional; a connector whose objects do not come from
84
+ * the connection answers identically either way.
85
+ */
86
+ export async function triggersObjects(client, key, operation, connectionId, timeoutMs) {
87
+ const payload = await runJson(client, isTriggersObjects(key, operation, connectionId), timeoutMs);
88
+ let data = get(payload, "Data");
89
+ if (isRecord(data))
90
+ data = get(data, "items");
91
+ return Array.isArray(data) ? data : [];
92
+ }
93
+ /** `triggers_describe`: `EventParameters` / `FilterFields` / `OutputFields` for one object. */
94
+ export async function triggersDescribe(client, key, operation, objectName, connectionId, timeoutMs) {
95
+ const payload = await runJson(client, isTriggersDescribe(key, operation, objectName, connectionId), timeoutMs);
96
+ return pyOr(get(payload, "Data"), {});
97
+ }
98
+ /** `k[0].lower() + k[1:] if k[:1].isupper() else k`. */
99
+ function lowerKey(k) {
100
+ return startsWithUpper(k) ? lowerFirst(k) : k;
101
+ }
102
+ /** `_camelize`: lower-case the first letter of every key, recursively. */
103
+ export function camelize(value) {
104
+ if (isRecord(value)) {
105
+ const out = {};
106
+ for (const [k, v] of Object.entries(value))
107
+ out[lowerKey(k)] = camelize(v);
108
+ return out;
109
+ }
110
+ if (Array.isArray(value))
111
+ return value.map(camelize);
112
+ return value;
113
+ }
114
+ /** The top-level-only key lowering `camel_fields` and `normalize_bucket_fields` do inline. */
115
+ function lowerTop(f) {
116
+ const low = {};
117
+ for (const [k, v] of Object.entries(f))
118
+ low[lowerKey(k)] = v;
119
+ return low;
120
+ }
121
+ /** `_is_hidden`: a hidden parameter that is safe to DROP — hidden and optional. */
122
+ export function isHidden(low) {
123
+ const raw = pyOr(get(low, "design"), {});
124
+ if (!isRecord(raw))
125
+ return false;
126
+ const design = camelize(raw);
127
+ const hidden = truthy(pyOr(get(design, "isHidden"), get(design, "hidden")));
128
+ return hidden && !truthy(get(low, "required"));
129
+ }
130
+ /** `_keep_hidden_marker`: flag a kept parameter the designer hides (necessarily a required one). */
131
+ export function keepHiddenMarker(low, field) {
132
+ const design = camelize(pyOr(get(low, "design"), {}));
133
+ if (isRecord(design) && truthy(pyOr(get(design, "isHidden"), get(design, "hidden"))))
134
+ field.hidden = true;
135
+ return field;
136
+ }
137
+ /** `(f.get("sortOrder") is None, f.get("sortOrder") or 0)`. */
138
+ function sortOrderKey(f) {
139
+ const so = get(f, "sortOrder");
140
+ return [so === null ? 1 : 0, truthy(so) ? so : 0];
141
+ }
142
+ /**
143
+ * `_with_cascade`: mark each parameter that another one has to resolve before
144
+ * it, then order by the designer's `sortOrder` (unset last), stably.
145
+ */
146
+ export function withCascade(fields) {
147
+ const names = new Set(fields.map((f) => get(f, "name")));
148
+ for (const field of fields) {
149
+ const reference = pyOr(get(field, "reference"), {});
150
+ const path = pyStr(pyOr(get(reference, "path"), ""));
151
+ const explicit = pyIter(pyOr(get(reference, "dependsOn"), []));
152
+ const own = get(field, "name");
153
+ const depends = sortedStrings([...names]
154
+ .filter((other) => truthy(other) && other !== own && (path.includes(`{${pyStr(other)}}`) || explicit.includes(other)))
155
+ .map((other) => pyStr(other)));
156
+ if (depends.length > 0) {
157
+ field.dependsOn = depends;
158
+ if (!("dependsOn" in reference))
159
+ reference.dependsOn = depends;
160
+ }
161
+ }
162
+ fields.sort((a, b) => {
163
+ const ka = sortOrderKey(a);
164
+ const kb = sortOrderKey(b);
165
+ if (ka[0] !== kb[0])
166
+ return ka[0] - kb[0];
167
+ const x = ka[1];
168
+ const y = kb[1];
169
+ return x < y ? -1 : x > y ? 1 : 0;
170
+ });
171
+ return fields;
172
+ }
173
+ /** `camel_fields`: describe/objects fields normalized to the registry's camelCase shape. */
174
+ export function camelFields(fields) {
175
+ const out = [];
176
+ for (const f of pyIter(pyOr(fields, []))) {
177
+ if (!isRecord(f))
178
+ continue;
179
+ const low = lowerTop(f);
180
+ const name = get(low, "name");
181
+ if (!truthy(name))
182
+ continue;
183
+ if (isHidden(low))
184
+ continue;
185
+ const field = { name };
186
+ for (const k of ["displayName", "description", "required", "dataType", "sortOrder"]) {
187
+ if (get(low, k) !== null)
188
+ field[k] = low[k];
189
+ }
190
+ if (get(low, "reference") !== null)
191
+ field.reference = camelize(low.reference);
192
+ keepHiddenMarker(low, field);
193
+ // Describe's `Type` is the DATA type; an objects-row Parameter's `type` is
194
+ // the BUCKET — those go through `normalizeBucketFields` instead.
195
+ if (get(low, "type") !== null && !("dataType" in field)) {
196
+ field.type = low.type;
197
+ field.dataType = low.type;
198
+ }
199
+ else if ("dataType" in field) {
200
+ field.type = field.dataType;
201
+ }
202
+ out.push(field);
203
+ }
204
+ return out;
205
+ }
206
+ /** `normalize_bucket_fields`: `triggers objects → Parameters[]` rows, camelCase, bucket made explicit. */
207
+ export function normalizeBucketFields(parameters) {
208
+ const out = [];
209
+ for (const f of pyIter(pyOr(parameters, []))) {
210
+ if (!isRecord(f))
211
+ continue;
212
+ const low = lowerTop(f);
213
+ const name = get(low, "name");
214
+ if (!truthy(name))
215
+ continue;
216
+ if (isHidden(low))
217
+ continue;
218
+ const field = {
219
+ name,
220
+ bucket: BUCKETS.get(pyStr(pyOr(get(low, "type"), "")).toLowerCase()) ?? "event",
221
+ };
222
+ for (const k of ["displayName", "description", "required", "sortOrder"]) {
223
+ if (get(low, k) !== null)
224
+ field[k] = low[k];
225
+ }
226
+ if (get(low, "reference") !== null)
227
+ field.reference = camelize(low.reference);
228
+ const dataType = pyOr(get(low, "dataType"), "string");
229
+ field.type = dataType;
230
+ field.dataType = dataType;
231
+ keepHiddenMarker(low, field);
232
+ out.push(field);
233
+ }
234
+ return out;
235
+ }
236
+ /**
237
+ * `union_event_parameters`: describe's EventParameters ∪ the object row's
238
+ * Parameters, deduped by name; `required` is an OR; the object row's bucket
239
+ * survives the merge.
240
+ */
241
+ export function unionEventParameters(declared, objectParameters) {
242
+ const merged = new Map();
243
+ for (const field of camelFields(declared)) {
244
+ if (!("bucket" in field))
245
+ field.bucket = "event";
246
+ merged.set(field.name, field);
247
+ }
248
+ for (const field of normalizeBucketFields(objectParameters)) {
249
+ const existing = merged.get(field.name);
250
+ if (existing === undefined) {
251
+ merged.set(field.name, field);
252
+ continue;
253
+ }
254
+ existing.required = truthy(get(existing, "required")) || truthy(get(field, "required"));
255
+ existing.bucket = field.bucket;
256
+ for (const k of ["displayName", "description", "reference", "sortOrder"]) {
257
+ if (get(field, k) !== null && get(existing, k) === null)
258
+ existing[k] = field[k];
259
+ }
260
+ }
261
+ return withCascade([...merged.values()]);
262
+ }
263
+ const EVENT_FACTS = [
264
+ ["EventMode", "eventMode"],
265
+ ["ByoaConnection", "byoaConnection"],
266
+ ["IsWebhookUrlVisible", "isWebhookUrlVisible"],
267
+ ];
268
+ /** `merge_describe_into_node`: fold a `triggers describe` answer into a registry trigger definition (a copy). */
269
+ export function mergeDescribeIntoNode(node, describe, objectParameters = null) {
270
+ const out = { ...node };
271
+ out.eventParameters = { fields: unionEventParameters(get(describe, "EventParameters"), objectParameters) };
272
+ out.filterFields = { fields: camelFields(get(describe, "FilterFields")) };
273
+ if (truthy(get(describe, "OutputFields"))) {
274
+ out.outputResponseDefinition = { fields: camelFields(get(describe, "OutputFields")) };
275
+ }
276
+ for (const [src, dst] of EVENT_FACTS)
277
+ if (get(describe, src) !== null)
278
+ out[dst] = describe[src];
279
+ return out;
280
+ }
281
+ /** `event_facts`: the three facts that change how a subscription is prepared or tested; the object row wins. */
282
+ export function eventFacts(node, objectRow) {
283
+ const row = pyOr(objectRow, {});
284
+ const fact = (rowKey, nodeKey, dflt) => {
285
+ if (get(row, rowKey) !== null)
286
+ return row[rowKey];
287
+ if (get(node, nodeKey) !== null)
288
+ return node[nodeKey];
289
+ return dflt;
290
+ };
291
+ const textBlocks = pyOr(get(pyOr(get(row, "Design"), {}), "TextBlocks"), get(pyOr(get(row, "design"), {}), "textBlocks"), []);
292
+ return {
293
+ eventMode: pyStr(pyOr(get(node, "eventMode"), "polling")),
294
+ byoaConnection: truthy(fact("ByoaConnection", "byoaConnection", false)),
295
+ isWebhookUrlVisible: truthy(fact("IsWebhookUrlVisible", "isWebhookUrlVisible", false)),
296
+ textBlocks: pyIter(textBlocks).map((t) => pyStr(t)),
297
+ };
298
+ }
299
+ // ---------------------------------------------------------------------------
300
+ // The generator's cached event calls and its event passes
301
+ // ---------------------------------------------------------------------------
302
+ /**
303
+ * `triggers_objects_cached`: cached `uip is triggers objects <key> <OPERATION>`,
304
+ * called WITHOUT a connection on purpose. A failure is reported and cached as
305
+ * an empty catalog — a REAL failure, that is. The Python had no offline mode,
306
+ * so every failure it cached was the tenant's answer. Under `--offline` the
307
+ * client refuses the call instead; caching that as `{objects: []}` would make
308
+ * the next ONLINE build over the same `--keep-temp` caches see an empty event
309
+ * catalogue and expand nothing, silently. So a refusal is reported and used,
310
+ * but never written down. (The four describe/list helpers in `context.ts` now
311
+ * apply the same rule explicitly. They used to rest on an accident — a
312
+ * refusal's empty stdout is not JSON, so they returned before their cache
313
+ * write — which held only for as long as the offline client wrote nothing to
314
+ * stdout. That is precisely the accident this flag replaced, so they check the
315
+ * flag instead.)
316
+ */
317
+ export async function triggersObjectsCached(ctx, key, operation) {
318
+ const name = `${safeCacheName(schemaCacheScope(ctx), key, operation, "_trigger_objects")}.json`;
319
+ if (ctx.caches.is.has(name))
320
+ return pyOr(get(ctx.caches.is.read(name), "objects"), []);
321
+ let rows = [];
322
+ try {
323
+ rows = await triggersObjects(ctx.client, key, operation, undefined, ctx.timeoutMs);
324
+ }
325
+ catch (error) {
326
+ if (!(error instanceof EventDiscoveryError))
327
+ throw error;
328
+ ctx.reporter.warn(` WARN: trigger objects failed for ${key} ${operation}: ${error.message}`);
329
+ if (error.refused)
330
+ return rows;
331
+ }
332
+ writeCache(ctx.caches.is, name, { objects: rows });
333
+ return rows;
334
+ }
335
+ /**
336
+ * `triggers_describe_cached`: cached `uip is triggers describe <key> <OPERATION> <object>`.
337
+ * A refused call is not cached, for the reason given on {@link triggersObjectsCached}.
338
+ */
339
+ export async function triggersDescribeCached(ctx, key, operation, objectName) {
340
+ const name = `${safeCacheName(schemaCacheScope(ctx), key, operation, objectName, "_trigger_describe")}.json`;
341
+ if (ctx.caches.is.has(name))
342
+ return pyOr(get(ctx.caches.is.read(name), "describe"), {});
343
+ let data = {};
344
+ try {
345
+ data = await triggersDescribe(ctx.client, key, operation, objectName, undefined, ctx.timeoutMs);
346
+ }
347
+ catch (error) {
348
+ if (!(error instanceof EventDiscoveryError))
349
+ throw error;
350
+ ctx.reporter.warn(` WARN: trigger describe failed for ${key} ${operation} ${objectName}: ${error.message}`);
351
+ if (error.refused)
352
+ return data;
353
+ }
354
+ writeCache(ctx.caches.is, name, { describe: data });
355
+ return data;
356
+ }
357
+ /**
358
+ * `_apply_event_schema`: put an event's OWN schema on the entry, replacing the
359
+ * node manifest's. `inputSchema` becomes the union rule's answer; `eventSchema`
360
+ * is appended to the entry and later stamped onto the sidecar.
361
+ */
362
+ export function applyEventSchema(entry, describe, obj) {
363
+ const e = entry;
364
+ const d = pyOr(describe, {});
365
+ const o = pyOr(obj, {});
366
+ const fields = unionEventParameters(get(d, "EventParameters"), get(o, "Parameters"));
367
+ e.inputSchema = { fields };
368
+ const outputFields = camelFields(get(d, "OutputFields"));
369
+ if (outputFields.length > 0)
370
+ e.outputSchema = { fields: outputFields };
371
+ const eventSchema = { eventParameters: fields, filterFields: camelFields(get(d, "FilterFields")) };
372
+ for (const [src, dst] of EVENT_FACTS)
373
+ if (get(d, src) !== null)
374
+ eventSchema[dst] = d[src];
375
+ e.eventSchema = eventSchema;
376
+ return entry;
377
+ }
378
+ /** `_describe_curated_events`: give each CURATED event its own schema, in place. */
379
+ export async function describeCuratedEvents(ctx, entries) {
380
+ if (entries.length === 0)
381
+ return;
382
+ const results = await mapConcurrent(entries, ctx.concurrency, async (entry) => {
383
+ const key = item(item(entry, "connector"), "key");
384
+ const operation = item(entry, "operation");
385
+ const eventOperation = pyOr(get(operation, "eventOperation"), "");
386
+ const objectName = pyOr(get(operation, "objectName"), "");
387
+ if (!truthy(eventOperation) || !truthy(objectName))
388
+ return [entry, null, null];
389
+ const rows = await triggersObjectsCached(ctx, key, eventOperation);
390
+ const obj = rows.find((r) => get(r, "Name") === objectName) ?? null;
391
+ return [entry, await triggersDescribeCached(ctx, key, eventOperation, objectName), obj];
392
+ });
393
+ let described = 0;
394
+ for (const [entry, describe, obj] of results) {
395
+ if (!truthy(describe))
396
+ continue;
397
+ applyEventSchema(entry, describe, obj);
398
+ described += 1;
399
+ }
400
+ ctx.reporter.info(` ${described}/${entries.length} curated events described (subscription parameters, filter fields, payload)`);
401
+ }
402
+ /**
403
+ * `_make_expanded_event_entry`: one per-object variant of a generic connector
404
+ * EVENT. The variant keeps `activityType: GenericTrigger` and the template's
405
+ * nodeType deliberately; the object rides on `operation.objectName`.
406
+ */
407
+ export function makeExpandedEventEntry(template, obj, describe) {
408
+ const entry = deepCopy(template);
409
+ const objectName = pyOr(get(obj, "Name"), "");
410
+ const objectDisplay = pyOr(get(obj, "DisplayName"), objectName);
411
+ const operation = item(entry, "operation");
412
+ operation.objectName = objectName;
413
+ operation.objectDisplayName = objectDisplay;
414
+ operation.sourceNodeType = item(template, "nodeType");
415
+ const label = pyOr(get(pyOr(get(template, "display"), {}), "label"), "");
416
+ item(entry, "display").label = pyStrip(`${pyStr(label)} (${pyStr(objectDisplay)})`);
417
+ return applyEventSchema(entry, describe, obj);
418
+ }
419
+ /**
420
+ * `_expand_generic_events`: expand each generic EVENT template into one entry
421
+ * per event object. Variants come back in template × object order (the Python
422
+ * appended them as the describes completed); `build` sorts them before
423
+ * planning.
424
+ */
425
+ export async function expandGenericEvents(ctx, templates) {
426
+ if (templates.length === 0)
427
+ return [];
428
+ const work = [];
429
+ for (const template of templates) {
430
+ const key = item(item(template, "connector"), "key");
431
+ const operation = pyOr(get(item(template, "operation"), "eventOperation"), "");
432
+ if (!truthy(operation))
433
+ continue;
434
+ for (const obj of await triggersObjectsCached(ctx, key, operation)) {
435
+ if (truthy(get(obj, "Name")))
436
+ work.push([template, key, operation, obj]);
437
+ }
438
+ }
439
+ if (work.length === 0)
440
+ return [];
441
+ const describes = await mapConcurrent(work, ctx.concurrency, ([, key, operation, obj]) => triggersDescribeCached(ctx, key, operation, item(obj, "Name")));
442
+ const expanded = work.map(([template, , , obj], i) => makeExpandedEventEntry(template, obj, pyOr(describes[i], {})));
443
+ ctx.reporter.info(` ${expanded.length} generic EVENT variants expanded from ${templates.length} template(s)`);
444
+ return expanded;
445
+ }
446
+ /**
447
+ * `resolve_connector_events`: give every connector EVENT its real schema;
448
+ * expand the expandable ones. Returns `[entries, expanded]` — `entries` is the
449
+ * input with curated events described in place.
450
+ */
451
+ export async function resolveConnectorEvents(ctx, entries) {
452
+ const curated = [];
453
+ const generic = [];
454
+ for (const e of entries) {
455
+ const at = get(item(e, "operation"), "activityType", "");
456
+ if (at === "CuratedTrigger") {
457
+ curated.push(e);
458
+ }
459
+ else if (at === "GenericTrigger") {
460
+ // UNKNOWN is not "static": expand only where the connector positively
461
+ // says its objects do not come from the connection.
462
+ const discovery = get(pyOr(get(e, "connector"), {}), "discovery");
463
+ if (!truthy(discovery)) {
464
+ ctx.reporter.warn(` WARN: ${item(e, "nodeType")} not expanded — no connector discovery metadata, so whether its ` +
465
+ "objects come from the connection is unknown");
466
+ }
467
+ else if (!objectsComeFromTheConnection(e)) {
468
+ generic.push(e);
469
+ }
470
+ }
471
+ }
472
+ if (curated.length === 0 && generic.length === 0)
473
+ return [entries, []];
474
+ await describeCuratedEvents(ctx, curated);
475
+ return [entries, await expandGenericEvents(ctx, generic)];
476
+ }
@@ -0,0 +1,77 @@
1
+ import type { LibraryEntry } from "../model/library.js";
2
+ import { type BuildContext } from "./context.js";
3
+ import { type Dict } from "./py.js";
4
+ /** `{op_name: template}` for one connector, in first-seen order. */
5
+ export type TemplatesByOp = Map<string, LibraryEntry>;
6
+ /**
7
+ * `_generic_templates_by_op`: bucket Generic CRUD entries by IS operation
8
+ * name per connector. Generic CRUD actions carry an `operation` verb
9
+ * (`Create`/`Delete`/`Retrieve`/`List`/`Update`/`Replace`) but no
10
+ * `httpMethod`; the IS catalog returns the same verbs under
11
+ * `AvailableOperations[].Name`.
12
+ */
13
+ export declare function genericTemplatesByOp(genericEntries: readonly LibraryEntry[]): Map<string, TemplatesByOp>;
14
+ /**
15
+ * `_make_expanded_entry`: one expanded entry from a Generic template + an
16
+ * (object, op) pair. `obj` is a `resources list` row, `op` an element of
17
+ * `AvailableOperations`, `opData` the per-operation describe `Data` (may be
18
+ * absent — the entry is still built, with an empty schema). The template's
19
+ * discovery flag is copied, never inferred from field counts.
20
+ */
21
+ export declare function makeExpandedEntry(template: LibraryEntry, obj: unknown, op: unknown, opData: unknown): LibraryEntry;
22
+ /** `_unique_by_name`: first occurrence of each `Name`, in order. */
23
+ export declare function uniqueByName(objects: unknown): Map<string, Dict>;
24
+ /**
25
+ * `_list_generic_objects`: list and deduplicate the objects available to one
26
+ * connector. When the caller named specific objects and the catalog does not
27
+ * contain them, retry once with a refresh before believing it — a stale
28
+ * `uip is resources list` cache reports a real object as missing.
29
+ */
30
+ export declare function listGenericObjects(ctx: BuildContext, connectorKey: string, templatesByOp: TemplatesByOp, objectNames?: readonly string[]): Promise<Dict[]>;
31
+ /**
32
+ * `expand_generic_for_connector`: materialize per-(object, op) entries for one
33
+ * connector, sequentially — the shape `prepare` uses. `objectNames`, when
34
+ * given, restricts the objects described; an empty list describes none.
35
+ */
36
+ export declare function expandGenericForConnector(ctx: BuildContext, connectorKey: string, templatesByOp: TemplatesByOp, objectNames?: readonly string[]): Promise<LibraryEntry[]>;
37
+ /**
38
+ * `objects_come_from_the_connection`: true when this connector's object set is
39
+ * only knowable per connection. Field discovery alone does NOT qualify.
40
+ */
41
+ export declare function objectsComeFromTheConnection(entry: unknown): boolean;
42
+ /**
43
+ * `as_discovery_entry`: recast a Generic CRUD template as the discovery entry
44
+ * shipped for it — flagged, object cleared, labelled by verb ("Update Record",
45
+ * "List Records").
46
+ */
47
+ export declare function asDiscoveryEntry(entry: LibraryEntry): LibraryEntry;
48
+ /**
49
+ * `disambiguate_discovery_labels`: keep two discovery entries on one connector
50
+ * from sharing a label. Only the colliding ones are renamed, from their own
51
+ * node type (`search-records` → "Search Records", via `str.title()`).
52
+ */
53
+ export declare function disambiguateDiscoveryLabels(entries: LibraryEntry[]): LibraryEntry[];
54
+ /**
55
+ * A deterministic order for expanded entries: connector key, object name,
56
+ * operation name, node type — stable, so ties keep their input order.
57
+ *
58
+ * DELIBERATE FIX. The Python appended expansions as thread-pool futures
59
+ * completed, so the order two entries reached `write_library` in — and, when
60
+ * both claimed one path with equal input counts, which one survived — varied
61
+ * from run to run. Sorting here makes the library a function of its inputs.
62
+ * The index is sorted separately, so this changes no bytes when no such tie
63
+ * exists.
64
+ */
65
+ export declare function sortExpandedEntries(entries: readonly LibraryEntry[]): LibraryEntry[];
66
+ /**
67
+ * `expand_all_generics`: expand every Generic entry into per-(object, op)
68
+ * entries. Returns `[curatedAndPassthrough, expanded]`: Curated entries pass
69
+ * through untouched; HTTP-passthrough Generic entries pass through as
70
+ * one-per-connector entries; CRUD Generic entries are dropped in favour of
71
+ * their expansions; object-dynamic ones become discovery entries.
72
+ *
73
+ * The three remote phases (list objects, describe objects, describe
74
+ * operations) each run under `mapConcurrent`, so `expanded` comes back in
75
+ * connector × object × operation order rather than completion order.
76
+ */
77
+ export declare function expandAllGenerics(ctx: BuildContext, entries: readonly LibraryEntry[]): Promise<[LibraryEntry[], LibraryEntry[]]>;