@bpmnkit/plugins 0.0.8

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 (140) hide show
  1. package/README.md +153 -0
  2. package/dist/ai-bridge/css.d.ts +2 -0
  3. package/dist/ai-bridge/css.js +366 -0
  4. package/dist/ai-bridge/index.d.ts +44 -0
  5. package/dist/ai-bridge/index.js +132 -0
  6. package/dist/ai-bridge/panel.d.ts +27 -0
  7. package/dist/ai-bridge/panel.js +715 -0
  8. package/dist/ascii-view/css.d.ts +4 -0
  9. package/dist/ascii-view/css.js +83 -0
  10. package/dist/ascii-view/index.d.ts +25 -0
  11. package/dist/ascii-view/index.js +92 -0
  12. package/dist/command-palette/css.d.ts +4 -0
  13. package/dist/command-palette/css.js +157 -0
  14. package/dist/command-palette/index.d.ts +53 -0
  15. package/dist/command-palette/index.js +378 -0
  16. package/dist/command-palette-editor/index.d.ts +35 -0
  17. package/dist/command-palette-editor/index.js +60 -0
  18. package/dist/config-panel/css.d.ts +4 -0
  19. package/dist/config-panel/css.js +719 -0
  20. package/dist/config-panel/index.d.ts +36 -0
  21. package/dist/config-panel/index.js +73 -0
  22. package/dist/config-panel/renderer.d.ts +127 -0
  23. package/dist/config-panel/renderer.js +1162 -0
  24. package/dist/config-panel/types.d.ts +118 -0
  25. package/dist/config-panel/types.js +2 -0
  26. package/dist/config-panel-bpmn/index.d.ts +58 -0
  27. package/dist/config-panel-bpmn/index.js +1320 -0
  28. package/dist/config-panel-bpmn/template-engine.d.ts +17 -0
  29. package/dist/config-panel-bpmn/template-engine.js +307 -0
  30. package/dist/config-panel-bpmn/template-to-service-task.d.ts +30 -0
  31. package/dist/config-panel-bpmn/template-to-service-task.js +92 -0
  32. package/dist/config-panel-bpmn/template-types.d.ts +126 -0
  33. package/dist/config-panel-bpmn/template-types.js +6 -0
  34. package/dist/config-panel-bpmn/templates/generated.d.ts +3 -0
  35. package/dist/config-panel-bpmn/templates/generated.js +57865 -0
  36. package/dist/config-panel-bpmn/util.d.ts +46 -0
  37. package/dist/config-panel-bpmn/util.js +195 -0
  38. package/dist/dmn-editor/css.d.ts +3 -0
  39. package/dist/dmn-editor/css.js +667 -0
  40. package/dist/dmn-editor/dmn-editor.d.ts +34 -0
  41. package/dist/dmn-editor/dmn-editor.js +646 -0
  42. package/dist/dmn-editor/drd-canvas.d.ts +85 -0
  43. package/dist/dmn-editor/drd-canvas.js +1087 -0
  44. package/dist/dmn-editor/index.d.ts +3 -0
  45. package/dist/dmn-editor/index.js +2 -0
  46. package/dist/dmn-viewer/css.d.ts +3 -0
  47. package/dist/dmn-viewer/css.js +151 -0
  48. package/dist/dmn-viewer/dmn-viewer.d.ts +30 -0
  49. package/dist/dmn-viewer/dmn-viewer.js +160 -0
  50. package/dist/dmn-viewer/feel.d.ts +3 -0
  51. package/dist/dmn-viewer/feel.js +2 -0
  52. package/dist/dmn-viewer/index.d.ts +14 -0
  53. package/dist/dmn-viewer/index.js +12 -0
  54. package/dist/element-docs/content.d.ts +20 -0
  55. package/dist/element-docs/content.js +1032 -0
  56. package/dist/element-docs/index.d.ts +7 -0
  57. package/dist/element-docs/index.js +643 -0
  58. package/dist/feel-playground/css.d.ts +4 -0
  59. package/dist/feel-playground/css.js +292 -0
  60. package/dist/feel-playground/index.d.ts +4 -0
  61. package/dist/feel-playground/index.js +3 -0
  62. package/dist/feel-playground/playground-plugin.d.ts +14 -0
  63. package/dist/feel-playground/playground-plugin.js +292 -0
  64. package/dist/form-editor/css.d.ts +3 -0
  65. package/dist/form-editor/css.js +703 -0
  66. package/dist/form-editor/form-editor.d.ts +48 -0
  67. package/dist/form-editor/form-editor.js +1039 -0
  68. package/dist/form-editor/index.d.ts +3 -0
  69. package/dist/form-editor/index.js +2 -0
  70. package/dist/form-viewer/css.d.ts +3 -0
  71. package/dist/form-viewer/css.js +303 -0
  72. package/dist/form-viewer/form-viewer.d.ts +29 -0
  73. package/dist/form-viewer/form-viewer.js +376 -0
  74. package/dist/form-viewer/index.d.ts +12 -0
  75. package/dist/form-viewer/index.js +11 -0
  76. package/dist/history/checkpoint.d.ts +10 -0
  77. package/dist/history/checkpoint.js +92 -0
  78. package/dist/history/css.d.ts +2 -0
  79. package/dist/history/css.js +130 -0
  80. package/dist/history/history-panel.d.ts +12 -0
  81. package/dist/history/history-panel.js +142 -0
  82. package/dist/history/index.d.ts +5 -0
  83. package/dist/history/index.js +3 -0
  84. package/dist/main-menu/css.d.ts +4 -0
  85. package/dist/main-menu/css.js +272 -0
  86. package/dist/main-menu/index.d.ts +59 -0
  87. package/dist/main-menu/index.js +285 -0
  88. package/dist/minimap/css.d.ts +10 -0
  89. package/dist/minimap/css.js +54 -0
  90. package/dist/minimap/index.d.ts +38 -0
  91. package/dist/minimap/index.js +69 -0
  92. package/dist/minimap/minimap.d.ts +35 -0
  93. package/dist/minimap/minimap.js +166 -0
  94. package/dist/optimize/index.d.ts +21 -0
  95. package/dist/optimize/index.js +304 -0
  96. package/dist/process-runner/css.d.ts +2 -0
  97. package/dist/process-runner/css.js +467 -0
  98. package/dist/process-runner/index.d.ts +56 -0
  99. package/dist/process-runner/index.js +549 -0
  100. package/dist/storage/auto-save.d.ts +11 -0
  101. package/dist/storage/auto-save.js +44 -0
  102. package/dist/storage/css.d.ts +2 -0
  103. package/dist/storage/css.js +2 -0
  104. package/dist/storage/db.d.ts +99 -0
  105. package/dist/storage/db.js +116 -0
  106. package/dist/storage/dialog.d.ts +15 -0
  107. package/dist/storage/dialog.js +201 -0
  108. package/dist/storage/export.d.ts +3 -0
  109. package/dist/storage/export.js +137 -0
  110. package/dist/storage/index.d.ts +44 -0
  111. package/dist/storage/index.js +201 -0
  112. package/dist/storage/sidebar.d.ts +2 -0
  113. package/dist/storage/sidebar.js +2 -0
  114. package/dist/storage/storage-api.d.ts +76 -0
  115. package/dist/storage/storage-api.js +342 -0
  116. package/dist/storage/types.d.ts +44 -0
  117. package/dist/storage/types.js +2 -0
  118. package/dist/storage-tabs-bridge/index.d.ts +96 -0
  119. package/dist/storage-tabs-bridge/index.js +502 -0
  120. package/dist/tabs/css.d.ts +3 -0
  121. package/dist/tabs/css.js +642 -0
  122. package/dist/tabs/file-resolver.d.ts +71 -0
  123. package/dist/tabs/file-resolver.js +46 -0
  124. package/dist/tabs/index.d.ts +15 -0
  125. package/dist/tabs/index.js +13 -0
  126. package/dist/tabs/tabs-plugin.d.ts +250 -0
  127. package/dist/tabs/tabs-plugin.js +1119 -0
  128. package/dist/token-highlight/css.d.ts +2 -0
  129. package/dist/token-highlight/css.js +81 -0
  130. package/dist/token-highlight/index.d.ts +44 -0
  131. package/dist/token-highlight/index.js +164 -0
  132. package/dist/watermark/css.d.ts +10 -0
  133. package/dist/watermark/css.js +54 -0
  134. package/dist/watermark/index.d.ts +48 -0
  135. package/dist/watermark/index.js +64 -0
  136. package/dist/zoom-controls/css.d.ts +4 -0
  137. package/dist/zoom-controls/css.js +48 -0
  138. package/dist/zoom-controls/index.d.ts +36 -0
  139. package/dist/zoom-controls/index.js +111 -0
  140. package/package.json +121 -0
