@uipath/maestro-builder-sdk 6.4.0 → 6.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/dist/cli/commands/registry.js +68 -12
  2. package/dist/registry/archive/entry-policy.d.ts +36 -0
  3. package/dist/registry/archive/entry-policy.js +60 -0
  4. package/dist/registry/archive/extract.d.ts +31 -0
  5. package/dist/registry/archive/extract.js +92 -0
  6. package/dist/registry/archive/zip-reader.d.ts +38 -0
  7. package/dist/registry/archive/zip-reader.js +256 -0
  8. package/dist/registry/codecs/pyjson.d.ts +62 -0
  9. package/dist/registry/codecs/pyjson.js +208 -0
  10. package/dist/registry/compat/compare.d.ts +18 -0
  11. package/dist/registry/compat/compare.js +32 -0
  12. package/dist/registry/compat/paths.d.ts +4 -0
  13. package/dist/registry/compat/paths.js +17 -0
  14. package/dist/registry/compat/pystr.d.ts +50 -0
  15. package/dist/registry/compat/pystr.js +221 -0
  16. package/dist/registry/concurrency.d.ts +10 -0
  17. package/dist/registry/concurrency.js +34 -0
  18. package/dist/registry/curated-overlay.d.ts +36 -4
  19. package/dist/registry/curated-overlay.js +87 -14
  20. package/dist/registry/errors.d.ts +26 -0
  21. package/dist/registry/errors.js +38 -0
  22. package/dist/registry/extractor.d.ts +9 -10
  23. package/dist/registry/extractor.js +5 -15
  24. package/dist/registry/files.d.ts +26 -0
  25. package/dist/registry/files.js +160 -0
  26. package/dist/registry/generators/descriptors.d.ts +81 -0
  27. package/dist/registry/generators/descriptors.js +405 -0
  28. package/dist/registry/generators/fields.d.ts +46 -0
  29. package/dist/registry/generators/fields.js +111 -0
  30. package/dist/registry/generators/markdown/common.d.ts +21 -0
  31. package/dist/registry/generators/markdown/common.js +55 -0
  32. package/dist/registry/generators/markdown/index.d.ts +33 -0
  33. package/dist/registry/generators/markdown/index.js +37 -0
  34. package/dist/registry/generators/markdown/page.d.ts +8 -0
  35. package/dist/registry/generators/markdown/page.js +52 -0
  36. package/dist/registry/generators/markdown/pyvalue.d.ts +34 -0
  37. package/dist/registry/generators/markdown/pyvalue.js +106 -0
  38. package/dist/registry/generators/markdown/sections/choosing-object.d.ts +2 -0
  39. package/dist/registry/generators/markdown/sections/choosing-object.js +52 -0
  40. package/dist/registry/generators/markdown/sections/example.d.ts +22 -0
  41. package/dist/registry/generators/markdown/sections/example.js +295 -0
  42. package/dist/registry/generators/markdown/sections/filtering.d.ts +2 -0
  43. package/dist/registry/generators/markdown/sections/filtering.js +76 -0
  44. package/dist/registry/generators/markdown/sections/header.d.ts +2 -0
  45. package/dist/registry/generators/markdown/sections/header.js +37 -0
  46. package/dist/registry/generators/markdown/sections/inputs.d.ts +30 -0
  47. package/dist/registry/generators/markdown/sections/inputs.js +151 -0
  48. package/dist/registry/generators/markdown/sections/lookups.d.ts +12 -0
  49. package/dist/registry/generators/markdown/sections/lookups.js +150 -0
  50. package/dist/registry/generators/markdown/sections/outputs.d.ts +3 -0
  51. package/dist/registry/generators/markdown/sections/outputs.js +9 -0
  52. package/dist/registry/generators/markdown/sections/resolving-fields.d.ts +2 -0
  53. package/dist/registry/generators/markdown/sections/resolving-fields.js +139 -0
  54. package/dist/registry/generators/markdown/sections/schema-dynamic.d.ts +2 -0
  55. package/dist/registry/generators/markdown/sections/schema-dynamic.js +86 -0
  56. package/dist/registry/generators/materialize.d.ts +30 -0
  57. package/dist/registry/generators/materialize.js +132 -0
  58. package/dist/registry/generators/md-symbols.d.ts +22 -0
  59. package/dist/registry/generators/md-symbols.js +58 -0
  60. package/dist/registry/generators/triggers.d.ts +15 -0
  61. package/dist/registry/generators/triggers.js +85 -0
  62. package/dist/registry/generators/type-registry.d.ts +50 -0
  63. package/dist/registry/generators/type-registry.js +154 -0
  64. package/dist/registry/generators/type-tree.d.ts +65 -0
  65. package/dist/registry/generators/type-tree.js +179 -0
  66. package/dist/registry/library-build/build.d.ts +57 -0
  67. package/dist/registry/library-build/build.js +286 -0
  68. package/dist/registry/library-build/canonical.d.ts +119 -0
  69. package/dist/registry/library-build/canonical.js +512 -0
  70. package/dist/registry/library-build/cli.d.ts +45 -0
  71. package/dist/registry/library-build/cli.js +182 -0
  72. package/dist/registry/library-build/context.d.ts +107 -0
  73. package/dist/registry/library-build/context.js +267 -0
  74. package/dist/registry/library-build/enrich.d.ts +36 -0
  75. package/dist/registry/library-build/enrich.js +158 -0
  76. package/dist/registry/library-build/events.d.ts +108 -0
  77. package/dist/registry/library-build/events.js +476 -0
  78. package/dist/registry/library-build/generic-expand.d.ts +77 -0
  79. package/dist/registry/library-build/generic-expand.js +363 -0
  80. package/dist/registry/library-build/plan.d.ts +71 -0
  81. package/dist/registry/library-build/plan.js +270 -0
  82. package/dist/registry/library-build/py.d.ts +45 -0
  83. package/dist/registry/library-build/py.js +167 -0
  84. package/dist/registry/library-build/schema-action.d.ts +38 -0
  85. package/dist/registry/library-build/schema-action.js +107 -0
  86. package/dist/registry/library-build/write.d.ts +6 -0
  87. package/dist/registry/library-build/write.js +26 -0
  88. package/dist/registry/library-reader.d.ts +20 -0
  89. package/dist/registry/library-reader.js +50 -0
  90. package/dist/registry/lookup-specs.d.ts +55 -0
  91. package/dist/registry/lookup-specs.js +140 -0
  92. package/dist/registry/materialize-cli.js +28 -14
  93. package/dist/registry/materializer.d.ts +25 -3
  94. package/dist/registry/materializer.js +45 -29
  95. package/dist/registry/model/library.d.ts +254 -0
  96. package/dist/registry/model/library.js +23 -0
  97. package/dist/registry/naming.d.ts +54 -0
  98. package/dist/registry/naming.js +142 -0
  99. package/dist/registry/orphans.d.ts +4 -0
  100. package/dist/registry/orphans.js +86 -0
  101. package/dist/registry/prepare/actions.d.ts +26 -0
  102. package/dist/registry/prepare/actions.js +94 -0
  103. package/dist/registry/prepare/connections.d.ts +181 -0
  104. package/dist/registry/prepare/connections.js +399 -0
  105. package/dist/registry/prepare/event.d.ts +96 -0
  106. package/dist/registry/prepare/event.js +268 -0
  107. package/dist/registry/prepare/handoff.d.ts +21 -0
  108. package/dist/registry/prepare/handoff.js +91 -0
  109. package/dist/registry/prepare/ledger.d.ts +104 -0
  110. package/dist/registry/prepare/ledger.js +216 -0
  111. package/dist/registry/prepare/messages.d.ts +190 -0
  112. package/dist/registry/prepare/messages.js +472 -0
  113. package/dist/registry/prepare/objects.d.ts +130 -0
  114. package/dist/registry/prepare/objects.js +318 -0
  115. package/dist/registry/prepare/overlay.d.ts +149 -0
  116. package/dist/registry/prepare/overlay.js +487 -0
  117. package/dist/registry/prepare/parents.d.ts +41 -0
  118. package/dist/registry/prepare/parents.js +122 -0
  119. package/dist/registry/prepare/pipeline.d.ts +91 -0
  120. package/dist/registry/prepare/pipeline.js +376 -0
  121. package/dist/registry/prepare/regenerate.d.ts +9 -0
  122. package/dist/registry/prepare/regenerate.js +50 -0
  123. package/dist/registry/prepare/resolve.d.ts +199 -0
  124. package/dist/registry/prepare/resolve.js +470 -0
  125. package/dist/registry/prepare/retry.d.ts +83 -0
  126. package/dist/registry/prepare/retry.js +109 -0
  127. package/dist/registry/prepare/source-import.d.ts +54 -0
  128. package/dist/registry/prepare/source-import.js +192 -0
  129. package/dist/registry/prepare/support.d.ts +41 -0
  130. package/dist/registry/prepare/support.js +178 -0
  131. package/dist/registry/prepare-cli.d.ts +16 -5
  132. package/dist/registry/prepare-cli.js +60 -53
  133. package/dist/registry/pull-cli.js +8 -7
  134. package/dist/registry/reporter.d.ts +26 -0
  135. package/dist/registry/reporter.js +41 -0
  136. package/dist/registry/uip/client.d.ts +99 -0
  137. package/dist/registry/uip/client.js +226 -0
  138. package/dist/registry/uip/commands.d.ts +40 -0
  139. package/dist/registry/uip/commands.js +80 -0
  140. package/dist/registry/uip/envelope.d.ts +37 -0
  141. package/dist/registry/uip/envelope.js +84 -0
  142. package/dist/registry/uip/response-cache.d.ts +28 -0
  143. package/dist/registry/uip/response-cache.js +57 -0
  144. package/lib/README.md +10 -8
  145. package/package.json +3 -3
  146. package/dist/generators/_connections.py +0 -331
  147. package/dist/generators/_events.py +0 -355
  148. package/dist/generators/_lookups.py +0 -114
  149. package/dist/generators/_naming.py +0 -185
  150. package/dist/generators/_objects.py +0 -299
  151. package/dist/generators/_resolve.py +0 -319
  152. package/dist/generators/convert_library_to_md.py +0 -1441
  153. package/dist/generators/extract_library.py +0 -125
  154. package/dist/generators/generate_connectors_ts.py +0 -936
  155. package/dist/generators/generate_library_json.py +0 -2527
  156. package/dist/generators/generate_triggers_ts.py +0 -110
  157. package/dist/generators/materialize_registry.py +0 -155
  158. package/dist/generators/prepare_connector.py +0 -1860
  159. package/dist/registry/python.d.ts +0 -26
  160. package/dist/registry/python.js +0 -46
