@uipath/maestro-builder-sdk 6.3.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 (161) hide show
  1. package/dist/api-index.md +6 -6
  2. package/dist/cli/commands/registry.js +68 -12
  3. package/dist/registry/archive/entry-policy.d.ts +36 -0
  4. package/dist/registry/archive/entry-policy.js +60 -0
  5. package/dist/registry/archive/extract.d.ts +31 -0
  6. package/dist/registry/archive/extract.js +92 -0
  7. package/dist/registry/archive/zip-reader.d.ts +38 -0
  8. package/dist/registry/archive/zip-reader.js +256 -0
  9. package/dist/registry/codecs/pyjson.d.ts +62 -0
  10. package/dist/registry/codecs/pyjson.js +208 -0
  11. package/dist/registry/compat/compare.d.ts +18 -0
  12. package/dist/registry/compat/compare.js +32 -0
  13. package/dist/registry/compat/paths.d.ts +4 -0
  14. package/dist/registry/compat/paths.js +17 -0
  15. package/dist/registry/compat/pystr.d.ts +50 -0
  16. package/dist/registry/compat/pystr.js +221 -0
  17. package/dist/registry/concurrency.d.ts +10 -0
  18. package/dist/registry/concurrency.js +34 -0
  19. package/dist/registry/curated-overlay.d.ts +36 -4
  20. package/dist/registry/curated-overlay.js +87 -14
  21. package/dist/registry/errors.d.ts +26 -0
  22. package/dist/registry/errors.js +38 -0
  23. package/dist/registry/extractor.d.ts +9 -10
  24. package/dist/registry/extractor.js +5 -15
  25. package/dist/registry/files.d.ts +26 -0
  26. package/dist/registry/files.js +160 -0
  27. package/dist/registry/generators/descriptors.d.ts +81 -0
  28. package/dist/registry/generators/descriptors.js +405 -0
  29. package/dist/registry/generators/fields.d.ts +46 -0
  30. package/dist/registry/generators/fields.js +111 -0
  31. package/dist/registry/generators/markdown/common.d.ts +21 -0
  32. package/dist/registry/generators/markdown/common.js +55 -0
  33. package/dist/registry/generators/markdown/index.d.ts +33 -0
  34. package/dist/registry/generators/markdown/index.js +37 -0
  35. package/dist/registry/generators/markdown/page.d.ts +8 -0
  36. package/dist/registry/generators/markdown/page.js +52 -0
  37. package/dist/registry/generators/markdown/pyvalue.d.ts +34 -0
  38. package/dist/registry/generators/markdown/pyvalue.js +106 -0
  39. package/dist/registry/generators/markdown/sections/choosing-object.d.ts +2 -0
  40. package/dist/registry/generators/markdown/sections/choosing-object.js +52 -0
  41. package/dist/registry/generators/markdown/sections/example.d.ts +22 -0
  42. package/dist/registry/generators/markdown/sections/example.js +295 -0
  43. package/dist/registry/generators/markdown/sections/filtering.d.ts +2 -0
  44. package/dist/registry/generators/markdown/sections/filtering.js +76 -0
  45. package/dist/registry/generators/markdown/sections/header.d.ts +2 -0
  46. package/dist/registry/generators/markdown/sections/header.js +37 -0
  47. package/dist/registry/generators/markdown/sections/inputs.d.ts +30 -0
  48. package/dist/registry/generators/markdown/sections/inputs.js +151 -0
  49. package/dist/registry/generators/markdown/sections/lookups.d.ts +12 -0
  50. package/dist/registry/generators/markdown/sections/lookups.js +150 -0
  51. package/dist/registry/generators/markdown/sections/outputs.d.ts +3 -0
  52. package/dist/registry/generators/markdown/sections/outputs.js +9 -0
  53. package/dist/registry/generators/markdown/sections/resolving-fields.d.ts +2 -0
  54. package/dist/registry/generators/markdown/sections/resolving-fields.js +139 -0
  55. package/dist/registry/generators/markdown/sections/schema-dynamic.d.ts +2 -0
  56. package/dist/registry/generators/markdown/sections/schema-dynamic.js +86 -0
  57. package/dist/registry/generators/materialize.d.ts +30 -0
  58. package/dist/registry/generators/materialize.js +132 -0
  59. package/dist/registry/generators/md-symbols.d.ts +22 -0
  60. package/dist/registry/generators/md-symbols.js +58 -0
  61. package/dist/registry/generators/triggers.d.ts +15 -0
  62. package/dist/registry/generators/triggers.js +85 -0
  63. package/dist/registry/generators/type-registry.d.ts +50 -0
  64. package/dist/registry/generators/type-registry.js +154 -0
  65. package/dist/registry/generators/type-tree.d.ts +65 -0
  66. package/dist/registry/generators/type-tree.js +179 -0
  67. package/dist/registry/library-build/build.d.ts +57 -0
  68. package/dist/registry/library-build/build.js +286 -0
  69. package/dist/registry/library-build/canonical.d.ts +119 -0
  70. package/dist/registry/library-build/canonical.js +512 -0
  71. package/dist/registry/library-build/cli.d.ts +45 -0
  72. package/dist/registry/library-build/cli.js +182 -0
  73. package/dist/registry/library-build/context.d.ts +107 -0
  74. package/dist/registry/library-build/context.js +267 -0
  75. package/dist/registry/library-build/enrich.d.ts +36 -0
  76. package/dist/registry/library-build/enrich.js +158 -0
  77. package/dist/registry/library-build/events.d.ts +108 -0
  78. package/dist/registry/library-build/events.js +476 -0
  79. package/dist/registry/library-build/generic-expand.d.ts +77 -0
  80. package/dist/registry/library-build/generic-expand.js +363 -0
  81. package/dist/registry/library-build/plan.d.ts +71 -0
  82. package/dist/registry/library-build/plan.js +270 -0
  83. package/dist/registry/library-build/py.d.ts +45 -0
  84. package/dist/registry/library-build/py.js +167 -0
  85. package/dist/registry/library-build/schema-action.d.ts +38 -0
  86. package/dist/registry/library-build/schema-action.js +107 -0
  87. package/dist/registry/library-build/write.d.ts +6 -0
  88. package/dist/registry/library-build/write.js +26 -0
  89. package/dist/registry/library-reader.d.ts +20 -0
  90. package/dist/registry/library-reader.js +50 -0
  91. package/dist/registry/lookup-specs.d.ts +55 -0
  92. package/dist/registry/lookup-specs.js +140 -0
  93. package/dist/registry/materialize-cli.js +28 -14
  94. package/dist/registry/materializer.d.ts +25 -3
  95. package/dist/registry/materializer.js +45 -29
  96. package/dist/registry/model/library.d.ts +254 -0
  97. package/dist/registry/model/library.js +23 -0
  98. package/dist/registry/naming.d.ts +54 -0
  99. package/dist/registry/naming.js +142 -0
  100. package/dist/registry/orphans.d.ts +4 -0
  101. package/dist/registry/orphans.js +86 -0
  102. package/dist/registry/prepare/actions.d.ts +26 -0
  103. package/dist/registry/prepare/actions.js +94 -0
  104. package/dist/registry/prepare/connections.d.ts +181 -0
  105. package/dist/registry/prepare/connections.js +399 -0
  106. package/dist/registry/prepare/event.d.ts +96 -0
  107. package/dist/registry/prepare/event.js +268 -0
  108. package/dist/registry/prepare/handoff.d.ts +21 -0
  109. package/dist/registry/prepare/handoff.js +91 -0
  110. package/dist/registry/prepare/ledger.d.ts +104 -0
  111. package/dist/registry/prepare/ledger.js +216 -0
  112. package/dist/registry/prepare/messages.d.ts +190 -0
  113. package/dist/registry/prepare/messages.js +472 -0
  114. package/dist/registry/prepare/objects.d.ts +130 -0
  115. package/dist/registry/prepare/objects.js +318 -0
  116. package/dist/registry/prepare/overlay.d.ts +149 -0
  117. package/dist/registry/prepare/overlay.js +487 -0
  118. package/dist/registry/prepare/parents.d.ts +41 -0
  119. package/dist/registry/prepare/parents.js +122 -0
  120. package/dist/registry/prepare/pipeline.d.ts +91 -0
  121. package/dist/registry/prepare/pipeline.js +376 -0
  122. package/dist/registry/prepare/regenerate.d.ts +9 -0
  123. package/dist/registry/prepare/regenerate.js +50 -0
  124. package/dist/registry/prepare/resolve.d.ts +199 -0
  125. package/dist/registry/prepare/resolve.js +470 -0
  126. package/dist/registry/prepare/retry.d.ts +83 -0
  127. package/dist/registry/prepare/retry.js +109 -0
  128. package/dist/registry/prepare/source-import.d.ts +54 -0
  129. package/dist/registry/prepare/source-import.js +192 -0
  130. package/dist/registry/prepare/support.d.ts +41 -0
  131. package/dist/registry/prepare/support.js +178 -0
  132. package/dist/registry/prepare-cli.d.ts +16 -5
  133. package/dist/registry/prepare-cli.js +60 -53
  134. package/dist/registry/pull-cli.js +8 -7
  135. package/dist/registry/reporter.d.ts +26 -0
  136. package/dist/registry/reporter.js +41 -0
  137. package/dist/registry/uip/client.d.ts +99 -0
  138. package/dist/registry/uip/client.js +226 -0
  139. package/dist/registry/uip/commands.d.ts +40 -0
  140. package/dist/registry/uip/commands.js +80 -0
  141. package/dist/registry/uip/envelope.d.ts +37 -0
  142. package/dist/registry/uip/envelope.js +84 -0
  143. package/dist/registry/uip/response-cache.d.ts +28 -0
  144. package/dist/registry/uip/response-cache.js +57 -0
  145. package/lib/README.md +10 -8
  146. package/package.json +3 -3
  147. package/dist/generators/_connections.py +0 -331
  148. package/dist/generators/_events.py +0 -355
  149. package/dist/generators/_lookups.py +0 -114
  150. package/dist/generators/_naming.py +0 -185
  151. package/dist/generators/_objects.py +0 -299
  152. package/dist/generators/_resolve.py +0 -319
  153. package/dist/generators/convert_library_to_md.py +0 -1441
  154. package/dist/generators/extract_library.py +0 -125
  155. package/dist/generators/generate_connectors_ts.py +0 -936
  156. package/dist/generators/generate_library_json.py +0 -2527
  157. package/dist/generators/generate_triggers_ts.py +0 -110
  158. package/dist/generators/materialize_registry.py +0 -155
  159. package/dist/generators/prepare_connector.py +0 -1860
  160. package/dist/registry/python.d.ts +0 -26
  161. package/dist/registry/python.js +0 -46