@@ -0,0 +1,1320 @@
1
+ /**
2
+ * @bpmnkit/canvas-plugin-config-panel-bpmn — BPMN element schemas for the
3
+ * config panel plugin.
4
+ *
5
+ * Registers config panel schemas for all standard BPMN element types. For
6
+ * service tasks the panel is template-aware: when `zeebe:modelerTemplate` is
7
+ * set on an element the matching template's property form is shown instead of
8
+ * the generic connector selector.
9
+ *
10
+ * ## Usage
11
+ * ```typescript
12
+ * import { createConfigPanelPlugin } from "@bpmnkit/canvas-plugin-config-panel";
13
+ * import { createConfigPanelBpmnPlugin } from "@bpmnkit/canvas-plugin-config-panel-bpmn";
14
+ *
15
+ * let editorRef: BpmnEditor | null = null;
16
+ * const configPanel = createConfigPanelPlugin({
17
+ * getDefinitions: () => editorRef?.getDefinitions() ?? null,
18
+ * applyChange: (fn) => { editorRef?.applyChange(fn); },
19
+ * });
20
+ * const configPanelBpmn = createConfigPanelBpmnPlugin(configPanel);
21
+ * const editor = new BpmnEditor({ container, xml, plugins: [configPanel, configPanelBpmn] });
22
+ * editorRef = editor;
23
+ * ```
24
+ *
25
+ * @packageDocumentation
26
+ */
27
+ import { zeebeExtensionsToXmlElements } from "@bpmnkit/core";
28
+ import { ELEMENT_TYPE_LABELS } from "@bpmnkit/editor";
29
+ import { buildRegistrationFromTemplate } from "./template-engine.js";
30
+ import { CAMUNDA_CONNECTOR_TEMPLATES } from "./templates/generated.js";
31
+ export { CAMUNDA_CONNECTOR_TEMPLATES } from "./templates/generated.js";
32
+ export { templateToServiceTaskOptions } from "./template-to-service-task.js";
33
+ import { buildPropertiesWithExampleOutput, findFlowElement, findSequenceFlow, getExampleOutputJson, getIoInput, getTaskHeader, parseCalledElement, parseZeebeError, parseZeebeEscalation, parseZeebeExtensions, parseZeebeMessage, parseZeebeScript, parseZeebeSignal, updateFlowElement, updateSequenceFlow, xmlLocalName, } from "./util.js";
34
+ /** Validates that a field value is valid JSON, or returns an error message. */
35
+ function validateJson(value) {
36
+ if (typeof value !== "string" || value.trim() === "")
37
+ return null;
38
+ try {
39
+ JSON.parse(value);
40
+ return null;
41
+ }
42
+ catch (e) {
43
+ return e instanceof SyntaxError ? e.message : "Invalid JSON";
44
+ }
45
+ }
46
+ // ── Built-in template registry ────────────────────────────────────────────────
47
+ /**
48
+ * All built-in Camunda connector templates, keyed by template id.
49
+ * Pre-built so that reference-equality comparisons in the renderer work.
50
+ */
51
+ const TEMPLATE_REGISTRY = new Map();
52
+ /** Templates applicable to service tasks. */
53
+ const SERVICE_TASK_TEMPLATES = CAMUNDA_CONNECTOR_TEMPLATES.filter((t) => t.appliesTo.includes("bpmn:ServiceTask") || t.appliesTo.includes("bpmn:Task"));
54
+ /** Extract the fixed task definition type from a template's Hidden binding. */
55
+ function extractTaskType(t) {
56
+ for (const p of t.properties) {
57
+ if (typeof p.value !== "string")
58
+ continue;
59
+ if ((p.binding.type === "zeebe:taskDefinition" &&
60
+ "property" in p.binding &&
61
+ p.binding.property === "type") ||
62
+ p.binding.type === "zeebe:taskDefinition:type") {
63
+ return p.value;
64
+ }
65
+ }
66
+ return undefined;
67
+ }
68
+ // Register all Camunda connector templates
69
+ for (const tpl of CAMUNDA_CONNECTOR_TEMPLATES) {
70
+ TEMPLATE_REGISTRY.set(tpl.id, buildRegistrationFromTemplate(tpl));
71
+ }
72
+ /**
73
+ * Task definition type → template id mapping (first-wins; used for
74
+ * backward-compat detection in `read` when `zeebe:modelerTemplate` is absent).
75
+ */
76
+ const TASK_TYPE_TO_TEMPLATE_ID = new Map();
77
+ for (const tpl of SERVICE_TASK_TEMPLATES) {
78
+ const taskType = extractTaskType(tpl);
79
+ if (taskType && !TASK_TYPE_TO_TEMPLATE_ID.has(taskType)) {
80
+ TASK_TYPE_TO_TEMPLATE_ID.set(taskType, tpl.id);
81
+ }
82
+ }
83
+ // ── General schema (all flow element types) ───────────────────────────────────
84
+ const GENERAL_SCHEMA = {
85
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Element name" }],
86
+ groups: [
87
+ {
88
+ id: "general",
89
+ label: "General",
90
+ fields: [
91
+ { key: "name", label: "Name", type: "text", placeholder: "Element name" },
92
+ {
93
+ key: "documentation",
94
+ label: "Documentation",
95
+ type: "textarea",
96
+ placeholder: "Add notes or documentation for this element…",
97
+ },
98
+ ],
99
+ },
100
+ ],
101
+ };
102
+ const GENERAL_ADAPTER = {
103
+ read(defs, id) {
104
+ const el = findFlowElement(defs, id);
105
+ if (!el)
106
+ return {};
107
+ return {
108
+ name: el.name ?? "",
109
+ documentation: el.documentation ?? "",
110
+ };
111
+ },
112
+ write(defs, id, values) {
113
+ return updateFlowElement(defs, id, (el) => ({
114
+ ...el,
115
+ name: typeof values.name === "string" ? values.name : el.name,
116
+ documentation: typeof values.documentation === "string"
117
+ ? values.documentation || undefined
118
+ : el.documentation,
119
+ }));
120
+ },
121
+ };
122
+ // ── Service task schema (generic — shown when no template is applied) ─────────
123
+ const CUSTOM_TASK_TYPE = "";
124
+ const IS_CUSTOM = (values) => values.connector === CUSTOM_TASK_TYPE;
125
+ /**
126
+ * Connector selector options keyed by template id (not task type) so each of
127
+ * the 116+ connectors gets its own entry, even when multiple share a task type.
128
+ */
129
+ const CONNECTOR_OPTIONS = [
130
+ { value: CUSTOM_TASK_TYPE, label: "Custom (no connector)" },
131
+ ...SERVICE_TASK_TEMPLATES.flatMap((t) => extractTaskType(t) ? [{ value: t.id, label: t.name }] : []).sort((a, b) => a.label.localeCompare(b.label)),
132
+ ];
133
+ const GENERIC_SERVICE_TASK_SCHEMA = {
134
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Task name" }],
135
+ groups: [
136
+ {
137
+ id: "general",
138
+ label: "General",
139
+ fields: [
140
+ { key: "name", label: "Name", type: "text", placeholder: "Task name" },
141
+ {
142
+ key: "connector",
143
+ label: "Connector",
144
+ type: "select",
145
+ options: CONNECTOR_OPTIONS,
146
+ hint: "Select a Camunda connector or use a custom job worker type.",
147
+ },
148
+ {
149
+ key: "taskType",
150
+ label: "Task type",
151
+ type: "text",
152
+ placeholder: "e.g. my-worker-type",
153
+ hint: "Zeebe job type string consumed by your worker.",
154
+ condition: IS_CUSTOM,
155
+ required: true,
156
+ },
157
+ { key: "retries", label: "Retries", type: "text", placeholder: "3" },
158
+ {
159
+ key: "exampleOutputJson",
160
+ label: "Example output (JSON)",
161
+ type: "textarea",
162
+ placeholder: '{"myVariable": "value"}',
163
+ hint: "Mock output written to process variables in play mode when no job worker is registered.",
164
+ validate: validateJson,
165
+ },
166
+ {
167
+ key: "documentation",
168
+ label: "Documentation",
169
+ type: "textarea",
170
+ placeholder: "Add notes or documentation…",
171
+ },
172
+ ],
173
+ },
174
+ ],
175
+ };
176
+ const SERVICE_TASK_ADAPTER = {
177
+ read(defs, id) {
178
+ const el = findFlowElement(defs, id);
179
+ if (!el)
180
+ return {};
181
+ const ext = parseZeebeExtensions(el.extensionElements);
182
+ const definitionType = ext.taskDefinition?.type ?? "";
183
+ // Detect template via explicit attribute OR by known task type (backward-compat)
184
+ const hasTemplate = Boolean(el.unknownAttributes?.["zeebe:modelerTemplate"]) ||
185
+ TASK_TYPE_TO_TEMPLATE_ID.has(definitionType);
186
+ // Connector selector value = the task definition type when template is active
187
+ const connector = hasTemplate ? definitionType : CUSTOM_TASK_TYPE;
188
+ return {
189
+ name: el.name ?? "",
190
+ documentation: el.documentation ?? "",
191
+ connector,
192
+ taskType: connector === CUSTOM_TASK_TYPE ? definitionType : "",
193
+ retries: ext.taskDefinition?.retries ?? "",
194
+ exampleOutputJson: getExampleOutputJson(ext),
195
+ };
196
+ },
197
+ write(defs, id, values) {
198
+ const connectorVal = strVal(values.connector);
199
+ const isCustom = connectorVal === CUSTOM_TASK_TYPE;
200
+ // connector is either a template id (new) or a task type (backward-compat read)
201
+ const newTemplateId = isCustom
202
+ ? undefined
203
+ : TEMPLATE_REGISTRY.has(connectorVal)
204
+ ? connectorVal
205
+ : TASK_TYPE_TO_TEMPLATE_ID.get(connectorVal);
206
+ if (newTemplateId) {
207
+ // Switching to (or already on) a template: stamp the attribute then delegate
208
+ // all field writing to the template adapter so it handles template-specific fields.
209
+ const withAttr = updateFlowElement(defs, id, (el) => ({
210
+ ...el,
211
+ name: typeof values.name === "string" ? values.name || undefined : el.name,
212
+ unknownAttributes: {
213
+ ...el.unknownAttributes,
214
+ "zeebe:modelerTemplate": newTemplateId,
215
+ },
216
+ }));
217
+ const templateReg = TEMPLATE_REGISTRY.get(newTemplateId);
218
+ if (templateReg)
219
+ return templateReg.adapter.write(withAttr, id, values);
220
+ return withAttr;
221
+ }
222
+ // Custom task or clearing a template
223
+ return updateFlowElement(defs, id, (el) => {
224
+ const name = typeof values.name === "string" ? values.name : el.name;
225
+ const documentation = typeof values.documentation === "string"
226
+ ? values.documentation || undefined
227
+ : el.documentation;
228
+ const taskType = strVal(values.taskType);
229
+ const retries = strVal(values.retries);
230
+ const exampleOutputJson = strVal(values.exampleOutputJson);
231
+ const currentExt = parseZeebeExtensions(el.extensionElements);
232
+ const newProperties = buildPropertiesWithExampleOutput(currentExt, exampleOutputJson);
233
+ const ZEEBE_EXTS = new Set(["taskDefinition", "ioMapping", "taskHeaders", "properties"]);
234
+ const otherExts = el.extensionElements.filter((x) => !ZEEBE_EXTS.has(xmlLocalName(x.name)));
235
+ const newZeebeExts = zeebeExtensionsToXmlElements({
236
+ taskDefinition: taskType ? { type: taskType, retries: retries || undefined } : undefined,
237
+ properties: newProperties,
238
+ });
239
+ // Remove modelerTemplate attribute when switching to custom
240
+ const { "zeebe:modelerTemplate": _t, "zeebe:modelerTemplateVersion": _v, ...rest } = el.unknownAttributes;
241
+ return {
242
+ ...el,
243
+ name,
244
+ documentation,
245
+ extensionElements: [...otherExts, ...newZeebeExts],
246
+ unknownAttributes: rest,
247
+ };
248
+ });
249
+ },
250
+ /**
251
+ * When `zeebe:modelerTemplate` is set on the element, switch to the
252
+ * matching template registration instead of the generic form.
253
+ */
254
+ resolve(defs, id) {
255
+ const el = findFlowElement(defs, id);
256
+ if (!el)
257
+ return null;
258
+ const templateId = el.unknownAttributes?.["zeebe:modelerTemplate"];
259
+ if (!templateId)
260
+ return null;
261
+ return TEMPLATE_REGISTRY.get(templateId) ?? null;
262
+ },
263
+ };
264
+ // ── Ad-hoc subprocess schema (template-aware, shown for adHocSubProcess) ──────
265
+ /** Templates applicable to ad-hoc subprocesses (AI agent pattern). */
266
+ const ADHOC_SUBPROCESS_TEMPLATES = CAMUNDA_CONNECTOR_TEMPLATES.filter((t) => t.appliesTo.includes("bpmn:SubProcess") || t.appliesTo.includes("bpmn:AdHocSubProcess"));
267
+ /** Connector selector for ad-hoc subprocess templates. */
268
+ const ADHOC_OPTIONS = [
269
+ { value: CUSTOM_TASK_TYPE, label: "Custom (no connector)" },
270
+ ...ADHOC_SUBPROCESS_TEMPLATES.map((t) => ({ value: t.id, label: t.name })).sort((a, b) => a.label.localeCompare(b.label)),
271
+ ];
272
+ const GENERIC_ADHOC_SCHEMA = {
273
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Subprocess name" }],
274
+ groups: [
275
+ {
276
+ id: "general",
277
+ label: "General",
278
+ fields: [
279
+ { key: "name", label: "Name", type: "text", placeholder: "Subprocess name" },
280
+ {
281
+ key: "connector",
282
+ label: "Template",
283
+ type: "select",
284
+ options: ADHOC_OPTIONS,
285
+ hint: "Attach a Camunda AI agent template or use a plain ad-hoc subprocess.",
286
+ },
287
+ {
288
+ key: "documentation",
289
+ label: "Documentation",
290
+ type: "textarea",
291
+ placeholder: "Add notes or documentation…",
292
+ },
293
+ ],
294
+ },
295
+ ],
296
+ };
297
+ const ADHOC_SUBPROCESS_ADAPTER = {
298
+ read(defs, id) {
299
+ const el = findFlowElement(defs, id);
300
+ if (!el)
301
+ return {};
302
+ return {
303
+ name: el.name ?? "",
304
+ documentation: el.documentation ?? "",
305
+ connector: el.unknownAttributes?.["zeebe:modelerTemplate"] ?? CUSTOM_TASK_TYPE,
306
+ };
307
+ },
308
+ write(defs, id, values) {
309
+ const connectorVal = strVal(values.connector);
310
+ const newTemplateId = connectorVal && connectorVal !== CUSTOM_TASK_TYPE && TEMPLATE_REGISTRY.has(connectorVal)
311
+ ? connectorVal
312
+ : undefined;
313
+ if (newTemplateId) {
314
+ const withAttr = updateFlowElement(defs, id, (el) => ({
315
+ ...el,
316
+ name: typeof values.name === "string" ? values.name || undefined : el.name,
317
+ unknownAttributes: { ...el.unknownAttributes, "zeebe:modelerTemplate": newTemplateId },
318
+ }));
319
+ const templateReg = TEMPLATE_REGISTRY.get(newTemplateId);
320
+ if (templateReg)
321
+ return templateReg.adapter.write(withAttr, id, values);
322
+ return withAttr;
323
+ }
324
+ // Custom or clearing a template
325
+ return updateFlowElement(defs, id, (el) => {
326
+ const { "zeebe:modelerTemplate": _t, "zeebe:modelerTemplateVersion": _v, "zeebe:modelerTemplateIcon": _i, ...rest } = el.unknownAttributes;
327
+ return {
328
+ ...el,
329
+ name: typeof values.name === "string" ? values.name : el.name,
330
+ documentation: typeof values.documentation === "string"
331
+ ? values.documentation || undefined
332
+ : el.documentation,
333
+ unknownAttributes: rest,
334
+ };
335
+ });
336
+ },
337
+ resolve(defs, id) {
338
+ const el = findFlowElement(defs, id);
339
+ if (!el)
340
+ return null;
341
+ const templateId = el.unknownAttributes?.["zeebe:modelerTemplate"];
342
+ if (!templateId)
343
+ return null;
344
+ return TEMPLATE_REGISTRY.get(templateId) ?? null;
345
+ },
346
+ };
347
+ // ── Helpers ───────────────────────────────────────────────────────────────────
348
+ function strVal(v) {
349
+ return typeof v === "string" ? v : "";
350
+ }
351
+ // ── All element types that get the general schema ─────────────────────────────
352
+ const GENERAL_TYPES = [
353
+ "sendTask",
354
+ "exclusiveGateway",
355
+ "parallelGateway",
356
+ "inclusiveGateway",
357
+ "eventBasedGateway",
358
+ "complexGateway",
359
+ "subProcess",
360
+ "transaction",
361
+ "manualTask",
362
+ "task",
363
+ ];
364
+ // ── User task schema (formId) ─────────────────────────────────────────────────
365
+ function makeUserTaskSchema() {
366
+ return {
367
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Task name" }],
368
+ groups: [
369
+ {
370
+ id: "general",
371
+ label: "General",
372
+ fields: [
373
+ { key: "name", label: "Name", type: "text", placeholder: "Task name" },
374
+ {
375
+ key: "formId",
376
+ label: "Form ID",
377
+ type: "text",
378
+ placeholder: "e.g. Form_0h3l094",
379
+ hint: "ID of the Camunda Form linked to this user task.",
380
+ },
381
+ {
382
+ key: "exampleOutputJson",
383
+ label: "Example output (JSON)",
384
+ type: "textarea",
385
+ placeholder: '{"myVariable": "value"}',
386
+ hint: "Mock output written to process variables in play mode when no job worker is registered.",
387
+ validate: validateJson,
388
+ },
389
+ {
390
+ key: "documentation",
391
+ label: "Documentation",
392
+ type: "textarea",
393
+ placeholder: "Add notes or documentation…",
394
+ },
395
+ ],
396
+ },
397
+ ],
398
+ };
399
+ }
400
+ const USER_TASK_ADAPTER = {
401
+ read(defs, id) {
402
+ const el = findFlowElement(defs, id);
403
+ if (!el)
404
+ return {};
405
+ const ext = parseZeebeExtensions(el.extensionElements);
406
+ return {
407
+ name: el.name ?? "",
408
+ documentation: el.documentation ?? "",
409
+ formId: ext.formDefinition?.formId ?? "",
410
+ exampleOutputJson: getExampleOutputJson(ext),
411
+ };
412
+ },
413
+ write(defs, id, values) {
414
+ return updateFlowElement(defs, id, (el) => {
415
+ const formId = strVal(values.formId);
416
+ const exampleOutputJson = strVal(values.exampleOutputJson);
417
+ const currentExt = parseZeebeExtensions(el.extensionElements);
418
+ const newProperties = buildPropertiesWithExampleOutput(currentExt, exampleOutputJson);
419
+ const ZEEBE_FORM_NAMES = new Set(["userTask", "formDefinition", "properties"]);
420
+ const otherExts = el.extensionElements.filter((x) => !ZEEBE_FORM_NAMES.has(xmlLocalName(x.name)));
421
+ const formExts = zeebeExtensionsToXmlElements({
422
+ formDefinition: formId ? { formId } : undefined,
423
+ properties: newProperties,
424
+ });
425
+ return {
426
+ ...el,
427
+ name: typeof values.name === "string" ? values.name : el.name,
428
+ documentation: typeof values.documentation === "string"
429
+ ? values.documentation || undefined
430
+ : el.documentation,
431
+ extensionElements: [...otherExts, ...formExts],
432
+ };
433
+ });
434
+ },
435
+ };
436
+ // ── Business rule task schema (decisionId + resultVariable) ──────────────────
437
+ function makeBusinessRuleTaskSchema() {
438
+ return {
439
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Task name" }],
440
+ groups: [
441
+ {
442
+ id: "general",
443
+ label: "General",
444
+ fields: [
445
+ { key: "name", label: "Name", type: "text", placeholder: "Task name" },
446
+ {
447
+ key: "decisionId",
448
+ label: "Decision ID",
449
+ type: "text",
450
+ placeholder: "e.g. Decision_1m0rvzp",
451
+ hint: "ID of the DMN decision to evaluate.",
452
+ },
453
+ {
454
+ key: "resultVariable",
455
+ label: "Result variable",
456
+ type: "text",
457
+ placeholder: "result",
458
+ hint: "Process variable that receives the decision output.",
459
+ },
460
+ {
461
+ key: "documentation",
462
+ label: "Documentation",
463
+ type: "textarea",
464
+ placeholder: "Add notes or documentation…",
465
+ },
466
+ ],
467
+ },
468
+ ],
469
+ };
470
+ }
471
+ const BUSINESS_RULE_TASK_ADAPTER = {
472
+ read(defs, id) {
473
+ const el = findFlowElement(defs, id);
474
+ if (!el)
475
+ return {};
476
+ const ext = parseZeebeExtensions(el.extensionElements);
477
+ return {
478
+ name: el.name ?? "",
479
+ documentation: el.documentation ?? "",
480
+ decisionId: ext.calledDecision?.decisionId ?? "",
481
+ resultVariable: ext.calledDecision?.resultVariable ?? "",
482
+ };
483
+ },
484
+ write(defs, id, values) {
485
+ return updateFlowElement(defs, id, (el) => {
486
+ const decisionId = strVal(values.decisionId);
487
+ const resultVariable = strVal(values.resultVariable) || "result";
488
+ const ZEEBE_DECISION_NAMES = new Set(["calledDecision"]);
489
+ const otherExts = el.extensionElements.filter((x) => !ZEEBE_DECISION_NAMES.has(xmlLocalName(x.name)));
490
+ const decisionExts = decisionId
491
+ ? zeebeExtensionsToXmlElements({ calledDecision: { decisionId, resultVariable } })
492
+ : [];
493
+ return {
494
+ ...el,
495
+ name: typeof values.name === "string" ? values.name : el.name,
496
+ documentation: typeof values.documentation === "string"
497
+ ? values.documentation || undefined
498
+ : el.documentation,
499
+ extensionElements: [...otherExts, ...decisionExts],
500
+ };
501
+ });
502
+ },
503
+ };
504
+ // ── Script task schema ────────────────────────────────────────────────────────
505
+ function makeScriptTaskSchema(onOpenFeelPlayground) {
506
+ return {
507
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Task name" }],
508
+ groups: [
509
+ {
510
+ id: "general",
511
+ label: "General",
512
+ fields: [
513
+ { key: "name", label: "Name", type: "text", placeholder: "Task name" },
514
+ {
515
+ key: "expression",
516
+ label: "FEEL expression",
517
+ type: "feel-expression",
518
+ feelFixed: true,
519
+ placeholder: "= someVariable",
520
+ hint: "FEEL expression evaluated by the script engine.",
521
+ ...(onOpenFeelPlayground
522
+ ? {
523
+ openInPlayground: (v) => {
524
+ const expr = v.expression;
525
+ if (typeof expr === "string")
526
+ onOpenFeelPlayground(expr.replace(/^=\s*/, ""));
527
+ },
528
+ }
529
+ : {}),
530
+ },
531
+ {
532
+ key: "resultVariable",
533
+ label: "Result variable",
534
+ type: "text",
535
+ placeholder: "result",
536
+ hint: "Process variable that receives the script output.",
537
+ },
538
+ {
539
+ key: "documentation",
540
+ label: "Documentation",
541
+ type: "textarea",
542
+ placeholder: "Add notes or documentation…",
543
+ },
544
+ ],
545
+ },
546
+ ],
547
+ };
548
+ }
549
+ const SCRIPT_TASK_ADAPTER = {
550
+ read(defs, id) {
551
+ const el = findFlowElement(defs, id);
552
+ if (!el)
553
+ return {};
554
+ const script = parseZeebeScript(el.extensionElements);
555
+ return {
556
+ name: el.name ?? "",
557
+ documentation: el.documentation ?? "",
558
+ expression: script.expression,
559
+ resultVariable: script.resultVariable,
560
+ };
561
+ },
562
+ write(defs, id, values) {
563
+ return updateFlowElement(defs, id, (el) => {
564
+ const expression = strVal(values.expression);
565
+ const resultVariable = strVal(values.resultVariable);
566
+ const ZEEBE_SCRIPT = new Set(["script"]);
567
+ const otherExts = el.extensionElements.filter((x) => !ZEEBE_SCRIPT.has(xmlLocalName(x.name)));
568
+ const scriptAttrs = { expression };
569
+ if (resultVariable)
570
+ scriptAttrs.resultVariable = resultVariable;
571
+ const scriptExt = expression
572
+ ? { name: "zeebe:script", attributes: scriptAttrs, children: [] }
573
+ : null;
574
+ return {
575
+ ...el,
576
+ name: typeof values.name === "string" ? values.name : el.name,
577
+ documentation: typeof values.documentation === "string"
578
+ ? values.documentation || undefined
579
+ : el.documentation,
580
+ extensionElements: scriptExt ? [...otherExts, scriptExt] : otherExts,
581
+ };
582
+ });
583
+ },
584
+ };
585
+ // ── Call activity schema ──────────────────────────────────────────────────────
586
+ function makeCallActivitySchema() {
587
+ return {
588
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Activity name" }],
589
+ groups: [
590
+ {
591
+ id: "general",
592
+ label: "General",
593
+ fields: [
594
+ { key: "name", label: "Name", type: "text", placeholder: "Activity name" },
595
+ {
596
+ key: "processId",
597
+ label: "Called process ID",
598
+ type: "text",
599
+ placeholder: "e.g. pdp-get-project-data",
600
+ hint: "ID of the process definition to call.",
601
+ },
602
+ {
603
+ key: "propagateAllChildVariables",
604
+ label: "Propagate all child variables",
605
+ type: "toggle",
606
+ },
607
+ {
608
+ key: "documentation",
609
+ label: "Documentation",
610
+ type: "textarea",
611
+ placeholder: "Add notes or documentation…",
612
+ },
613
+ ],
614
+ },
615
+ ],
616
+ };
617
+ }
618
+ const CALL_ACTIVITY_ADAPTER = {
619
+ read(defs, id) {
620
+ const el = findFlowElement(defs, id);
621
+ if (!el)
622
+ return {};
623
+ const called = parseCalledElement(el.extensionElements);
624
+ return {
625
+ name: el.name ?? "",
626
+ documentation: el.documentation ?? "",
627
+ processId: called.processId,
628
+ propagateAllChildVariables: called.propagateAllChildVariables,
629
+ };
630
+ },
631
+ write(defs, id, values) {
632
+ return updateFlowElement(defs, id, (el) => {
633
+ const pid = strVal(values.processId);
634
+ const propagate = values.propagateAllChildVariables === true || values.propagateAllChildVariables === "true";
635
+ const ZEEBE_CALLED = new Set(["calledElement"]);
636
+ const otherExts = el.extensionElements.filter((x) => !ZEEBE_CALLED.has(xmlLocalName(x.name)));
637
+ const calledExt = pid
638
+ ? {
639
+ name: "zeebe:calledElement",
640
+ attributes: {
641
+ processId: pid,
642
+ propagateAllChildVariables: String(propagate),
643
+ },
644
+ children: [],
645
+ }
646
+ : null;
647
+ return {
648
+ ...el,
649
+ name: typeof values.name === "string" ? values.name : el.name,
650
+ documentation: typeof values.documentation === "string"
651
+ ? values.documentation || undefined
652
+ : el.documentation,
653
+ extensionElements: calledExt ? [...otherExts, calledExt] : otherExts,
654
+ };
655
+ });
656
+ },
657
+ };
658
+ // ── Sequence flow schema ──────────────────────────────────────────────────────
659
+ function makeSequenceFlowSchema(onOpenFeelPlayground) {
660
+ return {
661
+ compact: [
662
+ {
663
+ key: "conditionExpression",
664
+ label: "Condition",
665
+ type: "text",
666
+ placeholder: "= expression",
667
+ condition: (values) => values._sourceType === "exclusiveGateway",
668
+ },
669
+ ],
670
+ groups: [
671
+ {
672
+ id: "general",
673
+ label: "General",
674
+ fields: [
675
+ { key: "name", label: "Name", type: "text", placeholder: "Edge label" },
676
+ {
677
+ key: "conditionExpression",
678
+ label: "Condition expression (FEEL)",
679
+ type: "feel-expression",
680
+ feelFixed: true,
681
+ placeholder: '= someVariable = "value"',
682
+ hint: "FEEL expression that must evaluate to true for this path to be taken.",
683
+ condition: (values) => values._sourceType === "exclusiveGateway",
684
+ ...(onOpenFeelPlayground
685
+ ? {
686
+ openInPlayground: (v) => {
687
+ const expr = v.conditionExpression;
688
+ if (typeof expr === "string")
689
+ onOpenFeelPlayground(expr.replace(/^=\s*/, ""));
690
+ },
691
+ }
692
+ : {}),
693
+ },
694
+ {
695
+ key: "isDefault",
696
+ label: "Default flow",
697
+ type: "toggle",
698
+ hint: "Mark as default path taken when no other condition evaluates to true.",
699
+ condition: (values) => values._sourceType === "exclusiveGateway",
700
+ },
701
+ ],
702
+ },
703
+ ],
704
+ };
705
+ }
706
+ const SEQUENCE_FLOW_ADAPTER = {
707
+ read(defs, id) {
708
+ const sf = findSequenceFlow(defs, id);
709
+ if (!sf)
710
+ return {};
711
+ // Check whether this flow is the default of its source gateway
712
+ const sourceEl = findFlowElement(defs, sf.sourceRef);
713
+ const isDefault = sourceEl &&
714
+ (sourceEl.type === "exclusiveGateway" ||
715
+ sourceEl.type === "inclusiveGateway" ||
716
+ sourceEl.type === "complexGateway") &&
717
+ sourceEl.default === sf.id;
718
+ return {
719
+ name: sf.name ?? "",
720
+ conditionExpression: sf.conditionExpression?.text ?? "",
721
+ isDefault: isDefault ?? false,
722
+ _sourceType: sourceEl?.type ?? "",
723
+ };
724
+ },
725
+ write(defs, id, values) {
726
+ const sf = findSequenceFlow(defs, id);
727
+ const sourceRef = sf?.sourceRef;
728
+ // Condition expressions are only valid on outgoing flows of exclusive gateways
729
+ const sourceEl = sf ? findFlowElement(defs, sf.sourceRef) : undefined;
730
+ const isExclusiveGateway = sourceEl?.type === "exclusiveGateway";
731
+ // Update the sequence flow itself
732
+ let result = updateSequenceFlow(defs, id, (flow) => {
733
+ const expr = isExclusiveGateway ? strVal(values.conditionExpression) : undefined;
734
+ return {
735
+ ...flow,
736
+ name: typeof values.name === "string" ? values.name || undefined : flow.name,
737
+ conditionExpression: expr
738
+ ? { text: expr, attributes: { "xsi:type": "bpmn:tFormalExpression" } }
739
+ : undefined,
740
+ };
741
+ });
742
+ // Update the source gateway's default attribute
743
+ if (sourceRef) {
744
+ const sourceEl = findFlowElement(result, sourceRef);
745
+ if (sourceEl &&
746
+ (sourceEl.type === "exclusiveGateway" ||
747
+ sourceEl.type === "inclusiveGateway" ||
748
+ sourceEl.type === "complexGateway")) {
749
+ const makeDefault = values.isDefault === true;
750
+ result = updateFlowElement(result, sourceRef, (el) => {
751
+ if (el.type === "exclusiveGateway" ||
752
+ el.type === "inclusiveGateway" ||
753
+ el.type === "complexGateway") {
754
+ return { ...el, default: makeDefault ? id : undefined };
755
+ }
756
+ return el;
757
+ });
758
+ }
759
+ }
760
+ return result;
761
+ },
762
+ };
763
+ // ── Timer event schema and adapter ────────────────────────────────────────────
764
+ const TIMER_SCHEMA = {
765
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Event name" }],
766
+ groups: [
767
+ {
768
+ id: "general",
769
+ label: "General",
770
+ fields: [
771
+ { key: "name", label: "Name", type: "text", placeholder: "Event name" },
772
+ {
773
+ key: "timerType",
774
+ label: "Timer type",
775
+ type: "select",
776
+ options: [
777
+ { value: "timeCycle", label: "Cycle" },
778
+ { value: "timeDuration", label: "Duration" },
779
+ { value: "timeDate", label: "Date" },
780
+ ],
781
+ },
782
+ {
783
+ key: "timeCycle",
784
+ label: "Cycle",
785
+ type: "feel-expression",
786
+ placeholder: '= "R5/PT10S"',
787
+ hint: "ISO 8601 repeating interval, e.g. R5/PT10S.",
788
+ condition: (v) => v.timerType === "timeCycle",
789
+ },
790
+ {
791
+ key: "timeDuration",
792
+ label: "Duration",
793
+ type: "feel-expression",
794
+ placeholder: '= duration("PT5M")',
795
+ hint: "ISO 8601 duration, e.g. PT15S or P14D.",
796
+ condition: (v) => v.timerType === "timeDuration",
797
+ },
798
+ {
799
+ key: "timeDate",
800
+ label: "Date",
801
+ type: "feel-expression",
802
+ placeholder: '= date and time("2025-01-01T09:00:00")',
803
+ hint: "ISO 8601 date-time, e.g. 2025-01-01T09:00:00Z.",
804
+ condition: (v) => v.timerType === "timeDate",
805
+ },
806
+ {
807
+ key: "documentation",
808
+ label: "Documentation",
809
+ type: "textarea",
810
+ placeholder: "Add notes or documentation…",
811
+ },
812
+ ],
813
+ },
814
+ ],
815
+ };
816
+ const TIMER_ADAPTER = {
817
+ read(defs, id) {
818
+ const el = findFlowElement(defs, id);
819
+ if (!el)
820
+ return {};
821
+ if (el.type !== "startEvent" &&
822
+ el.type !== "intermediateCatchEvent" &&
823
+ el.type !== "boundaryEvent")
824
+ return {};
825
+ const timerDef = el.eventDefinitions.find((d) => d.type === "timer");
826
+ let timerType = "timeCycle";
827
+ if (timerDef?.timeDuration !== undefined)
828
+ timerType = "timeDuration";
829
+ else if (timerDef?.timeDate !== undefined)
830
+ timerType = "timeDate";
831
+ return {
832
+ name: el.name ?? "",
833
+ documentation: el.documentation ?? "",
834
+ timerType,
835
+ timeCycle: timerDef?.timeCycle ?? "",
836
+ timeDuration: timerDef?.timeDuration ?? "",
837
+ timeDate: timerDef?.timeDate ?? "",
838
+ };
839
+ },
840
+ write(defs, id, values) {
841
+ return updateFlowElement(defs, id, (el) => {
842
+ if (el.type !== "startEvent" &&
843
+ el.type !== "intermediateCatchEvent" &&
844
+ el.type !== "boundaryEvent")
845
+ return el;
846
+ const timerType = strVal(values.timerType) || "timeCycle";
847
+ const newTimerDef = { type: "timer" };
848
+ if (timerType === "timeCycle")
849
+ newTimerDef.timeCycle = strVal(values.timeCycle);
850
+ else if (timerType === "timeDuration")
851
+ newTimerDef.timeDuration = strVal(values.timeDuration);
852
+ else
853
+ newTimerDef.timeDate = strVal(values.timeDate);
854
+ const hasDef = el.eventDefinitions.some((d) => d.type === "timer");
855
+ const updatedDefs = hasDef
856
+ ? el.eventDefinitions.map((d) => (d.type === "timer" ? newTimerDef : d))
857
+ : [...el.eventDefinitions, newTimerDef];
858
+ return {
859
+ ...el,
860
+ name: typeof values.name === "string" ? values.name : el.name,
861
+ documentation: typeof values.documentation === "string"
862
+ ? values.documentation || undefined
863
+ : el.documentation,
864
+ eventDefinitions: updatedDefs,
865
+ };
866
+ });
867
+ },
868
+ };
869
+ // ── Message event schema and adapter ──────────────────────────────────────────
870
+ const MESSAGE_EVENT_SCHEMA = {
871
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Event name" }],
872
+ groups: [
873
+ {
874
+ id: "general",
875
+ label: "General",
876
+ fields: [
877
+ { key: "name", label: "Name", type: "text", placeholder: "Event name" },
878
+ {
879
+ key: "messageName",
880
+ label: "Message name",
881
+ type: "feel-expression",
882
+ placeholder: '= "order-received"',
883
+ hint: "Name of the message. FEEL expression.",
884
+ },
885
+ {
886
+ key: "correlationKey",
887
+ label: "Correlation key",
888
+ type: "feel-expression",
889
+ placeholder: "= orderId",
890
+ hint: "Correlates the incoming message to a specific process instance.",
891
+ },
892
+ {
893
+ key: "documentation",
894
+ label: "Documentation",
895
+ type: "textarea",
896
+ placeholder: "Add notes or documentation…",
897
+ },
898
+ ],
899
+ },
900
+ ],
901
+ };
902
+ const MESSAGE_ADAPTER = {
903
+ read(defs, id) {
904
+ const el = findFlowElement(defs, id);
905
+ if (!el)
906
+ return {};
907
+ const msg = parseZeebeMessage(el.extensionElements);
908
+ return {
909
+ name: el.name ?? "",
910
+ documentation: el.documentation ?? "",
911
+ messageName: msg.name,
912
+ correlationKey: msg.correlationKey,
913
+ };
914
+ },
915
+ write(defs, id, values) {
916
+ return updateFlowElement(defs, id, (el) => {
917
+ const messageName = strVal(values.messageName);
918
+ const correlationKey = strVal(values.correlationKey);
919
+ const otherExts = el.extensionElements.filter((x) => xmlLocalName(x.name) !== "message");
920
+ const attrs = { name: messageName };
921
+ if (correlationKey)
922
+ attrs.correlationKey = correlationKey;
923
+ const msgExt = messageName ? { name: "zeebe:message", attributes: attrs, children: [] } : null;
924
+ return {
925
+ ...el,
926
+ name: typeof values.name === "string" ? values.name : el.name,
927
+ documentation: typeof values.documentation === "string"
928
+ ? values.documentation || undefined
929
+ : el.documentation,
930
+ extensionElements: msgExt ? [...otherExts, msgExt] : otherExts,
931
+ };
932
+ });
933
+ },
934
+ };
935
+ // ── Signal event schema and adapter ───────────────────────────────────────────
936
+ const SIGNAL_EVENT_SCHEMA = {
937
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Event name" }],
938
+ groups: [
939
+ {
940
+ id: "general",
941
+ label: "General",
942
+ fields: [
943
+ { key: "name", label: "Name", type: "text", placeholder: "Event name" },
944
+ {
945
+ key: "signalName",
946
+ label: "Signal name",
947
+ type: "feel-expression",
948
+ placeholder: '= "mySignal"',
949
+ hint: "Name of the signal. FEEL expression.",
950
+ },
951
+ {
952
+ key: "documentation",
953
+ label: "Documentation",
954
+ type: "textarea",
955
+ placeholder: "Add notes or documentation…",
956
+ },
957
+ ],
958
+ },
959
+ ],
960
+ };
961
+ const SIGNAL_ADAPTER = {
962
+ read(defs, id) {
963
+ const el = findFlowElement(defs, id);
964
+ if (!el)
965
+ return {};
966
+ const sig = parseZeebeSignal(el.extensionElements);
967
+ return {
968
+ name: el.name ?? "",
969
+ documentation: el.documentation ?? "",
970
+ signalName: sig.name,
971
+ };
972
+ },
973
+ write(defs, id, values) {
974
+ return updateFlowElement(defs, id, (el) => {
975
+ const signalName = strVal(values.signalName);
976
+ const otherExts = el.extensionElements.filter((x) => xmlLocalName(x.name) !== "signal");
977
+ const sigExt = signalName
978
+ ? { name: "zeebe:signal", attributes: { name: signalName }, children: [] }
979
+ : null;
980
+ return {
981
+ ...el,
982
+ name: typeof values.name === "string" ? values.name : el.name,
983
+ documentation: typeof values.documentation === "string"
984
+ ? values.documentation || undefined
985
+ : el.documentation,
986
+ extensionElements: sigExt ? [...otherExts, sigExt] : otherExts,
987
+ };
988
+ });
989
+ },
990
+ };
991
+ // ── Error event schema and adapter ────────────────────────────────────────────
992
+ const ERROR_EVENT_SCHEMA = {
993
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Event name" }],
994
+ groups: [
995
+ {
996
+ id: "general",
997
+ label: "General",
998
+ fields: [
999
+ { key: "name", label: "Name", type: "text", placeholder: "Event name" },
1000
+ {
1001
+ key: "errorCode",
1002
+ label: "Error code",
1003
+ type: "feel-expression",
1004
+ placeholder: '= "error-code-value"',
1005
+ hint: "Error code for this event. FEEL expression.",
1006
+ },
1007
+ {
1008
+ key: "documentation",
1009
+ label: "Documentation",
1010
+ type: "textarea",
1011
+ placeholder: "Add notes or documentation…",
1012
+ },
1013
+ ],
1014
+ },
1015
+ ],
1016
+ };
1017
+ const ERROR_ADAPTER = {
1018
+ read(defs, id) {
1019
+ const el = findFlowElement(defs, id);
1020
+ if (!el)
1021
+ return {};
1022
+ const err = parseZeebeError(el.extensionElements);
1023
+ return {
1024
+ name: el.name ?? "",
1025
+ documentation: el.documentation ?? "",
1026
+ errorCode: err.errorCode,
1027
+ };
1028
+ },
1029
+ write(defs, id, values) {
1030
+ return updateFlowElement(defs, id, (el) => {
1031
+ const errorCode = strVal(values.errorCode);
1032
+ const otherExts = el.extensionElements.filter((x) => xmlLocalName(x.name) !== "error");
1033
+ const errExt = errorCode
1034
+ ? { name: "zeebe:error", attributes: { errorCode }, children: [] }
1035
+ : null;
1036
+ return {
1037
+ ...el,
1038
+ name: typeof values.name === "string" ? values.name : el.name,
1039
+ documentation: typeof values.documentation === "string"
1040
+ ? values.documentation || undefined
1041
+ : el.documentation,
1042
+ extensionElements: errExt ? [...otherExts, errExt] : otherExts,
1043
+ };
1044
+ });
1045
+ },
1046
+ };
1047
+ // ── Escalation event schema and adapter ───────────────────────────────────────
1048
+ const ESCALATION_EVENT_SCHEMA = {
1049
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Event name" }],
1050
+ groups: [
1051
+ {
1052
+ id: "general",
1053
+ label: "General",
1054
+ fields: [
1055
+ { key: "name", label: "Name", type: "text", placeholder: "Event name" },
1056
+ {
1057
+ key: "escalationCode",
1058
+ label: "Escalation code",
1059
+ type: "feel-expression",
1060
+ placeholder: '= "escalation-code"',
1061
+ hint: "Escalation code for this event. FEEL expression.",
1062
+ },
1063
+ {
1064
+ key: "documentation",
1065
+ label: "Documentation",
1066
+ type: "textarea",
1067
+ placeholder: "Add notes or documentation…",
1068
+ },
1069
+ ],
1070
+ },
1071
+ ],
1072
+ };
1073
+ const ESCALATION_ADAPTER = {
1074
+ read(defs, id) {
1075
+ const el = findFlowElement(defs, id);
1076
+ if (!el)
1077
+ return {};
1078
+ const esc = parseZeebeEscalation(el.extensionElements);
1079
+ return {
1080
+ name: el.name ?? "",
1081
+ documentation: el.documentation ?? "",
1082
+ escalationCode: esc.escalationCode,
1083
+ };
1084
+ },
1085
+ write(defs, id, values) {
1086
+ return updateFlowElement(defs, id, (el) => {
1087
+ const escalationCode = strVal(values.escalationCode);
1088
+ const otherExts = el.extensionElements.filter((x) => xmlLocalName(x.name) !== "escalation");
1089
+ const escExt = escalationCode
1090
+ ? { name: "zeebe:escalation", attributes: { escalationCode }, children: [] }
1091
+ : null;
1092
+ return {
1093
+ ...el,
1094
+ name: typeof values.name === "string" ? values.name : el.name,
1095
+ documentation: typeof values.documentation === "string"
1096
+ ? values.documentation || undefined
1097
+ : el.documentation,
1098
+ extensionElements: escExt ? [...otherExts, escExt] : otherExts,
1099
+ };
1100
+ });
1101
+ },
1102
+ };
1103
+ // ── Conditional event schema and adapter ──────────────────────────────────────
1104
+ const CONDITIONAL_EVENT_SCHEMA = {
1105
+ compact: [{ key: "name", label: "Name", type: "text", placeholder: "Event name" }],
1106
+ groups: [
1107
+ {
1108
+ id: "general",
1109
+ label: "General",
1110
+ fields: [
1111
+ { key: "name", label: "Name", type: "text", placeholder: "Event name" },
1112
+ {
1113
+ key: "conditionExpression",
1114
+ label: "Condition expression",
1115
+ type: "feel-expression",
1116
+ feelFixed: true,
1117
+ placeholder: "= someVariable = true",
1118
+ hint: "FEEL expression that must evaluate to true for this event to trigger.",
1119
+ },
1120
+ {
1121
+ key: "documentation",
1122
+ label: "Documentation",
1123
+ type: "textarea",
1124
+ placeholder: "Add notes or documentation…",
1125
+ },
1126
+ ],
1127
+ },
1128
+ ],
1129
+ };
1130
+ const CONDITIONAL_ADAPTER = {
1131
+ read(defs, id) {
1132
+ const el = findFlowElement(defs, id);
1133
+ if (!el)
1134
+ return {};
1135
+ if (el.type !== "startEvent" &&
1136
+ el.type !== "intermediateCatchEvent" &&
1137
+ el.type !== "boundaryEvent")
1138
+ return {};
1139
+ const condDef = el.eventDefinitions.find((d) => d.type === "conditional");
1140
+ return {
1141
+ name: el.name ?? "",
1142
+ documentation: el.documentation ?? "",
1143
+ conditionExpression: condDef?.condition ?? "",
1144
+ };
1145
+ },
1146
+ write(defs, id, values) {
1147
+ return updateFlowElement(defs, id, (el) => {
1148
+ if (el.type !== "startEvent" &&
1149
+ el.type !== "intermediateCatchEvent" &&
1150
+ el.type !== "boundaryEvent")
1151
+ return el;
1152
+ const conditionExpression = strVal(values.conditionExpression);
1153
+ const newCondDef = {
1154
+ type: "conditional",
1155
+ condition: conditionExpression || undefined,
1156
+ };
1157
+ const hasDef = el.eventDefinitions.some((d) => d.type === "conditional");
1158
+ const updatedDefs = hasDef
1159
+ ? el.eventDefinitions.map((d) => (d.type === "conditional" ? newCondDef : d))
1160
+ : [...el.eventDefinitions, newCondDef];
1161
+ return {
1162
+ ...el,
1163
+ name: typeof values.name === "string" ? values.name : el.name,
1164
+ documentation: typeof values.documentation === "string"
1165
+ ? values.documentation || undefined
1166
+ : el.documentation,
1167
+ eventDefinitions: updatedDefs,
1168
+ };
1169
+ });
1170
+ },
1171
+ };
1172
+ // ── Event dispatcher adapters ─────────────────────────────────────────────────
1173
+ /** Map an event definition type to the matching schema+adapter pair. */
1174
+ function eventDefToRegistration(defType) {
1175
+ switch (defType) {
1176
+ case "timer":
1177
+ return { schema: TIMER_SCHEMA, adapter: TIMER_ADAPTER };
1178
+ case "message":
1179
+ return { schema: MESSAGE_EVENT_SCHEMA, adapter: MESSAGE_ADAPTER };
1180
+ case "signal":
1181
+ return { schema: SIGNAL_EVENT_SCHEMA, adapter: SIGNAL_ADAPTER };
1182
+ case "error":
1183
+ return { schema: ERROR_EVENT_SCHEMA, adapter: ERROR_ADAPTER };
1184
+ case "escalation":
1185
+ return { schema: ESCALATION_EVENT_SCHEMA, adapter: ESCALATION_ADAPTER };
1186
+ case "conditional":
1187
+ return { schema: CONDITIONAL_EVENT_SCHEMA, adapter: CONDITIONAL_ADAPTER };
1188
+ default:
1189
+ return null;
1190
+ }
1191
+ }
1192
+ const START_EVENT_ADAPTER = {
1193
+ read: GENERAL_ADAPTER.read,
1194
+ write: GENERAL_ADAPTER.write,
1195
+ resolve(defs, id) {
1196
+ const el = findFlowElement(defs, id);
1197
+ if (!el || el.type !== "startEvent")
1198
+ return null;
1199
+ const defType = el.eventDefinitions[0]?.type;
1200
+ if (!defType)
1201
+ return null;
1202
+ return eventDefToRegistration(defType);
1203
+ },
1204
+ };
1205
+ const END_EVENT_ADAPTER = {
1206
+ read: GENERAL_ADAPTER.read,
1207
+ write: GENERAL_ADAPTER.write,
1208
+ resolve(defs, id) {
1209
+ const el = findFlowElement(defs, id);
1210
+ if (!el || el.type !== "endEvent")
1211
+ return null;
1212
+ const defType = el.eventDefinitions[0]?.type;
1213
+ if (!defType)
1214
+ return null;
1215
+ return eventDefToRegistration(defType);
1216
+ },
1217
+ };
1218
+ const CATCH_EVENT_ADAPTER = {
1219
+ read: GENERAL_ADAPTER.read,
1220
+ write: GENERAL_ADAPTER.write,
1221
+ resolve(defs, id) {
1222
+ const el = findFlowElement(defs, id);
1223
+ if (!el || el.type !== "intermediateCatchEvent")
1224
+ return null;
1225
+ const defType = el.eventDefinitions[0]?.type;
1226
+ if (!defType)
1227
+ return null;
1228
+ return eventDefToRegistration(defType);
1229
+ },
1230
+ };
1231
+ const THROW_EVENT_ADAPTER = {
1232
+ read: GENERAL_ADAPTER.read,
1233
+ write: GENERAL_ADAPTER.write,
1234
+ resolve(defs, id) {
1235
+ const el = findFlowElement(defs, id);
1236
+ if (!el || el.type !== "intermediateThrowEvent")
1237
+ return null;
1238
+ const defType = el.eventDefinitions[0]?.type;
1239
+ if (!defType)
1240
+ return null;
1241
+ return eventDefToRegistration(defType);
1242
+ },
1243
+ };
1244
+ const BOUNDARY_EVENT_ADAPTER = {
1245
+ read: GENERAL_ADAPTER.read,
1246
+ write: GENERAL_ADAPTER.write,
1247
+ resolve(defs, id) {
1248
+ const el = findFlowElement(defs, id);
1249
+ if (!el || el.type !== "boundaryEvent")
1250
+ return null;
1251
+ const defType = el.eventDefinitions[0]?.type;
1252
+ if (!defType)
1253
+ return null;
1254
+ return eventDefToRegistration(defType);
1255
+ },
1256
+ };
1257
+ // ── Factory ───────────────────────────────────────────────────────────────────
1258
+ /**
1259
+ * Creates the BPMN config panel extension plugin.
1260
+ *
1261
+ * Registers property schemas for all standard BPMN element types. Service tasks
1262
+ * are template-aware: when `zeebe:modelerTemplate` is set the matching
1263
+ * connector template form is rendered; otherwise a generic connector selector
1264
+ * is shown. Custom templates can be registered via `registerTemplate`.
1265
+ *
1266
+ * @param configPanel - The base config panel plugin returned by
1267
+ * `createConfigPanelPlugin`.
1268
+ * @param options - Optional callbacks for FEEL playground navigation.
1269
+ */
1270
+ export function createConfigPanelBpmnPlugin(configPanel, options = {}) {
1271
+ const userTaskSchema = makeUserTaskSchema();
1272
+ const businessRuleTaskSchema = makeBusinessRuleTaskSchema();
1273
+ const callActivitySchema = makeCallActivitySchema();
1274
+ const scriptTaskSchema = makeScriptTaskSchema(options.openFeelPlayground);
1275
+ const sequenceFlowSchema = makeSequenceFlowSchema(options.openFeelPlayground);
1276
+ return {
1277
+ name: "config-panel-bpmn",
1278
+ install() {
1279
+ // Register general schema for common element types
1280
+ for (const type of GENERAL_TYPES) {
1281
+ configPanel.registerSchema(type, GENERAL_SCHEMA, GENERAL_ADAPTER);
1282
+ }
1283
+ // Events: dispatcher adapters resolve to event-definition-specific schemas
1284
+ configPanel.registerSchema("startEvent", GENERAL_SCHEMA, START_EVENT_ADAPTER);
1285
+ configPanel.registerSchema("endEvent", GENERAL_SCHEMA, END_EVENT_ADAPTER);
1286
+ configPanel.registerSchema("intermediateCatchEvent", GENERAL_SCHEMA, CATCH_EVENT_ADAPTER);
1287
+ configPanel.registerSchema("intermediateThrowEvent", GENERAL_SCHEMA, THROW_EVENT_ADAPTER);
1288
+ configPanel.registerSchema("boundaryEvent", GENERAL_SCHEMA, BOUNDARY_EVENT_ADAPTER);
1289
+ // Receive task: message name + correlation key
1290
+ configPanel.registerSchema("receiveTask", MESSAGE_EVENT_SCHEMA, MESSAGE_ADAPTER);
1291
+ // User task: formId + optional Open Form button
1292
+ configPanel.registerSchema("userTask", userTaskSchema, USER_TASK_ADAPTER);
1293
+ // Business rule task: decisionId + resultVariable + optional Open Decision button
1294
+ configPanel.registerSchema("businessRuleTask", businessRuleTaskSchema, BUSINESS_RULE_TASK_ADAPTER);
1295
+ // Service task: template-aware adapter
1296
+ configPanel.registerSchema("serviceTask", GENERIC_SERVICE_TASK_SCHEMA, SERVICE_TASK_ADAPTER);
1297
+ // Ad-hoc subprocess: template-aware adapter (AI Agent pattern)
1298
+ configPanel.registerSchema("adHocSubProcess", GENERIC_ADHOC_SCHEMA, ADHOC_SUBPROCESS_ADAPTER);
1299
+ // Script task: FEEL expression + result variable
1300
+ configPanel.registerSchema("scriptTask", scriptTaskSchema, SCRIPT_TASK_ADAPTER);
1301
+ // Call activity: called process ID + navigate button
1302
+ configPanel.registerSchema("callActivity", callActivitySchema, CALL_ACTIVITY_ADAPTER);
1303
+ // Sequence flow: condition expression (for gateway edges)
1304
+ configPanel.registerSchema("sequenceFlow", sequenceFlowSchema, SEQUENCE_FLOW_ADAPTER);
1305
+ },
1306
+ registerTemplate(template) {
1307
+ TEMPLATE_REGISTRY.set(template.id, buildRegistrationFromTemplate(template));
1308
+ const taskType = extractTaskType(template);
1309
+ if (taskType && !TASK_TYPE_TO_TEMPLATE_ID.has(taskType)) {
1310
+ TASK_TYPE_TO_TEMPLATE_ID.set(taskType, template.id);
1311
+ }
1312
+ if (!CONNECTOR_OPTIONS.some((o) => o.value === template.id)) {
1313
+ CONNECTOR_OPTIONS.push({ value: template.id, label: template.name });
1314
+ }
1315
+ },
1316
+ };
1317
+ }
1318
+ // Re-export types for external use
1319
+ export { ELEMENT_TYPE_LABELS };
1320
+ //# sourceMappingURL=index.js.map