@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,405 @@
1
+ /**
2
+ * Statically-typed connector descriptor modules from the canonical JSON library.
3
+ *
4
+ * For every Curated and HTTP-request operation, one `descriptor<Inputs,
5
+ * Outputs>()` const plus its `Inputs`/`Outputs` interfaces; for every connector
6
+ * EVENT, a `triggerDescriptor<Where, Outputs>()` const. Grouped one module per
7
+ * connector, `connectors/<connector-key>.ts`. Authored flows import the exported
8
+ * consts by name, so the module text is an API contract: it is byte-for-byte
9
+ * what `generate_connectors_ts.py` wrote. The differential that proved that
10
+ * ran while the Python was in the tree and was deleted with it, so
11
+ * `tests/registry-descriptors.test.ts` is now the description of the text.
12
+ *
13
+ * Inputs are the union of the sidecar's body fields and the entry's query/path/
14
+ * file-multipart parameters (see `fields.ts`); dotted and wildcard names are
15
+ * rebuilt as nested objects and arrays (`type-tree.ts`) and named once per
16
+ * connector (`type-registry.ts`). Outputs come from `outputSchema.fields`.
17
+ * `GenericExpanded` operations are skipped unless asked for, and a generic
18
+ * event's per-object variants are skipped because they share their template's
19
+ * node type and so would emit the same const twice.
20
+ */
21
+ import { PACKAGE_NAME } from "../../package-name.js";
22
+ import { pyJsonString, pyJsonStringify } from "../codecs/pyjson.js";
23
+ import { sortedByCodePoint, sortedStrings } from "../compat/compare.js";
24
+ import { afterLast, pyRstrip, pyStr, pyStrip, splitOnce } from "../compat/pystr.js";
25
+ import { referenceOf, specsFromFields } from "../lookup-specs.js";
26
+ import { isEventNodeType, } from "../model/library.js";
27
+ import { symbolIndex } from "../naming.js";
28
+ import { descriptorInputFields, lookupSpecs, markLookupFields } from "./fields.js";
29
+ import { NamedTypeRegistry } from "./type-registry.js";
30
+ import { buildTypeTree, pyTruthy, subtreeRequired } from "./type-tree.js";
31
+ /** `str(value or "")`. */
32
+ function strOrEmpty(value) {
33
+ return pyTruthy(value) ? pyStr(value) : "";
34
+ }
35
+ /** A field name is a valid identifier only if it has no dots, brackets, etc.; otherwise it is quoted. */
36
+ export function keyLiteral(name) {
37
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : pyJsonString(name);
38
+ }
39
+ /** One sentence for a lookup field's own doc comment: what it sends, how to get one. */
40
+ export function lookupFieldNote(declared) {
41
+ const reference = referenceOf(declared) ?? {};
42
+ const target = pyStr(pyTruthy(reference.objectName) ? reference.objectName : splitOnce(strOrEmpty(reference.path), "?")[0]);
43
+ const value = pyStr(pyTruthy(reference.lookupValue) ? reference.lookupValue : "id");
44
+ const field = strOrEmpty(declared.name);
45
+ return (`LOOKUP — sends a \`${target}.${value}\`, never a name or e-mail: ` +
46
+ `lookup(<Descriptor>, '${field}') (helpers in the LOOKUP lines above the descriptor).`);
47
+ }
48
+ /**
49
+ * One property line.
50
+ *
51
+ * An INPUT property is wrapped so it also accepts an expression, and a
52
+ * resolvable lookup additionally accepts an unresolved token. The wrapper goes
53
+ * here rather than on the leaf type so that `tags: input('tags')` — the whole
54
+ * collection as one expression — is legal alongside `tags: ['a', out(…)]`.
55
+ * An OUTPUT property is never wrapped: it came back from the connector.
56
+ */
57
+ export function emitNestedProperty(name, node, indent, namedTypes, allOptional, isInput = false) {
58
+ const lines = [];
59
+ const declared = node.declared ?? {};
60
+ let desc = pyStrip(strOrEmpty(declared.description)).replaceAll("\n", " ");
61
+ if (isInput && pyTruthy(declared.__lookup)) {
62
+ // The identity goes ON the field, not only in the LOOKUP block above the
63
+ // descriptor: "The reporter of the issue" beside `ConnectorLookupValue<string>`
64
+ // read as "any string", and an e-mail went into `fields.reporter.id`.
65
+ desc = pyStrip(`${desc} ${lookupFieldNote(declared)}`);
66
+ }
67
+ if (desc)
68
+ lines.push(`${indent}/** ${desc} */`);
69
+ const optional = allOptional || !subtreeRequired(node) ? "?" : "";
70
+ let rendered = namedTypes.typeFor(name, node);
71
+ if (isInput) {
72
+ const wrapper = pyTruthy(declared.__lookup) ? "ConnectorLookupValue" : "ConnectorValue";
73
+ rendered = `${wrapper}<${rendered}>`;
74
+ }
75
+ lines.push(`${indent}${keyLiteral(name)}${optional}: ${rendered};`);
76
+ return lines;
77
+ }
78
+ /** `export interface <name> { … }` over the roots of one tree. */
79
+ export function emitInterface(name, allOptional, namedTypes, fieldTree, isInput = false) {
80
+ const out = [`export interface ${name} {`];
81
+ for (const [fieldName, node] of fieldTree) {
82
+ out.push(...emitNestedProperty(fieldName, node, " ", namedTypes, allOptional, isInput));
83
+ }
84
+ out.push("}");
85
+ return out;
86
+ }
87
+ export const STRATEGY_PROSE = {
88
+ filter: "one request (the operation declares a server-side filter)",
89
+ scan: "paged scan (this operation declares no filterPattern)",
90
+ tree: "hierarchical walk (the collection is a tree)",
91
+ dependent: "dependent — another field must resolve first",
92
+ };
93
+ /**
94
+ * The grep-able header: the lookups stated in prose beside the data, for agents
95
+ * that read source with Grep and have no language server. Bare ` * ` body
96
+ * lines, to be spliced inside a `/** … *\/` block.
97
+ */
98
+ export function emitLookupComment(specs, symbol = "<Descriptor>") {
99
+ const lines = [];
100
+ for (const [field, spec] of specs) {
101
+ const target = splitOnce(pyStr(pyTruthy(spec.objectName) ? spec.objectName : spec.path), "?")[0];
102
+ lines.push(` * LOOKUP: \`${field}\` -> ${target}.${spec.value}`);
103
+ const aliases = spec.aliases;
104
+ const offered = aliases.size > 0 ? [...aliases.values()].map((a) => `${a}()`) : [];
105
+ const byList = spec.by.map((n) => `\`${n}\``).join(", ");
106
+ lines.push(` * by: ${byList}`);
107
+ // THE WHOLE CALL, not just the method names: naming `.byEmail` without the
108
+ // `lookup(Descriptor, 'field')` wrapper says helpers exist but not how to
109
+ // reach one.
110
+ const first = aliases.size > 0 ? [...aliases.values()][0] : undefined;
111
+ const call = first
112
+ ? `lookup(${symbol}, '${field}').${first}(value)`
113
+ : spec.by.length > 0
114
+ ? `lookup(${symbol}, '${field}').by('${spec.by[0]}', value)`
115
+ : `lookup(${symbol}, '${field}')`;
116
+ lines.push(` * use: ${call}`);
117
+ if (offered.length > 0) {
118
+ lines.push(` * also: ${[...aliases.values()].map((a) => `.${a}`).join(", ")} · .by(name, value)`);
119
+ }
120
+ else {
121
+ lines.push(" * also: .by(name, value) only — aliases collided");
122
+ }
123
+ lines.push(` * strategy: ${STRATEGY_PROSE[spec.strategy]}`);
124
+ if (spec.dependsOn && spec.dependsOn.length > 0) {
125
+ lines.push(` * resolve first: ${spec.dependsOn.map((d) => `\`${d}\``).join(", ")}`);
126
+ }
127
+ }
128
+ return lines;
129
+ }
130
+ /**
131
+ * The descriptor's `lookups:` literal — a RUNTIME value, not a type. `lookup()`
132
+ * reads `aliases` from it to build the `.byName()` family.
133
+ */
134
+ export function emitLookupsBlock(specs) {
135
+ if (specs.size === 0)
136
+ return [];
137
+ const lines = [" lookups: {"];
138
+ for (const [field, spec] of specs) {
139
+ lines.push(` ${keyLiteral(field)}: {`);
140
+ if (pyTruthy(spec.objectName))
141
+ lines.push(` objectName: ${pyJsonString(spec.objectName)},`);
142
+ lines.push(` path: ${pyJsonStringify(spec.path)},`);
143
+ lines.push(` by: ${pyJsonStringify(spec.by)},`);
144
+ lines.push(` value: ${pyJsonString(spec.value)},`);
145
+ const aliases = [...spec.aliases].map(([name, alias]) => `${alias}: ${pyJsonString(name)}`).join(", ");
146
+ lines.push(` aliases: {${aliases ? ` ${aliases} ` : ""}},`);
147
+ lines.push(` strategy: ${pyJsonString(spec.strategy)},`);
148
+ if (spec.dependsOn && spec.dependsOn.length > 0)
149
+ lines.push(` dependsOn: ${pyJsonStringify(spec.dependsOn)},`);
150
+ lines.push(" },");
151
+ }
152
+ lines.push(" },");
153
+ return lines;
154
+ }
155
+ /** `entry.get("version", "1.0.0")`: the key's value when present, even `null`. */
156
+ function versionOf(entry) {
157
+ return "version" in entry ? entry.version : "1.0.0";
158
+ }
159
+ /**
160
+ * A connector EVENT as a `triggerDescriptor()`, the shape `onEvent()` takes.
161
+ *
162
+ * The payload type comes from `outputSchema`, and `where` from the entry's
163
+ * event parameters. An entry that declares none keeps a loose
164
+ * `Record<string, string>`: that is either an event which genuinely takes no
165
+ * parameters (HTTP Webhook) or one nothing has described yet, and an empty
166
+ * interface would refuse every key in both cases.
167
+ */
168
+ export function emitEvent(entry, inFields, inputTree, outputTree, name, inputTypes, outputTypes, invariant = new Set()) {
169
+ const nodeType = entry.nodeType;
170
+ const eventId = afterLast(nodeType, ".");
171
+ const connectorKey = pyTruthy(entry.connector?.key) ? entry.connector.key : "";
172
+ const label = pyTruthy(entry.display?.label) ? entry.display.label : eventId;
173
+ const version = versionOf(entry);
174
+ const generic = entry.operation?.activityType === "GenericTrigger";
175
+ const lines = [];
176
+ let whereType = "Record<string, string>";
177
+ if (inFields.length > 0) {
178
+ whereType = `${name}Where`;
179
+ lines.push(...emitInterface(whereType, false, inputTypes, inputTree, true));
180
+ lines.push("");
181
+ }
182
+ lines.push(...emitInterface(`${name}Outputs`, true, outputTypes, outputTree));
183
+ lines.push("");
184
+ // A subscription's `where` values are resolved by `lookup()` exactly as a
185
+ // connector input's are, so the descriptor carries the same specs.
186
+ const specs = specsFromFields(inFields, invariant);
187
+ lines.push("/**");
188
+ lines.push(` * ${pyStr(label)} — ${nodeType}`);
189
+ lines.push(" *");
190
+ if (generic) {
191
+ lines.push(" * A GENERIC event: it fires for ONE object of the connection (an entity, a");
192
+ lines.push(` * table, a custom object), so name it — \`onEvent(${name}, { object: '<Object>' })\`.`);
193
+ lines.push(` * \`uip is triggers objects ${pyStr(connectorKey)} <EVENT> --connection-id <id>\` lists them.`);
194
+ }
195
+ else {
196
+ lines.push(` * Start a flow on it with \`onEvent(${name}, { where: { … } })\`, or pause for it`);
197
+ lines.push(` * with \`waitForEvent(${name}, { … })\`; \`where\` carries its event parameters.`);
198
+ }
199
+ // INSIDE the block: `emitLookupComment` returns bare ` * ` body lines.
200
+ if (specs.size > 0) {
201
+ lines.push(" *");
202
+ lines.push(...emitLookupComment(specs, name));
203
+ }
204
+ lines.push(" */");
205
+ lines.push(`export const ${name} = triggerDescriptor<${whereType}, ${name}Outputs>()({`);
206
+ lines.push(` connector: ${pyJsonStringify(connectorKey)},`);
207
+ lines.push(` event: ${pyJsonString(eventId)},`);
208
+ lines.push(` version: ${pyJsonStringify(version)},`);
209
+ lines.push(` displayName: ${pyJsonStringify(label)},`);
210
+ lines.push(...emitLookupsBlock(specs));
211
+ lines.push("} as const);");
212
+ return lines;
213
+ }
214
+ /** One operation: its `Inputs`/`Outputs` interfaces, doc comment and `descriptor()` const. */
215
+ export function emitOp(entry, v1def, inputTree, outputTree, name, inputTypes, outputTypes, invariant = new Set()) {
216
+ const nodeType = entry.nodeType;
217
+ const actionId = afterLast(nodeType, ".");
218
+ const operation = entry.operation ?? {};
219
+ const isGenericExpanded = operation.activityType === "GenericExpanded";
220
+ const label = pyTruthy(entry.display?.label) ? entry.display.label : actionId;
221
+ const version = versionOf(entry);
222
+ const runtime = entry.runtime ?? {};
223
+ const inFields = descriptorInputFields(entry, v1def);
224
+ if (isEventNodeType(nodeType)) {
225
+ return emitEvent(entry, inFields, inputTree, outputTree, name, inputTypes, outputTypes, invariant);
226
+ }
227
+ const lines = [];
228
+ lines.push(...emitInterface(`${name}Inputs`, false, inputTypes, inputTree, true));
229
+ lines.push("");
230
+ lines.push(...emitInterface(`${name}Outputs`, true, outputTypes, outputTree));
231
+ lines.push("");
232
+ // An operation whose real field set arrives at design time has an `Inputs`
233
+ // type that is either empty (no object chosen yet) or just the parents. The
234
+ // doc comment is the only place the typed surface can say so.
235
+ const schemaAction = operation.schemaAction ?? {};
236
+ const specs = lookupSpecs(entry, v1def, invariant);
237
+ const lookupComment = specs.size > 0 ? emitLookupComment(specs, name) : [];
238
+ const header = ` * ${pyStr(label)} — ${nodeType}`;
239
+ if (pyTruthy(operation.objectFromDiscovery)) {
240
+ lines.push("/**");
241
+ lines.push(header);
242
+ lines.push(" *");
243
+ lines.push(" * The object is chosen against the connection, so `Inputs` is empty");
244
+ lines.push(" * here. List the objects and read the chosen one's fields with");
245
+ lines.push(" * `uip is resources list` / `describe` before binding this node.");
246
+ if (lookupComment.length > 0) {
247
+ lines.push(" *");
248
+ lines.push(...lookupComment);
249
+ }
250
+ lines.push(" */");
251
+ }
252
+ else if (pyTruthy(schemaAction.parents)) {
253
+ const parents = schemaAction.parents.map((x) => `\`${pyStr(x)}\``).join(", ");
254
+ lines.push("/**");
255
+ lines.push(header);
256
+ lines.push(" *");
257
+ lines.push(` * \`Inputs\` carries only the fields that SELECT the schema (${parents}).`);
258
+ lines.push(` * The real set comes from the \`${pyStr(schemaAction.name)}\` design-time`);
259
+ lines.push(" * action — describe with those values supplied to see it.");
260
+ if (lookupComment.length > 0) {
261
+ lines.push(" *");
262
+ lines.push(...lookupComment);
263
+ }
264
+ lines.push(" */");
265
+ }
266
+ else if (lookupComment.length > 0) {
267
+ lines.push("/**");
268
+ lines.push(header);
269
+ lines.push(" *");
270
+ lines.push(...lookupComment);
271
+ lines.push(" */");
272
+ }
273
+ else {
274
+ lines.push(`/** ${pyStr(label)} — ${nodeType} */`);
275
+ }
276
+ lines.push(`export const ${name} = descriptor<${name}Inputs, ${name}Outputs>()({`);
277
+ lines.push(` nodeType: ${pyJsonString(nodeType)},`);
278
+ lines.push(` version: ${pyJsonStringify(version)},`);
279
+ if (isGenericExpanded && pyTruthy(operation.objectName)) {
280
+ lines.push(` objectName: ${pyJsonStringify(operation.objectName)},`);
281
+ }
282
+ if (pyTruthy(runtime.requiresConnection))
283
+ lines.push(" requiresConnection: true,");
284
+ if (pyTruthy(runtime.requiresFolderKey))
285
+ lines.push(" requiresFolderKey: true,");
286
+ lines.push(...emitLookupsBlock(specs));
287
+ lines.push("} as const);");
288
+ return lines;
289
+ }
290
+ /**
291
+ * The catalog names required inputs but the generated `Inputs` interface is
292
+ * empty: the descriptor would type-check a call the platform rejects. One line
293
+ * naming the operation and the fields, or `undefined` when there is no gap.
294
+ */
295
+ export function requiredInputGap(entry, inputTree) {
296
+ const required = (entry.inputSchema?.fields || [])
297
+ .filter((field) => field.required === true)
298
+ .map((field) => pyStr(pyTruthy(field.name) ? field.name : "<unnamed>"));
299
+ if (required.length === 0 || inputTree.size > 0)
300
+ return undefined;
301
+ const nodeType = "nodeType" in entry ? pyStr(entry.nodeType) : "<unknown node>";
302
+ return `${nodeType}: catalog requires ${required.join(", ")}, but the generated inputs interface is empty`;
303
+ }
304
+ /**
305
+ * `mark_lookup_fields`: flag the fields whose TypeScript type must also accept
306
+ * a `LookupToken` — exactly the set `lookupSpecs` generates helpers for. The
307
+ * work is `fields.ts`'s `markLookupFields`, which also merges the catalog's
308
+ * `reference` INTO a field whose own declaration lacks one, as the Python did;
309
+ * `lookupFieldNote` reads the lookup's target and value from that merged
310
+ * reference, so the doc comment on the field depends on the merge. This
311
+ * wrapper only narrows the result to the tree's field type.
312
+ */
313
+ function markedInputFields(entry, fields, invariant) {
314
+ return markLookupFields(entry, fields, invariant);
315
+ }
316
+ /**
317
+ * Render every connector module of the library.
318
+ *
319
+ * The Python `main()` wrote every module and only then exited 1 on
320
+ * {@link DescriptorOutput.requiredInputGaps}; this returns both and the caller
321
+ * decides. A missing entry or sidecar throws, as the Python did.
322
+ */
323
+ export function renderDescriptorModules(library, options = {}) {
324
+ const importSpecifier = options.importSpecifier ?? PACKAGE_NAME;
325
+ const includeGenericExpanded = options.includeGenericExpanded ?? false;
326
+ const subset = new Set((options.connectors ?? []).filter((c) => c.length > 0));
327
+ const invariant = library.invariant;
328
+ const rows = library.index.entries;
329
+ // Group Curated + http-request entries by connector key.
330
+ const byConnector = new Map();
331
+ for (const row of rows) {
332
+ if (row.activityType === "GenericExpanded" && !includeGenericExpanded)
333
+ continue;
334
+ // A generic EVENT's per-object variants share their template's nodeType and
335
+ // so would emit the same const twice. Only the template gets a descriptor:
336
+ // `onEvent(RecordCreated, { object: 'Message' })` names the object as an
337
+ // option, so one descriptor serves every object.
338
+ if (row.activityType === "GenericTrigger" && pyTruthy(row.objectName) && !includeGenericExpanded)
339
+ continue;
340
+ const key = row.connectorKey;
341
+ if (subset.size > 0 && !subset.has(key))
342
+ continue;
343
+ const group = byConnector.get(key);
344
+ if (group)
345
+ group.push(row);
346
+ else
347
+ byConnector.set(key, [row]);
348
+ }
349
+ // ONE symbol map for the whole run, shared with the Markdown generator.
350
+ const symbols = symbolIndex(rows, includeGenericExpanded);
351
+ const modules = new Map();
352
+ const requiredInputGaps = [];
353
+ for (const key of sortedStrings(byConnector.keys())) {
354
+ const inputTypes = new NamedTypeRegistry({ suffix: "Input", allOptional: false, mergeReused: false, isInput: true });
355
+ const outputTypes = new NamedTypeRegistry();
356
+ const loaded = [];
357
+ for (const row of sortedByCodePoint(byConnector.get(key), (r) => r.nodeType)) {
358
+ const entry = library.readEntry(row);
359
+ if (entry === undefined)
360
+ throw new Error(`library entry missing: ${library.entryPath(row)}`);
361
+ const v1def = library.readV1Def(row);
362
+ if (v1def === undefined)
363
+ throw new Error(`library sidecar missing: ${library.v1defPath(row)}`);
364
+ const isEvent = isEventNodeType(row.nodeType);
365
+ const operationInputFields = markedInputFields(entry, descriptorInputFields(entry, v1def), invariant);
366
+ const outputFields = entry.outputSchema?.fields || [];
367
+ const inputTree = buildTypeTree(operationInputFields);
368
+ // The required-input gap is a statement about a connector ACTION's
369
+ // inputs; an event's parameters are optional by construction.
370
+ const gap = isEvent ? undefined : requiredInputGap(entry, inputTree);
371
+ if (gap)
372
+ requiredInputGaps.push(gap);
373
+ const outputTree = buildTypeTree(outputFields);
374
+ inputTypes.discover(inputTree);
375
+ outputTypes.discover(outputTree);
376
+ loaded.push({ entry, v1def, inputTree, outputTree });
377
+ }
378
+ const operations = [];
379
+ for (const { entry, v1def, inputTree, outputTree } of loaded) {
380
+ const name = symbols.get(entry.nodeType);
381
+ if (name === undefined)
382
+ throw new Error(`no symbol for ${entry.nodeType}: the entry's nodeType is not in index.json`);
383
+ operations.push(...emitOp(entry, v1def, inputTree, outputTree, name, inputTypes, outputTypes, invariant), "");
384
+ }
385
+ const emittedTypes = inputTypes.emit(emitNestedProperty);
386
+ const rendered = [...emittedTypes, ...operations].join("\n");
387
+ let specifiers = ["descriptor", "triggerDescriptor"].filter((s) => rendered.includes(`= ${s}<`));
388
+ if (specifiers.length === 0)
389
+ specifiers = ["descriptor"];
390
+ for (const alias of ["ConnectorLookupValue", "ConnectorValue"]) {
391
+ if (rendered.includes(alias))
392
+ specifiers.push(`type ${alias}`);
393
+ }
394
+ const body = [`import { ${specifiers.join(", ")} } from ${pyJsonString(importSpecifier)};`, ""];
395
+ body.push(...emittedTypes);
396
+ body.push(...outputTypes.emit(emitNestedProperty));
397
+ body.push(...operations);
398
+ modules.set(`${key}.ts`, `${pyRstrip(body.join("\n"))}\n`);
399
+ }
400
+ return { modules, symbols, requiredInputGaps };
401
+ }
402
+ /** The text the Python printed before exiting 1 on gaps; call only with a non-empty list. */
403
+ export function formatRequiredInputGaps(gaps) {
404
+ return `connector wrapper generation stopped:\n- ${gaps.join("\n- ")}`;
405
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Which fields an operation exposes to an author.
3
+ *
4
+ * Body fields live in the sidecar's `inputDefinition.fields`; query, path and
5
+ * FILE multipart parameters live in the entry's `operation.parameters`. The
6
+ * descriptor generator, the Markdown generator and `prepare` all need the same
7
+ * answer, so the rules live here once. Ported from the top of
8
+ * `generate_connectors_ts.py`.
9
+ */
10
+ import { type LibraryEntry, type LibraryField, type V1Definition } from "../model/library.js";
11
+ import { type LookupSpecData } from "../lookup-specs.js";
12
+ /**
13
+ * A multipart parameter that is not a file. The runtime composes it from the
14
+ * body parameters; the author never fills it. Same rule as `core/library.ts`.
15
+ */
16
+ export declare function isContainerPart(parameter: {
17
+ type?: unknown;
18
+ dataType?: unknown;
19
+ }): boolean;
20
+ /**
21
+ * The complete input vocabulary the SDK runtime consumes: sidecar body fields,
22
+ * then query/path/file-multipart parameters, a parameter replacing a same-named
23
+ * body field in place because its wire location is authoritative.
24
+ */
25
+ export declare function authoritativeInputFields(entry: LibraryEntry, v1def: V1Definition | undefined): LibraryField[];
26
+ /**
27
+ * {@link authoritativeInputFields}, falling back to the catalog's
28
+ * `inputSchema.fields` when the union is empty and the catalog declares a
29
+ * required field, then appending the catalog's file-typed fields.
30
+ */
31
+ export declare function inputFields(entry: LibraryEntry, v1def: V1Definition | undefined): LibraryField[];
32
+ /**
33
+ * The one field list a descriptor's inputs — or an event's `where` — come from.
34
+ * An event's authored inputs are its subscription parameters, held only in the
35
+ * sidecar's `eventParameters.fields`.
36
+ */
37
+ export declare function descriptorInputFields(entry: LibraryEntry, v1def: V1Definition | undefined): LibraryField[];
38
+ /** `{ fieldName → spec }` for every input field that earns a `.by*` helper. */
39
+ export declare function lookupSpecs(entry: LibraryEntry, v1def: V1Definition | undefined, invariant: ReadonlySet<string>): Map<string, LookupSpecData>;
40
+ /**
41
+ * The fields whose TypeScript type must also accept a `LookupToken`: exactly the
42
+ * set {@link lookupSpecs} generates helpers for, decided by the same call.
43
+ */
44
+ export declare function markLookupFields(entry: LibraryEntry, fields: readonly LibraryField[], invariant: ReadonlySet<string>): Array<LibraryField & {
45
+ __lookup?: true;
46
+ }>;
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Which fields an operation exposes to an author.
3
+ *
4
+ * Body fields live in the sidecar's `inputDefinition.fields`; query, path and
5
+ * FILE multipart parameters live in the entry's `operation.parameters`. The
6
+ * descriptor generator, the Markdown generator and `prepare` all need the same
7
+ * answer, so the rules live here once. Ported from the top of
8
+ * `generate_connectors_ts.py`.
9
+ */
10
+ import { isEventNodeType } from "../model/library.js";
11
+ import { catalogReferences, classify, specsFromFields } from "../lookup-specs.js";
12
+ /**
13
+ * A multipart parameter that is not a file. The runtime composes it from the
14
+ * body parameters; the author never fills it. Same rule as `core/library.ts`.
15
+ */
16
+ export function isContainerPart(parameter) {
17
+ return parameter.type === "multipart" && String(parameter.dataType || "string").toLowerCase() !== "file";
18
+ }
19
+ /**
20
+ * The complete input vocabulary the SDK runtime consumes: sidecar body fields,
21
+ * then query/path/file-multipart parameters, a parameter replacing a same-named
22
+ * body field in place because its wire location is authoritative.
23
+ */
24
+ export function authoritativeInputFields(entry, v1def) {
25
+ const fields = (v1def?.inputDefinition?.fields ?? []).map((f) => ({ ...f }));
26
+ const positions = new Map();
27
+ fields.forEach((field, index) => positions.set(field.name, index));
28
+ for (const parameter of entry.operation?.parameters ?? []) {
29
+ const name = parameter.name;
30
+ const location = parameter.type;
31
+ if (!name || (location !== "query" && location !== "path" && location !== "multipart"))
32
+ continue;
33
+ if (isContainerPart(parameter))
34
+ continue;
35
+ const field = {
36
+ name,
37
+ type: parameter.dataType,
38
+ required: parameter.required === true,
39
+ };
40
+ if (parameter.displayName)
41
+ field.displayName = parameter.displayName;
42
+ if (parameter.description)
43
+ field.description = parameter.description;
44
+ // A QUERY parameter can carry a `reference` too — Slack's `send_as` does.
45
+ if (parameter.reference && typeof parameter.reference === "object")
46
+ field.reference = parameter.reference;
47
+ const at = positions.get(name);
48
+ if (at !== undefined) {
49
+ fields[at] = field;
50
+ }
51
+ else {
52
+ positions.set(name, fields.length);
53
+ fields.push(field);
54
+ }
55
+ }
56
+ return fields;
57
+ }
58
+ /**
59
+ * {@link authoritativeInputFields}, falling back to the catalog's
60
+ * `inputSchema.fields` when the union is empty and the catalog declares a
61
+ * required field, then appending the catalog's file-typed fields.
62
+ */
63
+ export function inputFields(entry, v1def) {
64
+ let fields = authoritativeInputFields(entry, v1def);
65
+ const catalogFields = entry.inputSchema?.fields ?? [];
66
+ if (fields.length === 0 && catalogFields.some((f) => f.required === true)) {
67
+ fields = catalogFields.map((f) => ({ ...f }));
68
+ }
69
+ const positions = new Set(fields.map((f) => f.name));
70
+ for (const catalogField of catalogFields) {
71
+ const name = catalogField.name;
72
+ if (!name || String(catalogField.type || "").toLowerCase() !== "file" || positions.has(name))
73
+ continue;
74
+ positions.add(name);
75
+ fields.push({ ...catalogField });
76
+ }
77
+ return fields;
78
+ }
79
+ /**
80
+ * The one field list a descriptor's inputs — or an event's `where` — come from.
81
+ * An event's authored inputs are its subscription parameters, held only in the
82
+ * sidecar's `eventParameters.fields`.
83
+ */
84
+ export function descriptorInputFields(entry, v1def) {
85
+ if (isEventNodeType(entry.nodeType || ""))
86
+ return v1def?.eventParameters?.fields ?? [];
87
+ return inputFields(entry, v1def);
88
+ }
89
+ /** `{ fieldName → spec }` for every input field that earns a `.by*` helper. */
90
+ export function lookupSpecs(entry, v1def, invariant) {
91
+ return specsFromFields(inputFields(entry, v1def), invariant, catalogReferences(entry));
92
+ }
93
+ /**
94
+ * The fields whose TypeScript type must also accept a `LookupToken`: exactly the
95
+ * set {@link lookupSpecs} generates helpers for, decided by the same call.
96
+ */
97
+ export function markLookupFields(entry, fields, invariant) {
98
+ // The catalog's `reference` is merged INTO the returned field, not only
99
+ // consulted: the descriptor's lookup note reads target and value from it.
100
+ const references = catalogReferences(entry);
101
+ return fields.map((field) => {
102
+ const merged = { ...field };
103
+ const name = merged.name;
104
+ if (!(merged.reference && typeof merged.reference === "object") && name !== undefined && references.has(name)) {
105
+ merged.reference = references.get(name);
106
+ }
107
+ if (classify(merged, invariant) === "lookup")
108
+ merged.__lookup = true;
109
+ return merged;
110
+ });
111
+ }
@@ -0,0 +1,21 @@
1
+ import { type Dict } from "./pyvalue.js";
2
+ /**
3
+ * One enum choice, as the literal an author would write. `inputSchema` uses
4
+ * plain strings; `inputDefinition` uses `{ name, value }` objects, and `value`
5
+ * is what the flow has to send.
6
+ */
7
+ export declare function enumValue(v: unknown): string;
8
+ /**
9
+ * The collection to call to resolve a field's value, or `""`. `path` is
10
+ * preferred over `objectName` because the schema documents `path` as the one
11
+ * used for the call; either may carry a `?query`, which is split off.
12
+ */
13
+ export declare function referenceCollection(f: Dict): string;
14
+ /** The `?query` half of whichever side {@link referenceCollection} used, or `""`. */
15
+ export declare function referenceQuery(f: Dict): string;
16
+ /**
17
+ * `[name, type, wireName]`: the source-facing name and type, plus the distinct
18
+ * wire name when there is one. A top-level `users[*]` is flattened wire
19
+ * notation the descriptor exposes as `users: string[]`.
20
+ */
21
+ export declare function authoringField(f: Dict): [name: string, type: string, wireName: string];
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Field-level helpers several sections share: how an enum choice prints, which
3
+ * collection a `reference` points at, and the authoring name of a field.
4
+ * Ported from the top of `convert_library_to_md.py`.
5
+ */
6
+ import { pyStr, pyStripChars, splitOnce } from "../../compat/pystr.js";
7
+ import { getStr, isDict, pyOr } from "./pyvalue.js";
8
+ /**
9
+ * One enum choice, as the literal an author would write. `inputSchema` uses
10
+ * plain strings; `inputDefinition` uses `{ name, value }` objects, and `value`
11
+ * is what the flow has to send.
12
+ */
13
+ export function enumValue(v) {
14
+ if (isDict(v))
15
+ return pyStr("value" in v ? v.value : "name" in v ? v.name : "");
16
+ return pyStr(v);
17
+ }
18
+ /** `reference.path or reference.objectName or ""`, when it is a string. */
19
+ function referenceSource(f) {
20
+ const reference = f.reference;
21
+ if (!isDict(reference))
22
+ return undefined;
23
+ const source = pyOr(reference.path, reference.objectName, "");
24
+ return typeof source === "string" ? source : undefined;
25
+ }
26
+ /**
27
+ * The collection to call to resolve a field's value, or `""`. `path` is
28
+ * preferred over `objectName` because the schema documents `path` as the one
29
+ * used for the call; either may carry a `?query`, which is split off.
30
+ */
31
+ export function referenceCollection(f) {
32
+ const source = referenceSource(f);
33
+ if (!source)
34
+ return "";
35
+ return pyStripChars(splitOnce(source, "?")[0], "/");
36
+ }
37
+ /** The `?query` half of whichever side {@link referenceCollection} used, or `""`. */
38
+ export function referenceQuery(f) {
39
+ const source = referenceSource(f);
40
+ if (source === undefined)
41
+ return "";
42
+ return splitOnce(source, "?")[1] ?? "";
43
+ }
44
+ /**
45
+ * `[name, type, wireName]`: the source-facing name and type, plus the distinct
46
+ * wire name when there is one. A top-level `users[*]` is flattened wire
47
+ * notation the descriptor exposes as `users: string[]`.
48
+ */
49
+ export function authoringField(f) {
50
+ const wireName = getStr(f, "name", "?");
51
+ const type = getStr(f, "type", "?");
52
+ if (wireName.endsWith("[*]") && !wireName.includes("."))
53
+ return [wireName.slice(0, -3), `${type}[]`, wireName];
54
+ return [wireName, type, ""];
55
+ }