@uipath/maestro-builder-sdk 5.2.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 (238) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +322 -0
  3. package/dist/bindings-v2.d.ts +37 -0
  4. package/dist/bindings-v2.js +155 -0
  5. package/dist/bindings.d.ts +6 -0
  6. package/dist/bindings.js +6 -0
  7. package/dist/bpmn/bpmn-expr-check.d.ts +41 -0
  8. package/dist/bpmn/bpmn-expr-check.js +218 -0
  9. package/dist/bpmn/bpmn-sdk.d.ts +2257 -0
  10. package/dist/bpmn/bpmn-sdk.js +1476 -0
  11. package/dist/bpmn/check-cli.d.ts +3 -0
  12. package/dist/bpmn/check-cli.js +32 -0
  13. package/dist/bpmn/check.d.ts +25 -0
  14. package/dist/bpmn/check.js +430 -0
  15. package/dist/bpmn/compile-cli.d.ts +3 -0
  16. package/dist/bpmn/compile-cli.js +60 -0
  17. package/dist/bpmn/connector.d.ts +56 -0
  18. package/dist/bpmn/connector.js +199 -0
  19. package/dist/bpmn/decompile-cli.d.ts +8 -0
  20. package/dist/bpmn/decompile-cli.js +54 -0
  21. package/dist/bpmn/decompile.d.ts +6 -0
  22. package/dist/bpmn/decompile.js +1108 -0
  23. package/dist/bpmn/format-profile.d.ts +27 -0
  24. package/dist/bpmn/format-profile.js +11 -0
  25. package/dist/bpmn/intsvc.d.ts +77 -0
  26. package/dist/bpmn/intsvc.js +155 -0
  27. package/dist/bpmn/load.d.ts +4 -0
  28. package/dist/bpmn/load.js +22 -0
  29. package/dist/bpmn/merge-cli.d.ts +3 -0
  30. package/dist/bpmn/merge-cli.js +45 -0
  31. package/dist/bpmn/merge.d.ts +19 -0
  32. package/dist/bpmn/merge.js +293 -0
  33. package/dist/bpmn/registry-types.json +1869 -0
  34. package/dist/bpmn/serialize.d.ts +28 -0
  35. package/dist/bpmn/serialize.js +757 -0
  36. package/dist/bpmn/tidy-cli.d.ts +2 -0
  37. package/dist/bpmn/tidy-cli.js +39 -0
  38. package/dist/bpmn/typed-node.d.ts +382 -0
  39. package/dist/bpmn/typed-node.js +551 -0
  40. package/dist/bpmn/uipath-moddle.v1.json +823 -0
  41. package/dist/bpmn/xml-ranges.d.ts +45 -0
  42. package/dist/bpmn/xml-ranges.js +151 -0
  43. package/dist/case/case-expr-check.d.ts +35 -0
  44. package/dist/case/case-expr-check.js +85 -0
  45. package/dist/case/case-sdk.d.ts +1282 -0
  46. package/dist/case/case-sdk.js +923 -0
  47. package/dist/case/check-cli.d.ts +3 -0
  48. package/dist/case/check-cli.js +31 -0
  49. package/dist/case/check.d.ts +18 -0
  50. package/dist/case/check.js +456 -0
  51. package/dist/case/compile-cli.d.ts +3 -0
  52. package/dist/case/compile-cli.js +291 -0
  53. package/dist/case/decompile-cli.d.ts +9 -0
  54. package/dist/case/decompile-cli.js +117 -0
  55. package/dist/case/decompile.d.ts +187 -0
  56. package/dist/case/decompile.js +1170 -0
  57. package/dist/case/entry-points.d.ts +79 -0
  58. package/dist/case/entry-points.js +118 -0
  59. package/dist/case/format-profile.d.ts +60 -0
  60. package/dist/case/format-profile.js +79 -0
  61. package/dist/case/ids.d.ts +24 -0
  62. package/dist/case/ids.js +76 -0
  63. package/dist/case/load.d.ts +12 -0
  64. package/dist/case/load.js +32 -0
  65. package/dist/case/preserve.d.ts +133 -0
  66. package/dist/case/preserve.js +105 -0
  67. package/dist/case/serialize.d.ts +10 -0
  68. package/dist/case/serialize.js +1414 -0
  69. package/dist/check-cli.d.ts +3 -0
  70. package/dist/check-cli.js +118 -0
  71. package/dist/check.d.ts +146 -0
  72. package/dist/check.js +4898 -0
  73. package/dist/cli/commands/bpmn.d.ts +2 -0
  74. package/dist/cli/commands/bpmn.js +73 -0
  75. package/dist/cli/commands/case.d.ts +2 -0
  76. package/dist/cli/commands/case.js +60 -0
  77. package/dist/cli/commands/flow.d.ts +2 -0
  78. package/dist/cli/commands/flow.js +99 -0
  79. package/dist/cli/commands/registry.d.ts +10 -0
  80. package/dist/cli/commands/registry.js +170 -0
  81. package/dist/cli/common.d.ts +30 -0
  82. package/dist/cli/common.js +92 -0
  83. package/dist/cli/index.d.ts +2 -0
  84. package/dist/cli/index.js +18 -0
  85. package/dist/cli-run.d.ts +2 -0
  86. package/dist/cli-run.js +42 -0
  87. package/dist/compile-cli.d.ts +3 -0
  88. package/dist/compile-cli.js +160 -0
  89. package/dist/config.d.ts +47 -0
  90. package/dist/config.js +201 -0
  91. package/dist/core/actions.d.ts +3244 -0
  92. package/dist/core/actions.js +1062 -0
  93. package/dist/core/binding-messages.d.ts +1 -0
  94. package/dist/core/binding-messages.js +16 -0
  95. package/dist/core/bindings.d.ts +12 -0
  96. package/dist/core/bindings.js +67 -0
  97. package/dist/core/ceql.d.ts +73 -0
  98. package/dist/core/ceql.js +272 -0
  99. package/dist/core/cli-spelling.d.ts +39 -0
  100. package/dist/core/cli-spelling.js +62 -0
  101. package/dist/core/connector-raw-node.d.ts +21 -0
  102. package/dist/core/connector-raw-node.js +47 -0
  103. package/dist/core/connectors.d.ts +184 -0
  104. package/dist/core/connectors.js +128 -0
  105. package/dist/core/expr-check.d.ts +93 -0
  106. package/dist/core/expr-check.js +186 -0
  107. package/dist/core/expr.d.ts +323 -0
  108. package/dist/core/expr.js +400 -0
  109. package/dist/core/index.d.ts +10 -0
  110. package/dist/core/index.js +10 -0
  111. package/dist/core/library.d.ts +440 -0
  112. package/dist/core/library.js +1183 -0
  113. package/dist/core/list-envelope.d.ts +45 -0
  114. package/dist/core/list-envelope.js +97 -0
  115. package/dist/core/lookup-store.d.ts +22 -0
  116. package/dist/core/lookup-store.js +85 -0
  117. package/dist/core/lookups.d.ts +227 -0
  118. package/dist/core/lookups.js +231 -0
  119. package/dist/core/node-classes.d.ts +234 -0
  120. package/dist/core/node-classes.js +158 -0
  121. package/dist/core/script-return.d.ts +68 -0
  122. package/dist/core/script-return.js +197 -0
  123. package/dist/core/stable-id.d.ts +28 -0
  124. package/dist/core/stable-id.js +37 -0
  125. package/dist/core-definitions.json +16249 -0
  126. package/dist/decompile-cli.d.ts +3 -0
  127. package/dist/decompile-cli.js +131 -0
  128. package/dist/decompile.d.ts +138 -0
  129. package/dist/decompile.js +2482 -0
  130. package/dist/emit.d.ts +10 -0
  131. package/dist/emit.js +60 -0
  132. package/dist/event-filters.d.ts +130 -0
  133. package/dist/event-filters.js +137 -0
  134. package/dist/flow-expr-check.d.ts +17 -0
  135. package/dist/flow-expr-check.js +193 -0
  136. package/dist/flow-sdk.d.ts +1341 -0
  137. package/dist/flow-sdk.js +1164 -0
  138. package/dist/flow-variables.d.ts +13 -0
  139. package/dist/flow-variables.js +42 -0
  140. package/dist/format-profile.d.ts +72 -0
  141. package/dist/format-profile.js +97 -0
  142. package/dist/generators/_connections.py +287 -0
  143. package/dist/generators/_events.py +355 -0
  144. package/dist/generators/_lookups.py +114 -0
  145. package/dist/generators/_naming.py +185 -0
  146. package/dist/generators/_objects.py +299 -0
  147. package/dist/generators/_resolve.py +273 -0
  148. package/dist/generators/convert_library_to_md.py +1441 -0
  149. package/dist/generators/extract_library.py +125 -0
  150. package/dist/generators/generate_connectors_ts.py +936 -0
  151. package/dist/generators/generate_library_json.py +2527 -0
  152. package/dist/generators/generate_triggers_ts.py +110 -0
  153. package/dist/generators/materialize_registry.py +155 -0
  154. package/dist/generators/prepare_connector.py +1688 -0
  155. package/dist/index.d.ts +35 -0
  156. package/dist/index.js +35 -0
  157. package/dist/ixp-definition.json +1982 -0
  158. package/dist/library.d.ts +6 -0
  159. package/dist/library.js +6 -0
  160. package/dist/load.d.ts +16 -0
  161. package/dist/load.js +95 -0
  162. package/dist/merge-cli.d.ts +3 -0
  163. package/dist/merge-cli.js +47 -0
  164. package/dist/merge.d.ts +125 -0
  165. package/dist/merge.js +0 -0
  166. package/dist/migrate.d.ts +15 -0
  167. package/dist/migrate.js +245 -0
  168. package/dist/node-runtime.d.ts +3 -0
  169. package/dist/node-runtime.js +22 -0
  170. package/dist/package-name.d.ts +17 -0
  171. package/dist/package-name.js +17 -0
  172. package/dist/ref-resolve.d.ts +77 -0
  173. package/dist/ref-resolve.js +170 -0
  174. package/dist/registry/cache.d.ts +55 -0
  175. package/dist/registry/cache.js +134 -0
  176. package/dist/registry/curated-overlay.d.ts +16 -0
  177. package/dist/registry/curated-overlay.js +62 -0
  178. package/dist/registry/extractor.d.ts +22 -0
  179. package/dist/registry/extractor.js +18 -0
  180. package/dist/registry/materialize-bin.d.ts +2 -0
  181. package/dist/registry/materialize-bin.js +7 -0
  182. package/dist/registry/materialize-cli.d.ts +1 -0
  183. package/dist/registry/materialize-cli.js +46 -0
  184. package/dist/registry/materializer.d.ts +23 -0
  185. package/dist/registry/materializer.js +34 -0
  186. package/dist/registry/path-cli.d.ts +1 -0
  187. package/dist/registry/path-cli.js +56 -0
  188. package/dist/registry/prepare-cli.d.ts +62 -0
  189. package/dist/registry/prepare-cli.js +185 -0
  190. package/dist/registry/pull-cli.d.ts +1 -0
  191. package/dist/registry/pull-cli.js +274 -0
  192. package/dist/registry/python.d.ts +26 -0
  193. package/dist/registry/python.js +46 -0
  194. package/dist/registry/search-cli.d.ts +52 -0
  195. package/dist/registry/search-cli.js +159 -0
  196. package/dist/schema-version.d.ts +165 -0
  197. package/dist/schema-version.js +148 -0
  198. package/dist/serialize.d.ts +64 -0
  199. package/dist/serialize.js +6398 -0
  200. package/lib/README.md +112 -0
  201. package/lib/library-json.samples/index.json +158 -0
  202. package/lib/library-json.samples/invariant-collections.json +83 -0
  203. package/lib/library-json.samples/list-envelopes.json +123 -0
  204. package/lib/library-json.samples/uipath-atlassian-jira/create-issue@1.0.0.json +131 -0
  205. package/lib/library-json.samples/uipath-atlassian-jira/create-issue@1.0.0.v1def.json +306 -0
  206. package/lib/library-json.samples/uipath-atlassian-jira/generic/create_issue/create@1.0.0.json +86 -0
  207. package/lib/library-json.samples/uipath-atlassian-jira/generic/create_issue/create@1.0.0.v1def.json +208 -0
  208. package/lib/library-json.samples/uipath-atlassian-jira/get-issue@1.0.0.json +2040 -0
  209. package/lib/library-json.samples/uipath-atlassian-jira/get-issue@1.0.0.v1def.json +195 -0
  210. package/lib/library-json.samples/uipath-google-vertex/execute-google-vertex-agent@1.0.0.json +169 -0
  211. package/lib/library-json.samples/uipath-google-vertex/execute-google-vertex-agent@1.0.0.v1def.json +209 -0
  212. package/lib/library-json.samples/uipath-http-webhook/event-http-webhook@1.0.0.json +63 -0
  213. package/lib/library-json.samples/uipath-http-webhook/event-http-webhook@1.0.0.v1def.json +262 -0
  214. package/lib/library-json.samples/uipath-http-webhook/trigger-http-webhook@1.0.0.json +63 -0
  215. package/lib/library-json.samples/uipath-http-webhook/trigger-http-webhook@1.0.0.v1def.json +213 -0
  216. package/lib/library-json.samples/uipath-microsoft-azureactivedirectory/list-groups@1.0.0.json +227 -0
  217. package/lib/library-json.samples/uipath-microsoft-azureactivedirectory/list-groups@1.0.0.v1def.json +373 -0
  218. package/lib/library-json.samples/uipath-microsoft-onedrive/event-file-created@1.0.0.json +116 -0
  219. package/lib/library-json.samples/uipath-microsoft-onedrive/event-file-created@1.0.0.v1def.json +369 -0
  220. package/lib/library-json.samples/uipath-microsoft-onedrive/event-list-item-added@1.0.0.json +56 -0
  221. package/lib/library-json.samples/uipath-microsoft-onedrive/event-list-item-added@1.0.0.v1def.json +256 -0
  222. package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-file-created@1.0.0.json +116 -0
  223. package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-file-created@1.0.0.v1def.json +320 -0
  224. package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-list-item-added@1.0.0.json +56 -0
  225. package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-list-item-added@1.0.0.v1def.json +207 -0
  226. package/lib/library-json.samples/uipath-microsoft-outlook365/event-email-received@1.0.0.json +107 -0
  227. package/lib/library-json.samples/uipath-microsoft-outlook365/event-email-received@1.0.0.v1def.json +264 -0
  228. package/lib/library-json.samples/uipath-microsoft-outlook365/send-email@1.0.0.json +142 -0
  229. package/lib/library-json.samples/uipath-microsoft-outlook365/send-email@1.0.0.v1def.json +306 -0
  230. package/lib/library-json.samples/uipath-microsoft-outlook365/trigger-email-received@1.0.0.json +107 -0
  231. package/lib/library-json.samples/uipath-microsoft-outlook365/trigger-email-received@1.0.0.v1def.json +217 -0
  232. package/lib/library-json.samples/uipath-microsoft-powerautomate/invoke-a-microsoft-power-automate-flow@1.0.0.json +85 -0
  233. package/lib/library-json.samples/uipath-microsoft-powerautomate/invoke-a-microsoft-power-automate-flow@1.0.0.v1def.json +209 -0
  234. package/lib/library-json.samples/uipath-salesforce-slack/send-message-to-user@1.0.0.json +755 -0
  235. package/lib/library-json.samples/uipath-salesforce-slack/send-message-to-user@1.0.0.v1def.json +513 -0
  236. package/lib/library-json.samples/uipath-uipath-dataservice/query-entity-records@1.0.0.json +122 -0
  237. package/lib/library-json.samples/uipath-uipath-dataservice/query-entity-records@1.0.0.v1def.json +227 -0
  238. package/package.json +85 -0
