@uipath/maestro-builder-sdk 6.3.0 → 6.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/api-index.md +6 -6
  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,268 @@
1
+ /**
2
+ * Prepare a connector EVENT — the trigger/wait twin of an activity.
3
+ *
4
+ * Connector events live one namespace over from actions —
5
+ * `uipath.connector.trigger.<key>.<event>` / `.event.<key>.<event>` — and
6
+ * their registry definition only answers with a connection, which `prepare`
7
+ * has by the time it gets here. The overlay entry is stored under the WAIT
8
+ * form (`uipath.connector.event.…`) because the registry only enriches the
9
+ * START form and the library derives the start form from its wait sibling —
10
+ * one stored variant serves both node roles.
11
+ *
12
+ * One stored shape: the registry definition with the `triggers describe`
13
+ * sections made true for this object (generic), or with the object row's
14
+ * `Parameters` unioned in (curated). `Library.eventParameterFields` reads
15
+ * `v1def.eventParameters.fields`, so the overlay v1def IS the store.
16
+ *
17
+ * Ported from `_prepare_event`, `_event_operation` and
18
+ * `_refuse_unless_byoa_connection` in `prepare_connector.py`. Every `uip` call
19
+ * goes through the {@link BuildContext}'s client; every printed line goes
20
+ * through its reporter, in the Python's order.
21
+ */
22
+ import { extractCanonical, nodeOf } from "../library-build/canonical.js";
23
+ import { registryGet } from "../library-build/context.js";
24
+ import { EventDiscoveryError, eventFacts, mergeDescribeIntoNode, triggersDescribe, triggersObjects, unionEventParameters, } from "../library-build/events.js";
25
+ import { get, isRecord } from "../library-build/py.js";
26
+ import { planLibrary } from "../library-build/plan.js";
27
+ import { ConnectionRefusal, byoaConnections, byoaListing, restoreBindings } from "./connections.js";
28
+ import { writeResolutions } from "./ledger.js";
29
+ import { PREPARE_COMMAND, PrepareRefusal, byoaListingFailedMessage, byoaRequiredMessage, describeFallbackWarning, generatedModulesLine, eventAllObjectsMessage, eventNextLine, eventParentFieldsMessage, eventSubscription, eventSummaryLine, filterableFieldsLine, genericEventNeedsObjectMessage, lookupVocabularyLines, objectNoteLine, objectOnCuratedEventMessage, objectRecordedLine, warnLine, webhookNoteLines, } from "./messages.js";
30
+ import { RESOLUTIONS_FILENAME, matchObject, recordObjectResolution } from "./objects.js";
31
+ import { lookupSpecsFromOverlay, mergeFilesInto } from "./overlay.js";
32
+ import { regenerateDescriptors } from "./regenerate.js";
33
+ import { isPlainObject, pyGet, pyOr, pyPathJoin, pyPathlibStr, pyStrAny, pyTruthy } from "./support.js";
34
+ /**
35
+ * `_event_operation`: the operation key every trigger API takes.
36
+ *
37
+ * NOT `operation.name`: that is the object's CRUD verb (`Create` on outlook
38
+ * `email-received`), while the event's own operation is `EMAIL_RECEIVED`,
39
+ * which `extractCanonical` stamps as `eventOperation` from the manifest's
40
+ * `model.context`. `name` happens to fall back to the same value when the
41
+ * manifest omits `connectorMethodInfo.operation`, so reading it works or
42
+ * silently queries the WRONG operation depending on which fallback rung a
43
+ * connector's manifest exercises.
44
+ */
45
+ export function eventOperation(operation) {
46
+ return pyStrAny(pyOr(pyOr(pyGet(operation, "eventOperation", null), pyGet(operation, "name", null)), ""));
47
+ }
48
+ /** The START and WAIT node types of one event. */
49
+ export function eventNodeTypes(connectorKey, action) {
50
+ return {
51
+ trigger: `uipath.connector.trigger.${connectorKey}.${action}`,
52
+ event: `uipath.connector.event.${connectorKey}.${action}`,
53
+ };
54
+ }
55
+ /**
56
+ * The one stored shape (see the module comment): the definition with the
57
+ * describe folded in, or with the object row's `Parameters` unioned into its
58
+ * own `eventParameters`; the entry's `outputSchema` taken from the definition's
59
+ * `outputResponseDefinition` when that has fields. Pure — copies of both inputs.
60
+ */
61
+ export function assembleEventDefinition(node, entry, describe, objectRow, eventNodeType) {
62
+ const objectParameters = objectRow === undefined ? null : pyGet(objectRow, "Parameters", null);
63
+ let stored;
64
+ if (describe !== undefined) {
65
+ stored = mergeDescribeIntoNode(node, describe, objectParameters);
66
+ }
67
+ else {
68
+ stored = { ...node };
69
+ const declared = pyOr(get(stored, "eventParameters"), {});
70
+ stored.eventParameters = { fields: unionEventParameters(get(declared, "fields"), objectParameters) };
71
+ }
72
+ const out = { ...entry };
73
+ const outputDefinition = pyOr(get(stored, "outputResponseDefinition"), {});
74
+ const outputFields = get(outputDefinition, "fields");
75
+ if (pyTruthy(outputFields))
76
+ out.outputSchema = { fields: outputFields };
77
+ stored.nodeType = eventNodeType;
78
+ out.nodeType = eventNodeType;
79
+ return { node: stored, entry: out };
80
+ }
81
+ /**
82
+ * `_refuse_unless_byoa_connection`: refuse a standard connection on an event
83
+ * object that requires BYOA. V1 measured the failure this prevents — the
84
+ * trigger deploys and then faults at runtime with a webhook error naming
85
+ * nothing about connections — and the requirement is knowable now.
86
+ *
87
+ * A refusal must leave nothing behind: stage 0 has already written this
88
+ * connection into `bindings.json` by the time the event's own metadata says it
89
+ * is the wrong KIND, so the file is restored first. A listing that FAILED is
90
+ * not a tenant with no BYOA connections; the remedies are opposite.
91
+ */
92
+ export async function refuseUnlessByoaConnection(client, connectorKey, connectionId, rollback, warn) {
93
+ const restore = () => {
94
+ if (rollback)
95
+ restoreBindings(rollback.path, rollback.snapshot, warn, rollback.shown);
96
+ };
97
+ let rows;
98
+ try {
99
+ rows = await byoaConnections(client, connectorKey);
100
+ }
101
+ catch (error) {
102
+ if (!(error instanceof ConnectionRefusal))
103
+ throw error;
104
+ restore();
105
+ throw new PrepareRefusal(byoaListingFailedMessage(connectionId, error), "BYOA_LISTING_FAILED");
106
+ }
107
+ if (rows.some((row) => pyStrAny(pyGet(row, "Id", null)) === connectionId))
108
+ return;
109
+ restore();
110
+ throw new PrepareRefusal(byoaRequiredMessage(connectionId, connectorKey, byoaListing(rows, connectorKey)), "BYOA_REQUIRED");
111
+ }
112
+ function fieldsOf(section) {
113
+ const fields = pyOr(get(pyOr(section, {}), "fields"), []);
114
+ return Array.isArray(fields) ? fields : [];
115
+ }
116
+ /**
117
+ * `_prepare_event`. Returns `undefined` when `<key> <action>` is not an event
118
+ * either, so the caller can raise the ordinary unknown-action refusal;
119
+ * everything else is handled here, refusals included. `ctx.connectionId` must
120
+ * already be the connection: the trigger definition only answers with one.
121
+ */
122
+ export async function prepareEvent(ctx, options) {
123
+ const { connectorKey, action, connectionId, out } = options;
124
+ const outShown = options.outShown ?? out;
125
+ const info = (line) => ctx.reporter.info(line);
126
+ const warn = (line) => ctx.reporter.warn(line);
127
+ const { trigger: triggerNodeType, event: eventNodeType } = eventNodeTypes(connectorKey, action);
128
+ const cacheName = await registryGet(ctx, triggerNodeType);
129
+ if (cacheName === undefined)
130
+ return undefined;
131
+ const payload = ctx.caches.registry.read(cacheName);
132
+ const canonical = extractCanonical(nodeOf(payload));
133
+ if (canonical === undefined)
134
+ return undefined;
135
+ if (pyTruthy(options.fields))
136
+ throw new PrepareRefusal(eventParentFieldsMessage(), "EVENT_NO_PARENT_FIELDS");
137
+ if (options.allObjects)
138
+ throw new PrepareRefusal(eventAllObjectsMessage(), "EVENT_NO_ALL_OBJECTS");
139
+ // `(raw.get("Data") or {}).get("Node") or raw.get("Node") or {}`.
140
+ const raw = isRecord(payload) ? payload : {};
141
+ const node = pyOr(pyOr(get(pyOr(get(raw, "Data"), {}), "Node"), get(raw, "Node")), {});
142
+ const entry = { ...canonical, operation: { ...canonical.operation } };
143
+ const op = entry.operation;
144
+ const operation = eventOperation(op);
145
+ const generic = op.activityType === "GenericTrigger";
146
+ // The objects listing is the generic catalog AND the per-object fact sheet
147
+ // (BYOA, webhook visibility, bucket-scoped Parameters). For a curated event
148
+ // it is advisory — the definition already carries the sections — so a
149
+ // listing failure degrades to a warning there and is fatal only where it is
150
+ // the only source of truth.
151
+ let rows;
152
+ try {
153
+ rows = (await triggersObjects(ctx.client, connectorKey, operation, connectionId, ctx.timeoutMs)).filter(isPlainObject);
154
+ }
155
+ catch (error) {
156
+ if (!(error instanceof EventDiscoveryError))
157
+ throw error;
158
+ if (generic)
159
+ throw error;
160
+ rows = [];
161
+ warn(warnLine(error));
162
+ }
163
+ let describe;
164
+ let objectRow;
165
+ let requestedObject;
166
+ let objectName = options.objectName ?? undefined;
167
+ if (generic) {
168
+ if (!pyTruthy(objectName)) {
169
+ throw new PrepareRefusal(genericEventNeedsObjectMessage(action, rows), "EVENT_OBJECT_REQUIRED");
170
+ }
171
+ // The author's name, not necessarily the API's — same ladder, same refusal
172
+ // semantics as a Generic activity. The listing is live (no CLI-side
173
+ // cache), so there is nothing to refresh on a miss.
174
+ const matched = matchObject(rows, objectName, operation);
175
+ if (matched.note)
176
+ info(objectNoteLine(matched.note));
177
+ objectRow = matched.row;
178
+ requestedObject = objectName;
179
+ objectName = pyStrAny(pyOr(pyGet(objectRow, "Name", null), objectName));
180
+ describe = await triggersDescribe(ctx.client, connectorKey, operation, objectName, connectionId, ctx.timeoutMs);
181
+ op.objectName = objectName;
182
+ op.objectDisplayName = pyStrAny(pyOr(pyGet(objectRow, "DisplayName", null), ""));
183
+ }
184
+ else {
185
+ if (pyTruthy(objectName)) {
186
+ throw new PrepareRefusal(objectOnCuratedEventMessage(action, pyGet(op, "objectName", null)), "EVENT_OBJECT_BUILT_IN");
187
+ }
188
+ const baked = pyStrAny(pyOr(pyGet(op, "objectName", null), ""));
189
+ objectRow = rows.find((row) => pyStrAny(pyGet(row, "Name", null)) === baked);
190
+ // `triggers describe` is the AUTHORITY for a curated event too: the
191
+ // registry definition mirrors it and the mirror is lossy (outlook
192
+ // `email-received`: 15 filter fields in describe, 14 in the registry).
193
+ // Fall back to the definition's own sections when describe cannot answer
194
+ // rather than failing the prepare — the mirror is still a usable vocabulary.
195
+ if (baked) {
196
+ try {
197
+ describe = await triggersDescribe(ctx.client, connectorKey, operation, baked, connectionId, ctx.timeoutMs);
198
+ }
199
+ catch (error) {
200
+ if (!(error instanceof EventDiscoveryError))
201
+ throw error;
202
+ warn(describeFallbackWarning(error, baked));
203
+ }
204
+ }
205
+ }
206
+ const facts = eventFacts(node, objectRow ?? null);
207
+ if (facts.byoaConnection) {
208
+ await refuseUnlessByoaConnection(ctx.client, connectorKey, connectionId, options.bindings, warn);
209
+ }
210
+ const definition = assembleEventDefinition(node, entry, describe, objectRow, eventNodeType);
211
+ const plan = planLibrary([definition.entry], () => definition.node);
212
+ for (const line of plan.warnings)
213
+ warn(line);
214
+ mergeFilesInto(plan.files, out);
215
+ if (generic) {
216
+ recordObjectResolution(out, eventNodeType, requestedObject, objectRow, operation);
217
+ // Named as the author spelled `--out`, not as the file was resolved.
218
+ info(objectRecordedLine(requestedObject, objectName, pyPathJoin(outShown, RESOLUTIONS_FILENAME)));
219
+ }
220
+ if (pyTruthy(options.resolve)) {
221
+ // A `LookupExhausted` propagates out of here as `ResolutionsExhausted`;
222
+ // the pipeline owns the retry, since re-running pinned means starting the
223
+ // whole command over, not this stage.
224
+ await writeResolutions({
225
+ client: ctx.client,
226
+ connectorKey,
227
+ connectionId,
228
+ out,
229
+ outShown,
230
+ nodeType: eventNodeType,
231
+ objectName,
232
+ resolve: options.resolve,
233
+ refreshLookups: options.refreshLookups,
234
+ reporter: ctx.reporter,
235
+ });
236
+ }
237
+ const jsonOnly = options.jsonOnly ?? false;
238
+ const modules = regenerateDescriptors(out, options.importSpecifier, jsonOnly);
239
+ // The generator's own last line, which the Python spawned and so printed
240
+ // here; under --json-only it never ran and said nothing.
241
+ if (!jsonOnly)
242
+ info(generatedModulesLine(modules.length, pyPathlibStr(outShown)));
243
+ const whereFields = fieldsOf(get(definition.node, "eventParameters"));
244
+ const filterFields = fieldsOf(get(definition.node, "filterFields"));
245
+ const mode = facts.eventMode;
246
+ info(eventSummaryLine(eventNodeType, outShown, operation, mode, whereFields.length, filterFields.length));
247
+ const command = options.prepareCommand ?? PREPARE_COMMAND;
248
+ for (const line of lookupVocabularyLines(connectorKey, action, objectName, lookupSpecsFromOverlay(out, eventNodeType, objectName), command)) {
249
+ info(line);
250
+ }
251
+ const filterable = filterableFieldsLine(filterFields.filter(isPlainObject));
252
+ if (filterable !== undefined)
253
+ info(filterable);
254
+ for (const line of webhookNoteLines(mode, facts.textBlocks))
255
+ warn(line);
256
+ const subscription = eventSubscription(connectorKey, action, generic ? objectName : undefined, pyOr(options.boundConnection, "<connection>"), pyOr(options.boundFolder, "shared"));
257
+ info(eventNextLine(subscription));
258
+ return {
259
+ eventNodeType,
260
+ operation,
261
+ generic,
262
+ ...(generic ? { objectName } : {}),
263
+ facts,
264
+ whereFields,
265
+ filterFields,
266
+ definition,
267
+ };
268
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `ConnectorValue<string>` → `string`, for the handoff skeleton. The generated
3
+ * property type says what the field ACCEPTS — the literal or an expression —
4
+ * which is right for the compiler and wrong for a one-line "what goes here"
5
+ * hint; the wrapper is not something the author types.
6
+ */
7
+ export declare function unwrapValue(tsType: string): string;
8
+ /**
9
+ * `_required_interface_fields`: `[name, type]` for every non-optional
10
+ * property of `export interface <name> { … }` in the generated module, or
11
+ * `undefined` when the interface is absent or a line does not parse — the
12
+ * caller must then say the skeleton is unavailable rather than print `{}`.
13
+ */
14
+ export declare function requiredInterfaceFields(generated: string, interfaceName: string): Array<[name: string, type: string]> | undefined;
15
+ /**
16
+ * `_descriptor_usage`: the handoff lines for `<outDir>/<connectorKey>.ts`,
17
+ * unprefixed (the caller prints each as `prepare-connector: next: …`).
18
+ * `cwd` is what the Python read as `Path.cwd()`: the import path is spelled
19
+ * relative to it, with a leading `./`.
20
+ */
21
+ export declare function descriptorUsage(outDir: string, connectorKey: string, boundConnection?: string, boundFolder?: string, cwd?: string): string[];
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Teach the author how to consume the connection-resolved descriptor.
3
+ *
4
+ * The live registry may use a different action id than the baked catalog. The
5
+ * generated descriptor is the authority for that resolved identity, so the
6
+ * success message must carry the author across that boundary instead of
7
+ * leaving them to reverse-engineer `index.json`. With exactly one descriptor
8
+ * in the module the handoff is a pasteable call whose inputs skeleton names
9
+ * every REQUIRED field with its type; with several, it says which file to open
10
+ * and refuses to guess an object.
11
+ *
12
+ * Ported from `_descriptor_usage`, `_required_interface_fields` and
13
+ * `_unwrap_value` in `prepare_connector.py`. The text lives in `messages.ts`;
14
+ * this module reads the generated module and parses it.
15
+ */
16
+ import { readFileSync } from "node:fs";
17
+ import { join, resolve } from "node:path";
18
+ import { relativePosix, toPosix } from "../compat/paths.js";
19
+ import { pyStrip } from "../compat/pystr.js";
20
+ import { descriptorUsageFallbackLines, descriptorUsageLines, descriptorUsageUnparsedLines } from "./messages.js";
21
+ /**
22
+ * `ConnectorValue<string>` → `string`, for the handoff skeleton. The generated
23
+ * property type says what the field ACCEPTS — the literal or an expression —
24
+ * which is right for the compiler and wrong for a one-line "what goes here"
25
+ * hint; the wrapper is not something the author types.
26
+ */
27
+ export function unwrapValue(tsType) {
28
+ const match = /^Connector(?:Lookup)?Value<(.+)>$/.exec(pyStrip(tsType));
29
+ return match ? match[1] : tsType;
30
+ }
31
+ const PROPERTY = /^ {2}([A-Za-z_$][A-Za-z0-9_$]*|"(?:\\.|[^"\\])*")(\?)?: (.+);$/;
32
+ /** `str.splitlines()`: every line boundary Python recognises. */
33
+ const LINE_BREAK = /\r\n|[\n\r\v\f\x1c\x1d\x1e\x85\u2028\u2029]/;
34
+ function escapeRegExp(s) {
35
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
36
+ }
37
+ /**
38
+ * `_required_interface_fields`: `[name, type]` for every non-optional
39
+ * property of `export interface <name> { … }` in the generated module, or
40
+ * `undefined` when the interface is absent or a line does not parse — the
41
+ * caller must then say the skeleton is unavailable rather than print `{}`.
42
+ */
43
+ export function requiredInterfaceFields(generated, interfaceName) {
44
+ const block = new RegExp(`^export interface ${escapeRegExp(interfaceName)} \\{([\\s\\S]*?)^\\}`, "m").exec(generated);
45
+ if (!block)
46
+ return undefined;
47
+ const required = [];
48
+ for (const line of block[1].split(LINE_BREAK)) {
49
+ const stripped = pyStrip(line);
50
+ if (!stripped || stripped.startsWith("/**") || stripped.startsWith("*") || stripped.startsWith("*/"))
51
+ continue;
52
+ const field = PROPERTY.exec(line);
53
+ if (!field)
54
+ return undefined;
55
+ if (!field[2])
56
+ required.push([field[1], unwrapValue(field[3])]);
57
+ }
58
+ return required;
59
+ }
60
+ const DESCRIPTOR_EXPORT = /^export const ([A-Za-z_$][A-Za-z0-9_$]*) = descriptor<([A-Za-z_$][A-Za-z0-9_$]*),/gm;
61
+ /**
62
+ * `_descriptor_usage`: the handoff lines for `<outDir>/<connectorKey>.ts`,
63
+ * unprefixed (the caller prints each as `prepare-connector: next: …`).
64
+ * `cwd` is what the Python read as `Path.cwd()`: the import path is spelled
65
+ * relative to it, with a leading `./`.
66
+ */
67
+ export function descriptorUsage(outDir, connectorKey, boundConnection = "<binding-name>", boundFolder = "<folder-binding-name>", cwd = process.cwd()) {
68
+ const source = join(outDir, `${connectorKey}.ts`);
69
+ let generated = "";
70
+ let exports = [];
71
+ try {
72
+ generated = readFileSync(resolve(cwd, source), "utf8");
73
+ exports = [...generated.matchAll(DESCRIPTOR_EXPORT)].map((m) => [m[1], m[2]]);
74
+ }
75
+ catch {
76
+ generated = "";
77
+ exports = [];
78
+ }
79
+ let importPath = relativePosix(cwd, resolve(cwd, source));
80
+ if (!importPath.startsWith("."))
81
+ importPath = `./${importPath}`;
82
+ const sourcePosix = toPosix(source);
83
+ if (exports.length === 1) {
84
+ const [descriptorName, inputsInterface] = exports[0];
85
+ const requiredFields = requiredInterfaceFields(generated, inputsInterface);
86
+ if (requiredFields === undefined)
87
+ return descriptorUsageUnparsedLines(sourcePosix, descriptorName);
88
+ return descriptorUsageLines(descriptorName, importPath, requiredFields, boundConnection, boundFolder);
89
+ }
90
+ return descriptorUsageFallbackLines(sourcePosix);
91
+ }
@@ -0,0 +1,104 @@
1
+ import type { Reporter } from "../reporter.js";
2
+ import type { UipClient } from "../uip/client.js";
3
+ import { type OverlayLookupSpec } from "./overlay.js";
4
+ import { LookupExhausted, type ResolveRequest } from "./resolve.js";
5
+ /**
6
+ * The key a resolution is recorded under: `JSON.stringify([nodeType, field,
7
+ * by, value])`, the same bytes `core/lookups.ts`'s `lookupKey` computes when
8
+ * `compile` looks one up.
9
+ *
10
+ * INTENTIONAL DIVERGENCE from the Python, which built the key with
11
+ * `json.dumps(..., separators=(",", ":"))` — compact like JavaScript, but with
12
+ * `ensure_ascii` left on, so a value with any character outside ASCII was
13
+ * written as `\uXXXX` INSIDE the key string. `JSON.stringify` writes the
14
+ * character itself. The two spellings of the same tuple are different strings,
15
+ * so every such resolution `prepare` recorded was invisible to the `compile`
16
+ * that had asked for it. Written raw here to match the reader;
17
+ * {@link findResolution} still reads the escaped spelling so a ledger the
18
+ * Python wrote keeps loading.
19
+ */
20
+ export declare function resolutionKey(nodeType: string, field: string, by: string, value: string): string;
21
+ /** The Python's spelling of the same key: compact separators, `ensure_ascii=True`. */
22
+ export declare function escapedResolutionKey(nodeType: string, field: string, by: string, value: string): string;
23
+ /** `[nodeType, field, by, match]` out of a recorded key, in either spelling, or `undefined`. */
24
+ export declare function parseResolutionKey(key: string): [nodeType: string, field: string, by: string, match: string] | undefined;
25
+ /** One recorded row, with the key it was found under (raw or escaped). */
26
+ export interface FoundResolution {
27
+ key: string;
28
+ row: Record<string, unknown>;
29
+ }
30
+ /**
31
+ * The recorded row for `(nodeType, field, by, value)`, whichever spelling it
32
+ * was written under — raw first, then the Python's escaped form. For a pure
33
+ * ASCII tuple the two are the same string.
34
+ */
35
+ export declare function findResolution(existing: Readonly<Record<string, unknown>>, nodeType: string, field: string, by: string, value: string): FoundResolution | undefined;
36
+ /**
37
+ * What one run set out to resolve: the specs read from the overlay and the
38
+ * requests in dependency order — the `specs, ordered` that
39
+ * `_retry_or_exit(args, node_type, specs, ordered, error)` took, so a probe of
40
+ * another connection asks exactly the questions this run asked.
41
+ */
42
+ export interface ResolutionPlan {
43
+ specs: ReadonlyMap<string, OverlayLookupSpec>;
44
+ ordered: readonly ResolveRequest[];
45
+ }
46
+ /**
47
+ * A {@link LookupExhausted} as {@link writeResolutions} surfaces it: the
48
+ * exhausted lookup plus the plan it belonged to. Same message, same facts,
49
+ * still a `LookupExhausted` — the pipeline catches this one class and has
50
+ * everything the retry needs, whichever path (action or event) was running.
51
+ *
52
+ * Raised ONLY for a scan that reached the collection's end. A capped scan is
53
+ * a `LookupExhausted` too and is deliberately NOT wrapped; see the throw site.
54
+ */
55
+ export declare class ResolutionsExhausted extends LookupExhausted {
56
+ readonly first: LookupExhausted;
57
+ readonly plan: ResolutionPlan;
58
+ constructor(first: LookupExhausted, plan: ResolutionPlan);
59
+ }
60
+ /** What one `--resolve` run needs; `objectName` narrows the overlay entry the specs are read from. */
61
+ export interface ResolveRun {
62
+ client: UipClient;
63
+ connectorKey: string;
64
+ connectionId: string;
65
+ /** The overlay directory to read and write (resolved). */
66
+ out: string;
67
+ /** `out` as the caller spelled it, for the `wrote … to` line; defaults to `out`. */
68
+ outShown?: string;
69
+ nodeType: string;
70
+ objectName?: string | null;
71
+ /** The raw `--resolve <field>:<by>=<value>` flags, in the order given. */
72
+ resolve: readonly string[];
73
+ refreshLookups?: boolean;
74
+ reporter: Reporter;
75
+ }
76
+ export interface ResolveOutcome {
77
+ /** The ledger written. */
78
+ path: string;
79
+ /** Rows in the ledger's `resolutions` after the write. */
80
+ count: number;
81
+ /** `field → id` for every field this run resolved or found recorded. */
82
+ resolved: Map<string, string>;
83
+ }
84
+ /**
85
+ * `_write_resolutions`: resolve every `--resolve` and merge the answers into
86
+ * the ledger. Reads and rewrites the WHOLE ledger, not just `resolutions` —
87
+ * the file also carries the `objects` section, and rewriting one section used
88
+ * to silently drop the other. A file that will not parse is REFUSED rather
89
+ * than reset (`readLedger`), the same rule `bindings.json` follows.
90
+ *
91
+ * `specs` defaults to the overlay's, read back from what was just written;
92
+ * a caller that already holds them passes them in.
93
+ */
94
+ export declare function writeResolutions(run: ResolveRun, specs?: ReadonlyMap<string, OverlayLookupSpec>): Promise<ResolveOutcome>;
95
+ /**
96
+ * `_resolved_parent_values`: `{parent → resolved id}` for every parent with a
97
+ * recorded resolution on `nodeType`, read from the ledger just written so the
98
+ * paste-ready `-f` re-run carries the ids THIS invocation resolved — never
99
+ * guessed, never retyped. A ledger that will not load answers nothing; the
100
+ * refusal for that belongs to the write that found it.
101
+ */
102
+ export declare function resolvedParentValues(out: string, nodeType: string, parents: readonly string[]): Map<string, string>;
103
+ /** `set(resolved_parents) == set(action_parents)`: every schema parent has a recorded resolution, the paste-ready re-run condition. */
104
+ export declare function allParentsResolved(actionParents: readonly string[], resolvedParents: ReadonlyMap<string, string>): boolean;