@@ -0,0 +1,487 @@
1
+ /**
2
+ * The `./connectors-local/` overlay: how one prepared operation is merged into
3
+ * it, and what `prepare` reads back out of it afterwards.
4
+ *
5
+ * Calls ACCUMULATE — each prepared op merges into the overlay rather than
6
+ * replacing it — and Generic object variants intentionally share a nodeType,
7
+ * so the identity of an entry is `(nodeType, version, objectName)`, never the
8
+ * nodeType alone. Descriptors live under `descriptors/<key>/…`; an overlay
9
+ * written before that rule is migrated on the next merge.
10
+ *
11
+ * Everything the summary and the notes need afterwards — field counts, the
12
+ * parent declarations, the lookup vocabulary — is read back from the overlay
13
+ * just written rather than recomputed, so a resolution is made against the
14
+ * SAME schema the descriptors were generated from, including any
15
+ * connection-resolved field that only exists because this `prepare` ran.
16
+ *
17
+ * Ported from `merge_into`, `_descriptor_overlay_path`,
18
+ * `_migrate_legacy_descriptor_roots`, `_overlay_has_generic_expanded`,
19
+ * `_count_input_fields`, `_parent_fields`, `_schema_action_parents`,
20
+ * `_keep_prepared_schema` and `_lookup_specs_from_overlay` in
21
+ * `prepare_connector.py`.
22
+ */
23
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmdirSync, statSync, writeFileSync } from "node:fs";
24
+ import { basename, dirname, join } from "node:path";
25
+ import { pyJsonDocument } from "../codecs/pyjson.js";
26
+ import { sortedByKeys, sortedStrings } from "../compat/compare.js";
27
+ import { toPosix } from "../compat/paths.js";
28
+ import { lookupSpecs } from "../generators/fields.js";
29
+ import { SCHEMA_VERSION } from "../library-build/canonical.js";
30
+ import { sidecarPath, unwrapV1Def } from "../library-reader.js";
31
+ import { catalogReferences, loadInvariantCollections, specsFromFields } from "../lookup-specs.js";
32
+ import { isEventNodeType } from "../model/library.js";
33
+ import { isPlainObject, pyGet, pyOr, pyStrAny, pyTruthy, pyTypeName } from "./support.js";
34
+ export const INDEX_FILENAME = "index.json";
35
+ export const DESCRIPTORS_ROOT = "descriptors";
36
+ /**
37
+ * `_variant_key`: identity for one resolvable activity schema variant. Keying
38
+ * only by nodeType made the last prepared object overwrite every earlier one.
39
+ */
40
+ export function variantKey(entry) {
41
+ return JSON.stringify([pyGet(entry, "nodeType", ""), pyGet(entry, "version", ""), pyGet(entry, "objectName", "")]);
42
+ }
43
+ /** `Path(p).parts` for a relative POSIX path: empties and `.` collapse, `..` is kept. */
44
+ function pathParts(p) {
45
+ return toPosix(p)
46
+ .split("/")
47
+ .filter((part) => part !== "" && part !== ".");
48
+ }
49
+ /** `_descriptor_overlay_path`: `<key>/op@1.json` → `descriptors/<key>/op@1.json`; already-rooted paths pass through. */
50
+ export function descriptorOverlayPath(path) {
51
+ const parts = pathParts(path);
52
+ if (parts.length > 0 && parts[0] === DESCRIPTORS_ROOT)
53
+ return parts.join("/");
54
+ return [DESCRIPTORS_ROOT, ...parts].join("/");
55
+ }
56
+ /**
57
+ * `_migrate_legacy_descriptor_roots`: move `<dst>/<key>/**` written by an
58
+ * earlier `prepare` under `<dst>/descriptors/<key>/**`, bottom-up, leaving no
59
+ * empty directory behind. Only roots the index still names are touched.
60
+ */
61
+ export function migrateLegacyDescriptorRoots(dst, entries) {
62
+ const legacyRoots = new Set();
63
+ for (const entry of entries) {
64
+ if (!isPlainObject(entry))
65
+ continue;
66
+ const path = pyGet(entry, "path", null);
67
+ if (!pyTruthy(path))
68
+ continue;
69
+ const parts = pathParts(pyStrAny(path));
70
+ if (parts.length > 0 && parts[0] !== DESCRIPTORS_ROOT)
71
+ legacyRoots.add(parts[0]);
72
+ }
73
+ for (const legacyRoot of sortedStrings(legacyRoots)) {
74
+ const sourceRoot = join(dst, legacyRoot);
75
+ if (!existsSync(sourceRoot) || !statSync(sourceRoot).isDirectory())
76
+ continue;
77
+ const destinationRoot = join(dst, DESCRIPTORS_ROOT, legacyRoot);
78
+ const walk = (dir) => {
79
+ const children = readdirSync(dir, { withFileTypes: true });
80
+ const subdirs = children.filter((c) => c.isDirectory()).map((c) => c.name);
81
+ // `os.walk(topdown=False)`: the children are handled before their parent.
82
+ for (const name of subdirs)
83
+ walk(join(dir, name));
84
+ const destination = join(destinationRoot, toPosix(dir.slice(sourceRoot.length)).replace(/^\//, ""));
85
+ mkdirSync(destination, { recursive: true });
86
+ for (const child of children) {
87
+ if (child.isFile())
88
+ renameSync(join(dir, child.name), join(destination, child.name));
89
+ }
90
+ for (const name of subdirs)
91
+ rmdirSync(join(dir, name));
92
+ };
93
+ walk(sourceRoot);
94
+ rmdirSync(sourceRoot);
95
+ }
96
+ }
97
+ function sortKey(entry, key) {
98
+ return pyStrAny(pyGet(entry, key, ""));
99
+ }
100
+ /**
101
+ * The overlay index rows after merging `srcEntries` over `dstEntries`: every
102
+ * row's `path` under `descriptors/`, a same-variant row refreshed, a new
103
+ * variant added, and the whole list sorted by
104
+ * `(connectorKey, nodeType, version, objectName, path)`. Pure — the inputs
105
+ * are copied, not rewritten.
106
+ */
107
+ export function mergeIndexEntries(dstEntries, srcEntries) {
108
+ const byVariant = new Map();
109
+ const place = (raw) => {
110
+ if (!isPlainObject(raw))
111
+ throw new TypeError("index entry is not an object"); // Python: `entry["path"]` on a non-dict
112
+ if (!Object.prototype.hasOwnProperty.call(raw, "path"))
113
+ throw new Error("KeyError: 'path'");
114
+ const entry = { ...raw, path: descriptorOverlayPath(pyStrAny(raw.path)) };
115
+ byVariant.set(variantKey(entry), entry);
116
+ };
117
+ for (const entry of dstEntries)
118
+ place(entry);
119
+ // Re-preparing the same object refreshes it; preparing a different object
120
+ // accumulates a sibling variant under the shared generic nodeType.
121
+ for (const entry of srcEntries)
122
+ place(entry);
123
+ return sortedByKeys([...byVariant.values()], (e) => sortKey(e, "connectorKey"), (e) => sortKey(e, "nodeType"), (e) => sortKey(e, "version"), (e) => sortKey(e, "objectName"), (e) => sortKey(e, "path"));
124
+ }
125
+ /**
126
+ * The bytes `connectors-local/index.json` is written in.
127
+ *
128
+ * INTENTIONAL DIVERGENCE from the Python, which wrote `json.dump(idx, indent=2)`
129
+ * with no trailing newline — the one file in the overlay without one, since
130
+ * `write_library` and the ledger both appended it. Every other JSON document
131
+ * the registry writes ends in a newline, and the overlay index now does too,
132
+ * so a diff of a re-prepared overlay stops showing a "\ No newline at end of
133
+ * file" marker on the index alone.
134
+ */
135
+ export function formatOverlayIndex(index) {
136
+ return pyJsonDocument(index);
137
+ }
138
+ /** `<dir>/index.json` parsed, or `undefined` when there is no file (a broken file throws, as `json.load` did). */
139
+ function readIndex(dir) {
140
+ const path = join(dir, INDEX_FILENAME);
141
+ if (!existsSync(path))
142
+ return undefined;
143
+ return JSON.parse(readFileSync(path, "utf8"));
144
+ }
145
+ /**
146
+ * `merge_into` over an in-memory library: the shape `planLibrary` produces.
147
+ * `files` must carry `index.json`; every other file is copied under
148
+ * `descriptors/` (a nested `index.json` is skipped, as the Python's walk did).
149
+ */
150
+ export function mergeFilesInto(files, dst) {
151
+ mkdirSync(dst, { recursive: true });
152
+ const srcIndexText = files.get(INDEX_FILENAME);
153
+ if (srcIndexText === undefined)
154
+ throw new Error(`merge_into: ${INDEX_FILENAME} missing from the prepared library`);
155
+ const srcIdx = JSON.parse(typeof srcIndexText === "string" ? srcIndexText : Buffer.from(srcIndexText).toString("utf8"));
156
+ const dstIdxRaw = readIndex(dst);
157
+ // A parseable index that is not an object is REFUSED, not replaced. The
158
+ // Python's `dst_idx.get("entries", [])` raised on a list, a string or a null
159
+ // and wrote nothing, so an overlay some other tool has mangled stays the
160
+ // author's to fix; silently starting a fresh index would discard every
161
+ // descriptor it still names. Only a MISSING file starts one.
162
+ if (dstIdxRaw !== undefined && !isPlainObject(dstIdxRaw)) {
163
+ throw new TypeError(`${join(dst, INDEX_FILENAME)}: expected a JSON object, found ${pyTypeName(dstIdxRaw)}`);
164
+ }
165
+ const dstIdx = dstIdxRaw === undefined ? { schemaVersion: SCHEMA_VERSION, entries: [] } : { ...dstIdxRaw };
166
+ const dstEntries = pyGet(dstIdx, "entries", []);
167
+ if (!Array.isArray(dstEntries))
168
+ throw new TypeError("index.json: \"entries\" is not a list");
169
+ migrateLegacyDescriptorRoots(dst, dstEntries);
170
+ const srcEntries = isPlainObject(srcIdx) ? pyGet(srcIdx, "entries", []) : [];
171
+ dstIdx.entries = mergeIndexEntries(dstEntries, Array.isArray(srcEntries) ? srcEntries : []);
172
+ writeFileSync(join(dst, INDEX_FILENAME), formatOverlayIndex(dstIdx), "utf8");
173
+ for (const [rel, content] of files) {
174
+ if (basename(rel) === INDEX_FILENAME)
175
+ continue;
176
+ const out = join(dst, ...descriptorOverlayPath(rel).split("/"));
177
+ mkdirSync(dirname(out), { recursive: true });
178
+ writeFileSync(out, content);
179
+ }
180
+ }
181
+ /** Every file under `root`, as `POSIX-relative path → bytes`. */
182
+ function readTree(root) {
183
+ const files = new Map();
184
+ const walk = (dir, prefix) => {
185
+ for (const child of readdirSync(dir, { withFileTypes: true })) {
186
+ const rel = prefix ? `${prefix}/${child.name}` : child.name;
187
+ if (child.isDirectory())
188
+ walk(join(dir, child.name), rel);
189
+ else if (child.isFile())
190
+ files.set(rel, new Uint8Array(readFileSync(join(dir, child.name))));
191
+ }
192
+ };
193
+ walk(root, "");
194
+ return files;
195
+ }
196
+ /** `merge_into(src, dst)`: merge a freshly written single-op library directory into the overlay. */
197
+ export function mergeInto(src, dst) {
198
+ mergeFilesInto(readTree(src), dst);
199
+ }
200
+ /** `_overlay_has_generic_expanded`: does the overlay hold ANY `GenericExpanded` entry, from any prepare? */
201
+ export function overlayHasGenericExpanded(out) {
202
+ let index;
203
+ try {
204
+ index = readIndex(out);
205
+ }
206
+ catch {
207
+ return false;
208
+ }
209
+ if (!isPlainObject(index))
210
+ return false;
211
+ const entries = pyOr(pyGet(index, "entries", null), []);
212
+ if (!Array.isArray(entries))
213
+ return false;
214
+ return entries.some((e) => isPlainObject(e) && e.activityType === "GenericExpanded");
215
+ }
216
+ /** A reader over a directory on disk. */
217
+ export function directoryReader(dir) {
218
+ return (rel) => JSON.parse(readFileSync(join(dir, ...rel.split("/")), "utf8"));
219
+ }
220
+ /** A reader over an in-memory library (`planLibrary`'s `files`), for the staged half of {@link keepPreparedSchema}. */
221
+ export function filesReader(files) {
222
+ return (rel) => {
223
+ const text = files.get(rel);
224
+ if (text === undefined)
225
+ throw new Error(`ENOENT: ${rel}`);
226
+ return JSON.parse(typeof text === "string" ? text : Buffer.from(text).toString("utf8"));
227
+ };
228
+ }
229
+ function requireKey(record, key) {
230
+ if (!isPlainObject(record))
231
+ throw new TypeError(`'${record === null ? "NoneType" : typeof record}' object is not subscriptable`);
232
+ if (!Object.prototype.hasOwnProperty.call(record, key))
233
+ throw new Error(`KeyError: ${JSON.stringify(key)}`);
234
+ return record[key];
235
+ }
236
+ /**
237
+ * The index rows for `nodeType`, narrowed to `objectName` when one is given
238
+ * (`object_name is not None` in the Python: an empty string IS a filter).
239
+ * Throws on any shape the Python's `except` clauses would have turned into
240
+ * the caller's "unavailable" answer.
241
+ */
242
+ function matchingRows(read, nodeType, objectName) {
243
+ const entries = requireKey(read(INDEX_FILENAME), "entries");
244
+ if (!Array.isArray(entries))
245
+ throw new TypeError("entries is not a list");
246
+ const rows = [];
247
+ for (const row of entries) {
248
+ if (requireKey(row, "nodeType") !== nodeType)
249
+ continue;
250
+ if (objectName !== undefined && objectName !== null && pyGet(row, "objectName", null) !== objectName)
251
+ continue;
252
+ rows.push(row);
253
+ }
254
+ return rows;
255
+ }
256
+ /**
257
+ * `_count_input_fields`: input-field count across the matching overlay
258
+ * variants, or `undefined` when nothing matched or a file could not be read
259
+ * — never `0` for "unavailable", which the summary line would print as a
260
+ * plausible success.
261
+ */
262
+ export function countInputFieldsFrom(read, nodeType, objectName) {
263
+ try {
264
+ const rows = matchingRows(read, nodeType, objectName);
265
+ if (rows.length === 0)
266
+ return undefined;
267
+ let total = 0;
268
+ for (const row of rows) {
269
+ const descriptor = read(pyStrAny(requireKey(row, "path")));
270
+ if (!isPlainObject(descriptor))
271
+ return undefined;
272
+ const inputSchema = pyGet(descriptor, "inputSchema", null);
273
+ if (!isPlainObject(inputSchema))
274
+ return undefined;
275
+ const fields = pyGet(inputSchema, "fields", null);
276
+ if (!Array.isArray(fields))
277
+ return undefined;
278
+ total += fields.length;
279
+ }
280
+ return total;
281
+ }
282
+ catch {
283
+ return undefined;
284
+ }
285
+ }
286
+ /** {@link countInputFieldsFrom} over an overlay directory. */
287
+ export function countInputFields(out, nodeType, objectName) {
288
+ return countInputFieldsFrom(directoryReader(out), nodeType, objectName);
289
+ }
290
+ const PLACEHOLDER = /\{([^{}]+)\}/g;
291
+ /**
292
+ * `_parent_fields`: `[parent field names, whether the prepared schema is parent-ONLY]`.
293
+ *
294
+ * A reference `path` carrying a `{placeholder}` is the declaration that an
295
+ * operation is parent-driven: `/project/{fields.project.key}/issuetypes` on
296
+ * `fields.issuetype.id` names the field whose value must be resolved first.
297
+ * The parent set is both halves of that relation — the placeholder names AND
298
+ * the fields that declare them — because both must be passed as `-f`.
299
+ * "Parent-only" then means every prepared field is a parent: the expected
300
+ * shape with no `-f`, and a silent failure WITH `-f`.
301
+ */
302
+ export function parentFieldsFrom(read, nodeType, objectName) {
303
+ const names = [];
304
+ const placeholders = new Set();
305
+ const declaring = new Set();
306
+ try {
307
+ for (const row of matchingRows(read, nodeType, objectName)) {
308
+ const descriptor = read(pyStrAny(requireKey(row, "path")));
309
+ if (!isPlainObject(descriptor))
310
+ throw new TypeError("descriptor is not a dict");
311
+ const inputSchema = pyGet(descriptor, "inputSchema", {});
312
+ if (!isPlainObject(inputSchema))
313
+ throw new TypeError("inputSchema is not a dict");
314
+ const fields = pyOr(pyGet(inputSchema, "fields", []), []);
315
+ if (!Array.isArray(fields))
316
+ throw new TypeError("fields is not a list");
317
+ for (const field of fields) {
318
+ if (!isPlainObject(field))
319
+ throw new TypeError("field is not a dict");
320
+ const name = pyGet(field, "name", null);
321
+ if (pyTruthy(name) && !names.includes(name))
322
+ names.push(name);
323
+ const reference = pyOr(pyGet(field, "reference", null), {});
324
+ if (!isPlainObject(reference))
325
+ throw new TypeError("reference is not a dict");
326
+ const path = pyOr(pyGet(reference, "path", null), "");
327
+ if (typeof path !== "string")
328
+ throw new TypeError("path is not a str");
329
+ const found = [...path.matchAll(PLACEHOLDER)].map((m) => m[1]);
330
+ if (found.length > 0) {
331
+ for (const p of found)
332
+ placeholders.add(p);
333
+ if (pyTruthy(name))
334
+ declaring.add(name);
335
+ }
336
+ }
337
+ }
338
+ }
339
+ catch {
340
+ return [[], false];
341
+ }
342
+ const parents = new Set([...placeholders, ...declaring]);
343
+ if (parents.size === 0)
344
+ return [[], false];
345
+ const ordered = names.filter((name) => parents.has(name));
346
+ ordered.push(...sortedStrings([...parents].filter((p) => !ordered.includes(p))));
347
+ return [ordered, names.every((name) => parents.has(name))];
348
+ }
349
+ /** {@link parentFieldsFrom} over an overlay directory. */
350
+ export function parentFields(out, nodeType, objectName) {
351
+ return parentFieldsFrom(directoryReader(out), nodeType, objectName);
352
+ }
353
+ /**
354
+ * `_schema_action_parents`: the parent inputs the PREPARED operation's own
355
+ * `operation.schemaAction.parents` names — the one declaration that is a
356
+ * PRECONDITION rather than a symptom: with any of these missing from `-f` the
357
+ * action cannot have run, whatever the describe returned. Declaration order,
358
+ * which is also the resolution order.
359
+ */
360
+ export function schemaActionParentsFrom(read, nodeType, objectName) {
361
+ const names = [];
362
+ try {
363
+ for (const row of matchingRows(read, nodeType, objectName)) {
364
+ const descriptor = read(pyStrAny(requireKey(row, "path")));
365
+ if (!isPlainObject(descriptor))
366
+ throw new TypeError("descriptor is not a dict");
367
+ const operation = pyOr(pyGet(descriptor, "operation", null), {});
368
+ if (!isPlainObject(operation))
369
+ throw new TypeError("operation is not a dict");
370
+ const action = pyOr(pyGet(operation, "schemaAction", null), {});
371
+ if (!isPlainObject(action))
372
+ throw new TypeError("schemaAction is not a dict");
373
+ const parents = pyOr(pyGet(action, "parents", null), []);
374
+ if (!Array.isArray(parents))
375
+ throw new TypeError("parents is not a list");
376
+ for (const name of parents) {
377
+ if (pyTruthy(name) && !names.includes(name))
378
+ names.push(name);
379
+ }
380
+ }
381
+ }
382
+ catch {
383
+ return [];
384
+ }
385
+ return names;
386
+ }
387
+ /** {@link schemaActionParentsFrom} over an overlay directory. */
388
+ export function schemaActionParents(out, nodeType, objectName) {
389
+ return schemaActionParentsFrom(directoryReader(out), nodeType, objectName);
390
+ }
391
+ function hasParent(parentFieldValues, name) {
392
+ return parentFieldValues instanceof Map ? parentFieldValues.has(name) : Object.prototype.hasOwnProperty.call(parentFieldValues, name);
393
+ }
394
+ /**
395
+ * `_keep_prepared_schema`: `[kept, discarded]` when this run must NOT
396
+ * overwrite the overlay, else `undefined`.
397
+ *
398
+ * `--resolve` records a lookup; `-f` supplies the value a schema action reads.
399
+ * A run carrying only the former cannot have run the action, so what it has
400
+ * to write is the UNRESOLVED entry — and the merge replaces by variant key, so
401
+ * it lands on top of a schema an earlier `-f` run resolved (Jira
402
+ * `create-issue`: 21 fields down to 3, #666). Three conditions, all necessary:
403
+ * the EXISTING overlay declares `schemaAction.parents`; none of them arrived
404
+ * as `-f`; and the staged entry has STRICTLY fewer input fields than the one
405
+ * on disk — equal or richer still merges, so a fresher entry is never pinned
406
+ * by a stale overlay, and a first prepare (no overlay) writes normally.
407
+ *
408
+ * `staged` is the freshly built library: a directory, or `planLibrary`'s
409
+ * in-memory files.
410
+ */
411
+ export function keepPreparedSchema(out, nodeType, staged, parentFieldValues, objectName) {
412
+ const parents = schemaActionParents(out, nodeType, objectName);
413
+ if (parents.length === 0 || parents.some((name) => hasParent(parentFieldValues, name)))
414
+ return undefined;
415
+ const existing = countInputFields(out, nodeType, objectName);
416
+ const incoming = countInputFieldsFrom(typeof staged === "string" ? directoryReader(staged) : filesReader(staged), nodeType, objectName);
417
+ if (existing === undefined || incoming === undefined || existing <= incoming)
418
+ return undefined;
419
+ return [existing, incoming];
420
+ }
421
+ function withFilterPatterns(specs, catalogFields) {
422
+ const references = catalogReferences({ inputSchema: { fields: [...catalogFields] } });
423
+ const out = new Map();
424
+ for (const [field, spec] of specs) {
425
+ const copy = { ...spec };
426
+ const filterPattern = references.get(field)?.filterPattern;
427
+ if (pyTruthy(filterPattern))
428
+ copy.filterPattern = pyStrAny(filterPattern);
429
+ out.set(field, copy);
430
+ }
431
+ return out;
432
+ }
433
+ /**
434
+ * `_lookup_specs_from_overlay`: the prepared operation's lookup specs, read
435
+ * back from the overlay just written. Generic object variants share a
436
+ * nodeType, so `objectName` narrows to the one just prepared (a variant with
437
+ * no object name still answers, as the Python's `not in ("", None, name)` did);
438
+ * the FIRST matching entry answers.
439
+ *
440
+ * An EVENT's resolvable fields are its subscription parameters, and
441
+ * `v1def.eventParameters.fields` is the ONE authority for those — the same
442
+ * list the SDK, `check` and the descriptor generator read.
443
+ *
444
+ * INTENTIONAL DIVERGENCE from the Python. It selected `eventParameters` as the
445
+ * `fields_source` but then handed that source to `lookup_specs`, which reads
446
+ * `v1def.inputDefinition.fields` FIRST and falls back to the source only when
447
+ * the sidecar's input list is empty AND the source has a required field. On a
448
+ * prepared event whose trigger definition carries message-body inputs, the
449
+ * specs therefore came from the BODY fields — the exact wrong list — and an
450
+ * optional event parameter produced no spec at all. Here an event's specs are
451
+ * built from `eventParameters.fields` directly, which is what the descriptor
452
+ * generator emits `lookups` from (`descriptorInputFields`), so `--resolve`,
453
+ * the vocabulary print and the generated `.by*` helpers agree.
454
+ */
455
+ export function lookupSpecsFromOverlay(out, nodeType, objectName) {
456
+ const indexPath = join(out, INDEX_FILENAME);
457
+ if (!existsSync(indexPath))
458
+ return new Map();
459
+ const index = JSON.parse(readFileSync(indexPath, "utf8"));
460
+ const entries = isPlainObject(index) ? pyGet(index, "entries", []) : [];
461
+ if (!Array.isArray(entries))
462
+ return new Map();
463
+ const invariant = loadInvariantCollections(out);
464
+ for (const row of entries) {
465
+ if (!isPlainObject(row) || row.nodeType !== nodeType)
466
+ continue;
467
+ if (pyTruthy(objectName)) {
468
+ const rowObject = pyGet(row, "objectName", null);
469
+ if (rowObject !== "" && rowObject !== null && rowObject !== objectName)
470
+ continue;
471
+ }
472
+ const rel = pyStrAny(requireKey(row, "path"));
473
+ const entryPath = join(out, ...rel.split("/"));
474
+ if (!existsSync(entryPath))
475
+ continue;
476
+ const canonical = JSON.parse(readFileSync(entryPath, "utf8"));
477
+ const v1Path = join(out, ...sidecarPath(rel).split("/"));
478
+ const v1def = (existsSync(v1Path) ? unwrapV1Def(JSON.parse(readFileSync(v1Path, "utf8"))) : undefined) ?? {};
479
+ if (isEventNodeType(nodeType)) {
480
+ const parameters = v1def.eventParameters?.fields ?? [];
481
+ const fields = Array.isArray(parameters) ? parameters : [];
482
+ return withFilterPatterns(specsFromFields(fields, invariant, catalogReferences({ inputSchema: { fields } })), fields);
483
+ }
484
+ return withFilterPatterns(lookupSpecs(canonical, v1def, invariant), canonical.inputSchema?.fields ?? []);
485
+ }
486
+ return new Map();
487
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * `['a=1', 'b=2']` → `{a: '1', b: '2'}`, refusing a missing `=`.
3
+ *
4
+ * Values may themselves contain `=` (a Salesforce SOQL body token), so the
5
+ * split is on the FIRST separator only. Insertion order is the `-f` order on
6
+ * the command line, which is also the order the describe is issued with.
7
+ */
8
+ export declare function parseFields(pairs: readonly string[]): Map<string, string>;
9
+ /** One api-type ObjectAction: its name and the parent-field names its rules read, in rule order. */
10
+ export type ObjectActionSpec = readonly [actionName: string, parentFields: string[]];
11
+ /**
12
+ * `_object_action_specs`: each api-type ObjectAction the registry node
13
+ * declares and its ordered parent-field names. `payload` is the raw
14
+ * `{"Data": {"Node": …}}` document; anything not shaped that way answers `[]`,
15
+ * as the Python's `except (KeyError, TypeError)` did.
16
+ */
17
+ export declare function objectActionSpecs(payload: unknown): ObjectActionSpec[];
18
+ /** The design-time schema replay request `customFieldsRequestDetails` carries. */
19
+ export interface ObjectActionRequest {
20
+ objectActionName: string;
21
+ parameterValues: Array<[name: string, value: string]>;
22
+ }
23
+ type ParentFields = ReadonlyMap<string, string> | Readonly<Record<string, string>>;
24
+ /**
25
+ * `_object_action_request`: the replay request for the ObjectAction whose
26
+ * parent set is EXACTLY the `-f` names given, or `undefined` when none
27
+ * matches (a partial set is not a match). Parameter names are spelled the way
28
+ * the platform stores nested paths: `fields.project.key` → `fields_sub_project_sub_key`.
29
+ */
30
+ export declare function objectActionRequest(payload: unknown, parentFields: ParentFields): ObjectActionRequest | undefined;
31
+ /**
32
+ * `_declared_parents`: parent-input names the registry declares through
33
+ * api-type ObjectActions, flattened in declaration order and deduplicated.
34
+ *
35
+ * The second way an operation says "my fields depend on a value": not a
36
+ * `{placeholder}` in a reference path (Jira), but a design-time action whose
37
+ * rules name the inputs it reads (Data Fabric `create-entity-record` declares
38
+ * `GenerateSchema` over `entityName`).
39
+ */
40
+ export declare function declaredParents(payload: unknown): string[];
41
+ export {};
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Parent fields: the `-f NAME=VALUE` inputs a schema-dynamic operation resolves
3
+ * its real field set from, and the registry's own declaration of them.
4
+ *
5
+ * Schema-dynamic ops resolve their fields from the VALUES of a few parent
6
+ * fields, not from the connection alone (Jira `create-issue`: 2 fields with no
7
+ * `-f`, 19 with project + issue type). Studio Web persists the ObjectAction and
8
+ * the parent values that produced a field list so it can replay the same
9
+ * schema; the registry owns the action name and rule order, `-f` owns the
10
+ * values. This module reads both halves.
11
+ *
12
+ * Ported from `_parse_fields`, `_object_action_specs`, `_object_action_request`
13
+ * and `_declared_parents` in `prepare_connector.py`. Every function here is
14
+ * pure over the PARSED `flow registry get` payload — the Python re-read the
15
+ * cached file each time; the port takes the object the cache already holds.
16
+ */
17
+ import { pyStrip } from "../compat/pystr.js";
18
+ import { PrepareRefusal, fieldFlagMessage } from "./messages.js";
19
+ import { isPlainObject, pyOr, pyTruthy } from "./support.js";
20
+ /**
21
+ * `['a=1', 'b=2']` → `{a: '1', b: '2'}`, refusing a missing `=`.
22
+ *
23
+ * Values may themselves contain `=` (a Salesforce SOQL body token), so the
24
+ * split is on the FIRST separator only. Insertion order is the `-f` order on
25
+ * the command line, which is also the order the describe is issued with.
26
+ */
27
+ export function parseFields(pairs) {
28
+ const parsed = new Map();
29
+ for (const pair of pairs) {
30
+ const at = pair.indexOf("=");
31
+ const name = at < 0 ? pair : pair.slice(0, at);
32
+ if (at < 0 || !pyStrip(name)) {
33
+ throw new PrepareRefusal(fieldFlagMessage(pair), "FIELD_FLAG_MALFORMED");
34
+ }
35
+ parsed.set(pyStrip(name), pair.slice(at + 1));
36
+ }
37
+ return parsed;
38
+ }
39
+ /**
40
+ * `_object_action_specs`: each api-type ObjectAction the registry node
41
+ * declares and its ordered parent-field names. `payload` is the raw
42
+ * `{"Data": {"Node": …}}` document; anything not shaped that way answers `[]`,
43
+ * as the Python's `except (KeyError, TypeError)` did.
44
+ */
45
+ export function objectActionSpecs(payload) {
46
+ if (!isPlainObject(payload) || !isPlainObject(payload.Data) || !isPlainObject(payload.Data.Node))
47
+ return [];
48
+ const node = payload.Data.Node;
49
+ const methodInfo = pyOr(node.connectorMethodInfo, {});
50
+ const design = isPlainObject(methodInfo) ? pyOr(methodInfo.design, {}) : {};
51
+ const actions = isPlainObject(design) ? pyOr(design.actions, []) : [];
52
+ if (!Array.isArray(actions))
53
+ return [];
54
+ const specs = [];
55
+ for (const action of actions) {
56
+ if (!isPlainObject(action) || action.actionType !== "api")
57
+ continue;
58
+ const actionName = action.name;
59
+ if (typeof actionName !== "string" || !actionName)
60
+ continue;
61
+ const rules = pyOr(action.rules, []);
62
+ if (!Array.isArray(rules))
63
+ return []; // Python: iterating a non-list raises TypeError → []
64
+ const refs = [];
65
+ for (const rule of rules) {
66
+ if (!isPlainObject(rule))
67
+ return [];
68
+ if (pyTruthy(rule.isCleared) || !pyTruthy(rule.refFieldName))
69
+ continue;
70
+ refs.push(String(rule.refFieldName));
71
+ }
72
+ if (refs.length > 0)
73
+ specs.push([actionName, refs]);
74
+ }
75
+ return specs;
76
+ }
77
+ function parentEntries(parentFields) {
78
+ return parentFields instanceof Map ? [...parentFields.entries()] : Object.entries(parentFields);
79
+ }
80
+ /**
81
+ * `_object_action_request`: the replay request for the ObjectAction whose
82
+ * parent set is EXACTLY the `-f` names given, or `undefined` when none
83
+ * matches (a partial set is not a match). Parameter names are spelled the way
84
+ * the platform stores nested paths: `fields.project.key` → `fields_sub_project_sub_key`.
85
+ */
86
+ export function objectActionRequest(payload, parentFields) {
87
+ const entries = parentEntries(parentFields);
88
+ if (entries.length === 0)
89
+ return undefined;
90
+ const given = new Set(entries.map(([name]) => name));
91
+ const values = new Map(entries);
92
+ for (const [actionName, refs] of objectActionSpecs(payload)) {
93
+ if (refs.length === 0)
94
+ continue;
95
+ const declared = new Set(refs);
96
+ if (declared.size !== given.size || [...declared].some((ref) => !given.has(ref)))
97
+ continue;
98
+ return {
99
+ objectActionName: actionName,
100
+ parameterValues: refs.map((name) => [name.replaceAll(".", "_sub_"), values.get(name)]),
101
+ };
102
+ }
103
+ return undefined;
104
+ }
105
+ /**
106
+ * `_declared_parents`: parent-input names the registry declares through
107
+ * api-type ObjectActions, flattened in declaration order and deduplicated.
108
+ *
109
+ * The second way an operation says "my fields depend on a value": not a
110
+ * `{placeholder}` in a reference path (Jira), but a design-time action whose
111
+ * rules name the inputs it reads (Data Fabric `create-entity-record` declares
112
+ * `GenerateSchema` over `entityName`).
113
+ */
114
+ export function declaredParents(payload) {
115
+ const names = [];
116
+ for (const [, refs] of objectActionSpecs(payload)) {
117
+ for (const ref of refs)
118
+ if (!names.includes(ref))
119
+ names.push(ref);
120
+ }
121
+ return names;
122
+ }