@uipath/maestro-builder-sdk 6.4.0 → 6.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/bpmn/uipath-moddle.v1.json +21 -0
  2. package/dist/cli/commands/registry.js +68 -12
  3. package/dist/registry/archive/entry-policy.d.ts +36 -0
  4. package/dist/registry/archive/entry-policy.js +60 -0
  5. package/dist/registry/archive/extract.d.ts +31 -0
  6. package/dist/registry/archive/extract.js +92 -0
  7. package/dist/registry/archive/zip-reader.d.ts +38 -0
  8. package/dist/registry/archive/zip-reader.js +256 -0
  9. package/dist/registry/codecs/pyjson.d.ts +62 -0
  10. package/dist/registry/codecs/pyjson.js +208 -0
  11. package/dist/registry/compat/compare.d.ts +18 -0
  12. package/dist/registry/compat/compare.js +32 -0
  13. package/dist/registry/compat/paths.d.ts +4 -0
  14. package/dist/registry/compat/paths.js +17 -0
  15. package/dist/registry/compat/pystr.d.ts +50 -0
  16. package/dist/registry/compat/pystr.js +221 -0
  17. package/dist/registry/concurrency.d.ts +10 -0
  18. package/dist/registry/concurrency.js +34 -0
  19. package/dist/registry/curated-overlay.d.ts +36 -4
  20. package/dist/registry/curated-overlay.js +87 -14
  21. package/dist/registry/errors.d.ts +26 -0
  22. package/dist/registry/errors.js +38 -0
  23. package/dist/registry/extractor.d.ts +9 -10
  24. package/dist/registry/extractor.js +5 -15
  25. package/dist/registry/files.d.ts +26 -0
  26. package/dist/registry/files.js +160 -0
  27. package/dist/registry/generators/descriptors.d.ts +81 -0
  28. package/dist/registry/generators/descriptors.js +405 -0
  29. package/dist/registry/generators/fields.d.ts +46 -0
  30. package/dist/registry/generators/fields.js +111 -0
  31. package/dist/registry/generators/markdown/common.d.ts +21 -0
  32. package/dist/registry/generators/markdown/common.js +55 -0
  33. package/dist/registry/generators/markdown/index.d.ts +33 -0
  34. package/dist/registry/generators/markdown/index.js +37 -0
  35. package/dist/registry/generators/markdown/page.d.ts +8 -0
  36. package/dist/registry/generators/markdown/page.js +52 -0
  37. package/dist/registry/generators/markdown/pyvalue.d.ts +34 -0
  38. package/dist/registry/generators/markdown/pyvalue.js +106 -0
  39. package/dist/registry/generators/markdown/sections/choosing-object.d.ts +2 -0
  40. package/dist/registry/generators/markdown/sections/choosing-object.js +52 -0
  41. package/dist/registry/generators/markdown/sections/example.d.ts +22 -0
  42. package/dist/registry/generators/markdown/sections/example.js +295 -0
  43. package/dist/registry/generators/markdown/sections/filtering.d.ts +2 -0
  44. package/dist/registry/generators/markdown/sections/filtering.js +76 -0
  45. package/dist/registry/generators/markdown/sections/header.d.ts +2 -0
  46. package/dist/registry/generators/markdown/sections/header.js +37 -0
  47. package/dist/registry/generators/markdown/sections/inputs.d.ts +30 -0
  48. package/dist/registry/generators/markdown/sections/inputs.js +151 -0
  49. package/dist/registry/generators/markdown/sections/lookups.d.ts +12 -0
  50. package/dist/registry/generators/markdown/sections/lookups.js +150 -0
  51. package/dist/registry/generators/markdown/sections/outputs.d.ts +3 -0
  52. package/dist/registry/generators/markdown/sections/outputs.js +9 -0
  53. package/dist/registry/generators/markdown/sections/resolving-fields.d.ts +2 -0
  54. package/dist/registry/generators/markdown/sections/resolving-fields.js +139 -0
  55. package/dist/registry/generators/markdown/sections/schema-dynamic.d.ts +2 -0
  56. package/dist/registry/generators/markdown/sections/schema-dynamic.js +86 -0
  57. package/dist/registry/generators/materialize.d.ts +30 -0
  58. package/dist/registry/generators/materialize.js +132 -0
  59. package/dist/registry/generators/md-symbols.d.ts +22 -0
  60. package/dist/registry/generators/md-symbols.js +58 -0
  61. package/dist/registry/generators/triggers.d.ts +15 -0
  62. package/dist/registry/generators/triggers.js +85 -0
  63. package/dist/registry/generators/type-registry.d.ts +50 -0
  64. package/dist/registry/generators/type-registry.js +154 -0
  65. package/dist/registry/generators/type-tree.d.ts +65 -0
  66. package/dist/registry/generators/type-tree.js +179 -0
  67. package/dist/registry/library-build/build.d.ts +57 -0
  68. package/dist/registry/library-build/build.js +286 -0
  69. package/dist/registry/library-build/canonical.d.ts +119 -0
  70. package/dist/registry/library-build/canonical.js +512 -0
  71. package/dist/registry/library-build/cli.d.ts +45 -0
  72. package/dist/registry/library-build/cli.js +182 -0
  73. package/dist/registry/library-build/context.d.ts +107 -0
  74. package/dist/registry/library-build/context.js +267 -0
  75. package/dist/registry/library-build/enrich.d.ts +36 -0
  76. package/dist/registry/library-build/enrich.js +158 -0
  77. package/dist/registry/library-build/events.d.ts +108 -0
  78. package/dist/registry/library-build/events.js +476 -0
  79. package/dist/registry/library-build/generic-expand.d.ts +77 -0
  80. package/dist/registry/library-build/generic-expand.js +363 -0
  81. package/dist/registry/library-build/plan.d.ts +71 -0
  82. package/dist/registry/library-build/plan.js +270 -0
  83. package/dist/registry/library-build/py.d.ts +45 -0
  84. package/dist/registry/library-build/py.js +167 -0
  85. package/dist/registry/library-build/schema-action.d.ts +38 -0
  86. package/dist/registry/library-build/schema-action.js +107 -0
  87. package/dist/registry/library-build/write.d.ts +6 -0
  88. package/dist/registry/library-build/write.js +26 -0
  89. package/dist/registry/library-reader.d.ts +20 -0
  90. package/dist/registry/library-reader.js +50 -0
  91. package/dist/registry/lookup-specs.d.ts +55 -0
  92. package/dist/registry/lookup-specs.js +140 -0
  93. package/dist/registry/materialize-cli.js +28 -14
  94. package/dist/registry/materializer.d.ts +25 -3
  95. package/dist/registry/materializer.js +45 -29
  96. package/dist/registry/model/library.d.ts +254 -0
  97. package/dist/registry/model/library.js +23 -0
  98. package/dist/registry/naming.d.ts +54 -0
  99. package/dist/registry/naming.js +142 -0
  100. package/dist/registry/orphans.d.ts +4 -0
  101. package/dist/registry/orphans.js +86 -0
  102. package/dist/registry/prepare/actions.d.ts +26 -0
  103. package/dist/registry/prepare/actions.js +94 -0
  104. package/dist/registry/prepare/connections.d.ts +181 -0
  105. package/dist/registry/prepare/connections.js +399 -0
  106. package/dist/registry/prepare/event.d.ts +96 -0
  107. package/dist/registry/prepare/event.js +268 -0
  108. package/dist/registry/prepare/handoff.d.ts +21 -0
  109. package/dist/registry/prepare/handoff.js +91 -0
  110. package/dist/registry/prepare/ledger.d.ts +104 -0
  111. package/dist/registry/prepare/ledger.js +216 -0
  112. package/dist/registry/prepare/messages.d.ts +190 -0
  113. package/dist/registry/prepare/messages.js +472 -0
  114. package/dist/registry/prepare/objects.d.ts +130 -0
  115. package/dist/registry/prepare/objects.js +318 -0
  116. package/dist/registry/prepare/overlay.d.ts +149 -0
  117. package/dist/registry/prepare/overlay.js +487 -0
  118. package/dist/registry/prepare/parents.d.ts +41 -0
  119. package/dist/registry/prepare/parents.js +122 -0
  120. package/dist/registry/prepare/pipeline.d.ts +91 -0
  121. package/dist/registry/prepare/pipeline.js +376 -0
  122. package/dist/registry/prepare/regenerate.d.ts +9 -0
  123. package/dist/registry/prepare/regenerate.js +50 -0
  124. package/dist/registry/prepare/resolve.d.ts +199 -0
  125. package/dist/registry/prepare/resolve.js +470 -0
  126. package/dist/registry/prepare/retry.d.ts +83 -0
  127. package/dist/registry/prepare/retry.js +109 -0
  128. package/dist/registry/prepare/source-import.d.ts +54 -0
  129. package/dist/registry/prepare/source-import.js +192 -0
  130. package/dist/registry/prepare/support.d.ts +41 -0
  131. package/dist/registry/prepare/support.js +178 -0
  132. package/dist/registry/prepare-cli.d.ts +16 -5
  133. package/dist/registry/prepare-cli.js +60 -53
  134. package/dist/registry/pull-cli.js +8 -7
  135. package/dist/registry/reporter.d.ts +26 -0
  136. package/dist/registry/reporter.js +41 -0
  137. package/dist/registry/uip/client.d.ts +99 -0
  138. package/dist/registry/uip/client.js +226 -0
  139. package/dist/registry/uip/commands.d.ts +40 -0
  140. package/dist/registry/uip/commands.js +80 -0
  141. package/dist/registry/uip/envelope.d.ts +37 -0
  142. package/dist/registry/uip/envelope.js +84 -0
  143. package/dist/registry/uip/response-cache.d.ts +28 -0
  144. package/dist/registry/uip/response-cache.js +57 -0
  145. package/lib/README.md +10 -8
  146. package/package.json +3 -3
  147. package/dist/generators/_connections.py +0 -331
  148. package/dist/generators/_events.py +0 -355
  149. package/dist/generators/_lookups.py +0 -114
  150. package/dist/generators/_naming.py +0 -185
  151. package/dist/generators/_objects.py +0 -299
  152. package/dist/generators/_resolve.py +0 -319
  153. package/dist/generators/convert_library_to_md.py +0 -1441
  154. package/dist/generators/extract_library.py +0 -125
  155. package/dist/generators/generate_connectors_ts.py +0 -936
  156. package/dist/generators/generate_library_json.py +0 -2527
  157. package/dist/generators/generate_triggers_ts.py +0 -110
  158. package/dist/generators/materialize_registry.py +0 -155
  159. package/dist/generators/prepare_connector.py +0 -1860
  160. package/dist/registry/python.d.ts +0 -26
  161. package/dist/registry/python.js +0 -46