@@ -0,0 +1,512 @@
1
+ /**
2
+ * Canonical extraction: one raw `flow registry get` node into one library entry.
3
+ *
4
+ * Everything here is a pure function of its arguments. The key ORDER of the
5
+ * entry it builds is the Python's dict order and is load-bearing: the entry is
6
+ * written with `json.dump(indent=2)` and the library's sha256 is published, so
7
+ * a key that moves is a different library.
8
+ *
9
+ * Also here: the connector-record stamping (`merge_connector_record`), the
10
+ * tier readers, the three node-type → connector-key parsers, the published-
11
+ * catalog filter, and the curated override tables the generator carries.
12
+ */
13
+ import { pyStr, pyStrip, lowerFirst, startsWithUpper } from "../compat/pystr.js";
14
+ import { sortedStrings } from "../compat/compare.js";
15
+ import { get, isRecord, pyIter, pyOr, partition, truthy } from "./py.js";
16
+ import { schemaAction } from "./schema-action.js";
17
+ export const SCHEMA_VERSION = "1";
18
+ /** `(connectorKey, objectName)` as a Map key; `null`s stay distinct from `""`. */
19
+ function curatedKey(connectorKey, objectName) {
20
+ return JSON.stringify([connectorKey ?? null, objectName ?? null]);
21
+ }
22
+ /**
23
+ * Ops the IS CLI exposes as a POST whose inputs are query-string params with a
24
+ * required (often empty) body. A dispatcher reads
25
+ * `operation.dispatchStyle == "query-post"` and dispatches
26
+ * `run create <obj> --query <inputs> --body '{}'` instead of the default
27
+ * create→--body. The registry does not expose per-parameter location, so this
28
+ * is a small explicit list rather than derived.
29
+ */
30
+ export const CURATED_DISPATCH_STYLE = new Map([
31
+ [curatedKey("uipath-uipath-dataservice", "QueryEntityRecords_V3"), "query-post"],
32
+ // The V2 curated sibling, and the one the REGISTRY node
33
+ // `uipath.connector.uipath-uipath-dataservice.query-entity-records` maps to:
34
+ // POST /v2/{entityName}/qer, every input a path or query parameter, body `{}`.
35
+ [curatedKey("uipath-uipath-dataservice", "QueryEntityRecordsCurated"), "query-post"],
36
+ ]);
37
+ /**
38
+ * Per-parameter value-grammar tags. A dispatcher validates the COMPUTED input
39
+ * against `operation.paramGrammars` at every dispatch boundary. "sql-where":
40
+ * Data Service query expressions (RHS string literals must be single-quoted).
41
+ */
42
+ export const CURATED_PARAM_GRAMMARS = new Map([
43
+ [curatedKey("uipath-uipath-dataservice", "QueryEntityRecords_V3"), { queryExpression: "sql-where" }],
44
+ [curatedKey("uipath-uipath-dataservice", "QueryEntityRecords"), { queryExpression: "sql-where" }],
45
+ [curatedKey("uipath-uipath-dataservice", "QueryEntityRecordsCurated"), { queryExpression: "sql-where" }],
46
+ ]);
47
+ /**
48
+ * A few connector ops the registry enumerates only in a form `uip is resources
49
+ * run` can't dispatch. The Outlook reply op is enumerated as the MULTIPART
50
+ * `ReplyToEmailV2`; the connector also exposes the non-multipart sibling
51
+ * `ReplyToEmail` that Flow v1 uses. Keyed by (connectorKey, registry
52
+ * objectName); deep-merged into `operation` AFTER enrichment so it wins over
53
+ * the `is resources describe` params.
54
+ */
55
+ export const CURATED_OP_OVERRIDE = new Map([
56
+ [
57
+ curatedKey("uipath-microsoft-outlook365", "ReplyToEmailV2"),
58
+ {
59
+ operation: {
60
+ objectName: "ReplyToEmail",
61
+ path: "/ReplyToEmail",
62
+ parameters: [
63
+ {
64
+ name: "id",
65
+ type: "query",
66
+ dataType: "string",
67
+ required: true,
68
+ displayName: "Message ID",
69
+ description: "The id of the email to reply to",
70
+ },
71
+ ],
72
+ },
73
+ },
74
+ ],
75
+ ]);
76
+ /**
77
+ * `apply_curated_overrides`: deep-merge CURATED_OP_OVERRIDE into matching
78
+ * entries, AFTER enrichment, so the curated shape wins over the registry-
79
+ * described one. Matches on (connectorKey, objectName) using the entry's
80
+ * current (registry) objectName. Returns how many entries were touched.
81
+ */
82
+ export function applyCuratedOverrides(entries, overrides = CURATED_OP_OVERRIDE) {
83
+ let n = 0;
84
+ for (const e of entries) {
85
+ const key = curatedKey(get(pyOr(get(e, "connector", {}), {}), "key", ""), get(pyOr(get(e, "operation", {}), {}), "objectName", ""));
86
+ const override = overrides.get(key);
87
+ if (!truthy(override))
88
+ continue;
89
+ const target = e;
90
+ for (const [section, fields] of Object.entries(override)) {
91
+ if (!(section in target) || target[section] === undefined)
92
+ target[section] = {};
93
+ const sectionValue = target[section];
94
+ for (const [k, v] of Object.entries(fields))
95
+ sectionValue[k] = v;
96
+ }
97
+ n += 1;
98
+ }
99
+ return n;
100
+ }
101
+ /** The `value` of the context entry named `name`, or `null`. */
102
+ export function contextValue(context, name) {
103
+ for (const c of pyIter(context)) {
104
+ if (get(c, "name") === name)
105
+ return get(c, "value");
106
+ }
107
+ return null;
108
+ }
109
+ export function hasContext(context, name) {
110
+ return pyIter(context).some((c) => get(c, "name") === name);
111
+ }
112
+ /**
113
+ * `_parameter_metadata`: field-shaped metadata carried by each method
114
+ * parameter, keyed by name.
115
+ *
116
+ * A method parameter and a node field describe the same input in two
117
+ * different schemas. `displayName`, `reference` and the choice list exist on
118
+ * both sides, so they are the only ones worth moving across. A parameter's
119
+ * `enum` is deprecated in favour of `enhancedEnum`, whose `{name, value}`
120
+ * pairs flatten to the literals a field's `enum` holds.
121
+ */
122
+ export function parameterMetadata(connectorMethodInfo) {
123
+ const out = new Map();
124
+ for (const p of pyIter(pyOr(get(pyOr(connectorMethodInfo, {}), "parameters"), []))) {
125
+ const name = get(p, "name");
126
+ if (typeof name !== "string" || !name)
127
+ continue;
128
+ const meta = {};
129
+ if (get(p, "displayName") !== null)
130
+ meta.displayName = get(p, "displayName");
131
+ if (get(p, "reference") !== null)
132
+ meta.reference = get(p, "reference");
133
+ if (isRecord(p) && "enum" in p) {
134
+ meta.enum = p.enum ?? null;
135
+ }
136
+ else if (truthy(get(p, "enhancedEnum"))) {
137
+ const values = pyIter(get(p, "enhancedEnum"))
138
+ .filter((e) => isRecord(e) && get(e, "value") !== null)
139
+ .map((e) => get(e, "value"));
140
+ if (values.length > 0)
141
+ meta.enum = values;
142
+ }
143
+ if (truthy(meta))
144
+ out.set(name, meta);
145
+ }
146
+ return out;
147
+ }
148
+ /** `Data.Node` from a parsed `flow registry get` response, or `null`. */
149
+ export function nodeOf(payload) {
150
+ return get(get(payload, "Data", {}), "Node");
151
+ }
152
+ /**
153
+ * `extract_canonical`: the library entry for one registry `Data.Node`, or
154
+ * `undefined` when the node is filtered out (not enabled, not cross-platform,
155
+ * experimental, or without a `connectorDetail` block).
156
+ */
157
+ export function extractCanonical(nodeData, ctx = {}) {
158
+ const node = nodeData;
159
+ if (!truthy(node))
160
+ return undefined;
161
+ const nodeType = get(node, "nodeType", "");
162
+ const model = pyOr(get(node, "model", {}), {});
163
+ const context = pyOr(get(model, "context", []), []);
164
+ // Activity-level metadata lives in the editor form: search
165
+ // `node.form.sections[*].fields[*].componentProps` for the `connectorDetail`
166
+ // block — isEnabled / targetPlatform / isExperimental / configuration.
167
+ let connectorDetail;
168
+ for (const section of pyIter(pyOr(get(pyOr(get(node, "form", {}), {}), "sections", []), []))) {
169
+ for (const f of pyIter(pyOr(get(section, "fields", []), []))) {
170
+ const cp = get(pyOr(get(f, "componentProps"), {}), "connectorDetail");
171
+ if (isRecord(cp) && "configuration" in cp) {
172
+ connectorDetail = cp;
173
+ break;
174
+ }
175
+ }
176
+ if (connectorDetail)
177
+ break;
178
+ }
179
+ if (!truthy(connectorDetail))
180
+ return undefined;
181
+ if (!truthy(get(connectorDetail, "isEnabled", false)))
182
+ return undefined;
183
+ if (get(connectorDetail, "targetPlatform") !== "CrossPlatform")
184
+ return undefined;
185
+ if (truthy(get(connectorDetail, "isExperimental", true)))
186
+ return undefined;
187
+ const configuration = get(connectorDetail, "configuration", "{}");
188
+ if (typeof configuration !== "string") {
189
+ throw new TypeError(`the JSON object must be str, bytes or bytearray, not ${configuration === null ? "NoneType" : typeof configuration}`);
190
+ }
191
+ let config;
192
+ try {
193
+ config = JSON.parse(configuration);
194
+ }
195
+ catch {
196
+ return undefined;
197
+ }
198
+ const activityType = get(config, "activityType", "");
199
+ // The human-readable verb lives on connectorMethodInfo when present; fall
200
+ // back to the model.context entry, then to the configuration blob (Generic
201
+ // CRUD actions carry it only there).
202
+ const cmi = pyOr(get(node, "connectorMethodInfo", {}), {});
203
+ const operationName = pyOr(get(cmi, "operation"), contextValue(context, "operation"), get(config, "operation"), "");
204
+ const path = pyOr(get(cmi, "path"), "");
205
+ const curated = pyOr(get(cmi, "curated"), {});
206
+ const objectDisplayName = pyOr(get(curated, "displayName"), "");
207
+ // Input fields: take `inputDefinition.fields` when they exist and fill their
208
+ // gaps from `connectorMethodInfo.parameters`; build them from the parameters
209
+ // when they do not. Gaps only — a value the node already states wins.
210
+ const paramMeta = parameterMetadata(cmi);
211
+ const declaredFields = pyOr(get(get(node, "inputDefinition", {}), "fields"), []);
212
+ const inputFields = [];
213
+ if (truthy(declaredFields)) {
214
+ for (const declared of pyIter(declaredFields)) {
215
+ if (!isRecord(declared))
216
+ throw new TypeError("cannot convert dictionary update sequence element to a dict");
217
+ const merged = { ...declared };
218
+ const meta = paramMeta.get(get(merged, "name")) ?? {};
219
+ for (const [key, value] of Object.entries(meta)) {
220
+ if (!(key in merged) || merged[key] === undefined)
221
+ merged[key] = value;
222
+ }
223
+ inputFields.push(merged);
224
+ }
225
+ }
226
+ else {
227
+ for (const p of pyIter(pyOr(get(cmi, "parameters", []), []))) {
228
+ const f = {
229
+ name: get(p, "name", ""),
230
+ type: get(p, "dataType", "string"),
231
+ required: get(p, "required", false),
232
+ description: get(p, "description", ""),
233
+ };
234
+ Object.assign(f, paramMeta.get(f.name) ?? {});
235
+ inputFields.push(f);
236
+ }
237
+ }
238
+ let outputFields = get(get(node, "outputDefinition", {}), "fields");
239
+ if (!truthy(outputFields))
240
+ outputFields = get(get(node, "outputResponseDefinition", {}), "fields");
241
+ if (!truthy(outputFields))
242
+ outputFields = [];
243
+ const display = pyOr(get(node, "display", {}), {});
244
+ const isTrigger = pyStr(activityType).endsWith("Trigger");
245
+ const operation = {
246
+ name: operationName,
247
+ objectName: get(config, "objectName", ""),
248
+ objectDisplayName,
249
+ httpMethod: get(config, "httpMethod", ""),
250
+ path,
251
+ subType: get(config, "subType", ""),
252
+ supportsStreaming: get(config, "supportsStreaming", false),
253
+ activityType,
254
+ };
255
+ // A connector EVENT's operation is not `operation.name` — that is the
256
+ // object's CRUD verb. The event's own operation is the `model.context` entry
257
+ // the platform stamps (`EMAIL_RECEIVED`), the key every trigger API takes.
258
+ if (isTrigger)
259
+ operation.eventOperation = pyOr(contextValue(context, "operation"), "");
260
+ if (isTrigger && truthy(get(node, "eventMode")))
261
+ operation.eventMode = pyOr(get(node, "eventMode"), "");
262
+ const entry = {
263
+ schemaVersion: ctx.schemaVersion ?? SCHEMA_VERSION,
264
+ nodeType,
265
+ version: pyOr(get(config, "version", ""), get(node, "version", "")),
266
+ category: get(node, "category", ""),
267
+ tags: get(node, "tags", []),
268
+ connector: {
269
+ key: get(config, "connectorKey", ""),
270
+ },
271
+ operation,
272
+ display: {
273
+ label: pyOr(get(display, "label", ""), get(node, "displayName", "")),
274
+ description: pyOr(get(display, "description", ""), get(node, "description", "")),
275
+ icon: get(display, "icon", ""),
276
+ iconBackground: get(display, "iconBackground", ""),
277
+ iconBackgroundDark: get(display, "iconBackgroundDark", ""),
278
+ },
279
+ runtime: {
280
+ bpmnType: get(model, "type", ""),
281
+ serviceType: get(model, "serviceType", ""),
282
+ activityConfigurationVersion: pyOr(contextValue(context, "activityConfigurationVersion"), ""),
283
+ requiresConnection: hasContext(context, "connection"),
284
+ requiresFolderKey: hasContext(context, "folderKey"),
285
+ // Connector discovery metadata replaces the old empty-schema heuristic
286
+ // later in mergeConnectorRecord().
287
+ requiresConnectionForSchema: false,
288
+ },
289
+ inputSchema: { fields: inputFields },
290
+ outputSchema: { fields: outputFields },
291
+ };
292
+ // Some operations resolve their real field set from the VALUES of a few
293
+ // parent fields; record what a caller has to do about it.
294
+ const action = schemaAction(cmi, inputFields);
295
+ if (truthy(action))
296
+ operation.schemaAction = action;
297
+ const tableKey = curatedKey(get(config, "connectorKey", ""), get(config, "objectName", ""));
298
+ const dispatchStyle = (ctx.dispatchStyles ?? CURATED_DISPATCH_STYLE).get(tableKey);
299
+ if (truthy(dispatchStyle))
300
+ operation.dispatchStyle = dispatchStyle;
301
+ const paramGrammars = (ctx.paramGrammars ?? CURATED_PARAM_GRAMMARS).get(tableKey);
302
+ if (truthy(paramGrammars))
303
+ operation.paramGrammars = paramGrammars;
304
+ return entry;
305
+ }
306
+ // ---------------------------------------------------------------------------
307
+ // Connector records (`is connectors metadata`)
308
+ // ---------------------------------------------------------------------------
309
+ /** Accept the boolean JSON contract and older Yes/No cache rows. */
310
+ export function asBool(value) {
311
+ if (typeof value === "boolean")
312
+ return value;
313
+ if (typeof value === "string")
314
+ return ["true", "yes", "1"].includes(pyStrip(value).toLowerCase());
315
+ return false;
316
+ }
317
+ /** Read the connector tier across CLI response casing variants. */
318
+ export function connectorTier(record) {
319
+ if (!isRecord(record))
320
+ return null;
321
+ let value = get(record, "Tier");
322
+ if (value === null)
323
+ value = get(record, "tier");
324
+ return value !== null ? pyStrip(pyStr(value)) : null;
325
+ }
326
+ /** Normalize current and legacy connector tier spellings to `tier1`/`tier2`. */
327
+ export function normalizedConnectorTier(record) {
328
+ const value = connectorTier(record);
329
+ if (value === null)
330
+ return null;
331
+ const normalized = value.toLowerCase().replace(/[^a-z0-9]/gu, "");
332
+ if (["1", "t1", "tier1"].includes(normalized))
333
+ return "tier1";
334
+ if (["2", "t2", "tier2"].includes(normalized))
335
+ return "tier2";
336
+ return null;
337
+ }
338
+ /**
339
+ * Keep the node types whose connector metadata declares `tier` — actions and
340
+ * connector events both, per `scopedConnectorKey`. Returns the kept node types
341
+ * and the connector keys that matched.
342
+ */
343
+ export function filterNodeTypesByTier(nodeTypes, connectorRecords, tier) {
344
+ if (tier !== "tier1" && tier !== "tier2")
345
+ throw new Error(`unsupported connector tier: ${tier}`);
346
+ const matching = new Set();
347
+ for (const [key, record] of connectorRecords)
348
+ if (normalizedConnectorTier(record) === tier)
349
+ matching.add(key);
350
+ const kept = nodeTypes.filter((nt) => {
351
+ const key = scopedConnectorKey(nt);
352
+ return key !== null && matching.has(key);
353
+ });
354
+ return [kept, matching];
355
+ }
356
+ /** `merge_connector_record`: stamp connector discovery metadata and its derived flag. */
357
+ export function mergeConnectorRecord(entry, record) {
358
+ if (!truthy(record))
359
+ return entry;
360
+ const connector = entry.connector;
361
+ const name = get(record, "Name");
362
+ if (truthy(name))
363
+ connector.name = name;
364
+ const tier = connectorTier(record);
365
+ if (tier !== null)
366
+ connector.tier = tier;
367
+ const discovery = {
368
+ type: pyOr(get(record, "DiscoveryType"), ""),
369
+ hasObjectDiscovery: asBool(get(record, "HasObjectDiscovery")),
370
+ hasFieldDiscovery: asBool(get(record, "HasFieldDiscovery")),
371
+ hasCustomObjectDiscovery: asBool(get(record, "HasCustomObjectDiscovery")),
372
+ hasCustomFieldDiscovery: asBool(get(record, "HasCustomFieldDiscovery")),
373
+ };
374
+ connector.discovery = discovery;
375
+ entry.runtime.requiresConnectionForSchema = Object.entries(discovery).some(([key, value]) => key !== "type" && truthy(value));
376
+ return entry;
377
+ }
378
+ // ---------------------------------------------------------------------------
379
+ // Node types
380
+ // ---------------------------------------------------------------------------
381
+ const CONNECTOR_PREFIX = "uipath.connector.";
382
+ const EVENT_PREFIX = "uipath.connector.event.";
383
+ const TRIGGER_PREFIX = "uipath.connector.trigger.";
384
+ /**
385
+ * The connector key for an ACTION node type, `uipath.connector.<key>.<op>`, or
386
+ * `null`. The event forms carry an extra segment and are answered by
387
+ * `eventConnectorKey` instead.
388
+ */
389
+ export function actionConnectorKey(nodeType) {
390
+ if (!nodeType.startsWith(CONNECTOR_PREFIX))
391
+ return null;
392
+ const remainder = nodeType.slice(CONNECTOR_PREFIX.length);
393
+ if (remainder.startsWith("trigger.") || remainder.startsWith("event."))
394
+ return null;
395
+ const [connectorKey, separator] = partition(remainder, ".");
396
+ return separator && connectorKey ? connectorKey : null;
397
+ }
398
+ /**
399
+ * The connector key for a WAIT-form event node type,
400
+ * `uipath.connector.event.<key>.<event>`, or `null`. Only the wait form is
401
+ * generated: `registry get` refuses the start form without a connection, and
402
+ * the SDK derives the start form from its wait sibling.
403
+ */
404
+ export function eventConnectorKey(nodeType) {
405
+ if (!nodeType.startsWith(EVENT_PREFIX))
406
+ return null;
407
+ const [connectorKey, separator] = partition(nodeType.slice(EVENT_PREFIX.length), ".");
408
+ return separator && connectorKey ? connectorKey : null;
409
+ }
410
+ /** The connector a node type belongs to, for SCOPE filtering: action or wait event. */
411
+ export function scopedConnectorKey(nodeType) {
412
+ return actionConnectorKey(nodeType) ?? eventConnectorKey(nodeType);
413
+ }
414
+ /** The connector a node type belongs to, in ANY of its three forms — trigger included. */
415
+ export function catalogConnectorKey(nodeType) {
416
+ const text = pyStr(pyOr(nodeType, ""));
417
+ const key = scopedConnectorKey(text);
418
+ if (key)
419
+ return key;
420
+ if (!text.startsWith(TRIGGER_PREFIX))
421
+ return null;
422
+ const [candidate, , event] = partition(text.slice(TRIGGER_PREFIX.length), ".");
423
+ return candidate && event ? candidate : null;
424
+ }
425
+ export const FIRST_PARTY_PREFIX = "uipath-";
426
+ export const MOCK_MARKER = "mock";
427
+ /**
428
+ * Does this connector key belong in the PUBLISHED catalog? `custom-<org>-*` are
429
+ * one tenant's own connectors and `uipath-mock-*` are eval fixtures; neither
430
+ * resolves for another user.
431
+ */
432
+ export function isFirstPartyConnector(key) {
433
+ const lowered = pyStr(pyOr(key, "")).toLowerCase();
434
+ return lowered.startsWith(FIRST_PARTY_PREFIX) && !lowered.includes(MOCK_MARKER);
435
+ }
436
+ /**
437
+ * `(kept node types, dropped connector keys)` for a FULL build. A node type
438
+ * with no connector key at all is not a connector node and is kept.
439
+ */
440
+ export function selectPublishedConnectors(nodeTypes) {
441
+ const droppedSet = new Set();
442
+ for (const nt of nodeTypes) {
443
+ const key = catalogConnectorKey(nt);
444
+ if (key && !isFirstPartyConnector(key))
445
+ droppedSet.add(key);
446
+ }
447
+ const dropped = sortedStrings(droppedSet);
448
+ if (dropped.length === 0)
449
+ return [[...nodeTypes], []];
450
+ const kept = nodeTypes.filter((nt) => {
451
+ const key = catalogConnectorKey(nt);
452
+ return isFirstPartyConnector(key) || !key;
453
+ });
454
+ return [kept, dropped];
455
+ }
456
+ /** The action segment of a `uipath.connector.<key>.<action>` node type. */
457
+ export function actionIdFromNodeType(nodeType) {
458
+ const parts = nodeType.split(".");
459
+ return parts.length >= 4 ? parts[parts.length - 1] : nodeType.replaceAll(".", "-");
460
+ }
461
+ // ---------------------------------------------------------------------------
462
+ // Generic templates and HTTP passthroughs
463
+ // ---------------------------------------------------------------------------
464
+ /**
465
+ * HTTP escape hatches (`*-http-request`, objectName usually `httpRequest` but
466
+ * sometimes null) are kept as-is, not expanded — there is no object catalog to
467
+ * iterate.
468
+ */
469
+ const HTTP_PASSTHROUGH_OBJECT_NAMES = new Set(["httpRequest", "HttpRequest", "http_request"]);
470
+ export function isHttpPassthrough(entry) {
471
+ if (HTTP_PASSTHROUGH_OBJECT_NAMES.has(get(entry.operation, "objectName")))
472
+ return true;
473
+ // Some connectors leave objectName null on the http-request action; fall
474
+ // back to recognising the nodeType suffix.
475
+ return actionIdFromNodeType(entry.nodeType).endsWith("http-request");
476
+ }
477
+ // ---------------------------------------------------------------------------
478
+ // IS payload key normalization
479
+ // ---------------------------------------------------------------------------
480
+ /** Map a PascalCase key to camelCase (first character lowered). */
481
+ export function toLowerCamel(s) {
482
+ if (typeof s !== "string" || !s)
483
+ return s;
484
+ return (startsWithUpper(s) ? lowerFirst(s) : s);
485
+ }
486
+ /**
487
+ * Recursively lower-camel-case every dict key in `value`. The IS CLI started
488
+ * returning PascalCase keys for parameters, fields and references; the library
489
+ * uses camelCase, so the on-disk schema stays stable across CLI versions.
490
+ */
491
+ export function normalizeFieldKeys(value) {
492
+ if (isRecord(value)) {
493
+ const out = {};
494
+ for (const [k, v] of Object.entries(value))
495
+ out[toLowerCamel(k)] = normalizeFieldKeys(v);
496
+ return out;
497
+ }
498
+ if (Array.isArray(value))
499
+ return value.map(normalizeFieldKeys);
500
+ return value;
501
+ }
502
+ /** `_pick`: the first non-None value among `d[k]` for the given keys, else `null`. */
503
+ export function pick(d, ...keys) {
504
+ if (!isRecord(d))
505
+ return null;
506
+ for (const k of keys) {
507
+ const v = d[k];
508
+ if (v !== null && v !== undefined)
509
+ return v;
510
+ }
511
+ return null;
512
+ }
@@ -0,0 +1,45 @@
1
+ import { type Reporter } from "../reporter.js";
2
+ import { type UipClient } from "../uip/client.js";
3
+ import { type BuildOptions, type CatalogScope } from "./build.js";
4
+ export interface BuildLibraryCliOptions extends BuildOptions {
5
+ /** `--cache-dir`: raw `flow registry get` responses and the search result. */
6
+ cacheDir: string;
7
+ /** `--is-cache-dir`: `is resources describe/list` and `is triggers` payloads. */
8
+ isCacheDir: string;
9
+ /** `--connectors-cache-dir`: `is connectors metadata` payloads. */
10
+ connectorsCacheDir: string;
11
+ /** `--keep-temp`: leave the three cache directories behind. */
12
+ keepTemp: boolean;
13
+ /** `--concurrency`: parallel remote CLI calls. */
14
+ concurrency: number;
15
+ /**
16
+ * `--offline`: never run `uip`; treat an uncached call as a failed one.
17
+ *
18
+ * For replaying a build from the caches a previous run left behind, which is
19
+ * what the pipeline publishes when something looks wrong. See
20
+ * {@link OfflineUipClient} for why failing is the faithful answer.
21
+ */
22
+ offline: boolean;
23
+ }
24
+ /** argparse's defaults, verbatim. */
25
+ export declare const DEFAULTS: {
26
+ readonly outputDir: "./library";
27
+ readonly cacheDir: "./.registry-cache";
28
+ readonly isCacheDir: "./.is-cache";
29
+ readonly connectorsCacheDir: "./.is-connectors-cache";
30
+ readonly catalogScope: CatalogScope;
31
+ readonly concurrency: 10;
32
+ };
33
+ /**
34
+ * The options for this argv, or a {@link UsageError} carrying argparse's
35
+ * wording. Both `--flag value` and `--flag=value` are accepted, as argparse
36
+ * accepts them; anything that is neither a known flag nor a flag's value is an
37
+ * unrecognized argument, because the generator takes no positionals.
38
+ */
39
+ export declare function parseArgs(argv: readonly string[]): BuildLibraryCliOptions;
40
+ /** What a test swaps in for the tenant and the terminal. */
41
+ export interface RunDeps {
42
+ client?: UipClient;
43
+ reporter?: Reporter;
44
+ }
45
+ export declare function run(argv: string[], deps?: RunDeps): Promise<number>;