@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.
- package/dist/api-index.md +6 -6
- package/dist/cli/commands/registry.js +68 -12
- package/dist/registry/archive/entry-policy.d.ts +36 -0
- package/dist/registry/archive/entry-policy.js +60 -0
- package/dist/registry/archive/extract.d.ts +31 -0
- package/dist/registry/archive/extract.js +92 -0
- package/dist/registry/archive/zip-reader.d.ts +38 -0
- package/dist/registry/archive/zip-reader.js +256 -0
- package/dist/registry/codecs/pyjson.d.ts +62 -0
- package/dist/registry/codecs/pyjson.js +208 -0
- package/dist/registry/compat/compare.d.ts +18 -0
- package/dist/registry/compat/compare.js +32 -0
- package/dist/registry/compat/paths.d.ts +4 -0
- package/dist/registry/compat/paths.js +17 -0
- package/dist/registry/compat/pystr.d.ts +50 -0
- package/dist/registry/compat/pystr.js +221 -0
- package/dist/registry/concurrency.d.ts +10 -0
- package/dist/registry/concurrency.js +34 -0
- package/dist/registry/curated-overlay.d.ts +36 -4
- package/dist/registry/curated-overlay.js +87 -14
- package/dist/registry/errors.d.ts +26 -0
- package/dist/registry/errors.js +38 -0
- package/dist/registry/extractor.d.ts +9 -10
- package/dist/registry/extractor.js +5 -15
- package/dist/registry/files.d.ts +26 -0
- package/dist/registry/files.js +160 -0
- package/dist/registry/generators/descriptors.d.ts +81 -0
- package/dist/registry/generators/descriptors.js +405 -0
- package/dist/registry/generators/fields.d.ts +46 -0
- package/dist/registry/generators/fields.js +111 -0
- package/dist/registry/generators/markdown/common.d.ts +21 -0
- package/dist/registry/generators/markdown/common.js +55 -0
- package/dist/registry/generators/markdown/index.d.ts +33 -0
- package/dist/registry/generators/markdown/index.js +37 -0
- package/dist/registry/generators/markdown/page.d.ts +8 -0
- package/dist/registry/generators/markdown/page.js +52 -0
- package/dist/registry/generators/markdown/pyvalue.d.ts +34 -0
- package/dist/registry/generators/markdown/pyvalue.js +106 -0
- package/dist/registry/generators/markdown/sections/choosing-object.d.ts +2 -0
- package/dist/registry/generators/markdown/sections/choosing-object.js +52 -0
- package/dist/registry/generators/markdown/sections/example.d.ts +22 -0
- package/dist/registry/generators/markdown/sections/example.js +295 -0
- package/dist/registry/generators/markdown/sections/filtering.d.ts +2 -0
- package/dist/registry/generators/markdown/sections/filtering.js +76 -0
- package/dist/registry/generators/markdown/sections/header.d.ts +2 -0
- package/dist/registry/generators/markdown/sections/header.js +37 -0
- package/dist/registry/generators/markdown/sections/inputs.d.ts +30 -0
- package/dist/registry/generators/markdown/sections/inputs.js +151 -0
- package/dist/registry/generators/markdown/sections/lookups.d.ts +12 -0
- package/dist/registry/generators/markdown/sections/lookups.js +150 -0
- package/dist/registry/generators/markdown/sections/outputs.d.ts +3 -0
- package/dist/registry/generators/markdown/sections/outputs.js +9 -0
- package/dist/registry/generators/markdown/sections/resolving-fields.d.ts +2 -0
- package/dist/registry/generators/markdown/sections/resolving-fields.js +139 -0
- package/dist/registry/generators/markdown/sections/schema-dynamic.d.ts +2 -0
- package/dist/registry/generators/markdown/sections/schema-dynamic.js +86 -0
- package/dist/registry/generators/materialize.d.ts +30 -0
- package/dist/registry/generators/materialize.js +132 -0
- package/dist/registry/generators/md-symbols.d.ts +22 -0
- package/dist/registry/generators/md-symbols.js +58 -0
- package/dist/registry/generators/triggers.d.ts +15 -0
- package/dist/registry/generators/triggers.js +85 -0
- package/dist/registry/generators/type-registry.d.ts +50 -0
- package/dist/registry/generators/type-registry.js +154 -0
- package/dist/registry/generators/type-tree.d.ts +65 -0
- package/dist/registry/generators/type-tree.js +179 -0
- package/dist/registry/library-build/build.d.ts +57 -0
- package/dist/registry/library-build/build.js +286 -0
- package/dist/registry/library-build/canonical.d.ts +119 -0
- package/dist/registry/library-build/canonical.js +512 -0
- package/dist/registry/library-build/cli.d.ts +45 -0
- package/dist/registry/library-build/cli.js +182 -0
- package/dist/registry/library-build/context.d.ts +107 -0
- package/dist/registry/library-build/context.js +267 -0
- package/dist/registry/library-build/enrich.d.ts +36 -0
- package/dist/registry/library-build/enrich.js +158 -0
- package/dist/registry/library-build/events.d.ts +108 -0
- package/dist/registry/library-build/events.js +476 -0
- package/dist/registry/library-build/generic-expand.d.ts +77 -0
- package/dist/registry/library-build/generic-expand.js +363 -0
- package/dist/registry/library-build/plan.d.ts +71 -0
- package/dist/registry/library-build/plan.js +270 -0
- package/dist/registry/library-build/py.d.ts +45 -0
- package/dist/registry/library-build/py.js +167 -0
- package/dist/registry/library-build/schema-action.d.ts +38 -0
- package/dist/registry/library-build/schema-action.js +107 -0
- package/dist/registry/library-build/write.d.ts +6 -0
- package/dist/registry/library-build/write.js +26 -0
- package/dist/registry/library-reader.d.ts +20 -0
- package/dist/registry/library-reader.js +50 -0
- package/dist/registry/lookup-specs.d.ts +55 -0
- package/dist/registry/lookup-specs.js +140 -0
- package/dist/registry/materialize-cli.js +28 -14
- package/dist/registry/materializer.d.ts +25 -3
- package/dist/registry/materializer.js +45 -29
- package/dist/registry/model/library.d.ts +254 -0
- package/dist/registry/model/library.js +23 -0
- package/dist/registry/naming.d.ts +54 -0
- package/dist/registry/naming.js +142 -0
- package/dist/registry/orphans.d.ts +4 -0
- package/dist/registry/orphans.js +86 -0
- package/dist/registry/prepare/actions.d.ts +26 -0
- package/dist/registry/prepare/actions.js +94 -0
- package/dist/registry/prepare/connections.d.ts +181 -0
- package/dist/registry/prepare/connections.js +399 -0
- package/dist/registry/prepare/event.d.ts +96 -0
- package/dist/registry/prepare/event.js +268 -0
- package/dist/registry/prepare/handoff.d.ts +21 -0
- package/dist/registry/prepare/handoff.js +91 -0
- package/dist/registry/prepare/ledger.d.ts +104 -0
- package/dist/registry/prepare/ledger.js +216 -0
- package/dist/registry/prepare/messages.d.ts +190 -0
- package/dist/registry/prepare/messages.js +472 -0
- package/dist/registry/prepare/objects.d.ts +130 -0
- package/dist/registry/prepare/objects.js +318 -0
- package/dist/registry/prepare/overlay.d.ts +149 -0
- package/dist/registry/prepare/overlay.js +487 -0
- package/dist/registry/prepare/parents.d.ts +41 -0
- package/dist/registry/prepare/parents.js +122 -0
- package/dist/registry/prepare/pipeline.d.ts +91 -0
- package/dist/registry/prepare/pipeline.js +376 -0
- package/dist/registry/prepare/regenerate.d.ts +9 -0
- package/dist/registry/prepare/regenerate.js +50 -0
- package/dist/registry/prepare/resolve.d.ts +199 -0
- package/dist/registry/prepare/resolve.js +470 -0
- package/dist/registry/prepare/retry.d.ts +83 -0
- package/dist/registry/prepare/retry.js +109 -0
- package/dist/registry/prepare/source-import.d.ts +54 -0
- package/dist/registry/prepare/source-import.js +192 -0
- package/dist/registry/prepare/support.d.ts +41 -0
- package/dist/registry/prepare/support.js +178 -0
- package/dist/registry/prepare-cli.d.ts +16 -5
- package/dist/registry/prepare-cli.js +60 -53
- package/dist/registry/pull-cli.js +8 -7
- package/dist/registry/reporter.d.ts +26 -0
- package/dist/registry/reporter.js +41 -0
- package/dist/registry/uip/client.d.ts +99 -0
- package/dist/registry/uip/client.js +226 -0
- package/dist/registry/uip/commands.d.ts +40 -0
- package/dist/registry/uip/commands.js +80 -0
- package/dist/registry/uip/envelope.d.ts +37 -0
- package/dist/registry/uip/envelope.js +84 -0
- package/dist/registry/uip/response-cache.d.ts +28 -0
- package/dist/registry/uip/response-cache.js +57 -0
- package/lib/README.md +10 -8
- package/package.json +3 -3
- package/dist/generators/_connections.py +0 -331
- package/dist/generators/_events.py +0 -355
- package/dist/generators/_lookups.py +0 -114
- package/dist/generators/_naming.py +0 -185
- package/dist/generators/_objects.py +0 -299
- package/dist/generators/_resolve.py +0 -319
- package/dist/generators/convert_library_to_md.py +0 -1441
- package/dist/generators/extract_library.py +0 -125
- package/dist/generators/generate_connectors_ts.py +0 -936
- package/dist/generators/generate_library_json.py +0 -2527
- package/dist/generators/generate_triggers_ts.py +0 -110
- package/dist/generators/materialize_registry.py +0 -155
- package/dist/generators/prepare_connector.py +0 -1860
- package/dist/registry/python.d.ts +0 -26
- package/dist/registry/python.js +0 -46
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic-action expansion: one library entry per `(connector, object,
|
|
3
|
+
* operation)` the IS resources catalog advertises, using the matching Generic
|
|
4
|
+
* registry action as a template.
|
|
5
|
+
*
|
|
6
|
+
* A Generic registry action (`uipath.connector.<key>.insert-record`) is an
|
|
7
|
+
* untyped runtime dispatcher: the object is picked at design time. The
|
|
8
|
+
* expanded entries keep the SOURCE Generic nodeType so an emitter can produce
|
|
9
|
+
* a valid Flow node by re-stamping `operation.objectName` onto it, and land
|
|
10
|
+
* under `<connector>/generic/<object>/<op>@<ver>.json`.
|
|
11
|
+
*
|
|
12
|
+
* Connectors whose objects live on the tenant (`hasObjectDiscovery` /
|
|
13
|
+
* `hasCustomObjectDiscovery`) are NOT expanded: they ship one entry per verb,
|
|
14
|
+
* marked `objectFromDiscovery`, and the author lists objects against their own
|
|
15
|
+
* connection.
|
|
16
|
+
*/
|
|
17
|
+
import { sortedByKeys } from "../compat/compare.js";
|
|
18
|
+
import { afterLast, pyStr, pyStrip, pyTitle } from "../compat/pystr.js";
|
|
19
|
+
import { mapConcurrent } from "../concurrency.js";
|
|
20
|
+
import { messageOf } from "../errors.js";
|
|
21
|
+
import { isHttpPassthrough, normalizeFieldKeys, pick } from "./canonical.js";
|
|
22
|
+
import { isDescribeObject, isDescribeOperation, isResourcesList } from "./context.js";
|
|
23
|
+
import { deepCopy, get, item, pyIter, pyOr, truthy } from "./py.js";
|
|
24
|
+
/**
|
|
25
|
+
* `_generic_templates_by_op`: bucket Generic CRUD entries by IS operation
|
|
26
|
+
* name per connector. Generic CRUD actions carry an `operation` verb
|
|
27
|
+
* (`Create`/`Delete`/`Retrieve`/`List`/`Update`/`Replace`) but no
|
|
28
|
+
* `httpMethod`; the IS catalog returns the same verbs under
|
|
29
|
+
* `AvailableOperations[].Name`.
|
|
30
|
+
*/
|
|
31
|
+
export function genericTemplatesByOp(genericEntries) {
|
|
32
|
+
const out = new Map();
|
|
33
|
+
for (const e of genericEntries) {
|
|
34
|
+
if (isHttpPassthrough(e))
|
|
35
|
+
continue;
|
|
36
|
+
const opName = pyStrip(pyStr(pyOr(get(item(e, "operation"), "name"), "")));
|
|
37
|
+
if (!opName)
|
|
38
|
+
continue;
|
|
39
|
+
const key = item(item(e, "connector"), "key");
|
|
40
|
+
if (!truthy(key))
|
|
41
|
+
continue;
|
|
42
|
+
let byOp = out.get(key);
|
|
43
|
+
if (!byOp) {
|
|
44
|
+
byOp = new Map();
|
|
45
|
+
out.set(key, byOp);
|
|
46
|
+
}
|
|
47
|
+
byOp.set(opName, e);
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* `_make_expanded_entry`: one expanded entry from a Generic template + an
|
|
53
|
+
* (object, op) pair. `obj` is a `resources list` row, `op` an element of
|
|
54
|
+
* `AvailableOperations`, `opData` the per-operation describe `Data` (may be
|
|
55
|
+
* absent — the entry is still built, with an empty schema). The template's
|
|
56
|
+
* discovery flag is copied, never inferred from field counts.
|
|
57
|
+
*/
|
|
58
|
+
export function makeExpandedEntry(template, obj, op, opData) {
|
|
59
|
+
const entry = deepCopy(template);
|
|
60
|
+
const templateOperation = item(template, "operation");
|
|
61
|
+
const objectName = pyOr(get(obj, "Name"), "");
|
|
62
|
+
const objectDisplay = pyOr(get(obj, "DisplayName"), objectName);
|
|
63
|
+
const opName = pyOr(pick(op, "Name", "name"), "");
|
|
64
|
+
const opMethod = pyOr(pick(op, "Method", "method"), get(templateOperation, "httpMethod", ""));
|
|
65
|
+
const opPath = pyOr(pick(op, "Path", "path"), "");
|
|
66
|
+
const opDescription = pyOr(pick(op, "Description", "description"), "");
|
|
67
|
+
const operation = item(entry, "operation");
|
|
68
|
+
operation.objectName = objectName;
|
|
69
|
+
operation.objectDisplayName = objectDisplay;
|
|
70
|
+
operation.name = opName;
|
|
71
|
+
operation.httpMethod = opMethod;
|
|
72
|
+
operation.path = opPath;
|
|
73
|
+
operation.subType = pyOr(get(obj, "SubType"), get(templateOperation, "subType", ""));
|
|
74
|
+
operation.activityType = "GenericExpanded";
|
|
75
|
+
operation.sourceNodeType = item(template, "nodeType");
|
|
76
|
+
const display = item(entry, "display");
|
|
77
|
+
display.label = pyStrip(`${pyStr(opName)} ${pyStr(objectDisplay)}`);
|
|
78
|
+
if (truthy(opDescription))
|
|
79
|
+
display.description = opDescription;
|
|
80
|
+
// Pull schema + path template from the per-operation describe payload.
|
|
81
|
+
if (truthy(opData)) {
|
|
82
|
+
const opInner = pyOr(pick(opData, "Operation", "operation"), {});
|
|
83
|
+
const opInnerPath = pick(opInner, "Path", "path");
|
|
84
|
+
if (truthy(opInnerPath))
|
|
85
|
+
operation.pathTemplate = opInnerPath;
|
|
86
|
+
const params = pyOr(pick(opData, "Parameters", "parameters"), []);
|
|
87
|
+
if (truthy(params))
|
|
88
|
+
operation.parameters = normalizeFieldKeys(params);
|
|
89
|
+
const requestFields = pyOr(pick(opData, "RequestFields", "requestFields"), []);
|
|
90
|
+
const responseFields = pyOr(pick(opData, "ResponseFields", "responseFields"), []);
|
|
91
|
+
entry.inputSchema = { fields: normalizeFieldKeys(requestFields) };
|
|
92
|
+
entry.outputSchema = { fields: normalizeFieldKeys(responseFields) };
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
entry.inputSchema = { fields: [] };
|
|
96
|
+
entry.outputSchema = { fields: [] };
|
|
97
|
+
}
|
|
98
|
+
return entry;
|
|
99
|
+
}
|
|
100
|
+
/** `_unique_by_name`: first occurrence of each `Name`, in order. */
|
|
101
|
+
export function uniqueByName(objects) {
|
|
102
|
+
const unique = new Map();
|
|
103
|
+
for (const obj of pyIter(pyOr(objects, []))) {
|
|
104
|
+
const objectName = get(obj, "Name");
|
|
105
|
+
if (truthy(objectName) && !unique.has(objectName))
|
|
106
|
+
unique.set(objectName, obj);
|
|
107
|
+
}
|
|
108
|
+
return unique;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* `_list_generic_objects`: list and deduplicate the objects available to one
|
|
112
|
+
* connector. When the caller named specific objects and the catalog does not
|
|
113
|
+
* contain them, retry once with a refresh before believing it — a stale
|
|
114
|
+
* `uip is resources list` cache reports a real object as missing.
|
|
115
|
+
*/
|
|
116
|
+
export async function listGenericObjects(ctx, connectorKey, templatesByOp, objectNames) {
|
|
117
|
+
const operationFilter = templatesByOp.size === 1 ? [...templatesByOp.keys()][0] : null;
|
|
118
|
+
const objects = await isResourcesList(ctx, connectorKey, { operation: operationFilter });
|
|
119
|
+
let unique = uniqueByName(objects);
|
|
120
|
+
if (truthy(objectNames) && !objectNames.every((name) => unique.has(name))) {
|
|
121
|
+
const refreshed = uniqueByName(await isResourcesList(ctx, connectorKey, { operation: operationFilter, refresh: true }));
|
|
122
|
+
// Keep the cached answer when the refresh failed outright, so a network
|
|
123
|
+
// blip does not turn a working lookup into "not found".
|
|
124
|
+
if (refreshed.size > 0)
|
|
125
|
+
unique = refreshed;
|
|
126
|
+
}
|
|
127
|
+
return [...unique.values()];
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* `expand_generic_for_connector`: materialize per-(object, op) entries for one
|
|
131
|
+
* connector, sequentially — the shape `prepare` uses. `objectNames`, when
|
|
132
|
+
* given, restricts the objects described; an empty list describes none.
|
|
133
|
+
*/
|
|
134
|
+
export async function expandGenericForConnector(ctx, connectorKey, templatesByOp, objectNames) {
|
|
135
|
+
const objects = await listGenericObjects(ctx, connectorKey, templatesByOp, objectNames);
|
|
136
|
+
if (objects.length === 0)
|
|
137
|
+
return [];
|
|
138
|
+
// Dedupe by (object_name, op_name): some IS catalogs list the same object
|
|
139
|
+
// twice (uipath-servicenow-servicenow exposes `curated_incident_task` twice).
|
|
140
|
+
const seenPairs = new Set();
|
|
141
|
+
const out = [];
|
|
142
|
+
for (const obj of objects) {
|
|
143
|
+
const objectName = get(obj, "Name");
|
|
144
|
+
if (!truthy(objectName))
|
|
145
|
+
continue;
|
|
146
|
+
if (objectNames !== undefined && !objectNames.includes(objectName))
|
|
147
|
+
continue;
|
|
148
|
+
const objData = await isDescribeObject(ctx, connectorKey, objectName);
|
|
149
|
+
if (!truthy(objData))
|
|
150
|
+
continue;
|
|
151
|
+
const available = pyOr(pick(objData, "AvailableOperations", "availableOperations"), []);
|
|
152
|
+
for (const op of pyIter(available)) {
|
|
153
|
+
const opName = pick(op, "Name", "name");
|
|
154
|
+
if (!truthy(opName))
|
|
155
|
+
continue;
|
|
156
|
+
const pair = JSON.stringify([objectName, opName]);
|
|
157
|
+
if (seenPairs.has(pair))
|
|
158
|
+
continue;
|
|
159
|
+
seenPairs.add(pair);
|
|
160
|
+
const template = templatesByOp.get(opName);
|
|
161
|
+
if (!truthy(template))
|
|
162
|
+
continue;
|
|
163
|
+
const opData = await isDescribeOperation(ctx, connectorKey, objectName, opName);
|
|
164
|
+
out.push(makeExpandedEntry(template, obj, op, opData));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* `objects_come_from_the_connection`: true when this connector's object set is
|
|
171
|
+
* only knowable per connection. Field discovery alone does NOT qualify.
|
|
172
|
+
*/
|
|
173
|
+
export function objectsComeFromTheConnection(entry) {
|
|
174
|
+
const discovery = pyOr(get(pyOr(get(entry, "connector"), {}), "discovery"), {});
|
|
175
|
+
return truthy(pyOr(get(discovery, "hasObjectDiscovery"), get(discovery, "hasCustomObjectDiscovery")));
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* `as_discovery_entry`: recast a Generic CRUD template as the discovery entry
|
|
179
|
+
* shipped for it — flagged, object cleared, labelled by verb ("Update Record",
|
|
180
|
+
* "List Records").
|
|
181
|
+
*/
|
|
182
|
+
export function asDiscoveryEntry(entry) {
|
|
183
|
+
const operation = item(entry, "operation");
|
|
184
|
+
operation.objectFromDiscovery = true;
|
|
185
|
+
operation.objectName = "";
|
|
186
|
+
const name = pyOr(get(operation, "name"), "");
|
|
187
|
+
if (truthy(name)) {
|
|
188
|
+
item(entry, "display").label = name === "List" ? "List Records" : `${pyStr(name)} Record`;
|
|
189
|
+
}
|
|
190
|
+
return entry;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* `disambiguate_discovery_labels`: keep two discovery entries on one connector
|
|
194
|
+
* from sharing a label. Only the colliding ones are renamed, from their own
|
|
195
|
+
* node type (`search-records` → "Search Records", via `str.title()`).
|
|
196
|
+
*/
|
|
197
|
+
export function disambiguateDiscoveryLabels(entries) {
|
|
198
|
+
const groups = new Map();
|
|
199
|
+
for (const entry of entries) {
|
|
200
|
+
if (!truthy(get(pyOr(get(entry, "operation"), {}), "objectFromDiscovery")))
|
|
201
|
+
continue;
|
|
202
|
+
const key = JSON.stringify([
|
|
203
|
+
get(pyOr(get(entry, "connector"), {}), "key"),
|
|
204
|
+
get(pyOr(get(entry, "display"), {}), "label"),
|
|
205
|
+
]);
|
|
206
|
+
const group = groups.get(key);
|
|
207
|
+
if (group)
|
|
208
|
+
group.push(entry);
|
|
209
|
+
else
|
|
210
|
+
groups.set(key, [entry]);
|
|
211
|
+
}
|
|
212
|
+
for (const items of groups.values()) {
|
|
213
|
+
if (items.length < 2)
|
|
214
|
+
continue;
|
|
215
|
+
for (const entry of items) {
|
|
216
|
+
const slug = afterLast(pyStr(pyOr(get(entry, "nodeType"), "")), ".");
|
|
217
|
+
if (slug)
|
|
218
|
+
item(entry, "display").label = pyTitle(slug.replaceAll("-", " ").replaceAll("_", " "));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return entries;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* A deterministic order for expanded entries: connector key, object name,
|
|
225
|
+
* operation name, node type — stable, so ties keep their input order.
|
|
226
|
+
*
|
|
227
|
+
* DELIBERATE FIX. The Python appended expansions as thread-pool futures
|
|
228
|
+
* completed, so the order two entries reached `write_library` in — and, when
|
|
229
|
+
* both claimed one path with equal input counts, which one survived — varied
|
|
230
|
+
* from run to run. Sorting here makes the library a function of its inputs.
|
|
231
|
+
* The index is sorted separately, so this changes no bytes when no such tie
|
|
232
|
+
* exists.
|
|
233
|
+
*/
|
|
234
|
+
export function sortExpandedEntries(entries) {
|
|
235
|
+
return sortedByKeys(entries, (e) => pyStr(pyOr(get(pyOr(get(e, "connector"), {}), "key"), "")), (e) => pyStr(pyOr(get(pyOr(get(e, "operation"), {}), "objectName"), "")), (e) => pyStr(pyOr(get(pyOr(get(e, "operation"), {}), "name"), "")), (e) => pyStr(pyOr(get(e, "nodeType"), "")));
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* `expand_all_generics`: expand every Generic entry into per-(object, op)
|
|
239
|
+
* entries. Returns `[curatedAndPassthrough, expanded]`: Curated entries pass
|
|
240
|
+
* through untouched; HTTP-passthrough Generic entries pass through as
|
|
241
|
+
* one-per-connector entries; CRUD Generic entries are dropped in favour of
|
|
242
|
+
* their expansions; object-dynamic ones become discovery entries.
|
|
243
|
+
*
|
|
244
|
+
* The three remote phases (list objects, describe objects, describe
|
|
245
|
+
* operations) each run under `mapConcurrent`, so `expanded` comes back in
|
|
246
|
+
* connector × object × operation order rather than completion order.
|
|
247
|
+
*/
|
|
248
|
+
export async function expandAllGenerics(ctx, entries) {
|
|
249
|
+
const curatedAndPassthrough = [];
|
|
250
|
+
const genericCrud = [];
|
|
251
|
+
let discoveryEntries = 0;
|
|
252
|
+
for (const e of entries) {
|
|
253
|
+
const at = get(item(e, "operation"), "activityType", "");
|
|
254
|
+
if (at === "Generic") {
|
|
255
|
+
if (isHttpPassthrough(e)) {
|
|
256
|
+
curatedAndPassthrough.push(e);
|
|
257
|
+
}
|
|
258
|
+
else if (objectsComeFromTheConnection(e)) {
|
|
259
|
+
curatedAndPassthrough.push(asDiscoveryEntry(e));
|
|
260
|
+
discoveryEntries += 1;
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
genericCrud.push(e);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
// A generic connector EVENT is answered by `resolveConnectorEvents`;
|
|
268
|
+
// Curated or anything else passes through.
|
|
269
|
+
curatedAndPassthrough.push(e);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
disambiguateDiscoveryLabels(curatedAndPassthrough);
|
|
273
|
+
const templates = genericTemplatesByOp(genericCrud);
|
|
274
|
+
ctx.reporter.info(` ${genericCrud.length} Generic CRUD entries across ${templates.size} connectors`);
|
|
275
|
+
ctx.reporter.info(` ${discoveryEntries} kept unexpanded: objects come from the connection`);
|
|
276
|
+
ctx.reporter.info(` ${entries.filter((e) => isHttpPassthrough(e)).length} HTTP-passthrough entries (kept as-is)`);
|
|
277
|
+
const expanded = [];
|
|
278
|
+
if (templates.size === 0)
|
|
279
|
+
return [curatedAndPassthrough, expanded];
|
|
280
|
+
// Phase 1: the object catalog of every templated connector.
|
|
281
|
+
const templateKeys = [...templates.keys()];
|
|
282
|
+
let listed = 0;
|
|
283
|
+
let objectsSoFar = 0;
|
|
284
|
+
const listings = await mapConcurrent(templateKeys, ctx.concurrency, async (k) => {
|
|
285
|
+
let objects = [];
|
|
286
|
+
try {
|
|
287
|
+
objects = await listGenericObjects(ctx, k, templates.get(k));
|
|
288
|
+
}
|
|
289
|
+
catch (exc) {
|
|
290
|
+
ctx.reporter.warn(` WARN: object listing failed for ${k}: ${messageOf(exc)}`);
|
|
291
|
+
}
|
|
292
|
+
listed += 1;
|
|
293
|
+
objectsSoFar += objects.length;
|
|
294
|
+
if (listed % 25 === 0 || listed === templateKeys.length) {
|
|
295
|
+
ctx.reporter.info(` Listed objects for ${listed}/${templateKeys.length} connectors (${objectsSoFar} objects so far)`);
|
|
296
|
+
}
|
|
297
|
+
return objects;
|
|
298
|
+
});
|
|
299
|
+
const connectorObjects = [];
|
|
300
|
+
templateKeys.forEach((k, i) => {
|
|
301
|
+
for (const obj of listings[i])
|
|
302
|
+
connectorObjects.push([k, obj]);
|
|
303
|
+
});
|
|
304
|
+
// Phase 2: which operations each object offers, matched to a template.
|
|
305
|
+
let described = 0;
|
|
306
|
+
let candidatesSoFar = 0;
|
|
307
|
+
const perObject = await mapConcurrent(connectorObjects, ctx.concurrency, async ([k, obj]) => {
|
|
308
|
+
const found = [];
|
|
309
|
+
let objData;
|
|
310
|
+
try {
|
|
311
|
+
objData = await isDescribeObject(ctx, k, item(obj, "Name"));
|
|
312
|
+
}
|
|
313
|
+
catch (exc) {
|
|
314
|
+
objData = undefined;
|
|
315
|
+
ctx.reporter.warn(` WARN: object describe failed for ${k}/${pyStr(item(obj, "Name"))}: ${messageOf(exc)}`);
|
|
316
|
+
}
|
|
317
|
+
if (truthy(objData)) {
|
|
318
|
+
const available = pyOr(pick(objData, "AvailableOperations", "availableOperations"), []);
|
|
319
|
+
const seenOpNames = new Set();
|
|
320
|
+
for (const op of pyIter(available)) {
|
|
321
|
+
const opName = pick(op, "Name", "name");
|
|
322
|
+
if (!truthy(opName) || seenOpNames.has(opName))
|
|
323
|
+
continue;
|
|
324
|
+
seenOpNames.add(opName);
|
|
325
|
+
const template = templates.get(k).get(opName);
|
|
326
|
+
if (truthy(template))
|
|
327
|
+
found.push([k, obj, op, template]);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
described += 1;
|
|
331
|
+
candidatesSoFar += found.length;
|
|
332
|
+
if (described % 100 === 0 || described === connectorObjects.length) {
|
|
333
|
+
ctx.reporter.info(` Described ${described}/${connectorObjects.length} objects (${candidatesSoFar} operations so far)`);
|
|
334
|
+
}
|
|
335
|
+
return found;
|
|
336
|
+
});
|
|
337
|
+
const operationCandidates = perObject.flat();
|
|
338
|
+
// Phase 3: the per-operation schema, and the entry.
|
|
339
|
+
let expandedCount = 0;
|
|
340
|
+
const results = await mapConcurrent(operationCandidates, ctx.concurrency, async ([k, obj, op, template]) => {
|
|
341
|
+
const objectName = item(obj, "Name");
|
|
342
|
+
const opName = pick(op, "Name", "name");
|
|
343
|
+
let opData;
|
|
344
|
+
try {
|
|
345
|
+
opData = await isDescribeOperation(ctx, k, objectName, opName);
|
|
346
|
+
}
|
|
347
|
+
catch (exc) {
|
|
348
|
+
opData = undefined;
|
|
349
|
+
ctx.reporter.warn(` WARN: operation describe failed for ${k}/${objectName}/${opName}: ${messageOf(exc)}`);
|
|
350
|
+
}
|
|
351
|
+
const entry = makeExpandedEntry(template, obj, op, opData);
|
|
352
|
+
expandedCount += 1;
|
|
353
|
+
if (expandedCount % 100 === 0 || expandedCount === operationCandidates.length) {
|
|
354
|
+
ctx.reporter.info(` Expanded ${expandedCount}/${operationCandidates.length} object operations`);
|
|
355
|
+
}
|
|
356
|
+
return entry;
|
|
357
|
+
});
|
|
358
|
+
expanded.push(...results);
|
|
359
|
+
const schemaDynamic = expanded.filter((e) => truthy(get(item(e, "runtime"), "requiresConnectionForSchema"))).length;
|
|
360
|
+
ctx.reporter.info(` expanded ${expanded.length} entries from ${genericCrud.length} Generic CRUD actions`);
|
|
361
|
+
ctx.reporter.info(` schema-dynamic among expanded: ${schemaDynamic}/${expanded.length}`);
|
|
362
|
+
return [curatedAndPassthrough, expanded];
|
|
363
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { IndexRow, LibraryEntry, TriggerCatalogueRow } from "../model/library.js";
|
|
2
|
+
import { actionIdFromNodeType } from "./canonical.js";
|
|
3
|
+
import { type Dict } from "./py.js";
|
|
4
|
+
export { actionIdFromNodeType };
|
|
5
|
+
/** `_safe_path_segment`: sanitize a string for use as one path segment. */
|
|
6
|
+
export declare function safePathSegment(s: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* `_input_field_count`: how much of a schema an entry carries — the tie-break
|
|
9
|
+
* when two claim one path, since whichever loses is dropped from the catalogue.
|
|
10
|
+
*/
|
|
11
|
+
export declare function inputFieldCount(entry: unknown): number;
|
|
12
|
+
/**
|
|
13
|
+
* `_entry_relpath`: where to write `entry` relative to the library root, sans
|
|
14
|
+
* extension, `/`-separated. Expanded Generic entries land under
|
|
15
|
+
* `<connector>/generic/<object>/<op>@<ver>`; per-object EVENT variants under
|
|
16
|
+
* `<connector>/events/<object>/<action>@<ver>` (they keep the template's
|
|
17
|
+
* nodeType, so the object is what separates them).
|
|
18
|
+
*/
|
|
19
|
+
export declare function entryRelpath(entry: LibraryEntry): string;
|
|
20
|
+
/**
|
|
21
|
+
* `_build_v1_definition`: project a registry `Data.Node` into the shape a
|
|
22
|
+
* `.flow` file expects under `definitions[]`: drop `connectorMethodInfo` and
|
|
23
|
+
* `outputResponseDefinition`, add the three fields the editor stamps on every
|
|
24
|
+
* connector definition when absent (`setdefault`: a present `null` stays).
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildV1Definition(nodeData: unknown): Dict;
|
|
27
|
+
/**
|
|
28
|
+
* The raw `Data.Node` for a node type whose registry response the build
|
|
29
|
+
* holds, or `undefined` when it holds none (Python: `v1def_source_paths.get`
|
|
30
|
+
* answered nothing). May throw; the planner reports and moves on.
|
|
31
|
+
*/
|
|
32
|
+
export type NodeLoader = (nodeType: string) => unknown;
|
|
33
|
+
export interface LibraryPlan {
|
|
34
|
+
/** POSIX-relative path → file text, `index.json` included. */
|
|
35
|
+
files: Map<string, string>;
|
|
36
|
+
/** The index rows, sorted as written. */
|
|
37
|
+
index: IndexRow[];
|
|
38
|
+
/** `WARN:` lines, verbatim. */
|
|
39
|
+
warnings: string[];
|
|
40
|
+
/** How many entry files the plan holds — `write_library`'s return value. */
|
|
41
|
+
written: number;
|
|
42
|
+
}
|
|
43
|
+
export interface PlanOptions {
|
|
44
|
+
schemaVersion?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* `write_library` without the disk.
|
|
48
|
+
*
|
|
49
|
+
* Collision protocol, per relpath: an identical second entry is skipped; one
|
|
50
|
+
* with fewer-or-equal input fields is skipped with a warning; a richer one
|
|
51
|
+
* replaces the earlier (its index row is dropped, its files are overwritten).
|
|
52
|
+
* The sidecar is projected from the node the loader answers, with the entry's
|
|
53
|
+
* resolved input fields stamped in (always for an expanded Generic node,
|
|
54
|
+
* otherwise only when the entry has any) and its `eventSchema` stamped in when
|
|
55
|
+
* present. Index rows carry `objectFromDiscovery` only when set.
|
|
56
|
+
*/
|
|
57
|
+
export declare function planLibrary(entries: readonly LibraryEntry[], loadNode: NodeLoader, opts?: PlanOptions): LibraryPlan;
|
|
58
|
+
/**
|
|
59
|
+
* Every entry file in `files` that has no index row: an empty list is the
|
|
60
|
+
* invariant `write_library` used to break (index one row longer than the
|
|
61
|
+
* directory). Sidecars and the known non-entry files are exempt.
|
|
62
|
+
*/
|
|
63
|
+
export declare function orphanCheck(files: ReadonlyMap<string, string>, index: readonly IndexRow[]): string[];
|
|
64
|
+
/**
|
|
65
|
+
* `write_triggers_side_file`'s rows: every trigger nodeType the registry
|
|
66
|
+
* search exposed, sorted by `(connectorKey, NodeType)`.
|
|
67
|
+
*/
|
|
68
|
+
export declare function triggerRows(searchPayload: unknown): TriggerCatalogueRow[];
|
|
69
|
+
export declare const TRIGGER_CATALOGUE_NOTE: string;
|
|
70
|
+
/** The side-file's text: the envelope around the rows, `json.dump(indent=2)` + newline. */
|
|
71
|
+
export declare function triggerCatalogueDocument(rows: readonly TriggerCatalogueRow[]): string;
|