@@ -0,0 +1,150 @@
1
+ /**
2
+ * `## Resolving lookups`: helpers first, enumeration commands only where owed.
3
+ *
4
+ * A field holding an id is resolved late through `lookup(...)` and one
5
+ * `prepare`; the `resources run list` command survives only for IDENTITY
6
+ * lookups, which have no helper and no offline vocabulary. Ported from
7
+ * `lookup_lines`, `enumeration_command` and `reference_strategy`.
8
+ */
9
+ import { pyJsonString } from "../../../codecs/pyjson.js";
10
+ import { sortedStrings } from "../../../compat/compare.js";
11
+ import { pyStr, splitOnce } from "../../../compat/pystr.js";
12
+ import { classify, referenceOf } from "../../../lookup-specs.js";
13
+ import { byAliases } from "../../../naming.js";
14
+ import { authoringField, referenceCollection, referenceQuery } from "../common.js";
15
+ import { firstValue, IDENT, pyOr, pyTruthy, strList } from "../pyvalue.js";
16
+ /** The `resources run list` call that shows what an identity-lookup field accepts. */
17
+ export function enumerationCommand(connectorKey, f) {
18
+ const collection = referenceCollection(f);
19
+ const query = referenceQuery(f);
20
+ let command = `uip is resources run list ${connectorKey} ${collection} --connection-id <id>`;
21
+ if (query)
22
+ command += ` --query "${query}"`;
23
+ return command;
24
+ }
25
+ /** Prose for how `prepare` will retrieve this collection. */
26
+ export function referenceStrategy(reference) {
27
+ if (pyTruthy(reference.dependsOn)) {
28
+ const depends = strList(reference.dependsOn)
29
+ .map((d) => `\`${d}\``)
30
+ .join(", ");
31
+ return `dependent — resolve ${depends} first`;
32
+ }
33
+ if (pyTruthy(reference.filterPattern))
34
+ return "one request (server-side filter)";
35
+ if (pyTruthy(reference.childPath))
36
+ return "hierarchical walk";
37
+ return "paged scan (no server-side filter declared)";
38
+ }
39
+ /**
40
+ * The section body, or `[]` when no field is a lookup. `symbol` is the
41
+ * generated descriptor's exported name, or `undefined` when the operation has
42
+ * no descriptor (a Generic CRUD expansion), in which case the string form of
43
+ * `lookup()` carries the real key and action.
44
+ */
45
+ export function lookupLines(connectorKey, action, fields, symbol, invariant) {
46
+ const resolvable = [];
47
+ const identity = [];
48
+ for (const f of fields) {
49
+ const kind = classify(f, invariant);
50
+ if (kind === "lookup")
51
+ resolvable.push([f, referenceOf(f)]);
52
+ else if (kind === "identity")
53
+ identity.push(f);
54
+ }
55
+ if (!resolvable.length && !identity.length)
56
+ return [];
57
+ const out = [];
58
+ if (resolvable.length) {
59
+ const [firstField, firstReference] = resolvable[0];
60
+ // Keyed by the WIRE name (`users[*]`), the string `--resolve` and
61
+ // buildConnectorInputs match on; the table's `field` column keeps the
62
+ // authoring name the reader writes as an object key.
63
+ const firstName = pyStr(pyOr(firstField.name, authoringField(firstField)[0]));
64
+ const firstNames = strList(firstReference.lookupNames);
65
+ const firstAliases = byAliases(firstNames);
66
+ const holder = symbol || `'${connectorKey}', '${action}'`;
67
+ const call = `lookup(${holder}, '${firstName}')`;
68
+ const sample = firstAliases.size
69
+ ? `.${firstValue(firstAliases)}('…')`
70
+ : firstNames.length
71
+ ? `.by('${firstNames[0]}', '…')`
72
+ : ".by('id', '…')";
73
+ // The MECHANISM — resolve, never hand-write the wire value — is true of
74
+ // every lookup. The old JUSTIFICATION was not: it asserted the value is an
75
+ // opaque, connection-specific id, which is false for 320 of the library's
76
+ // 2,563 rows and, on `uipath-salesforce-slack` `send-message-to-user`, for
77
+ // a row printed three lines below the claim (`send_as` sends the word
78
+ // `bot`) — #706. Nothing in the metadata says which case a field is in, so
79
+ // the id claim is stated as the CONDITION it always was, and the `sends`
80
+ // column — already printed below — answers which case this page is.
81
+ out.push("These fields send a value the connector defines, not the label you " +
82
+ "know it by — the `sends` column says which. Do not write that value " +
83
+ "by hand: where it is an opaque id it is also connection-specific, so " +
84
+ "a pasted one resolves to nobody (or to somebody else) against a " +
85
+ "different connection. Resolve it late:");
86
+ out.push("");
87
+ out.push("```ts");
88
+ // A dotted wire name is a legal object key only when quoted.
89
+ const key = IDENT.test(firstName) ? firstName : pyJsonString(firstName);
90
+ out.push(`${key}: ${call}${sample}`);
91
+ out.push("```");
92
+ out.push("");
93
+ out.push("| field | helpers | matches on | sends |");
94
+ out.push("| --- | --- | --- | --- |");
95
+ for (const [f, reference] of resolvable) {
96
+ const [name] = authoringField(f);
97
+ const names = strList(reference.lookupNames);
98
+ const aliases = byAliases(names);
99
+ const helperCell = aliases.size
100
+ ? [...aliases.values()].map((a) => `\`.${a}(v)\``).join(" ")
101
+ : "_(generic form only)_";
102
+ // `objectName` can carry a query string; show the collection.
103
+ const target = pyOr(splitOnce(pyStr(pyOr(reference.objectName, "")), "?")[0], referenceCollection(f));
104
+ out.push(`| \`${name}\` | ${helperCell} | ` +
105
+ names.map((n) => `\`${n}\``).join(" ") +
106
+ ` | \`${target}.${pyStr(reference.lookupValue)}\` |`);
107
+ }
108
+ out.push("");
109
+ out.push("Any field also takes the generic form: `.by('<name>', value)`.");
110
+ out.push("");
111
+ // ORDERING, not just the command: author the token, let check name the command, run it once.
112
+ out.push("Author the `lookup()` call and finish the flow — `check` reports " +
113
+ "each token still unresolved with exactly this command; run it " +
114
+ "once before compiling (`compile` refuses an unresolved lookup):");
115
+ out.push("");
116
+ // ONE command carrying every --resolve, and no `--connection-id`.
117
+ out.push("```bash");
118
+ out.push(`npx flow-sdk registry prepare ${connectorKey} ${action} \\`);
119
+ const resolves = resolvable.map(([f, reference]) => {
120
+ const [name] = authoringField(f);
121
+ const names = pyOr(strList(reference.lookupNames), ["id"]);
122
+ return ` --resolve ${name}:${names[0]}=<value>`;
123
+ });
124
+ for (const line of resolves.slice(0, -1))
125
+ out.push(`${line} \\`);
126
+ out.push(resolves[resolves.length - 1]);
127
+ out.push("```");
128
+ out.push("");
129
+ const strategies = new Set(resolvable.map(([, r]) => referenceStrategy(r)));
130
+ out.push(`Strategy: ${sortedStrings(strategies).join("; ")}.`);
131
+ out.push("");
132
+ }
133
+ if (identity.length) {
134
+ out.push("**These fields take a plain string.** Their `reference` enumerates " +
135
+ "the legal values rather than mapping a name to an id — what you " +
136
+ "send back is what you searched for — so there is no helper and " +
137
+ "nothing to resolve. The values exist only behind the connection, " +
138
+ "so this is how to see what is accepted:");
139
+ out.push("");
140
+ out.push("```bash");
141
+ for (const f of identity) {
142
+ const [name] = authoringField(f);
143
+ out.push(`# ${name}`);
144
+ out.push(enumerationCommand(connectorKey, f));
145
+ }
146
+ out.push("```");
147
+ out.push("");
148
+ }
149
+ return out;
150
+ }
@@ -0,0 +1,3 @@
1
+ /** `## Outputs`: the entry's `outputSchema.fields`, one bullet each. The sidecar carries no output fields. */
2
+ import { type Dict } from "../pyvalue.js";
3
+ export declare function outputsLines(j: Dict): string[];
@@ -0,0 +1,9 @@
1
+ /** `## Outputs`: the entry's `outputSchema.fields`, one bullet each. The sidecar carries no output fields. */
2
+ import { dictOf, listOf, pyOr } from "../pyvalue.js";
3
+ import { fieldLine } from "./inputs.js";
4
+ export function outputsLines(j) {
5
+ const outFields = listOf(pyOr(dictOf(j.outputSchema).fields, []));
6
+ if (!outFields.length)
7
+ return [];
8
+ return ["## Outputs", "", ...outFields.map((f) => fieldLine(dictOf(f))), ""];
9
+ }
@@ -0,0 +1,2 @@
1
+ import { type Dict } from "../pyvalue.js";
2
+ export declare function schemaActionLines(connectorKey: string, objectName: string, verb: string, action: Dict, fields: readonly Dict[]): string[];
@@ -0,0 +1,139 @@
1
+ /**
2
+ * `## Resolving the field set`: how to get an operation's real field set when
3
+ * parent VALUES select it (Jira `create-issue` describes to 2 fields with
4
+ * nothing supplied and 32 once a project and an issue type are given).
5
+ * Ported from `schema_action_lines`.
6
+ */
7
+ import { pyStr, splitOnce } from "../../../compat/pystr.js";
8
+ import { referenceCollection, referenceQuery } from "../common.js";
9
+ import { dictOf, listOf, pyOr, pyTruthy } from "../pyvalue.js";
10
+ export function schemaActionLines(connectorKey, objectName, verb, action, fields) {
11
+ const parents = listOf(pyOr(action.parents, []));
12
+ if (!parents.length)
13
+ return [];
14
+ const byName = new Map();
15
+ for (const f of fields)
16
+ byName.set(f.name, f);
17
+ const trigger = action.trigger;
18
+ const replaces = action.replaces;
19
+ const obj = pyOr(objectName, "<object>");
20
+ const out = ["## Resolving the field set", ""];
21
+ out.push("The inputs listed below are only the ones that SELECT the field set. " +
22
+ "The real set depends on the values of " +
23
+ parents.map((p) => `\`${pyStr(p)}\``).join(", ") +
24
+ " — a different value gives a different set, so there is no single " +
25
+ "field list for this operation.");
26
+ out.push("");
27
+ if (pyTruthy(trigger)) {
28
+ out.push(`It is not automatic: \`${pyStr(trigger)}\` is a trigger rather than data, ` +
29
+ "so the schema has to be requested once the values are in place.");
30
+ out.push("");
31
+ }
32
+ out.push("Resolve the parents in this order, then describe again:");
33
+ out.push("");
34
+ parents.forEach((parentRaw, i) => {
35
+ const position = i + 1;
36
+ const parent = pyStr(parentRaw);
37
+ const field = dictOf(byName.get(parentRaw));
38
+ const reference = dictOf(pyOr(field.reference, {}));
39
+ const collection = referenceCollection(field);
40
+ if (!collection) {
41
+ out.push(`${position}. \`${parent}\` — supply the value; it is not chosen from a list.`);
42
+ out.push("");
43
+ return;
44
+ }
45
+ const query = referenceQuery(field);
46
+ const pattern = pyStr(pyOr(reference.filterPattern, ""));
47
+ const names = listOf(pyOr(reference.lookupNames, []));
48
+ const send = pyStr(pyOr(reference.lookupValue, "id"));
49
+ out.push(`${position}. \`${parent}\` — from \`${collection}\`` +
50
+ (names.length ? `, match on ${names.map((n) => `\`${pyStr(n)}\``).join(" or ")}` : "") +
51
+ `, send \`${send}\`.`);
52
+ // A placeholder means an earlier parent's value goes here.
53
+ if (collection.includes("{")) {
54
+ out.push(" Substitute the value resolved above for the `{…}` part of the collection.");
55
+ }
56
+ out.push("");
57
+ out.push(" ```bash");
58
+ out.push(` uip is resources run list ${connectorKey} ${collection} --connection-id <id> \\`);
59
+ if (pattern) {
60
+ // The connector declares its own search; substituting the term narrows server-side.
61
+ out.push(` --query "${pattern.replaceAll("{filter}", "<search-term>")}" \\`);
62
+ }
63
+ else if (query) {
64
+ out.push(` --query "${query}" \\`);
65
+ }
66
+ const projection = pyOr(names.slice(0, 2), ["name"])
67
+ .map((n) => `${splitOnce(pyStr(n), "[")[0]}:${pyStr(n)}`)
68
+ .join(",");
69
+ out.push(` --output-filter "{items: items[*].{${projection},` +
70
+ `send:${send}}, page: Pagination.{h:HasMore,t:NextPageToken}}" \\`);
71
+ out.push(" --output json");
72
+ out.push(" ```");
73
+ out.push("");
74
+ if (pattern) {
75
+ out.push(" The connector declares its own search, so narrow with " +
76
+ "the term the user gave rather than reading pages: one " +
77
+ "call usually returns a handful, and `page` comes back " +
78
+ "either absent or with `h` as `\"false\"`. Page only if " +
79
+ "the term matches nothing.");
80
+ }
81
+ else {
82
+ out.push(" Page it: `page.h` is the `elements-has-more` response " +
83
+ "header verbatim, so it is the string `\"true\"` rather " +
84
+ "than a boolean, and `page` is absent altogether when the " +
85
+ "connector sends no pagination headers. While it reads " +
86
+ "`\"true\"` and the value has not appeared, pass `page.t` " +
87
+ "back as `--query \"nextPage=<token>\"` and read the next " +
88
+ "page. Stop on the match. The projection keeps each page " +
89
+ "small but does not reduce the call count.");
90
+ out.push("");
91
+ out.push(" Before paging, check this field's `Reference` in the " +
92
+ "`describe` output for a `FilterPattern`: it is " +
93
+ "connection-scoped, so it is not in this catalog, but when " +
94
+ "present it is the connector's own search and removes the " +
95
+ "paging — Jira's project lookup declares `query={filter}`, " +
96
+ "which turns 420-plus rows across pages into two in one call. " +
97
+ "Substitute the wanted term into whatever pattern this field " +
98
+ "declares; the shape is:");
99
+ out.push("");
100
+ out.push(" ```bash");
101
+ out.push(` uip is resources run list ${connectorKey} ${collection} --connection-id <id> \\`);
102
+ out.push(' --query "<filter-pattern with the term substituted>" --output json');
103
+ out.push(" ```");
104
+ }
105
+ out.push("");
106
+ });
107
+ const supplied = parents.map((p) => `-f ${pyStr(p)}=<value>`).join(" ");
108
+ out.push(`${parents.length + 1}. Describe again with the resolved values:`);
109
+ out.push("");
110
+ out.push(" ```bash");
111
+ out.push(` uip is resources describe ${connectorKey} ${pyStr(obj)} --operation ${verb} \\`);
112
+ out.push(` --connection-id <id> ${supplied} --output json`);
113
+ out.push(" ```");
114
+ out.push("");
115
+ if (replaces === "output") {
116
+ out.push(" This replaces the **response** fields — it tells you the shape " +
117
+ "of what comes back, not what to send.");
118
+ }
119
+ else if (replaces === "both") {
120
+ out.push(" This replaces the **request** fields AND the **response** " +
121
+ "fields — both halves of this operation change with the values " +
122
+ "above, so read the response shape from the same call.");
123
+ }
124
+ else {
125
+ out.push(" This replaces the **request** fields.");
126
+ }
127
+ out.push("");
128
+ // Only meaningful where the action rewrites the INPUT (matches the CLI's `isParentOnlyResult` gate).
129
+ if (replaces === "input" || replaces === "both") {
130
+ out.push("If the result contains only the parents again, one of the values " +
131
+ "does not exist on this connection. The call reports `Success` " +
132
+ "either way, so the count alone cannot tell that apart from having " +
133
+ "supplied nothing — check whether anything beyond the parents came " +
134
+ "back. Too FEW parents fails loudly instead, with " +
135
+ "`No api-type ObjectAction matched`.");
136
+ out.push("");
137
+ }
138
+ return out;
139
+ }
@@ -0,0 +1,2 @@
1
+ import { type Dict } from "../pyvalue.js";
2
+ export declare function schemaDynamicLines(j: Dict, nodeType: string): string[];
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The blockquote on an operation whose real inputs arrive from a live
3
+ * connection (`runtime.requiresConnectionForSchema`). It points the agent at
4
+ * `registry prepare` without claiming every marked operation is incomplete; a
5
+ * discovery entry (`objectFromDiscovery`) is missing its OBJECT rather than
6
+ * field values, so its prose and its `--object` flag differ.
7
+ */
8
+ import { afterLast, pyStr } from "../../../compat/pystr.js";
9
+ import { dictOf, listOf, pyOr, pyTruthy } from "../pyvalue.js";
10
+ export function schemaDynamicLines(j, nodeType) {
11
+ if (!pyTruthy(dictOf(j.runtime).requiresConnectionForSchema))
12
+ return [];
13
+ const conn = dictOf(j.connector);
14
+ const op = dictOf(j.operation);
15
+ const key = pyStr(pyOr(conn.key, "<key>"));
16
+ const prefix = `uipath.connector.${key}.`;
17
+ const action = nodeType.startsWith(prefix) ? nodeType.slice(prefix.length) : afterLast(nodeType, ".");
18
+ const parents = listOf(pyOr(dictOf(op.schemaAction).parents, []));
19
+ const discovery = pyTruthy(op.objectFromDiscovery);
20
+ const out = [];
21
+ let supply;
22
+ if (discovery) {
23
+ out.push("> ⚠️ **The object is not in this catalog.** This node works on " +
24
+ "any object in the connection, including custom ones, so it " +
25
+ "carries no inputs here — what has to be resolved is the " +
26
+ "OBJECT, not a field value. Author first, then one command " +
27
+ "resolves it; **Choosing the object** below has the rules.");
28
+ supply = "--object <name-as-the-task-said-it>";
29
+ }
30
+ else {
31
+ out.push("> ⚠️ **This operation resolves its real inputs from a live " +
32
+ "connection.** The inputs listed below are only what the " +
33
+ "tenant-agnostic snapshot carries. Do NOT hand-guess the " +
34
+ "missing names.");
35
+ out.push(">");
36
+ if (parents.length) {
37
+ out.push("> Its parents are named outright, with the call that " +
38
+ "resolves each one, under **Resolving the field set** " +
39
+ "below. Read that first.");
40
+ }
41
+ else {
42
+ out.push("> Any input whose reference path carries a `{placeholder}` " +
43
+ "is a parent: resolve a value for EVERY parent first, then " +
44
+ "prepare the operation with all of them. Too few parents is " +
45
+ "refused; a wrong VALUE is not — it resolves right back to " +
46
+ "the parent fields.");
47
+ }
48
+ // The `-f` stays visible even when the catalog cannot name the parents.
49
+ supply = parents.length
50
+ ? parents.map((name) => `-f ${pyStr(name)}=<value>`).join(" ")
51
+ : "-f <parent-field>=<value> ...";
52
+ }
53
+ out.push(">");
54
+ out.push("> ```bash");
55
+ // ONE spelling on every page, the one `check` prints; no `--connection-id`.
56
+ if (discovery) {
57
+ out.push(`> npx flow-sdk registry prepare ${key} ${action} ${supply}`);
58
+ }
59
+ else if (supply) {
60
+ out.push(`> npx flow-sdk registry prepare ${key} ${action} \\`);
61
+ out.push(`> ${supply}`);
62
+ }
63
+ else {
64
+ out.push(`> npx flow-sdk registry prepare ${key} ${action}`);
65
+ }
66
+ out.push("> ```");
67
+ out.push(">");
68
+ let overlay = "> It writes a `./connectors-local/` overlay the compiler " +
69
+ "auto-detects (its fields are unioned onto this entry).";
70
+ if (!discovery) {
71
+ overlay +=
72
+ " The full loop — how to find the parents and resolve their " +
73
+ "values — is in the connector guide " +
74
+ "(`references/connector-params.md`) under \"Schema-dynamic " +
75
+ "operations: the parent-field loop\".";
76
+ }
77
+ out.push(overlay);
78
+ out.push("> This requires a working live connection. For an explicitly " +
79
+ "offline/validate-only flow that needs no connection-specific fields, " +
80
+ "use the published static inputs below instead; a placeholder " +
81
+ "connection cannot materialize a live schema. If that input list is " +
82
+ "empty, pass `{}`: offline evidence proves operation identity and " +
83
+ "control routing, not the provider payload or dispatch.");
84
+ out.push("");
85
+ return out;
86
+ }
@@ -0,0 +1,30 @@
1
+ import { type Reporter } from "../reporter.js";
2
+ export interface MaterializeOptions {
3
+ /** The extracted library; must hold an `index.json`. */
4
+ libraryJsonPath: string;
5
+ /** Where the Markdown goes. Omit to skip it. */
6
+ libraryMarkdownPath?: string;
7
+ /** Where the descriptor modules go. Omit to generate none. */
8
+ connectorsPath?: string;
9
+ /** Connector keys to emit descriptors for, instead of all of them. */
10
+ connectorSubset?: readonly string[];
11
+ /** Module specifier the generated TypeScript imports the SDK from. */
12
+ connectorImport?: string;
13
+ /** `registry-triggers.json`, for `<key>.triggers.ts` modules beside the descriptors. */
14
+ triggersPath?: string;
15
+ /** Also emit descriptors for GenericExpanded entries (the overlay's case). */
16
+ includeGenericExpanded?: boolean;
17
+ /** Fail when a page cites a symbol the modules do not export. */
18
+ checkSymbols?: boolean;
19
+ reporter?: Reporter;
20
+ }
21
+ export interface MaterializeResult {
22
+ markdownFiles: number;
23
+ typescriptModules: number;
24
+ /** `nodeType → exported const`, for callers that print a handoff. */
25
+ symbols: Map<string, string>;
26
+ warnings: string[];
27
+ }
28
+ export declare function materialize(options: MaterializeOptions): MaterializeResult;
29
+ /** Remove a staging directory left by an interrupted run, if any. */
30
+ export declare function cleanStaging(target: string): void;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Turn an extracted `library-json/` into the two derived views: Markdown pages
3
+ * an agent reads and TypeScript descriptor modules an author imports.
4
+ *
5
+ * This is `materialize_registry.py` without the subprocesses: the three
6
+ * generators it used to spawn are function calls. What it keeps is every
7
+ * observable rule of the Python — the connectors directory is wiped first, the
8
+ * Markdown is staged beside its target and swapped in, a missing trigger
9
+ * catalogue is an error, the trigger subset is deliberately NOT narrowed to the
10
+ * connector subset (the Python never forwarded it; file counts depend on that),
11
+ * and a required-input gap fails the run after the descriptor modules are
12
+ * written but before the trigger modules are, the way the descriptor
13
+ * generator's `sys.exit` did.
14
+ *
15
+ * New, behind a flag: `checkSymbols` runs the cross-check that used to be a
16
+ * separate script in `unpack-library.sh`, so a page can never cite a symbol
17
+ * the descriptors do not export.
18
+ */
19
+ import { PACKAGE_NAME } from "../../package-name.js";
20
+ import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
21
+ import { join, resolve } from "node:path";
22
+ import { pyJsonDocument } from "../codecs/pyjson.js";
23
+ import { Refusal } from "../errors.js";
24
+ import { listFiles, resetDir, stageAndSwap, writeText } from "../files.js";
25
+ import { loadLibrary } from "../library-reader.js";
26
+ import { silentReporter } from "../reporter.js";
27
+ import { formatRequiredInputGaps, renderDescriptorModules } from "./descriptors.js";
28
+ import { renderMarkdownLibrary } from "./markdown/index.js";
29
+ import { citedSymbolMismatches, formatMismatches } from "./md-symbols.js";
30
+ import { renderTriggerModules } from "./triggers.js";
31
+ const DEFAULT_IMPORT = PACKAGE_NAME;
32
+ function countFiles(dir, suffix, recursive) {
33
+ if (!dir || !existsSync(dir))
34
+ return 0;
35
+ if (recursive)
36
+ return listFiles(dir, (rel) => rel.endsWith(suffix)).length;
37
+ return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(suffix)).length;
38
+ }
39
+ /** The pages on disk under a Markdown tree, keyed like `renderMarkdownLibrary` keys them. */
40
+ function readPages(markdownDir) {
41
+ const pages = new Map();
42
+ for (const rel of listFiles(markdownDir, (r) => r.endsWith(".md"))) {
43
+ pages.set(rel, readFileSync(join(markdownDir, rel), "utf8"));
44
+ }
45
+ return pages;
46
+ }
47
+ export function materialize(options) {
48
+ const reporter = options.reporter ?? silentReporter;
49
+ const libraryJson = resolve(options.libraryJsonPath);
50
+ const libraryMd = options.libraryMarkdownPath ? resolve(options.libraryMarkdownPath) : undefined;
51
+ const connectors = options.connectorsPath ? resolve(options.connectorsPath) : undefined;
52
+ if (!existsSync(join(libraryJson, "index.json"))) {
53
+ throw new Refusal(`materialize-registry: missing ${join(libraryJson, "index.json")}`, "LIBRARY_MISSING");
54
+ }
55
+ const outputs = [libraryMd, connectors].filter((p) => p !== undefined);
56
+ if (outputs.includes(libraryJson) || new Set(outputs).size !== outputs.length) {
57
+ throw new Refusal("materialize-registry: input and output directories must be distinct", "OUTPUTS_NOT_DISTINCT");
58
+ }
59
+ const warnings = [];
60
+ const library = loadLibrary(libraryJson);
61
+ if (connectors !== undefined)
62
+ resetDir(connectors);
63
+ let pages;
64
+ if (libraryMd !== undefined) {
65
+ // Generate beside the target and swap, exactly as pull-cli does for
66
+ // library-json: two `registry pull`s racing on the shared final path (a
67
+ // coding agent issues tool calls in parallel) once left one of them with
68
+ // "Directory not empty" mid-write.
69
+ const rendered = renderMarkdownLibrary(library);
70
+ for (const warning of rendered.warnings)
71
+ reporter.warn(warning);
72
+ warnings.push(...rendered.warnings);
73
+ stageAndSwap(libraryMd, (staging) => {
74
+ for (const [rel, text] of rendered.pages)
75
+ writeText(join(staging, rel), text);
76
+ writeText(join(staging, "index.json"), pyJsonDocument(rendered.index));
77
+ });
78
+ pages = rendered.pages;
79
+ }
80
+ let modules = new Map();
81
+ let symbols = new Map();
82
+ if (connectors !== undefined) {
83
+ const output = renderDescriptorModules(library, {
84
+ importSpecifier: options.connectorImport ?? DEFAULT_IMPORT,
85
+ connectors: options.connectorSubset?.filter((c) => c.length > 0),
86
+ includeGenericExpanded: options.includeGenericExpanded,
87
+ });
88
+ modules = output.modules;
89
+ symbols = output.symbols;
90
+ for (const [name, text] of modules)
91
+ writeText(join(connectors, name), text);
92
+ // The descriptor generator wrote every module and then exited 1 on a gap,
93
+ // and `materialize_registry.py` ran it with `check=True`, so the trigger
94
+ // generator that came next never ran. Same here: the descriptor modules
95
+ // are on disk, the failure is as loud, and no `.triggers.ts` is written
96
+ // beside them.
97
+ if (output.requiredInputGaps.length > 0) {
98
+ throw new Refusal(formatRequiredInputGaps(output.requiredInputGaps), "REQUIRED_INPUT_GAP");
99
+ }
100
+ if (options.triggersPath) {
101
+ const triggersPath = resolve(options.triggersPath);
102
+ if (!existsSync(triggersPath)) {
103
+ throw new Refusal(`materialize-registry: missing trigger catalog ${triggersPath}`, "TRIGGERS_MISSING");
104
+ }
105
+ const catalogue = JSON.parse(readFileSync(triggersPath, "utf8"));
106
+ // The connector subset is not forwarded here, as the Python never did.
107
+ for (const [name, text] of renderTriggerModules(catalogue, { importSpecifier: options.connectorImport ?? DEFAULT_IMPORT })) {
108
+ writeText(join(connectors, name), text);
109
+ }
110
+ }
111
+ }
112
+ if (options.checkSymbols && connectors !== undefined) {
113
+ const checked = pages ?? (libraryMd && existsSync(libraryMd) ? readPages(libraryMd) : undefined);
114
+ if (checked) {
115
+ const bad = citedSymbolMismatches(checked, modules);
116
+ if (bad.length > 0)
117
+ throw new Refusal(formatMismatches(bad), "MD_SYMBOL_MISMATCH");
118
+ }
119
+ }
120
+ const result = {
121
+ markdownFiles: countFiles(libraryMd, ".md", true),
122
+ typescriptModules: countFiles(connectors, ".ts", false),
123
+ symbols,
124
+ warnings,
125
+ };
126
+ reporter.info(`materialize-registry: generated ${result.markdownFiles} markdown file(s) and ${result.typescriptModules} TypeScript module(s)`);
127
+ return result;
128
+ }
129
+ /** Remove a staging directory left by an interrupted run, if any. */
130
+ export function cleanStaging(target) {
131
+ rmSync(`${target}.tmp-${process.pid}`, { recursive: true, force: true });
132
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Every descriptor symbol the Markdown cites must exist in the emitted TypeScript.
3
+ *
4
+ * The `## Example` on each page names an exported const —
5
+ * `connector(SendMessageToUser, …)` — and imports it from
6
+ * `./connectors/<key>.ts`. Neither the const name nor the `.by*` aliases are in
7
+ * the JSON library: both are derived by `naming.symbolIndex`, which the
8
+ * Markdown and descriptor generators share precisely so they cannot disagree.
9
+ * This is the check that proves the sharing is real rather than intended.
10
+ * Ported from `check_md_symbols.py`.
11
+ */
12
+ /** `{ connectorKey → exported const names }` from `<key>.ts` module texts, `.triggers.ts` excluded. */
13
+ export declare function exportedSymbols(modules: ReadonlyMap<string, string>): Map<string, Set<string>>;
14
+ /**
15
+ * One line per page that cites a symbol its module does not export, as
16
+ * `<page> cites <key>.<Symbol>`, in page order.
17
+ */
18
+ export declare function citedSymbolMismatches(pages: ReadonlyMap<string, string>, modules: ReadonlyMap<string, string>): string[];
19
+ /** How many mismatches to print before truncating. */
20
+ export declare const MAX_REPORTED = 20;
21
+ /** The stderr text `check_md_symbols.py` printed, for the same mismatches. */
22
+ export declare function formatMismatches(bad: readonly string[]): string;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Every descriptor symbol the Markdown cites must exist in the emitted TypeScript.
3
+ *
4
+ * The `## Example` on each page names an exported const —
5
+ * `connector(SendMessageToUser, …)` — and imports it from
6
+ * `./connectors/<key>.ts`. Neither the const name nor the `.by*` aliases are in
7
+ * the JSON library: both are derived by `naming.symbolIndex`, which the
8
+ * Markdown and descriptor generators share precisely so they cannot disagree.
9
+ * This is the check that proves the sharing is real rather than intended.
10
+ * Ported from `check_md_symbols.py`.
11
+ */
12
+ /** `export const X =` at a line start; `\w` as Python spells it, Unicode-aware. */
13
+ const EXPORTED = /^export const ([\p{L}\p{N}_]+) =/gmu;
14
+ /** `import { X } from './connectors/<key>.ts'` or `./connectors-local/…`. */
15
+ const CITED = /import \{ ([\p{L}\p{N}_]+) \} from '\.\/connectors(?:-local)?\/([^']+)\.ts'/gu;
16
+ /** `{ connectorKey → exported const names }` from `<key>.ts` module texts, `.triggers.ts` excluded. */
17
+ export function exportedSymbols(modules) {
18
+ const out = new Map();
19
+ for (const [fileName, text] of modules) {
20
+ // `<key>.triggers.ts` is the connector-EVENT surface, a different catalogue
21
+ // with its own identity-only descriptors. Pages never import from it.
22
+ if (!fileName.endsWith(".ts") || fileName.endsWith(".triggers.ts"))
23
+ continue;
24
+ const names = new Set();
25
+ for (const match of text.matchAll(EXPORTED))
26
+ names.add(match[1]);
27
+ out.set(fileName.slice(0, -3), names);
28
+ }
29
+ return out;
30
+ }
31
+ /**
32
+ * One line per page that cites a symbol its module does not export, as
33
+ * `<page> cites <key>.<Symbol>`, in page order.
34
+ */
35
+ export function citedSymbolMismatches(pages, modules) {
36
+ const exported = exportedSymbols(modules);
37
+ const bad = [];
38
+ for (const [page, text] of pages) {
39
+ for (const match of text.matchAll(CITED)) {
40
+ const [, symbol, key] = match;
41
+ if (!exported.get(key)?.has(symbol))
42
+ bad.push(`${page} cites ${key}.${symbol}`);
43
+ }
44
+ }
45
+ return bad;
46
+ }
47
+ /** How many mismatches to print before truncating. */
48
+ export const MAX_REPORTED = 20;
49
+ /** The stderr text `check_md_symbols.py` printed, for the same mismatches. */
50
+ export function formatMismatches(bad) {
51
+ const lines = [`error: ${bad.length} markdown page(s) cite a descriptor symbol that was never emitted:`];
52
+ for (const line of bad.slice(0, MAX_REPORTED))
53
+ lines.push(` ${line}`);
54
+ if (bad.length > MAX_REPORTED)
55
+ lines.push(` … and ${bad.length - MAX_REPORTED} more`);
56
+ lines.push(" Both generators derive symbol names through registry/naming.ts (symbolIndex).", " A mismatch means one of them stopped using it. See docs/CONNECTOR_LOOKUP_DESIGN.md §6.4.");
57
+ return lines.join("\n");
58
+ }