@uipath/maestro-builder-sdk 6.41.2 → 6.43.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 +99 -99
- package/dist/api-members.md +307 -307
- package/dist/check.d.ts +0 -19
- package/dist/check.js +40 -1036
- package/dist/cli/commands/registry.js +15 -0
- package/dist/compile-cli.js +2 -1
- package/dist/core/actions.d.ts +1 -713
- package/dist/core/actions.js +0 -193
- package/dist/core/agent-sidecar.d.ts +186 -0
- package/dist/core/agent-sidecar.js +409 -0
- package/dist/core/agent-tool-resources.d.ts +62 -0
- package/dist/core/agent-tool-resources.js +81 -0
- package/dist/core/cli-spelling.d.ts +9 -0
- package/dist/core/cli-spelling.js +9 -0
- package/dist/{config.js → core/connector-configuration.js} +2 -2
- package/dist/core/library-cache.d.ts +0 -9
- package/dist/core/library-cache.js +0 -9
- package/dist/core/library.d.ts +47 -0
- package/dist/core/library.js +135 -34
- package/dist/core/list-envelope.d.ts +36 -1
- package/dist/core/list-envelope.js +24 -19
- package/dist/core/lookup-store.d.ts +4 -3
- package/dist/core/lookup-store.js +46 -38
- package/dist/core/node-id-placeholder.d.ts +8 -0
- package/dist/core/node-id-placeholder.js +10 -0
- package/dist/core/read-text.d.ts +26 -0
- package/dist/core/read-text.js +43 -0
- package/dist/core/ts-literal.d.ts +6 -0
- package/dist/core/ts-literal.js +15 -0
- package/dist/core/typed-raw-node.js +0 -5
- package/dist/decompile.js +34 -822
- package/dist/flow-node-registry.js +2 -1
- package/dist/flow-node-type.d.ts +130 -6
- package/dist/flow-sdk.d.ts +1 -0
- package/dist/flow-sdk.js +1 -0
- package/dist/nodes/agent/flow/check.d.ts +88 -0
- package/dist/nodes/agent/flow/check.js +873 -0
- package/dist/nodes/agent/flow/factories.d.ts +134 -0
- package/dist/nodes/agent/flow/factories.js +143 -0
- package/dist/nodes/agent/flow/family.d.ts +3 -0
- package/dist/nodes/agent/flow/family.js +8 -0
- package/dist/nodes/agent/flow/index.d.ts +3 -0
- package/dist/nodes/agent/flow/index.js +3 -0
- package/dist/nodes/agent/flow/inline-agent-lift.d.ts +44 -0
- package/dist/nodes/agent/flow/inline-agent-lift.js +572 -0
- package/dist/nodes/agent/flow/inline-agent.d.ts +15 -0
- package/dist/nodes/agent/flow/inline-agent.js +526 -0
- package/dist/nodes/agent/flow/node-type.d.ts +20 -0
- package/dist/nodes/agent/flow/node-type.js +422 -0
- package/dist/nodes/agent/flow/resources.d.ts +502 -0
- package/dist/nodes/agent/flow/resources.js +1409 -0
- package/dist/nodes/agent/flow/spec.d.ts +448 -0
- package/dist/nodes/agent/flow/spec.js +1 -0
- package/dist/nodes/process/flow/check.d.ts +40 -2
- package/dist/nodes/process/flow/check.js +156 -0
- package/dist/nodes/process/flow/factories.d.ts +58 -3
- package/dist/nodes/process/flow/factories.js +59 -2
- package/dist/nodes/process/flow/node-type.d.ts +4 -1
- package/dist/nodes/process/flow/node-type.js +181 -10
- package/dist/nodes/process/flow/spec.d.ts +112 -5
- package/dist/registry/describe-cli.d.ts +10 -0
- package/dist/registry/describe-cli.js +41 -0
- package/dist/registry/library-reader.d.ts +12 -2
- package/dist/registry/library-reader.js +17 -16
- package/dist/registry/lookup-specs.d.ts +17 -1
- package/dist/registry/lookup-specs.js +3 -7
- package/dist/registry/path-cli.js +2 -1
- package/dist/registry/search-cli.js +2 -1
- package/dist/serialize.d.ts +9 -54
- package/dist/serialize.js +47 -2763
- package/package.json +2 -2
- /package/dist/{config.d.ts → core/connector-configuration.d.ts} +0 -0
- /package/dist/{ixp-definition.json → core/ixp-definition.json} +0 -0
package/dist/core/library.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* library — resolve Integration Service connector definitions from the on-disk
|
|
3
|
+
* connector library cache (the same `library-json` the Flow Code arm uses).
|
|
4
|
+
*
|
|
5
|
+
* For a connector action we need two files per action:
|
|
6
|
+
* <key>/<action>@<ver>.json the entry (connector key, HTTP method, endpoint)
|
|
7
|
+
* <key>/<action>@<ver>.v1def.json the Flow `definitions[]` shape (model, ports, …)
|
|
8
|
+
*/
|
|
9
|
+
import { type ReadText } from './read-text.js';
|
|
1
10
|
import { type LookupResolutions, type LookupSpec } from './lookups.js';
|
|
2
11
|
import type { ByoaEventFacts, ByoaLedger } from './byoa.js';
|
|
3
12
|
export interface ResolvedConnector {
|
|
@@ -217,8 +226,27 @@ export interface LibraryOpts {
|
|
|
217
226
|
* library exposed (a thin `describe` response must not shrink what compiles).
|
|
218
227
|
*/
|
|
219
228
|
overlayDir?: string;
|
|
229
|
+
/** Reads each library and overlay file. Defaults to the disk; a host without one passes its own. */
|
|
230
|
+
readText?: ReadText;
|
|
231
|
+
/** Receives the library's warnings. Defaults to stderr; a host without `process` passes its own. */
|
|
232
|
+
warn?: (message: string) => void;
|
|
220
233
|
}
|
|
221
234
|
export { ConnectorResolveError, type ConnectorResolveCode } from './connector-resolve-error.js';
|
|
235
|
+
/** What `registry describe` renders one operation's page from. */
|
|
236
|
+
export interface PageSource {
|
|
237
|
+
entry: any;
|
|
238
|
+
/** The Flow definition beside the entry, or `undefined` when there is none. */
|
|
239
|
+
v1def: any;
|
|
240
|
+
version: string;
|
|
241
|
+
/** True when the operation came from the prepared `connectors-local` overlay. */
|
|
242
|
+
prepared: boolean;
|
|
243
|
+
/** The folder the operation came from. */
|
|
244
|
+
dir: string;
|
|
245
|
+
/** That layer's index rows. The page names the operation's symbol from them. */
|
|
246
|
+
rows: readonly any[];
|
|
247
|
+
/** `objectName`s the ledgers declare connector-invariant. */
|
|
248
|
+
invariant: ReadonlySet<string>;
|
|
249
|
+
}
|
|
222
250
|
export declare class Library {
|
|
223
251
|
private layers;
|
|
224
252
|
/** The LIST-ENVELOPE LEDGER, overlay first so a prepared entry can add a row. */
|
|
@@ -239,6 +267,8 @@ export declare class Library {
|
|
|
239
267
|
readonly byoaVerifications: ByoaLedger;
|
|
240
268
|
/** Collections the ledger declares connector-invariant; overlay unions over baked. */
|
|
241
269
|
private readonly invariantCollections;
|
|
270
|
+
private readonly read;
|
|
271
|
+
private readonly warn;
|
|
242
272
|
constructor(dir: string, opts?: LibraryOpts);
|
|
243
273
|
/**
|
|
244
274
|
* Resolve a connector ACTION (`uipath.connector.<key>.<action>`).
|
|
@@ -267,6 +297,23 @@ export declare class Library {
|
|
|
267
297
|
*/
|
|
268
298
|
lookupSpecs(key: string, action: string, version?: string, objectName?: string): Record<string, LookupSpec>;
|
|
269
299
|
resolve(key: string, action: string, version?: string, objectName?: string): ResolvedConnector;
|
|
300
|
+
/**
|
|
301
|
+
* One operation's page source, for `registry describe`: the entry and definition
|
|
302
|
+
* of one index row, from the first layer that carries it. That is the prepared
|
|
303
|
+
* overlay before the library, the order `resolve` reads in. Without a version, the
|
|
304
|
+
* newest. A generic operation has a row per object, as `registry pull` writes a
|
|
305
|
+
* page per object: `objectName` picks the row; without it, the object-less row is
|
|
306
|
+
* used when there is one, else CONNECTOR_OBJECT_REQUIRED lists the objects. Rows are
|
|
307
|
+
* chosen as `resolveInLayer` chooses them, so a page shows only what `compile` accepts.
|
|
308
|
+
*/
|
|
309
|
+
pageSource(nodeType: string, version?: string, objectName?: string): PageSource;
|
|
310
|
+
/**
|
|
311
|
+
* The row an object the layer does not list reads its page from, as `resolveInLayer`
|
|
312
|
+
* case 1 accepts it: the one object-less row of an un-expanded generic operation.
|
|
313
|
+
* A single non-generic operation takes no object at all.
|
|
314
|
+
*/
|
|
315
|
+
private connectionScopedRow;
|
|
316
|
+
private pageOf;
|
|
270
317
|
/**
|
|
271
318
|
* Whether a connector step's schema is materialized locally — see
|
|
272
319
|
* {@link SchemaReadiness}. Index rows are enough for the `object` question
|
package/dist/core/library.js
CHANGED
|
@@ -6,10 +6,8 @@
|
|
|
6
6
|
* <key>/<action>@<ver>.json the entry (connector key, HTTP method, endpoint)
|
|
7
7
|
* <key>/<action>@<ver>.v1def.json the Flow `definitions[]` shape (model, ports, …)
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { prepareCommand } from './cli-spelling.js';
|
|
12
|
-
import { PULL_COMMAND } from './library-cache.js';
|
|
9
|
+
import { cacheFor, libraryPath, readJson, readTextFromDisk } from './read-text.js';
|
|
10
|
+
import { PULL_COMMAND, prepareCommand } from './cli-spelling.js';
|
|
13
11
|
import { editDistance } from './edit-distance.js';
|
|
14
12
|
import { loadListEnvelopes, lookupListEnvelope } from './list-envelope.js';
|
|
15
13
|
import { lookupSpecOf } from './lookups.js';
|
|
@@ -37,26 +35,35 @@ export class Library {
|
|
|
37
35
|
byoaVerifications;
|
|
38
36
|
/** Collections the ledger declares connector-invariant; overlay unions over baked. */
|
|
39
37
|
invariantCollections;
|
|
38
|
+
read;
|
|
39
|
+
warn;
|
|
40
40
|
constructor(dir, opts = {}) {
|
|
41
|
-
this.
|
|
42
|
-
this.
|
|
41
|
+
const read = (this.read = opts.readText ?? readTextFromDisk);
|
|
42
|
+
this.warn = opts.warn ?? ((message) => { globalThis.process?.stderr?.write(message); });
|
|
43
|
+
// A new identity per Library, so the per-reader caches never keep an overlay file: `prepare` rewrites them.
|
|
44
|
+
const readOverlay = (path) => read(path);
|
|
45
|
+
this.lookupResolutions = loadLookupResolutions(opts.overlayDir, readOverlay);
|
|
46
|
+
this.byoaVerifications = loadByoaLedger(opts.overlayDir, readOverlay);
|
|
43
47
|
this.invariantCollections = new Set([
|
|
44
|
-
...loadInvariantCollections(dir),
|
|
45
|
-
...loadInvariantCollections(opts.overlayDir),
|
|
48
|
+
...loadInvariantCollections(dir, read),
|
|
49
|
+
...loadInvariantCollections(opts.overlayDir, readOverlay),
|
|
46
50
|
]);
|
|
47
51
|
// Overlay first (local wins), then the baked library.
|
|
48
52
|
if (opts.overlayDir) {
|
|
49
|
-
const
|
|
50
|
-
if (
|
|
51
|
-
this.layers.push({ dir: opts.overlayDir, entries: JSON.parse(
|
|
52
|
-
this.envelopeRows.push(...loadListEnvelopes(opts.overlayDir));
|
|
53
|
+
const overlayIndex = readOverlay(libraryPath(opts.overlayDir, 'index.json'));
|
|
54
|
+
if (overlayIndex !== undefined)
|
|
55
|
+
this.layers.push({ dir: opts.overlayDir, entries: JSON.parse(overlayIndex).entries ?? [], overlay: true });
|
|
56
|
+
this.envelopeRows.push(...loadListEnvelopes(opts.overlayDir, readOverlay));
|
|
53
57
|
}
|
|
54
|
-
this.envelopeRows.push(...loadListEnvelopes(dir));
|
|
55
|
-
const idx =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
this.envelopeRows.push(...loadListEnvelopes(dir, read));
|
|
59
|
+
const idx = libraryPath(dir, 'index.json');
|
|
60
|
+
const entries = libraryEntries(read, idx);
|
|
61
|
+
if (entries === undefined)
|
|
62
|
+
throw new Error(`connector library not found at ${idx}. `
|
|
63
|
+
+ (read === readTextFromDisk
|
|
64
|
+
? `Run \`${PULL_COMMAND}\`, or point --library / $FLOW_SDK_LIBRARY_JSON at a library-json dir.`
|
|
65
|
+
: 'The reader passed to Library has no such file.'));
|
|
66
|
+
this.layers.push({ dir, entries });
|
|
60
67
|
}
|
|
61
68
|
/**
|
|
62
69
|
* Resolve a connector ACTION (`uipath.connector.<key>.<action>`).
|
|
@@ -102,6 +109,83 @@ export class Library {
|
|
|
102
109
|
resolve(key, action, version, objectName) {
|
|
103
110
|
return this.resolveType(`uipath.connector.${key}.${action}`, version, objectName);
|
|
104
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* One operation's page source, for `registry describe`: the entry and definition
|
|
114
|
+
* of one index row, from the first layer that carries it. That is the prepared
|
|
115
|
+
* overlay before the library, the order `resolve` reads in. Without a version, the
|
|
116
|
+
* newest. A generic operation has a row per object, as `registry pull` writes a
|
|
117
|
+
* page per object: `objectName` picks the row; without it, the object-less row is
|
|
118
|
+
* used when there is one, else CONNECTOR_OBJECT_REQUIRED lists the objects. Rows are
|
|
119
|
+
* chosen as `resolveInLayer` chooses them, so a page shows only what `compile` accepts.
|
|
120
|
+
*/
|
|
121
|
+
pageSource(nodeType, version, objectName) {
|
|
122
|
+
const layers = this.layers.flatMap((layer) => {
|
|
123
|
+
const matches = layer.entries.filter((e) => e?.nodeType === nodeType && (!version || String(e.version) === version)
|
|
124
|
+
&& typeof e.path === 'string' && e.path.endsWith('.json'));
|
|
125
|
+
if (!matches.length)
|
|
126
|
+
return [];
|
|
127
|
+
const newest = matches.map((e) => String(e.version))
|
|
128
|
+
.reduce((a, b) => (b.localeCompare(a, undefined, { numeric: true }) > 0 ? b : a));
|
|
129
|
+
return [{ layer, rows: matches.filter((e) => String(e.version) === newest) }];
|
|
130
|
+
});
|
|
131
|
+
if (!layers.length) {
|
|
132
|
+
throw new ConnectorResolveError('CONNECTOR_NOT_IN_LIBRARY', `connector not in library: ${nodeType}${version ? '@' + version : ''}.`
|
|
133
|
+
+ this.describeMiss(nodeType, version, undefined, false));
|
|
134
|
+
}
|
|
135
|
+
const objects = [...new Set(layers.flatMap(({ rows }) => rows.map((e) => e.objectName)
|
|
136
|
+
.filter((o) => typeof o === 'string' && o !== '')))].sort();
|
|
137
|
+
// `compile` refuses a missing object when any layer holds several rows with no one template row.
|
|
138
|
+
if (objectName === undefined && layers.some(({ rows }) => rows.length > 1 && rows.filter((e) => !e.objectName).length !== 1)) {
|
|
139
|
+
throw new ConnectorResolveError('CONNECTOR_OBJECT_REQUIRED', `connector ${nodeType} is a GENERIC operation covering ${objects.length} objects — `
|
|
140
|
+
+ `say which one with --object, e.g. --object ${objects[0] ?? '<object>'}. ${describeObjects(objects)}`);
|
|
141
|
+
}
|
|
142
|
+
for (const { layer, rows } of layers) {
|
|
143
|
+
const row = objectName === undefined
|
|
144
|
+
? (rows.length === 1 ? rows[0] : rows.find((e) => !e.objectName))
|
|
145
|
+
: rows.find((e) => e.objectName === objectName) ?? this.connectionScopedRow(nodeType, rows, objectName);
|
|
146
|
+
if (row)
|
|
147
|
+
return this.pageOf(nodeType, layer, row);
|
|
148
|
+
}
|
|
149
|
+
const parts = nodeType.split('.');
|
|
150
|
+
const eventNs = parts[2] === 'trigger' || parts[2] === 'event';
|
|
151
|
+
const near = nearest(objectName ?? '', objects);
|
|
152
|
+
throw new ConnectorResolveError('CONNECTOR_NOT_IN_LIBRARY', `connector ${nodeType} has no page for object "${objectName}"`
|
|
153
|
+
+ `${near ? ` (nearest: "${near}")` : ''}. ${describeObjects(objects)} If it is a connection-scoped object, run `
|
|
154
|
+
+ `\`${prepareCommand(eventNs ? parts[3] : parts[2], (eventNs ? parts.slice(4) : parts.slice(3)).join('.'), '--object', String(objectName))}\` `
|
|
155
|
+
+ 'to prepare it, then describe again.');
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The row an object the layer does not list reads its page from, as `resolveInLayer`
|
|
159
|
+
* case 1 accepts it: the one object-less row of an un-expanded generic operation.
|
|
160
|
+
* A single non-generic operation takes no object at all.
|
|
161
|
+
*/
|
|
162
|
+
connectionScopedRow(nodeType, rows, objectName) {
|
|
163
|
+
if (rows.length !== 1)
|
|
164
|
+
return undefined;
|
|
165
|
+
const only = rows[0];
|
|
166
|
+
if (only.activityType === 'Generic' || only.activityType === 'GenericTrigger' || only.objectFromDiscovery === true) {
|
|
167
|
+
return only.objectName ? undefined : only;
|
|
168
|
+
}
|
|
169
|
+
throw new ConnectorResolveError('CONNECTOR_OBJECT_NOT_GENERIC', `connector ${nodeType} does not have an object "${objectName}": it is a single, `
|
|
170
|
+
+ `non-generic operation addressing ${only.objectName ? `"${only.objectName}"` : 'no object'}. `
|
|
171
|
+
+ 'Drop --object (it is only for generic operations, whose node type covers many objects).');
|
|
172
|
+
}
|
|
173
|
+
pageOf(nodeType, layer, row) {
|
|
174
|
+
const entryPath = libraryPath(layer.dir, row.path);
|
|
175
|
+
const entry = readLibraryFile(nodeType, entryPath, () => readJson(this.read, entryPath));
|
|
176
|
+
const sidecarPath = v1defPathOf(entryPath);
|
|
177
|
+
const sidecar = this.read(sidecarPath);
|
|
178
|
+
const raw = sidecar === undefined ? undefined : readLibraryFile(nodeType, sidecarPath, () => JSON.parse(sidecar));
|
|
179
|
+
return {
|
|
180
|
+
entry,
|
|
181
|
+
v1def: Array.isArray(raw) ? raw[0] : raw,
|
|
182
|
+
version: String(row.version),
|
|
183
|
+
prepared: layer.overlay === true,
|
|
184
|
+
dir: layer.dir,
|
|
185
|
+
rows: layer.entries,
|
|
186
|
+
invariant: this.invariantCollections,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
105
189
|
/**
|
|
106
190
|
* Whether a connector step's schema is materialized locally — see
|
|
107
191
|
* {@link SchemaReadiness}. Index rows are enough for the `object` question
|
|
@@ -158,7 +242,7 @@ export class Library {
|
|
|
158
242
|
let entry;
|
|
159
243
|
try {
|
|
160
244
|
const source = rows.find(({ overlay }) => !overlay) ?? rows[0];
|
|
161
|
-
entry =
|
|
245
|
+
entry = readJson(this.read, libraryPath(source.dir, source.row.path));
|
|
162
246
|
}
|
|
163
247
|
catch {
|
|
164
248
|
return undefined;
|
|
@@ -191,7 +275,7 @@ export class Library {
|
|
|
191
275
|
continue;
|
|
192
276
|
if (wire.version && indexed.version !== wire.version)
|
|
193
277
|
continue;
|
|
194
|
-
const entry =
|
|
278
|
+
const entry = readJson(this.read, libraryPath(layer.dir, indexed.path));
|
|
195
279
|
const operation = entry.operation ?? {};
|
|
196
280
|
if (wire.httpMethod && String(operation.httpMethod ?? '').toUpperCase() !== wire.httpMethod.toUpperCase())
|
|
197
281
|
continue;
|
|
@@ -289,8 +373,9 @@ export class Library {
|
|
|
289
373
|
continue;
|
|
290
374
|
if (wire.version && indexed.version !== wire.version)
|
|
291
375
|
continue;
|
|
292
|
-
const
|
|
293
|
-
const
|
|
376
|
+
const entryPath = libraryPath(layer.dir, indexed.path);
|
|
377
|
+
const entry = readJson(this.read, entryPath);
|
|
378
|
+
const operation = normalizeEventOperation({ entry, v1def: readV1def(this.read, entryPath) }).entry.operation ?? {};
|
|
294
379
|
// A GENERIC event's entry names no object — the wire's object is the
|
|
295
380
|
// author's choice, so it identifies nothing here and comes back as `object`.
|
|
296
381
|
const generic = operation.activityType === 'GenericTrigger';
|
|
@@ -600,7 +685,7 @@ export class Library {
|
|
|
600
685
|
* `connector not in library: …send-mail-v2.` said nothing that would reveal
|
|
601
686
|
* that. It resorted to grepping the library's JSON out of `node_modules`.
|
|
602
687
|
*/
|
|
603
|
-
describeMiss(nodeType, version, objectName) {
|
|
688
|
+
describeMiss(nodeType, version, objectName, searched = true) {
|
|
604
689
|
// `uipath.connector.<key>.<op>`, or `uipath.connector.(trigger|event).<key>.<ev>`
|
|
605
690
|
// for the two event namespaces. No connector key contains a dot.
|
|
606
691
|
const parts = nodeType.split('.');
|
|
@@ -609,7 +694,8 @@ export class Library {
|
|
|
609
694
|
const asked = (eventNs ? parts.slice(4) : parts.slice(3)).join('.');
|
|
610
695
|
if (!key)
|
|
611
696
|
return '';
|
|
612
|
-
|
|
697
|
+
// `describe` names no folder: on a browser host it is a store the agent cannot open.
|
|
698
|
+
const where = searched ? ` Searched: ${this.layers.map((l) => l.dir).join(', ')}.` : '';
|
|
613
699
|
if (eventNs)
|
|
614
700
|
return this.describeEventMiss(parts[2], key, asked, where, version, objectName);
|
|
615
701
|
const ops = this.opsUnder(`uipath.connector.${key}.`);
|
|
@@ -753,7 +839,7 @@ export class Library {
|
|
|
753
839
|
return this.build(layer.dir, { ...only, objectName }, nodeType, { objectNameUnverified: true });
|
|
754
840
|
}
|
|
755
841
|
if (only.activityType === 'Generic' || only.objectFromDiscovery === true) {
|
|
756
|
-
|
|
842
|
+
this.warn(` warn: connector ${nodeType}: the library carries this operation un-expanded `
|
|
757
843
|
+ `(one object-less entry). Object "${objectName}" is accepted as a connection-scoped `
|
|
758
844
|
+ `object — the schema comes from the generic entry and the tenant resolves the name at `
|
|
759
845
|
+ `dispatch. If it is a typo, compile will not catch it.\n`);
|
|
@@ -784,7 +870,7 @@ export class Library {
|
|
|
784
870
|
// sibling whose path template is exactly `/<its object>` — the only shape a
|
|
785
871
|
// substitution can re-point cleanly.
|
|
786
872
|
const near = nearest(objectName, objects);
|
|
787
|
-
|
|
873
|
+
this.warn(` warn: connector ${nodeType}: object "${objectName}" is not one of the `
|
|
788
874
|
+ `${objects.length} the library lists for it${near ? ` (nearest: "${near}")` : ''}. `
|
|
789
875
|
+ `Accepted as a connection-scoped object — the schema comes from a sibling entry and the `
|
|
790
876
|
+ `tenant resolves the name at dispatch. If it is a typo, compile will not catch it.\n`);
|
|
@@ -800,7 +886,7 @@ export class Library {
|
|
|
800
886
|
pickDerivationSibling(dir, matches) {
|
|
801
887
|
const templateOf = (e) => {
|
|
802
888
|
try {
|
|
803
|
-
const entry =
|
|
889
|
+
const entry = readJson(this.read, libraryPath(dir, e.path));
|
|
804
890
|
return entry.operation?.pathTemplate ?? entry.operation?.path ?? '';
|
|
805
891
|
}
|
|
806
892
|
catch {
|
|
@@ -814,14 +900,14 @@ export class Library {
|
|
|
814
900
|
return substitutable ?? matches[0];
|
|
815
901
|
}
|
|
816
902
|
build(dir, e, nodeType, opts = {}) {
|
|
817
|
-
const entryPath =
|
|
903
|
+
const entryPath = libraryPath(dir, e.path);
|
|
818
904
|
// The two files the index row promises. A missing or corrupt one is a fault in
|
|
819
905
|
// the library on disk, not in the author's source, but compile cannot go on
|
|
820
906
|
// without it, so it is a typed refusal like the others: `check` reports it with
|
|
821
907
|
// the same code and text instead of passing a step compile then dies on with a
|
|
822
908
|
// bare ENOENT (#1015).
|
|
823
|
-
const entry = readLibraryFile(nodeType, entryPath, () =>
|
|
824
|
-
const v1def = readLibraryFile(nodeType, v1defPathOf(entryPath), () => readV1def(entryPath));
|
|
909
|
+
const entry = readLibraryFile(nodeType, entryPath, () => readJson(this.read, entryPath));
|
|
910
|
+
const v1def = readLibraryFile(nodeType, v1defPathOf(entryPath), () => readV1def(this.read, entryPath));
|
|
825
911
|
// A stamped object (case 4) reaches here with the index entry's `objectName`
|
|
826
912
|
// replaced but the sibling's FILE on disk — so restate the object and derive
|
|
827
913
|
// the endpoint from the sibling's own template, rather than shipping a node
|
|
@@ -846,7 +932,7 @@ export class Library {
|
|
|
846
932
|
&& entry.operation.objectName !== e.objectName) {
|
|
847
933
|
const stale = entry.operation.objectName;
|
|
848
934
|
const derived = deriveObjectPath(entry.operation, e.objectName);
|
|
849
|
-
|
|
935
|
+
this.warn(` warn: connector ${nodeType}: the library index says objectName "${e.objectName}" but its `
|
|
850
936
|
+ `definition file says "${stale}". Using the index and deriving the endpoint`
|
|
851
937
|
+ (derived.path ? ` (${entry.operation.path} -> ${derived.path})` : '')
|
|
852
938
|
+ `; the definition is the stale side when these disagree.\n`);
|
|
@@ -950,13 +1036,28 @@ function v1defPathOf(entryPath) {
|
|
|
950
1036
|
* damaged after it was built: an interrupted pull, a hand-edited overlay. The
|
|
951
1037
|
* remedy is the same either way: rebuild the layer the file belongs to.
|
|
952
1038
|
*/
|
|
1039
|
+
/** Each host reader's parsed library index. A library build does not change; the disk default still reads afresh. */
|
|
1040
|
+
const INDEX_CACHE = new WeakMap();
|
|
1041
|
+
/** The `entries` of the index at `path`, or `undefined` when there is none. */
|
|
1042
|
+
function libraryEntries(read, path) {
|
|
1043
|
+
const cache = read === readTextFromDisk ? undefined : cacheFor(INDEX_CACHE, read);
|
|
1044
|
+
const cached = cache?.get(path);
|
|
1045
|
+
if (cached)
|
|
1046
|
+
return cached;
|
|
1047
|
+
const text = read(path);
|
|
1048
|
+
if (text === undefined)
|
|
1049
|
+
return undefined;
|
|
1050
|
+
const entries = JSON.parse(text).entries ?? [];
|
|
1051
|
+
cache?.set(path, entries);
|
|
1052
|
+
return entries;
|
|
1053
|
+
}
|
|
953
1054
|
function readLibraryFile(nodeType, path, read) {
|
|
954
1055
|
try {
|
|
955
1056
|
return read();
|
|
956
1057
|
}
|
|
957
1058
|
catch (error) {
|
|
958
1059
|
const message = error instanceof Error ? error.message : String(error);
|
|
959
|
-
//
|
|
1060
|
+
// A read error already names the path; a JSON SyntaxError does not.
|
|
960
1061
|
const reason = message.includes(path) ? message : `${path}: ${message}`;
|
|
961
1062
|
throw new ConnectorResolveError('CONNECTOR_LIBRARY_ENTRY_UNREADABLE', `connector library entry for ${nodeType} is unreadable: ${reason}. The library index lists `
|
|
962
1063
|
+ `this operation, but the file is missing or not valid JSON, so the library on disk is incomplete. `
|
|
@@ -980,8 +1081,8 @@ function readLibraryFile(nodeType, path, read) {
|
|
|
980
1081
|
* reported both its connection and `FolderKey` as "not configured" (UV-16774) while
|
|
981
1082
|
* validate and debug — which fold the case — passed.
|
|
982
1083
|
*/
|
|
983
|
-
function readV1def(entryPath) {
|
|
984
|
-
const raw =
|
|
1084
|
+
function readV1def(read, entryPath) {
|
|
1085
|
+
const raw = readJson(read, v1defPathOf(entryPath));
|
|
985
1086
|
const v1def = Array.isArray(raw) ? raw[0] : raw;
|
|
986
1087
|
const bindings = v1def?.model?.bindings;
|
|
987
1088
|
if (typeof bindings?.resource === 'string' && bindings.resource.toLowerCase() === 'connection') {
|
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* list-envelope — read the LIST-ENVELOPE LEDGER that sits beside the connector
|
|
3
|
+
* library's `index.json`.
|
|
4
|
+
*
|
|
5
|
+
* ## What the ledger answers
|
|
6
|
+
*
|
|
7
|
+
* A connector LIST operation's node output is either the array itself (**bare**,
|
|
8
|
+
* `envelope: null`) or that array inside a one-field wrapper object
|
|
9
|
+
* (`envelope: "records"` ⇒ `{records: […]}`). **Nothing in the library declares
|
|
10
|
+
* which**: `outputSchema.fields` describes ONE record either way, and the two
|
|
11
|
+
* operations that declare no fields at all sit on opposite sides of what the
|
|
12
|
+
* corpus guessed ([#156](https://github.com/UiPath/flow-builder-sdk/issues/156)).
|
|
13
|
+
* It cannot be derived, so it is measured and written down — one row per
|
|
14
|
+
* operation, each carrying the evidence that put it there.
|
|
15
|
+
*
|
|
16
|
+
* ## The rule this file exists to enforce
|
|
17
|
+
*
|
|
18
|
+
* **An operation with no row is UNDECLARED, and undeclared means every reader
|
|
19
|
+
* behaves exactly as it did before the ledger existed.** The emitter declares no
|
|
20
|
+
* container, and a reader must go on treating an unknown name on a list output as
|
|
21
|
+
* a possible wrapper. 2,149 of the shipped library's 7,029 entries are List
|
|
22
|
+
* operations and the ledger holds eight — that ratio is the design, not a gap.
|
|
23
|
+
* It grows on demand, one measurement at a time.
|
|
24
|
+
*
|
|
25
|
+
* Measured 2026-08-02 (card G13, eight `flow debug` / product-runtime readings):
|
|
26
|
+
* every operation measured so far is **bare**, including both the corpus called
|
|
27
|
+
* wrappers. The ledger's own `$comment` carries the mechanism and the evidence
|
|
28
|
+
* tiers; this module only reads it.
|
|
29
|
+
*
|
|
30
|
+
* If a second reader is ever needed outside this package, copy this one rather
|
|
31
|
+
* than sharing it through a new package boundary: a 40-line JSON reader is the
|
|
32
|
+
* cheaper half. The LEDGER is the single source of truth — the readers are
|
|
33
|
+
* interchangeable.
|
|
34
|
+
*/
|
|
35
|
+
import { type ReadText } from './read-text.js';
|
|
1
36
|
/** One measured operation. `envelope: null` is the BARE array. */
|
|
2
37
|
export interface ListEnvelopeRow {
|
|
3
38
|
nodeType: string;
|
|
@@ -21,7 +56,7 @@ export interface ListEnvelopeRow {
|
|
|
21
56
|
* an empty ledger — every operation undeclared — which is what a library built
|
|
22
57
|
* before this card, or a bare fixture library, looks like.
|
|
23
58
|
*/
|
|
24
|
-
export declare function loadListEnvelopes(libraryDir: string | undefined): ListEnvelopeRow[];
|
|
59
|
+
export declare function loadListEnvelopes(libraryDir: string | undefined, read?: ReadText): ListEnvelopeRow[];
|
|
25
60
|
/**
|
|
26
61
|
* The row for one operation, or `undefined` when it is undeclared.
|
|
27
62
|
*
|
|
@@ -32,36 +32,41 @@
|
|
|
32
32
|
* cheaper half. The LEDGER is the single source of truth — the readers are
|
|
33
33
|
* interchangeable.
|
|
34
34
|
*/
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
const CACHE = new Map();
|
|
35
|
+
import { cacheFor, libraryPath, readTextFromDisk } from './read-text.js';
|
|
36
|
+
const CACHE = new WeakMap();
|
|
38
37
|
/**
|
|
39
38
|
* Every declared row, from `<libraryDir>/list-envelopes.json`. An absent file is
|
|
40
39
|
* an empty ledger — every operation undeclared — which is what a library built
|
|
41
40
|
* before this card, or a bare fixture library, looks like.
|
|
42
41
|
*/
|
|
43
|
-
export function loadListEnvelopes(libraryDir) {
|
|
42
|
+
export function loadListEnvelopes(libraryDir, read = readTextFromDisk) {
|
|
44
43
|
if (!libraryDir)
|
|
45
44
|
return [];
|
|
46
|
-
const
|
|
45
|
+
const cache = cacheFor(CACHE, read);
|
|
46
|
+
const cached = cache.get(libraryDir);
|
|
47
47
|
if (cached)
|
|
48
48
|
return cached;
|
|
49
|
+
let text;
|
|
50
|
+
try {
|
|
51
|
+
text = read(libraryPath(libraryDir, 'list-envelopes.json'));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Not cached: the reader may serve the file on a later call.
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
49
57
|
let rows = [];
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// edit is losing the exactness, never breaking a compile.
|
|
61
|
-
rows = [];
|
|
62
|
-
}
|
|
58
|
+
try {
|
|
59
|
+
const parsed = JSON.parse(text ?? '{}');
|
|
60
|
+
if (Array.isArray(parsed.operations))
|
|
61
|
+
rows = parsed.operations;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// A malformed ledger reads as an EMPTY one rather than throwing: the
|
|
65
|
+
// permissive path is the pre-ledger behaviour, so the worst case of a bad
|
|
66
|
+
// edit is losing the exactness, never breaking a compile.
|
|
67
|
+
rows = [];
|
|
63
68
|
}
|
|
64
|
-
|
|
69
|
+
cache.set(libraryDir, rows);
|
|
65
70
|
return rows;
|
|
66
71
|
}
|
|
67
72
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ReadText } from './read-text.js';
|
|
1
2
|
import type { ByoaLedger } from './byoa.js';
|
|
2
3
|
import type { LookupResolutions } from './lookups.js';
|
|
3
4
|
/**
|
|
@@ -8,7 +9,7 @@ import type { LookupResolutions } from './lookups.js';
|
|
|
8
9
|
* — it is a compile error naming the `prepare` command to run, which is the
|
|
9
10
|
* behaviour an author wants from a missing resolution anyway.
|
|
10
11
|
*/
|
|
11
|
-
export declare function loadLookupResolutions(overlayDir: string | undefined): LookupResolutions;
|
|
12
|
+
export declare function loadLookupResolutions(overlayDir: string | undefined, read?: ReadText): LookupResolutions;
|
|
12
13
|
/**
|
|
13
14
|
* Load the `byoa` section of `<overlayDir>/resolutions.json` — the BYOA checks
|
|
14
15
|
* `registry prepare` recorded (see `core/byoa.ts`) — or an empty map.
|
|
@@ -19,7 +20,7 @@ export declare function loadLookupResolutions(overlayDir: string | undefined): L
|
|
|
19
20
|
* call rather than cached: `prepare` rewrites this file, and a stale "never
|
|
20
21
|
* checked" answer would refuse a flow that was just verified.
|
|
21
22
|
*/
|
|
22
|
-
export declare function loadByoaLedger(overlayDir: string | undefined): ByoaLedger;
|
|
23
|
+
export declare function loadByoaLedger(overlayDir: string | undefined, read?: ReadText): ByoaLedger;
|
|
23
24
|
/**
|
|
24
25
|
* The invariant-collection ledger's `objectName` set, from a library dir.
|
|
25
26
|
*
|
|
@@ -31,4 +32,4 @@ export declare function loadByoaLedger(overlayDir: string | undefined): ByoaLedg
|
|
|
31
32
|
* @param libraryDir - A `library-json` directory, or the local overlay.
|
|
32
33
|
* @returns Every `objectName` declared connector-invariant.
|
|
33
34
|
*/
|
|
34
|
-
export declare function loadInvariantCollections(libraryDir: string | undefined): Set<string>;
|
|
35
|
+
export declare function loadInvariantCollections(libraryDir: string | undefined, read?: ReadText): Set<string>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { join } from 'node:path';
|
|
1
|
+
import { cacheFor, libraryPath, readTextFromDisk } from './read-text.js';
|
|
3
2
|
/**
|
|
4
3
|
* core/lookup-store — the two on-disk side-tables the lookup surface reads.
|
|
5
4
|
*
|
|
@@ -14,7 +13,7 @@ import { join } from 'node:path';
|
|
|
14
13
|
* the CLIs need to read files. Keeping the split makes that structural rather
|
|
15
14
|
* than a thing to remember.
|
|
16
15
|
*/
|
|
17
|
-
const RESOLUTION_CACHE = new
|
|
16
|
+
const RESOLUTION_CACHE = new WeakMap();
|
|
18
17
|
/**
|
|
19
18
|
* Load `<overlayDir>/resolutions.json`, or an empty map.
|
|
20
19
|
*
|
|
@@ -23,26 +22,32 @@ const RESOLUTION_CACHE = new Map();
|
|
|
23
22
|
* — it is a compile error naming the `prepare` command to run, which is the
|
|
24
23
|
* behaviour an author wants from a missing resolution anyway.
|
|
25
24
|
*/
|
|
26
|
-
export function loadLookupResolutions(overlayDir) {
|
|
25
|
+
export function loadLookupResolutions(overlayDir, read = readTextFromDisk) {
|
|
27
26
|
if (!overlayDir)
|
|
28
27
|
return {};
|
|
29
|
-
const
|
|
28
|
+
const cache = cacheFor(RESOLUTION_CACHE, read);
|
|
29
|
+
const cached = cache.get(overlayDir);
|
|
30
30
|
if (cached)
|
|
31
31
|
return cached;
|
|
32
|
+
let text;
|
|
33
|
+
try {
|
|
34
|
+
text = read(libraryPath(overlayDir, 'resolutions.json'));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Not cached: the reader may serve the file on a later call.
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
32
40
|
let rows = {};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (parsed.resolutions && typeof parsed.resolutions === 'object') {
|
|
38
|
-
rows = parsed.resolutions;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
rows = {};
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(text ?? '{}');
|
|
43
|
+
if (parsed.resolutions && typeof parsed.resolutions === 'object') {
|
|
44
|
+
rows = parsed.resolutions;
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
|
-
|
|
47
|
+
catch {
|
|
48
|
+
rows = {};
|
|
49
|
+
}
|
|
50
|
+
cache.set(overlayDir, rows);
|
|
46
51
|
return rows;
|
|
47
52
|
}
|
|
48
53
|
/**
|
|
@@ -55,14 +60,11 @@ export function loadLookupResolutions(overlayDir) {
|
|
|
55
60
|
* call rather than cached: `prepare` rewrites this file, and a stale "never
|
|
56
61
|
* checked" answer would refuse a flow that was just verified.
|
|
57
62
|
*/
|
|
58
|
-
export function loadByoaLedger(overlayDir) {
|
|
63
|
+
export function loadByoaLedger(overlayDir, read = readTextFromDisk) {
|
|
59
64
|
if (!overlayDir)
|
|
60
65
|
return {};
|
|
61
|
-
const file = join(overlayDir, 'resolutions.json');
|
|
62
|
-
if (!existsSync(file))
|
|
63
|
-
return {};
|
|
64
66
|
try {
|
|
65
|
-
const parsed = JSON.parse(
|
|
67
|
+
const parsed = JSON.parse(read(libraryPath(overlayDir, 'resolutions.json')) ?? '{}');
|
|
66
68
|
const byoa = parsed?.byoa;
|
|
67
69
|
return byoa && typeof byoa === 'object' && !Array.isArray(byoa) ? byoa : {};
|
|
68
70
|
}
|
|
@@ -81,30 +83,36 @@ export function loadByoaLedger(overlayDir) {
|
|
|
81
83
|
* @param libraryDir - A `library-json` directory, or the local overlay.
|
|
82
84
|
* @returns Every `objectName` declared connector-invariant.
|
|
83
85
|
*/
|
|
84
|
-
export function loadInvariantCollections(libraryDir) {
|
|
86
|
+
export function loadInvariantCollections(libraryDir, read = readTextFromDisk) {
|
|
85
87
|
if (!libraryDir)
|
|
86
88
|
return new Set();
|
|
87
|
-
const
|
|
89
|
+
const cache = cacheFor(INVARIANT_CACHE, read);
|
|
90
|
+
const cached = cache.get(libraryDir);
|
|
88
91
|
if (cached)
|
|
89
92
|
return cached;
|
|
93
|
+
let text;
|
|
94
|
+
try {
|
|
95
|
+
text = read(libraryPath(libraryDir, 'invariant-collections.json'));
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Not cached: the reader may serve the file on a later call.
|
|
99
|
+
return new Set();
|
|
100
|
+
}
|
|
90
101
|
const out = new Set();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (typeof name === 'string')
|
|
99
|
-
out.add(name);
|
|
100
|
-
}
|
|
102
|
+
try {
|
|
103
|
+
const parsed = JSON.parse(text ?? '{}');
|
|
104
|
+
if (Array.isArray(parsed.collections)) {
|
|
105
|
+
for (const row of parsed.collections) {
|
|
106
|
+
const name = row?.objectName;
|
|
107
|
+
if (typeof name === 'string')
|
|
108
|
+
out.add(name);
|
|
101
109
|
}
|
|
102
110
|
}
|
|
103
|
-
catch {
|
|
104
|
-
/* an empty ledger — see above */
|
|
105
|
-
}
|
|
106
111
|
}
|
|
107
|
-
|
|
112
|
+
catch {
|
|
113
|
+
/* an empty ledger — see above */
|
|
114
|
+
}
|
|
115
|
+
cache.set(libraryDir, out);
|
|
108
116
|
return out;
|
|
109
117
|
}
|
|
110
|
-
const INVARIANT_CACHE = new
|
|
118
|
+
const INVARIANT_CACHE = new WeakMap();
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `{nodeId}` placeholder a definition writes where a node's own id goes
|
|
3
|
+
* (an output's source, a model's context), filled in for one node. Every
|
|
4
|
+
* node's definition-derived fields go through this one rule, so an agent's
|
|
5
|
+
* outputs and every other node's cannot fill it differently.
|
|
6
|
+
*/
|
|
7
|
+
/** `obj` with every `{nodeId}` in its JSON replaced by `nodeId`, as a fresh copy. */
|
|
8
|
+
export declare function subNodeId<T>(obj: T, nodeId: string): T;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `{nodeId}` placeholder a definition writes where a node's own id goes
|
|
3
|
+
* (an output's source, a model's context), filled in for one node. Every
|
|
4
|
+
* node's definition-derived fields go through this one rule, so an agent's
|
|
5
|
+
* outputs and every other node's cannot fill it differently.
|
|
6
|
+
*/
|
|
7
|
+
/** `obj` with every `{nodeId}` in its JSON replaced by `nodeId`, as a fresh copy. */
|
|
8
|
+
export function subNodeId(obj, nodeId) {
|
|
9
|
+
return JSON.parse(JSON.stringify(obj).split('{nodeId}').join(nodeId));
|
|
10
|
+
}
|