@@ -0,0 +1,1476 @@
1
+ import { connector as makeConnector } from '../core/actions.js';
2
+ import { checkBpmnExpressions, BpmnBuildError } from './bpmn-expr-check.js';
3
+ import { BPMN_SCHEMA_VERSIONS } from './format-profile.js';
4
+ import { schemaVersionRefusal } from '../schema-version.js';
5
+ import { bindingRequiredFields, REGISTRY_GAPS, typedNodeOutputVar } from './typed-node.js';
6
+ export { BpmnBuildError } from './bpmn-expr-check.js';
7
+ function normTimer(t) {
8
+ return typeof t === 'string' ? { duration: t } : t;
9
+ }
10
+ // ─── Definitions registry (shared across the top process + sub-processes) ─────
11
+ /**
12
+ * Collects `uipath:binding` declarations, de-duped by id.
13
+ *
14
+ * Shared with every scope rather than living on the top-level builder, because a
15
+ * typed node can NEED a binding — `Orchestrator.StartAgentJob` requires its
16
+ * process and folder to be `=bindings.<id>` references — and such a node can be
17
+ * authored inside a sub-process. Same reasoning as {@link DefinitionsRegistry}.
18
+ */
19
+ class BindingsRegistry {
20
+ /** Every declared binding, in declaration order — `BuiltBpmn.bindings`. */
21
+ bindings = [];
22
+ /**
23
+ * Declare once and keep the FIRST declaration, so an author's own `.binding()`
24
+ * wins over one a typed node would have derived.
25
+ *
26
+ * @param decl - The binding to declare.
27
+ * @returns The declaration in force for that id — the existing one on a collision.
28
+ */
29
+ declare(decl) {
30
+ const existing = this.bindings.find((b) => b.id === decl.id);
31
+ if (existing)
32
+ return existing;
33
+ this.bindings.push(decl);
34
+ return decl;
35
+ }
36
+ /**
37
+ * @param id - The binding id to look for.
38
+ * @returns Whether that id is already declared.
39
+ */
40
+ has(id) {
41
+ return this.bindings.some((b) => b.id === id);
42
+ }
43
+ }
44
+ /** Collects definitions-level message/error declarations, de-duped by name. */
45
+ class DefinitionsRegistry {
46
+ /** Every declared message, in declaration order — `BuiltBpmn.messages`. */
47
+ messages = [];
48
+ /** Every declared error, in declaration order — `BuiltBpmn.errors`. */
49
+ errors = [];
50
+ messageByName = new Map();
51
+ errorByName = new Map();
52
+ /**
53
+ * Declare a message once and reuse it, returning the id events reference.
54
+ *
55
+ * @param name - The message's name.
56
+ * @returns The message id to put in an event's definition.
57
+ */
58
+ messageRef(name) {
59
+ const existing = this.messageByName.get(name);
60
+ if (existing)
61
+ return existing;
62
+ const id = uniqueId('Message', this.messageByName.size);
63
+ this.messageByName.set(name, id);
64
+ this.messages.push({ id, name });
65
+ return id;
66
+ }
67
+ /**
68
+ * Declare an error once and reuse it, returning the id events reference.
69
+ *
70
+ * @param name - The error's name.
71
+ * @param code - The error code the runtime matches on.
72
+ * @returns The error id to put in an event's definition.
73
+ */
74
+ errorRef(name, code) {
75
+ const existing = this.errorByName.get(name);
76
+ if (existing)
77
+ return existing;
78
+ const id = uniqueId('Error', this.errorByName.size);
79
+ this.errorByName.set(name, id);
80
+ this.errors.push({ id, name, code });
81
+ return id;
82
+ }
83
+ }
84
+ function uniqueId(prefix, index) {
85
+ return `${prefix}_${index + 1}`;
86
+ }
87
+ /**
88
+ * Resolve an end/boundary error option into an error definition.
89
+ *
90
+ * **The code defaults to the name**, and must: the code is what the runtime
91
+ * matches a failure against, and `uip maestro bpmn validate` rejects an error
92
+ * boundary whose referenced error has none (`ERROR_BOUNDARY_EVENT_REQUIRES_ERROR_CODE`).
93
+ * Omitting it emitted `<bpmn:error name="Boom"/>` with no code, which failed
94
+ * validation and — because the runtime then falls back to the SYNTHESIZED
95
+ * element id (`Error_1`) — could never match a real failure, so the handler was
96
+ * unreachable. Passing `{ name, code }` still sets them independently.
97
+ */
98
+ function toErrorDef(defs, error) {
99
+ // `true` is catch-any: no `bpmn:error` is declared and no `errorRef` is written.
100
+ if (error === true)
101
+ return { type: 'error' };
102
+ const spec = typeof error === 'string' ? { name: error } : error;
103
+ return { type: 'error', errorRef: defs.errorRef(spec.name, spec.code ?? spec.name) };
104
+ }
105
+ // ─── Graph scope (shared by the top process and sub-processes) ────────────────
106
+ /**
107
+ * The set of node/flow/variable methods common to the process root and every
108
+ * sub-process. Methods return `this`, so fluent chaining preserves the concrete
109
+ * builder type (top-level or sub-process).
110
+ */
111
+ class ScopeBuilder {
112
+ /** @internal */
113
+ _nodes = [];
114
+ /** @internal */
115
+ _flows = [];
116
+ /** @internal */
117
+ _vars = [];
118
+ // An explicit field, not a constructor parameter property: an inline comment
119
+ // on a parameter property never reaches the emitted `.d.ts` property, so the
120
+ // `@internal` tag would be lost there.
121
+ /** @internal */
122
+ _defs;
123
+ /** @internal */
124
+ _bindingRegistry;
125
+ /**
126
+ * @param defs - Definitions-level message/error declarations, shared with every scope.
127
+ * @param bindings - `uipath:binding` declarations, shared so a typed node can
128
+ * declare one from inside a sub-process.
129
+ * @returns The scope builder.
130
+ */
131
+ constructor(defs, bindings) {
132
+ this._defs = defs;
133
+ this._bindingRegistry = bindings;
134
+ }
135
+ push(node) {
136
+ this._nodes.push(node);
137
+ return this;
138
+ }
139
+ // ── Events ──
140
+ /**
141
+ * A start event (authorable definitions: none / message / timer).
142
+ *
143
+ * @remarks
144
+ * With no definition at all, the process is started by a CALLER — an API call,
145
+ * `uip maestro`, a parent process, or an Orchestrator trigger configured against
146
+ * the deployed process. `timer` moves the schedule INTO the artifact, where the
147
+ * platform's scheduler reads it. `trigger` additionally declares the timed start
148
+ * as a typed platform trigger — see {@link StartOpts.trigger}.
149
+ *
150
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
151
+ * @param opts - `name`, the definition — message or timer — and `trigger`.
152
+ * @returns This builder, so calls chain.
153
+ * @throws If `trigger` is set without `timer`, which would emit a trigger with no
154
+ * schedule that every offline gate still calls valid.
155
+ */
156
+ startEvent(id, opts = {}) {
157
+ if (opts.trigger !== undefined && opts.timer === undefined) {
158
+ throw new Error(`startEvent "${id}": \`trigger\` needs \`timer\` — the schedule lives in the ` +
159
+ `bpmn:timerEventDefinition, not in the trigger extension, so a trigger without one emits an ` +
160
+ `empty definition and fires never. \`uip maestro bpmn validate\` reports that as Valid.`);
161
+ }
162
+ if (opts.trigger !== undefined && opts.payload !== undefined) {
163
+ throw new Error(`startEvent "${id}": \`trigger\` and \`payload\` both write the event's extension element; pass one.`);
164
+ }
165
+ return this.push({
166
+ kind: 'startEvent',
167
+ id,
168
+ name: opts.name,
169
+ payload: opts.payload,
170
+ // `error` is handled here rather than in `startCatchDef` because it needs the
171
+ // definitions registry to intern the `bpmn:error`, exactly as `endEvent` does —
172
+ // and because an intermediate catch event (the other `startCatchDef` caller)
173
+ // cannot carry one: BPMN catches errors at a boundary or an event sub-process
174
+ // start, never mid-flow.
175
+ event: opts.error !== undefined ? toErrorDef(this._defs, opts.error) : this.startCatchDef(opts),
176
+ trigger: opts.trigger === undefined
177
+ ? undefined
178
+ : { type: 'Intsvc.TimerTrigger', outputVar: opts.trigger === true ? undefined : opts.trigger.outputVar },
179
+ });
180
+ }
181
+ /**
182
+ * An end event (authorable definitions: none / message / error / terminate).
183
+ *
184
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
185
+ * @param opts - `name`, and the definition — message, error, or terminate.
186
+ * @returns This builder, so calls chain.
187
+ */
188
+ endEvent(id, opts = {}) {
189
+ let event;
190
+ if (opts.terminate)
191
+ event = { type: 'terminate' };
192
+ else if (opts.error !== undefined)
193
+ event = toErrorDef(this._defs, opts.error);
194
+ else if (opts.message !== undefined)
195
+ event = { type: 'message', messageRef: this._defs.messageRef(opts.message) };
196
+ return this.push({ kind: 'endEvent', id, name: opts.name, event, payload: opts.payload });
197
+ }
198
+ /**
199
+ * An intermediate catch event (authorable: message / timer).
200
+ *
201
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
202
+ * @param opts - `name`, and the definition — message or timer.
203
+ * @returns This builder, so calls chain.
204
+ */
205
+ intermediateCatchEvent(id, opts = {}) {
206
+ return this.push({
207
+ kind: 'intermediateCatchEvent',
208
+ id,
209
+ name: opts.name,
210
+ event: this.startCatchDef(opts),
211
+ payload: opts.payload,
212
+ });
213
+ }
214
+ /**
215
+ * An intermediate throw event (authorable: none / message).
216
+ *
217
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
218
+ * @param opts - `name`, and the definition — none or message.
219
+ * @returns This builder, so calls chain.
220
+ */
221
+ intermediateThrowEvent(id, opts = {}) {
222
+ const event = opts.message !== undefined ? { type: 'message', messageRef: this._defs.messageRef(opts.message) } : undefined;
223
+ return this.push({ kind: 'intermediateThrowEvent', id, name: opts.name, event, payload: opts.payload });
224
+ }
225
+ /**
226
+ * A boundary event on an activity (authorable: message / timer / error).
227
+ *
228
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
229
+ * @param opts - The activity it attaches to, whether it interrupts, and the definition — message, timer, or error.
230
+ * @returns This builder, so calls chain.
231
+ */
232
+ boundaryEvent(id, opts) {
233
+ let event;
234
+ if (opts.error !== undefined)
235
+ event = toErrorDef(this._defs, opts.error);
236
+ else if (opts.timer !== undefined)
237
+ event = { type: 'timer', timer: normTimer(opts.timer) };
238
+ else if (opts.message !== undefined)
239
+ event = { type: 'message', messageRef: this._defs.messageRef(opts.message) };
240
+ else
241
+ throw new Error(`boundaryEvent "${id}" needs one of { message, timer, error }.`);
242
+ return this.push({
243
+ kind: 'boundaryEvent',
244
+ id,
245
+ name: opts.name,
246
+ attachedTo: opts.attachedTo,
247
+ cancelActivity: opts.cancelActivity ?? true,
248
+ event,
249
+ payload: opts.payload,
250
+ });
251
+ }
252
+ startCatchDef(opts) {
253
+ if (opts.timer !== undefined)
254
+ return { type: 'timer', timer: normTimer(opts.timer) };
255
+ if (opts.message !== undefined)
256
+ return { type: 'message', messageRef: this._defs.messageRef(opts.message) };
257
+ return undefined;
258
+ }
259
+ // ── Gateways ──
260
+ /**
261
+ * An exclusive gateway — exactly one outgoing flow is taken (`bpmn:exclusiveGateway`).
262
+ *
263
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
264
+ * @param opts - `name`, and the `default` flow taken when no condition matches.
265
+ * @returns This builder, so calls chain.
266
+ */
267
+ exclusiveGateway(id, opts = {}) {
268
+ return this.push({ kind: 'exclusiveGateway', id, name: opts.name, default: opts.default });
269
+ }
270
+ /**
271
+ * A parallel gateway — every outgoing flow is taken, and a join waits for every incoming one (`bpmn:parallelGateway`).
272
+ *
273
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
274
+ * @param opts - `name` for the gateway.
275
+ * @returns This builder, so calls chain.
276
+ */
277
+ parallelGateway(id, opts = {}) {
278
+ return this.push({ kind: 'parallelGateway', id, name: opts.name });
279
+ }
280
+ /**
281
+ * An inclusive gateway — every outgoing flow whose condition holds is taken (`bpmn:inclusiveGateway`).
282
+ *
283
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
284
+ * @param opts - `name`, and the `default` flow taken when no condition matches.
285
+ * @returns This builder, so calls chain.
286
+ */
287
+ inclusiveGateway(id, opts = {}) {
288
+ return this.push({ kind: 'inclusiveGateway', id, name: opts.name, default: opts.default });
289
+ }
290
+ /**
291
+ * An event-based gateway — the first of the events it leads to wins, and the rest are cancelled (`bpmn:eventBasedGateway`).
292
+ *
293
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
294
+ * @param opts - `name` for the gateway.
295
+ * @returns This builder, so calls chain.
296
+ */
297
+ eventBasedGateway(id, opts = {}) {
298
+ return this.push({ kind: 'eventBasedGateway', id, name: opts.name });
299
+ }
300
+ // ── Tasks ──
301
+ /**
302
+ * A script task (Jint JavaScript body + input/output mappings).
303
+ *
304
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
305
+ * @param opts - The Jint JavaScript `script`, and its input/output mappings.
306
+ * @returns This builder, so calls chain.
307
+ */
308
+ scriptTask(id, opts) {
309
+ return this.push({
310
+ kind: 'scriptTask',
311
+ id,
312
+ name: opts.name,
313
+ script: opts.script,
314
+ scriptFormat: opts.scriptFormat ?? 'JavaScript',
315
+ inputs: opts.inputs ?? {},
316
+ outputs: opts.outputs ?? {},
317
+ outputRows: opts.outputRows,
318
+ type: opts.type ?? 'BPMN.Variables',
319
+ inputSchema: opts.inputSchema,
320
+ tags: opts.tags,
321
+ retry: opts.retry,
322
+ errorMapping: opts.errorMapping,
323
+ loop: opts.loop,
324
+ });
325
+ }
326
+ /**
327
+ * A plain task that assigns variables (`BPMN.Variables`).
328
+ *
329
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
330
+ * @param opts - `name`, and the variable assignments the task makes.
331
+ * @returns This builder, so calls chain.
332
+ */
333
+ task(id, opts = {}) {
334
+ return this.push({
335
+ kind: 'task',
336
+ id,
337
+ name: opts.name,
338
+ set: opts.set ?? {},
339
+ tags: opts.tags,
340
+ retry: opts.retry,
341
+ loop: opts.loop,
342
+ errorMapping: opts.errorMapping,
343
+ });
344
+ }
345
+ /**
346
+ * A task element carrying NO `uipath:*` payload — an abstract task, in BPMN's terms.
347
+ *
348
+ * @remarks
349
+ * The platform accepts all seven task elements bare, so an artifact can legitimately
350
+ * contain one, and until this existed the builder could not express any of them. That
351
+ * made such a file unimportable: `bpmn-decompile` refused six of the seven outright,
352
+ * and the seventh — `bpmn:task` — was worse, because it came back through `.task()`
353
+ * and silently GAINED a `BPMN.Variables` mapping it never had.
354
+ *
355
+ * Not the same as {@link ScopeBuilder.task}, which emits a `BPMN.Variables` mapping to
356
+ * assign variables, or {@link ScopeBuilder.humanTask}, which emits an `Actions.HITL`
357
+ * activity. Those are tasks that DO something; this is a task element that stands for
358
+ * work the process does not model — the shape a diagram-first tool writes before
359
+ * anything is wired up.
360
+ *
361
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
362
+ * @param opts - `name`, and which task `element` to emit (default `bpmn:task`).
363
+ * @returns This builder, so calls chain.
364
+ *
365
+ * @example
366
+ * ```ts
367
+ * bpmn('review')
368
+ * .startEvent('s')
369
+ * .plainTask('approve', { name: 'Approve', element: 'bpmn:userTask' })
370
+ * .endEvent('e')
371
+ * ```
372
+ */
373
+ plainTask(id, opts = {}) {
374
+ return this.push({
375
+ kind: 'plainTask',
376
+ id,
377
+ name: opts.name,
378
+ element: opts.element ?? 'bpmn:task',
379
+ tags: opts.tags,
380
+ retry: opts.retry,
381
+ errorMapping: opts.errorMapping,
382
+ loop: opts.loop,
383
+ });
384
+ }
385
+ connector(id, a, b, c, d) {
386
+ // Reuse the shared connector() overload to parse descriptor-or-string into a
387
+ // ConnectorSpec; then carry its fields onto the BPMN node.
388
+ const spec = typeof a === 'string'
389
+ ? makeConnector(a, b, c, d)
390
+ : makeConnector(a, b, c);
391
+ if (spec.kind !== 'connector')
392
+ throw new Error('connector(): unexpected spec kind');
393
+ const bpmnOpts = (typeof a === 'string' ? d : c) ?? {};
394
+ return this.push({
395
+ kind: 'connector',
396
+ id,
397
+ name: bpmnOpts.name,
398
+ key: spec.key,
399
+ action: spec.action,
400
+ version: spec.version,
401
+ connection: spec.connection,
402
+ folder: spec.folder,
403
+ object: spec.object,
404
+ inputs: spec.inputs,
405
+ outputVar: bpmnOpts.outputVar,
406
+ skipCondition: bpmnOpts.skipCondition,
407
+ retry: bpmnOpts.retry,
408
+ loop: bpmnOpts.loop,
409
+ });
410
+ }
411
+ /**
412
+ * An **HTTP request** service task (`bpmn:sendTask` +
413
+ * `uipath:activity` / `Intsvc.UnifiedHttpRequest`).
414
+ *
415
+ * @remarks
416
+ * The first registry-backed typed node (`docs/BPMN_COVERAGE_DESIGN.md` Phase 3).
417
+ * Its wire shape — element, tag, context fields, output row — comes from the
418
+ * committed registry snapshot rather than hand-written emission, so it cannot
419
+ * drift from what the platform validates. Chosen as the pilot because it needs
420
+ * no tenant discovery (`RequiresDiscovery: No`) and the local engine dispatches
421
+ * it for real in `--mode live`.
422
+ *
423
+ * The response lands in `<id>_response` unless `outputVar` says otherwise, and
424
+ * that variable is readable downstream as `=vars.<name>` with no `.var()`.
425
+ *
426
+ * @example
427
+ * ```ts
428
+ * bpmn('sync')
429
+ * .var('body', 'object')
430
+ * .startEvent('start')
431
+ * .http('fetch', { method: 'GET', url: 'https://api.example.com/orders' })
432
+ * .task('keep', { set: { body: '=js:vars.fetch_response' } })
433
+ * .endEvent('done')
434
+ * .sequenceFlow('start', 'fetch')
435
+ * .sequenceFlow('fetch', 'keep')
436
+ * .sequenceFlow('keep', 'done')
437
+ * .build();
438
+ * ```
439
+ *
440
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
441
+ * @param opts - The request, plus `name`, `outputVar`, and the shared activity options.
442
+ * @returns This builder, so calls chain.
443
+ */
444
+ http(id, opts) {
445
+ const { name, url, method, headers, parameters, body, outputs, outputVar, skipCondition, retry, loop, errorMapping } = opts;
446
+ return this.push({
447
+ kind: 'typed',
448
+ id,
449
+ name,
450
+ type: 'Intsvc.UnifiedHttpRequest',
451
+ // These three are `json` context fields, so they go through as VALUES — the
452
+ // emitter encodes a json field's body. This wrapper used to `JSON.stringify`
453
+ // them itself, working around an emitter that wrote `[object Object]` for
454
+ // anything but a string; now the generic `.activity()` path accepts the same
455
+ // values this one does.
456
+ context: {
457
+ method: method ?? 'GET',
458
+ url,
459
+ ...(headers !== undefined ? { headers } : {}),
460
+ ...(parameters !== undefined ? { parameters } : {}),
461
+ ...(body !== undefined ? { body } : {}),
462
+ },
463
+ inputs: {},
464
+ outputs,
465
+ outputVar,
466
+ skipCondition,
467
+ retry,
468
+ loop,
469
+ errorMapping,
470
+ });
471
+ }
472
+ /**
473
+ * @internal Turn a literal into a binding reference where the platform demands
474
+ * one, declaring the binding on the author's behalf.
475
+ *
476
+ * `Orchestrator.StartAgentJob` requires its `name` and `folderPath` to be
477
+ * `=bindings.<id>` references AND requires those bindings to target
478
+ * `resource: process` with the right `propertyAttribute` — a literal is rejected,
479
+ * and so is a binding pointing at the wrong property. Both facts are in the
480
+ * registry snapshot, so the SDK can satisfy them from the plain value the author
481
+ * wrote instead of making them hand-build two bindings and get the attributes
482
+ * right. An author who passes an `=`-expression already knows what they want and
483
+ * is left alone.
484
+ */
485
+ bindRequiredContext(id, type, context) {
486
+ const out = { ...context };
487
+ for (const field of bindingRequiredFields(type)) {
488
+ const value = out[field.name];
489
+ if (typeof value !== 'string' || value.startsWith('='))
490
+ continue;
491
+ const bindingId = `${id}_${field.name}`;
492
+ this._bindingRegistry.declare({
493
+ id: bindingId,
494
+ name: field.displayName ?? field.name,
495
+ resource: field.bindingInfo?.resource ?? 'process',
496
+ propertyAttribute: field.bindingInfo?.propertyAttribute ?? field.name,
497
+ // A fact the registry omits and the validator enforces — see REGISTRY_GAPS.
498
+ resourceSubType: REGISTRY_GAPS[type]?.resourceSubType,
499
+ default: value,
500
+ resourceKey: value,
501
+ });
502
+ out[field.name] = `=bindings.${bindingId}`;
503
+ }
504
+ return out;
505
+ }
506
+ /** @internal Shared body of the Orchestrator process-invocation methods. */
507
+ orchestratorNode(id, type, opts) {
508
+ return this.push({
509
+ kind: 'typed',
510
+ id,
511
+ name: opts.name,
512
+ type,
513
+ context: this.bindRequiredContext(id, type, {
514
+ name: opts.process,
515
+ ...(opts.folder !== undefined ? { folderPath: opts.folder } : {}),
516
+ ...(opts.releaseKey !== undefined ? { releaseKey: opts.releaseKey } : {}),
517
+ }),
518
+ inputs: opts.input ?? {},
519
+ outputs: opts.outputs,
520
+ outputVar: opts.outputVar,
521
+ skipCondition: opts.skipCondition,
522
+ tags: opts.tags,
523
+ retry: opts.retry,
524
+ errorMapping: opts.errorMapping,
525
+ loop: opts.loop,
526
+ });
527
+ }
528
+ /**
529
+ * Start an **RPA process** and wait for it (`Orchestrator.StartJob`).
530
+ *
531
+ * @example
532
+ * ```ts
533
+ * bpmn('nightly')
534
+ * .startEvent('start')
535
+ * .startProcess('rpa', { process: 'InvoicePosting', folder: 'Finance', input: { batch: 42 } })
536
+ * .endEvent('done')
537
+ * .sequenceFlow('start', 'rpa')
538
+ * .sequenceFlow('rpa', 'done')
539
+ * .build();
540
+ * ```
541
+ *
542
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
543
+ * @param opts - Which process, folder, and input arguments.
544
+ * @returns This builder, so calls chain.
545
+ */
546
+ startProcess(id, opts) {
547
+ return this.orchestratorNode(id, 'Orchestrator.StartJob', opts);
548
+ }
549
+ /**
550
+ * Start an **agent** and wait for it (`Orchestrator.StartAgentJob`).
551
+ *
552
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
553
+ * @param opts - Which agent process, folder, and input arguments.
554
+ * @returns This builder, so calls chain.
555
+ */
556
+ startAgent(id, opts) {
557
+ return this.orchestratorNode(id, 'Orchestrator.StartAgentJob', opts);
558
+ }
559
+ /**
560
+ * Invoke an **agentic process** as a call activity
561
+ * (`Orchestrator.StartAgenticProcess`, or `…Async` when `async` is set).
562
+ *
563
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
564
+ * @param opts - Which process, folder, input arguments, and whether to wait.
565
+ * @returns This builder, so calls chain.
566
+ */
567
+ startAgenticProcess(id, opts) {
568
+ return this.orchestratorNode(id, opts.async ? 'Orchestrator.StartAgenticProcessAsync' : 'Orchestrator.StartAgenticProcess', opts);
569
+ }
570
+ /**
571
+ * Invoke a **case-management process** as a call activity
572
+ * (`Orchestrator.StartCaseMgmtProcess`, or `…Async` when `async` is set).
573
+ *
574
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
575
+ * @param opts - Which case process, folder, input arguments, and whether to wait.
576
+ * @returns This builder, so calls chain.
577
+ */
578
+ startCaseProcess(id, opts) {
579
+ return this.orchestratorNode(id, opts.async ? 'Orchestrator.StartCaseMgmtProcessAsync' : 'Orchestrator.StartCaseMgmtProcess', opts);
580
+ }
581
+ /**
582
+ * Execute an **API workflow**, fire-and-forget
583
+ * (`Orchestrator.ExecuteApiWorkflowAsync`).
584
+ *
585
+ * @remarks
586
+ * The registry offers only the async form, so there is nothing to wait on: the
587
+ * node maps back an error and nothing else.
588
+ *
589
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
590
+ * @param opts - Which workflow, folder, and input arguments.
591
+ * @returns This builder, so calls chain.
592
+ */
593
+ executeApiWorkflow(id, opts) {
594
+ return this.orchestratorNode(id, 'Orchestrator.ExecuteApiWorkflowAsync', opts);
595
+ }
596
+ /**
597
+ * Execute a **business rule** and wait for it (`Orchestrator.BusinessRules`, on a
598
+ * `bpmn:businessRuleTask`).
599
+ *
600
+ * @remarks
601
+ * **Not a DMN decision table**, despite the element and the label. The registry spec
602
+ * is an Orchestrator process invocation — `releaseKey`/`folderId`/`folderPath`/`name`
603
+ * plus `JobArguments`, `BindingPattern: businessRule` over `Resource: process` —
604
+ * structurally identical to {@link ScopeBuilder.startProcess}. `process` names a
605
+ * package whose Orchestrator process type is `BusinessRules`; whatever decision logic
606
+ * exists lives INSIDE that package, not in the `.bpmn`. So this is the job-start
607
+ * family's sixth member, not a rules subsystem, and it shares every option with it.
608
+ *
609
+ * Distinct from the Case SDK's `rule()`, which declares DNF lifecycle conditions
610
+ * (`case-entered`, `required-tasks-completed`, …) deciding when a stage enters or
611
+ * exits. The two share a word and nothing else.
612
+ *
613
+ * The extension type is marked `[Preview]` by the registry. That is a caveat on the
614
+ * SHAPE, not on this method: a preview extension type can gain or rename fields, and
615
+ * the snapshot refresh (`scripts/materialize-bpmn-registry.mjs`) is what would surface
616
+ * it as a data diff.
617
+ *
618
+ * @example
619
+ * ```ts
620
+ * bpmn('pricing')
621
+ * .var('tier', 'string', { default: 'none' })
622
+ * .startEvent('start')
623
+ * .businessRule('tier', {
624
+ * process: 'DiscountTier',
625
+ * folder: 'Finance',
626
+ * input: { amount: 4200 },
627
+ * outputs: { tier: '=response.tier' },
628
+ * })
629
+ * .endEvent('done')
630
+ * .sequenceFlow('start', 'tier')
631
+ * .sequenceFlow('tier', 'done')
632
+ * .build();
633
+ * ```
634
+ *
635
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
636
+ * @param opts - Which rule package, folder, and input arguments. `outputVar` defaults
637
+ * to `<id>_businessRuleResponse` — see the note in the implementation for why this one
638
+ * type does not take its default from the spec.
639
+ * @returns This builder, so calls chain.
640
+ */
641
+ businessRule(id, opts) {
642
+ // The ONE place this type departs from its family, and deliberately.
643
+ //
644
+ // Every other member takes its default output variable from the registry's
645
+ // `OutputName` (`Process response` → `<id>_processResponse`). This type's
646
+ // `OutputName` is literally its designer LABEL, `Execute business rule [Preview]`,
647
+ // so the derived default would be `<id>_executeBusinessRulePreview`: unreadable,
648
+ // and — worse — it encodes the preview marker in a name authors write expressions
649
+ // against. When `[Preview]` drops the label becomes `Execute business rule`, the
650
+ // derived default silently becomes `<id>_executeBusinessRule`, and every
651
+ // `vars.<id>_executeBusinessRulePreview` reference in every authored process starts
652
+ // resolving to undefined — a silent wrong-answer change from a cosmetic spec edit.
653
+ //
654
+ // Pinning it here makes the default independent of the label. The output ROW still
655
+ // carries the registry's own `name`, so the artifact is unchanged.
656
+ return this.orchestratorNode(id, 'Orchestrator.BusinessRules', {
657
+ ...opts,
658
+ outputVar: opts.outputVar ?? `${id}_businessRuleResponse`,
659
+ });
660
+ }
661
+ /**
662
+ * Add an item to an Orchestrator **queue** (`Orchestrator.CreateQueueItem`, or
663
+ * `Orchestrator.CreateAndWaitForQueueItem` when `wait` is set).
664
+ *
665
+ * @example
666
+ * ```ts
667
+ * bpmn('intake')
668
+ * .startEvent('start')
669
+ * .queueItem('enqueue', { queue: 'Invoices', folder: 'Finance', item: { id: 'INV-1' } })
670
+ * .endEvent('done')
671
+ * .sequenceFlow('start', 'enqueue')
672
+ * .sequenceFlow('enqueue', 'done')
673
+ * .build();
674
+ * ```
675
+ *
676
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
677
+ * @param opts - Which queue and folder, the item content, and whether to wait for it.
678
+ * @returns This builder, so calls chain.
679
+ */
680
+ queueItem(id, opts) {
681
+ return this.push({
682
+ kind: 'typed',
683
+ id,
684
+ name: opts.name,
685
+ type: opts.wait ? 'Orchestrator.CreateAndWaitForQueueItem' : 'Orchestrator.CreateQueueItem',
686
+ context: this.bindRequiredContext(id, opts.wait ? 'Orchestrator.CreateAndWaitForQueueItem' : 'Orchestrator.CreateQueueItem', {
687
+ queueName: opts.queue,
688
+ folderPath: opts.folder,
689
+ }),
690
+ inputs: opts.item ?? {},
691
+ outputs: opts.outputs,
692
+ outputVar: opts.outputVar,
693
+ skipCondition: opts.skipCondition,
694
+ tags: opts.tags,
695
+ retry: opts.retry,
696
+ errorMapping: opts.errorMapping,
697
+ loop: opts.loop,
698
+ });
699
+ }
700
+ /**
701
+ * A **human task** — an Action App task a person completes (`bpmn:userTask` +
702
+ * `uipath:activity` / `Actions.HITL`).
703
+ *
704
+ * @remarks
705
+ * The offline runtime can stand in for the human — `--hitl-response` injects an
706
+ * outcome — so an approval gate's branches are testable with no tenant and no
707
+ * waiting. That is why this is the one discovery-requiring type worth authoring
708
+ * early:
709
+ *
710
+ * ```bash
711
+ * flow-debug Invoice.bpmn --mock --virtual-time --hitl-response 'approve={"Action":"approve"}'
712
+ * ```
713
+ *
714
+ * Map the decision into a variable with `outputs` if anything branches on it —
715
+ * see {@link HumanTaskOpts.outputs} for why the type's own output is not enough.
716
+ *
717
+ * @example
718
+ * **An approval gate whose branches can be exercised offline**
719
+ * ```ts
720
+ * bpmn('invoice')
721
+ * .var('outcome', 'string', { default: 'none' })
722
+ * .startEvent('start')
723
+ * .humanTask('approve', {
724
+ * app: 'app-123',
725
+ * title: 'Approve the invoice',
726
+ * actions: ['approve', 'reject'],
727
+ * input: { amount: 100 },
728
+ * outputs: { decision: '=Action' },
729
+ * })
730
+ * .exclusiveGateway('gw', { default: 'fReject' })
731
+ * .task('ok', { set: { outcome: 'approved' } })
732
+ * .task('no', { set: { outcome: 'rejected' } })
733
+ * .exclusiveGateway('join', { default: 'fJoin' })
734
+ * .endEvent('done')
735
+ * .sequenceFlow('start', 'approve')
736
+ * .sequenceFlow('approve', 'gw')
737
+ * .sequenceFlow('gw', 'ok', { id: 'fApprove', condition: '=js:vars.decision == "approve"' })
738
+ * .sequenceFlow('gw', 'no', { id: 'fReject' })
739
+ * .sequenceFlow('ok', 'join', { id: 'fOk' })
740
+ * .sequenceFlow('no', 'join', { id: 'fNo' })
741
+ * .sequenceFlow('join', 'done', { id: 'fJoin' })
742
+ * .build();
743
+ * ```
744
+ *
745
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
746
+ * @param opts - The app, the outcomes offered, what the human sees, and which
747
+ * response fields to keep.
748
+ * @returns This builder, so calls chain.
749
+ */
750
+ humanTask(id, opts) {
751
+ return this.push({
752
+ kind: 'typed',
753
+ id,
754
+ name: opts.name,
755
+ type: 'Actions.HITL',
756
+ context: {
757
+ appId: opts.app,
758
+ ...(opts.appVersion !== undefined ? { appVersion: opts.appVersion } : {}),
759
+ ...(opts.actions !== undefined ? { actions: opts.actions.join(',') } : {}),
760
+ ...(opts.title !== undefined ? { taskTitle: opts.title } : {}),
761
+ ...(opts.key !== undefined ? { key: opts.key } : {}),
762
+ },
763
+ inputs: opts.input ?? {},
764
+ outputVar: opts.outputVar,
765
+ outputs: opts.outputs,
766
+ skipCondition: opts.skipCondition,
767
+ tags: opts.tags,
768
+ retry: opts.retry,
769
+ errorMapping: opts.errorMapping,
770
+ loop: opts.loop,
771
+ });
772
+ }
773
+ /**
774
+ * Wait for an internal message from another Maestro process
775
+ * (`Maestro.ReceiveMessageEvent` on a `bpmn:intermediateCatchEvent`).
776
+ *
777
+ * @remarks
778
+ * **No offline behavioural assertion is possible for this node**, and that is
779
+ * worth knowing before relying on it. The local engine treats intermediate catch
780
+ * and throw events as pass-throughs (`engine.ts`: "For lightweight engine, just
781
+ * pass through"), so a local run walks straight past this event without waiting,
782
+ * without correlating, and without populating its output. A green local ladder
783
+ * therefore proves the graph runs and the artifact validates — nothing about the
784
+ * message actually arriving. See Phase 5 in `docs/BPMN_COVERAGE_DESIGN.md`.
785
+ *
786
+ * The throw side (`Maestro.SendMessageEvent`) is deliberately NOT authored: its
787
+ * required `Reference` payload field cannot be expressed in a way the platform
788
+ * validator accepts. See `unsatisfiableRequiredInputs`.
789
+ *
790
+ * @example
791
+ * **Wait for another process to signal a shipment**
792
+ * ```ts
793
+ * bpmn('fulfil')
794
+ * .var('orderId', 'string', { default: 'ORD-1' })
795
+ * .var('shipment', 'object')
796
+ * .startEvent('start')
797
+ * .receiveMessage('await', {
798
+ * message: 'ShipmentReady',
799
+ * reference: '=js:vars.orderId',
800
+ * outputVar: 'shipment',
801
+ * })
802
+ * .endEvent('done')
803
+ * .sequenceFlow('start', 'await')
804
+ * .sequenceFlow('await', 'done')
805
+ * .build();
806
+ * ```
807
+ *
808
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
809
+ * @param opts - The message name, the correlation reference, and what to keep.
810
+ * @returns This builder, so calls chain.
811
+ */
812
+ receiveMessage(id, opts) {
813
+ return this.push({
814
+ kind: 'typed',
815
+ id,
816
+ name: opts.name,
817
+ type: 'Maestro.ReceiveMessageEvent',
818
+ context: { name: opts.message },
819
+ inputs: { Reference: opts.reference },
820
+ outputVar: opts.outputVar,
821
+ skipCondition: opts.skipCondition,
822
+ tags: opts.tags,
823
+ retry: opts.retry,
824
+ errorMapping: opts.errorMapping,
825
+ loop: opts.loop,
826
+ });
827
+ }
828
+ /**
829
+ * Wait mid-flow for an Integration Service connector event
830
+ * (`Intsvc.WaitForEvent` on a `bpmn:receiveTask`).
831
+ *
832
+ * @remarks
833
+ * The sibling of {@link ScopeBuilder.eventTrigger}: one connector operation, two
834
+ * placements. This one pauses a RUNNING process; `.eventTrigger()` starts one.
835
+ * The platform ships both for the same operation and only the library namespace
836
+ * differs, which is the same pairing Flow models as `waitForEvent()` / `onEvent()`.
837
+ *
838
+ * **This family DOES have an offline behavioural assertion**, unlike the other
839
+ * wait-shaped nodes: the engine mocks a receive task like any other, so
840
+ * `--mock-data` supplies a payload and a gateway branches on it. Map the field
841
+ * with `outputs` — the type's own `source="."` row is not resolved locally.
842
+ *
843
+ * @example
844
+ * **Wait for an email, then branch on it**
845
+ * ```ts
846
+ * bpmn('intake')
847
+ * .binding('outlook', { value: 'conn-uuid', resource: 'Connection', propertyAttribute: 'ConnectionId' })
848
+ * .binding('shared', { value: 'folder-key', resource: 'Connection', propertyAttribute: 'folderKey' })
849
+ * .var('subject', 'string', { default: '' })
850
+ * .startEvent('start')
851
+ * .waitForEvent('mail', 'uipath-microsoft-outlook365', 'email-received', {
852
+ * connection: 'outlook',
853
+ * folder: 'shared',
854
+ * outputs: { subject: '=subject' },
855
+ * })
856
+ * .endEvent('done')
857
+ * .sequenceFlow('start', 'mail')
858
+ * .sequenceFlow('mail', 'done')
859
+ * .build();
860
+ * ```
861
+ *
862
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
863
+ * @param connector - The connector key, e.g. `'uipath-microsoft-outlook365'`.
864
+ * @param event - The event name, e.g. `'email-received'`.
865
+ * @param opts - Connection/folder bindings, the `where` scope, and what to keep.
866
+ * @returns This builder, so calls chain.
867
+ */
868
+ waitForEvent(id, connector, event, opts) {
869
+ return this.push({
870
+ kind: 'connectorEvent',
871
+ id,
872
+ name: opts.name,
873
+ subscribe: 'event',
874
+ key: connector,
875
+ eventName: event,
876
+ version: opts.version,
877
+ connection: opts.connection,
878
+ folder: opts.folder,
879
+ folderPath: opts.folderPath,
880
+ filter: opts.filter,
881
+ parameters: opts.parameters,
882
+ outputVar: opts.outputVar,
883
+ outputs: opts.outputs,
884
+ skipCondition: opts.skipCondition,
885
+ tags: opts.tags,
886
+ retry: opts.retry,
887
+ errorMapping: opts.errorMapping,
888
+ loop: opts.loop,
889
+ });
890
+ }
891
+ /**
892
+ * START the process when an Integration Service connector event fires
893
+ * (`Intsvc.EventTrigger` on a `bpmn:startEvent` carrying a message event
894
+ * definition).
895
+ *
896
+ * @remarks
897
+ * Its own method rather than a flag on `.startEvent()` — the opposite call from
898
+ * the timer trigger, and for a stated reason. `Intsvc.TimerTrigger` declares NO
899
+ * fields, so the only thing to configure there is an output variable and a flag
900
+ * suffices. This type declares nine, including the connection and folder
901
+ * bindings, which is a method's worth of surface.
902
+ *
903
+ * **No offline behavioural assertion**, unlike `.waitForEvent()`: this is a start
904
+ * event, and the engine applies a start event's output rows against the variable
905
+ * scope rather than a mocked result, so nothing populates the payload locally.
906
+ *
907
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
908
+ * @param connector - The connector key, e.g. `'uipath-http-webhook'`.
909
+ * @param event - The event name, e.g. `'http-webhook'`.
910
+ * @param opts - Connection/folder bindings, the `where` scope, and what to keep.
911
+ * @returns This builder, so calls chain.
912
+ */
913
+ eventTrigger(id, connector, event, opts) {
914
+ return this.push({
915
+ kind: 'connectorEvent',
916
+ id,
917
+ name: opts.name,
918
+ subscribe: 'trigger',
919
+ key: connector,
920
+ eventName: event,
921
+ version: opts.version,
922
+ connection: opts.connection,
923
+ folder: opts.folder,
924
+ folderPath: opts.folderPath,
925
+ filter: opts.filter,
926
+ parameters: opts.parameters,
927
+ outputVar: opts.outputVar,
928
+ outputs: opts.outputs,
929
+ skipCondition: opts.skipCondition,
930
+ tags: opts.tags,
931
+ retry: opts.retry,
932
+ errorMapping: opts.errorMapping,
933
+ loop: opts.loop,
934
+ });
935
+ }
936
+ /**
937
+ * Invoke an external agent through an Integration Service connector
938
+ * (`Intsvc.SyncAgentExecution` / `Intsvc.AsyncAgentExecution`).
939
+ *
940
+ * @remarks
941
+ * `mode: 'sync'` waits for the agent's answer; `'async'` starts it and moves on.
942
+ * The descriptor must be an `AgentExecution` operation — the library is the
943
+ * authority, and a plain connector action is refused rather than emitted as a node
944
+ * the validator accepts and the runtime dispatches to the wrong handler.
945
+ *
946
+ * **The shipped library contains very few of these** (2 agent operations, 1
947
+ * workflow), so this is a narrow surface by nature, not by omission.
948
+ *
949
+ * @example
950
+ * ```ts
951
+ * bpmn('triage')
952
+ * .binding('vertex', { value: 'conn-uuid', resource: 'Connection', propertyAttribute: 'ConnectionId' })
953
+ * .binding('shared', { value: 'folder-key', resource: 'Connection', propertyAttribute: 'folderKey' })
954
+ * .startEvent('start')
955
+ * .externalAgent('summarise', 'uipath-google-vertex', 'execute-google-vertex-agent',
956
+ * { prompt: 'Summarise the ticket' },
957
+ * { mode: 'sync', connection: 'vertex', folder: 'shared' })
958
+ * .endEvent('done')
959
+ * .sequenceFlow('start', 'summarise')
960
+ * .sequenceFlow('summarise', 'done')
961
+ * .build();
962
+ * ```
963
+ *
964
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
965
+ * @param connector - The connector key.
966
+ * @param action - The `AgentExecution` operation name.
967
+ * @param inputs - The invocation payload.
968
+ * @param opts - `mode`, the connection/folder bindings, and what to keep.
969
+ * @returns This builder, so calls chain.
970
+ */
971
+ externalAgent(id, connector, action, inputs, opts) {
972
+ return this.externalTask('agent', id, connector, action, inputs, opts);
973
+ }
974
+ /**
975
+ * Invoke an external workflow through an Integration Service connector
976
+ * (`Intsvc.SyncWorkflowExecution` / `Intsvc.AsyncWorkflowExecution`).
977
+ *
978
+ * @remarks
979
+ * The workflow counterpart of {@link ScopeBuilder.externalAgent}; the descriptor
980
+ * must be a `ProcessExecution` operation.
981
+ *
982
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
983
+ * @param connector - The connector key.
984
+ * @param action - The `ProcessExecution` operation name.
985
+ * @param inputs - The invocation payload.
986
+ * @param opts - `mode`, the connection/folder bindings, and what to keep.
987
+ * @returns This builder, so calls chain.
988
+ */
989
+ externalWorkflow(id, connector, action, inputs, opts) {
990
+ return this.externalTask('workflow', id, connector, action, inputs, opts);
991
+ }
992
+ /** @internal Shared body of the two external-invocation methods. */
993
+ externalTask(family, id, connector, action, inputs, opts) {
994
+ return this.push({
995
+ kind: 'externalTask',
996
+ id,
997
+ name: opts.name,
998
+ family,
999
+ mode: opts.mode,
1000
+ key: connector,
1001
+ action,
1002
+ version: opts.version,
1003
+ inputs,
1004
+ connection: opts.connection,
1005
+ folder: opts.folder,
1006
+ outputVar: opts.outputVar,
1007
+ outputs: opts.outputs,
1008
+ skipCondition: opts.skipCondition,
1009
+ tags: opts.tags,
1010
+ retry: opts.retry,
1011
+ errorMapping: opts.errorMapping,
1012
+ loop: opts.loop,
1013
+ });
1014
+ }
1015
+ /**
1016
+ * ANY registry-backed node, by extension type — the generic form the typed
1017
+ * methods are sugar over.
1018
+ *
1019
+ * @remarks
1020
+ * Reach for a typed method first (`.http()`, `.startProcess()`, `.humanTask()`,
1021
+ * …): it names the fields, defaults what the registry defaults, and declares the
1022
+ * bindings a type demands. This is for the types that have no wrapper yet, and
1023
+ * it is what `bpmn-decompile` emits for them — which is why it exists at all.
1024
+ * Nothing is validated for you beyond what the registry states, so the emitted
1025
+ * node is exactly as correct as the `context` you pass.
1026
+ *
1027
+ * The element, tag, input shape, output row and event definition all come from
1028
+ * the committed registry snapshot, so the wire form is right even when the
1029
+ * ergonomics are bare.
1030
+ *
1031
+ * @example
1032
+ * ```ts
1033
+ * bpmn('agentic')
1034
+ * .startEvent('start')
1035
+ * .activity('call', 'A2A.AgentExecution', { inputs: { prompt: 'summarise' } })
1036
+ * .endEvent('done')
1037
+ * .sequenceFlow('start', 'call')
1038
+ * .sequenceFlow('call', 'done')
1039
+ * .build();
1040
+ * ```
1041
+ *
1042
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
1043
+ * @param type - The registry extension type, e.g. `'A2A.AgentExecution'`.
1044
+ * @param opts - Context values, payload, output mapping and the shared activity options.
1045
+ * @returns This builder, so calls chain.
1046
+ */
1047
+ activity(id, type, opts = {}) {
1048
+ return this.push({
1049
+ kind: 'typed',
1050
+ id,
1051
+ name: opts.name,
1052
+ type,
1053
+ context: opts.context ?? {},
1054
+ contextRows: opts.contextRows,
1055
+ inputs: opts.inputs ?? {},
1056
+ typeVersion: opts.typeVersion,
1057
+ inputSchema: opts.inputSchema,
1058
+ outputVar: opts.outputVar,
1059
+ outputs: opts.outputs,
1060
+ outputRows: opts.outputRows,
1061
+ skipCondition: opts.skipCondition,
1062
+ contextVerbatim: opts.contextVerbatim,
1063
+ tags: opts.tags,
1064
+ retry: opts.retry,
1065
+ errorMapping: opts.errorMapping,
1066
+ loop: opts.loop,
1067
+ });
1068
+ }
1069
+ // ── Sub-process (nested graph) ──
1070
+ /**
1071
+ * A sub-process — a scope of its own, with its own elements and flows (`bpmn:subProcess`).
1072
+ *
1073
+ * @param id - The element id, unique in the process and used by `sequenceFlow`.
1074
+ * @param fn - Receives a sub-builder for the elements inside it.
1075
+ * @param opts - `name`, and the loop characteristics when it repeats.
1076
+ * @returns This builder, so calls chain.
1077
+ */
1078
+ subProcess(id, fn, opts = {}) {
1079
+ const sb = new SubProcessBuilder(this._defs, this._bindingRegistry);
1080
+ fn(sb);
1081
+ return this.push(sb._buildNode(id, opts));
1082
+ }
1083
+ // ── Flows ──
1084
+ /**
1085
+ * A sequence flow from `source` to `target` (1-1 with `bpmn:sequenceFlow`).
1086
+ *
1087
+ * @param source - The id of the element the flow leaves.
1088
+ * @param target - The id of the element it arrives at.
1089
+ * @param opts - `name`, and the `condition` guarding it.
1090
+ * @returns This builder, so calls chain.
1091
+ */
1092
+ sequenceFlow(source, target, opts = {}) {
1093
+ this._flows.push({
1094
+ id: opts.id ?? `Flow_${source}_${target}`,
1095
+ source,
1096
+ target,
1097
+ name: opts.name,
1098
+ condition: opts.condition,
1099
+ });
1100
+ return this;
1101
+ }
1102
+ // ── Variables ──
1103
+ /**
1104
+ * A mutable flow variable (`uipath:inputOutput`). Root unless `elementId` scopes it.
1105
+ *
1106
+ * @param id - The variable's name.
1107
+ * @param type - A `types.*` descriptor.
1108
+ * @param opts - `direction`, a `default`, and `elementId` to scope it to one element.
1109
+ * @returns This builder, so calls chain.
1110
+ */
1111
+ var(id, type, opts = {}) {
1112
+ return this.declareVar(id, type, 'inputOutput', opts);
1113
+ }
1114
+ /**
1115
+ * A read-only entry input (`uipath:input`).
1116
+ *
1117
+ * @param id - The input's name.
1118
+ * @param type - A `types.*` descriptor.
1119
+ * @param opts - A `default`, and `elementId` to scope it to one element.
1120
+ * @returns This builder, so calls chain.
1121
+ */
1122
+ input(id, type, opts = {}) {
1123
+ return this.declareVar(id, type, 'input', opts);
1124
+ }
1125
+ /**
1126
+ * A return value (`uipath:output`).
1127
+ *
1128
+ * @param id - The output's name.
1129
+ * @param type - A `types.*` descriptor.
1130
+ * @param opts - A `default`, and `elementId` to scope it to one element.
1131
+ * @returns This builder, so calls chain.
1132
+ */
1133
+ output(id, type, opts = {}) {
1134
+ return this.declareVar(id, type, 'output', opts);
1135
+ }
1136
+ declareVar(id, type, direction, opts) {
1137
+ this._vars.push({
1138
+ id,
1139
+ name: opts.name ?? id,
1140
+ type,
1141
+ direction,
1142
+ default: opts.default,
1143
+ elementId: opts.elementId,
1144
+ schema: opts.schema,
1145
+ required: opts.required,
1146
+ });
1147
+ return this;
1148
+ }
1149
+ }
1150
+ /**
1151
+ * The variables a connector task's output rows land in.
1152
+ *
1153
+ * Per-node by default, because a `uipath:output`'s `var` IS the variable
1154
+ * declaration as far as the platform is concerned (its canvas model maps every
1155
+ * node output to a variable keyed by `var`). A shared name would therefore mean
1156
+ * two connectors writing one variable, with the second silently clobbering the
1157
+ * first — and it is the reason both the serializer and the expression check need
1158
+ * the same answer, hence one function.
1159
+ *
1160
+ * @param n - The connector node.
1161
+ * @returns The response and error variable names.
1162
+ *
1163
+ * @internal
1164
+ */
1165
+ export function connectorOutputVars(n) {
1166
+ return { response: n.outputVar ?? `${n.id}_response`, error: `${n.id}_Error` };
1167
+ }
1168
+ /**
1169
+ * The variables a typed node writes — the ONE definition of that set.
1170
+ *
1171
+ * It had grown three: this one (what an expression may read), `implicitNodeVars` in
1172
+ * serialize (what gets declared in `uipath:variables`), and `derivedVariableIds` in
1173
+ * decompile (what a decompiled file must NOT re-declare). All three must agree, and
1174
+ * adding `outputRows` proved they do not stay agreed on their own: two were updated,
1175
+ * this one was missed, and a human task's mapped output stopped being visible to the
1176
+ * expression checker — `vars.decision` reported undeclared for a variable the node
1177
+ * plainly writes. So serialize now calls this instead of repeating it, and only
1178
+ * decompile's copy is separate, which its own comment already flags as load-bearing.
1179
+ *
1180
+ * @param n - The typed node.
1181
+ * @returns The variable ids its output rows write.
1182
+ * @internal
1183
+ */
1184
+ export function typedNodeDeclaredVars(n) {
1185
+ // Spelled-out rows REPLACE the derived pair, so they are the whole answer.
1186
+ if (n.outputRows)
1187
+ return n.outputRows.map((r) => r.var).filter((v) => v !== undefined);
1188
+ const v = typedNodeOutputVar(n.id, n.type, n.outputVar);
1189
+ return [...(v ? [v] : []), ...Object.keys(n.outputs ?? {})];
1190
+ }
1191
+ /**
1192
+ * Every variable name a node DECLARES by writing to it — the platform counts a
1193
+ * node's output `var` as a declaration, so a downstream `=vars.<name>` resolves
1194
+ * against it without any `uipath:variables` entry. The expression check mirrors
1195
+ * that, or it would reject reads the platform accepts.
1196
+ *
1197
+ * @param n - The node to inspect.
1198
+ * @returns The variable names it declares, if any.
1199
+ *
1200
+ * @internal
1201
+ */
1202
+ export function nodeDeclaredVars(n) {
1203
+ if (n.kind === 'connector') {
1204
+ const { response, error } = connectorOutputVars(n);
1205
+ return [response, error];
1206
+ }
1207
+ if (n.kind === 'typed')
1208
+ return typedNodeDeclaredVars(n);
1209
+ if (n.kind === 'scriptTask')
1210
+ return Object.keys(n.outputs);
1211
+ if (n.kind === 'task')
1212
+ return Object.keys(n.set);
1213
+ return [];
1214
+ }
1215
+ /** A sub-process body: the same graph methods, plus an internal node builder. */
1216
+ export class SubProcessBuilder extends ScopeBuilder {
1217
+ /** @internal */
1218
+ _buildNode(id, opts) {
1219
+ return {
1220
+ kind: 'subProcess',
1221
+ id,
1222
+ name: opts.name,
1223
+ payload: opts.payload,
1224
+ triggeredByEvent: opts.triggeredByEvent,
1225
+ tags: opts.tags,
1226
+ loop: opts.loop,
1227
+ retry: opts.retry,
1228
+ errorMapping: opts.errorMapping,
1229
+ nodes: this._nodes,
1230
+ flows: this._flows,
1231
+ variables: this._vars,
1232
+ };
1233
+ }
1234
+ }
1235
+ // ─── Top-level process builder ────────────────────────────────────────────────
1236
+ /**
1237
+ * The top-level process builder `bpmn()` returns — every graph method of the
1238
+ * shared scope (events, gateways, tasks, sub-processes, flows, variables) plus
1239
+ * the process's `.name()` and the `.build()` that finishes it.
1240
+ */
1241
+ export class BpmnBuilder extends ScopeBuilder {
1242
+ _id;
1243
+ _name;
1244
+ _metadata;
1245
+ /**
1246
+ * @param _id - The process id, as passed to `bpmn()`.
1247
+ * @returns The top-level builder.
1248
+ */
1249
+ constructor(_id) {
1250
+ super(new DefinitionsRegistry(), new BindingsRegistry());
1251
+ this._id = _id;
1252
+ }
1253
+ /**
1254
+ * Set the process's display name.
1255
+ *
1256
+ * @param n - The name the designer shows.
1257
+ * @returns This builder, so calls chain.
1258
+ */
1259
+ name(n) {
1260
+ this._name = n;
1261
+ return this;
1262
+ }
1263
+ /**
1264
+ * Process-level metadata — see {@link ProcessMetadata}.
1265
+ *
1266
+ * Repeated calls MERGE rather than replace, so a caller can set one field without
1267
+ * knowing what an earlier call set.
1268
+ *
1269
+ * @param meta - The fields to declare; anything omitted stays absent.
1270
+ * @returns This builder, so calls chain.
1271
+ *
1272
+ * @example
1273
+ * ```ts
1274
+ * bpmn('order').metadata({ migrationVersion: '11' })
1275
+ * ```
1276
+ */
1277
+ metadata(meta) {
1278
+ this._metadata = { ...this._metadata, ...meta };
1279
+ return this;
1280
+ }
1281
+ /**
1282
+ * Declare the Canvas SCHEMA generation this document targets
1283
+ * (`uipath:migrationVersion`).
1284
+ *
1285
+ * @remarks
1286
+ * The same field {@link ProcessMetadata.migrationVersion} sets, under the name
1287
+ * the Flow and Case builders use for the same concept — a document's schema
1288
+ * version, as opposed to anything about the process's own release. Both
1289
+ * spellings stay valid; the later call wins, as with any `.metadata()` field.
1290
+ *
1291
+ * Unlike Flow's file format and Case's grammar, this one does not change what
1292
+ * the serializer emits: the `.bpmn` is the same XML whatever generation is
1293
+ * stamped on it, so any non-empty value is accepted and carried through
1294
+ * verbatim. An empty one is refused, because an empty
1295
+ * `uipath:migrationVersion` fails product validation — absent beats blank.
1296
+ *
1297
+ * `bpmn-decompile` emits this call with the stamp it read, which is what keeps
1298
+ * a decompile → edit → recompile from dropping a generation Studio Web wrote
1299
+ * and some graders require.
1300
+ *
1301
+ * @example
1302
+ * ```ts
1303
+ * bpmn('order').schemaVersion('11.5')
1304
+ * ```
1305
+ *
1306
+ * @param version - The Canvas schema generation, e.g. `'11'` or `'11.5'`.
1307
+ * @returns This builder, so calls chain.
1308
+ * @throws Error when `version` is empty.
1309
+ */
1310
+ schemaVersion(version) {
1311
+ const refusal = schemaVersionRefusal(BPMN_SCHEMA_VERSIONS, version);
1312
+ if (refusal)
1313
+ throw new Error(refusal);
1314
+ this._metadata = { ...this._metadata, migrationVersion: version };
1315
+ return this;
1316
+ }
1317
+ /** Named schemas declared with `.schema()`, resolved when a row references one. */
1318
+ _schemas = new Map();
1319
+ /**
1320
+ * Name a JSON Schema once so several variables can share it.
1321
+ *
1322
+ * @remarks
1323
+ * Purely a source-level convenience: there is no shared-schema concept on the
1324
+ * wire, so every reference expands back to the same inline body and the emitted
1325
+ * XML is byte-identical either way. What it saves is the source — a real process
1326
+ * repeats a handful of shapes across dozens of variables, and one export carried
1327
+ * 57 schema bodies of which only 13 were distinct.
1328
+ *
1329
+ * `schema: errorSchema` covers the platform's own error envelope, which needs no
1330
+ * declaration at all; this is for the shapes that belong to a particular process.
1331
+ *
1332
+ * @param id - The name to reference it by, in `schema: id`.
1333
+ * @param schema - The JSON Schema, as a value rather than a string.
1334
+ * @returns This builder, so calls chain.
1335
+ *
1336
+ * @example
1337
+ * ```ts
1338
+ * bpmn('orders')
1339
+ * .schema('queueItem', { type: 'object', properties: { Id: { type: 'integer' } } })
1340
+ * .var('response1', 'jsonSchema', { name: 'response', schema: 'queueItem' })
1341
+ * .var('response2', 'jsonSchema', { name: 'response', schema: 'queueItem' })
1342
+ * ```
1343
+ */
1344
+ schema(id, schema) {
1345
+ this._schemas.set(id, schema);
1346
+ return this;
1347
+ }
1348
+ /**
1349
+ * Declare an external identifier the process needs supplied — a base URL, a
1350
+ * folder path, a process name (`uipath:binding`). Expressions read it as
1351
+ * `=bindings.<id>`, and an offline run resolves it to `value`.
1352
+ *
1353
+ * @remarks
1354
+ * A connector's `connection`/`folder` already produce their own bindings at
1355
+ * serialize time; this is for everything else, and it is what lets a value be
1356
+ * configured per environment rather than baked into the process. Declaring the
1357
+ * same id twice keeps the FIRST declaration.
1358
+ *
1359
+ * @example
1360
+ * ```ts
1361
+ * bpmn('sync')
1362
+ * .binding('apiBase', { name: 'API base URL', value: 'https://api.example.com' })
1363
+ * .var('url', 'string')
1364
+ * .startEvent('start')
1365
+ * .task('resolve', { set: { url: '=js:bindings.apiBase + "/v1/orders"' } })
1366
+ * ```
1367
+ *
1368
+ * @param id - The binding's id — what `=bindings.<id>` refers to.
1369
+ * @param opts - Display `name`, the `value` it defaults to, and which resource it addresses.
1370
+ * @returns This builder, so calls chain.
1371
+ */
1372
+ binding(id, opts = {}) {
1373
+ this._bindingRegistry.declare({
1374
+ id,
1375
+ name: opts.name ?? id,
1376
+ resource: opts.resource ?? 'custom',
1377
+ propertyAttribute: opts.propertyAttribute ?? 'value',
1378
+ resourceSubType: opts.resourceSubType,
1379
+ default: opts.value,
1380
+ resourceKey: opts.resourceKey ?? opts.value,
1381
+ });
1382
+ return this;
1383
+ }
1384
+ /**
1385
+ * Finish the process and return the graph the serializer turns into XML.
1386
+ *
1387
+ * @returns The built process — its elements, flows and variables.
1388
+ */
1389
+ build() {
1390
+ const built = {
1391
+ id: this._id,
1392
+ name: this._name,
1393
+ metadata: this._metadata,
1394
+ variables: this._vars,
1395
+ messages: this._defs.messages,
1396
+ errors: this._defs.errors,
1397
+ bindings: this._bindingRegistry.bindings,
1398
+ nodes: this._nodes,
1399
+ flows: this._flows,
1400
+ schemas: this._schemas,
1401
+ };
1402
+ // First-level validation, intrinsic to running the SDK: catch a reference to
1403
+ // an undeclared `vars.<name>` HERE — the one thing `uip maestro bpmn validate`
1404
+ // does not check — before the `.bpmn` is serialized. See bpmn-expr-check.
1405
+ const errors = checkBpmnExpressions(built).filter((d) => d.level === 'error');
1406
+ if (errors.length)
1407
+ throw new BpmnBuildError(built, errors);
1408
+ return built;
1409
+ }
1410
+ }
1411
+ /**
1412
+ * The `Error` variable's shape — the platform's own error envelope.
1413
+ *
1414
+ * Every node that can fail writes one, so a real process declares this schema once
1415
+ * per node and an imported one repeats it verbatim: 243 byte-identical copies across
1416
+ * five unrelated customer exports, 27 in a single 71-element process.
1417
+ *
1418
+ * It is a contract, not a convention. `V2EndEventBody.BuildErrorCoreResult` in
1419
+ * PO.BpmnEngine builds it — *"the ErrorEndEvent core result with one `Error` entry
1420
+ * and the six expected fields"* — as `code`, `message`, `detail`, `category`,
1421
+ * `status`, `element`, with `status` an HTTP status code and the rest strings.
1422
+ *
1423
+ * @example
1424
+ * ```ts
1425
+ * bpmn('p').var('error1', 'jsonSchema', { name: 'Error', schema: errorSchema })
1426
+ * ```
1427
+ *
1428
+ * @see errorSchemaWithResponse
1429
+ */
1430
+ export const errorSchema = {
1431
+ type: 'object',
1432
+ properties: {
1433
+ code: { type: 'string' },
1434
+ message: { type: 'string' },
1435
+ detail: { type: 'string' },
1436
+ category: { type: 'string' },
1437
+ status: { type: 'number' },
1438
+ element: { type: 'string' },
1439
+ },
1440
+ };
1441
+ /**
1442
+ * The same envelope plus `response` — what a node whose failure carries a payload
1443
+ * writes, and what the same source calls *"the generic failure mapping shape, which
1444
+ * includes a `response` key"*, distinguishing it from {@link errorSchema}.
1445
+ *
1446
+ * 42 copies across the corpus, against 243 of the six-field form.
1447
+ */
1448
+ export const errorSchemaWithResponse = {
1449
+ type: 'object',
1450
+ properties: {
1451
+ ...errorSchema.properties,
1452
+ response: { type: 'string' },
1453
+ },
1454
+ };
1455
+ /**
1456
+ * Start building a BPMN process with the given id.
1457
+ *
1458
+ * @example
1459
+ * **A minimal start → task → end process**
1460
+ * ```ts
1461
+ * export default bpmn('intake')
1462
+ * .startEvent('start')
1463
+ * .var('note', 'string', { default: '' })
1464
+ * .task('assign', { set: { note: 'hello' } })
1465
+ * .endEvent('done')
1466
+ * .sequenceFlow('start', 'assign')
1467
+ * .sequenceFlow('assign', 'done')
1468
+ * .build();
1469
+ * ```
1470
+ *
1471
+ * @param id - The process id.
1472
+ * @returns A {@link BpmnBuilder} to add elements to.
1473
+ */
1474
+ export function bpmn(id) {
1475
+ return new BpmnBuilder(id);
1476
+ }