@bpmnkit/plugins 0.0.18 → 0.0.23
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.
- package/README.md +2 -0
- package/dist/ai-bridge/css.js +40 -0
- package/dist/ai-bridge/index.d.ts +4 -1
- package/dist/ai-bridge/index.js +1 -0
- package/dist/ai-bridge/panel.d.ts +2 -3
- package/dist/ai-bridge/panel.js +246 -3
- package/dist/config-panel/css.d.ts +1 -1
- package/dist/config-panel/css.js +25 -0
- package/dist/config-panel/renderer.js +19 -4
- package/dist/config-panel-bpmn/index.d.ts +18 -0
- package/dist/config-panel-bpmn/index.js +534 -19
- package/dist/config-panel-bpmn/util.d.ts +14 -0
- package/dist/config-panel-bpmn/util.js +21 -0
- package/dist/connector-catalog/builtin-templates.d.ts +10 -0
- package/dist/connector-catalog/builtin-templates.js +585 -0
- package/dist/connector-catalog/css.d.ts +1 -1
- package/dist/connector-catalog/css.js +282 -0
- package/dist/connector-catalog/index.d.ts +27 -14
- package/dist/connector-catalog/index.js +76 -17
- package/dist/connector-catalog/panel.d.ts +34 -0
- package/dist/connector-catalog/panel.js +223 -0
- package/dist/presentation/index.d.ts +24 -0
- package/dist/presentation/index.js +680 -0
- package/dist/process-runner/css.js +237 -0
- package/dist/process-runner/index.d.ts +56 -0
- package/dist/process-runner/index.js +519 -36
- package/package.json +11 -7
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
*
|
|
25
25
|
* @packageDocumentation
|
|
26
26
|
*/
|
|
27
|
-
import { zeebeExtensionsToXmlElements } from "@bpmnkit/core";
|
|
27
|
+
import { buildValidationDmn, findValidationStructure, insertValidationStructure, removeValidationStructure, validationDecisionId, zeebeExtensionsToXmlElements, } from "@bpmnkit/core";
|
|
28
28
|
import { ELEMENT_TYPE_LABELS } from "@bpmnkit/editor";
|
|
29
29
|
import { buildRegistrationFromTemplate } from "./template-engine.js";
|
|
30
30
|
import { CAMUNDA_CONNECTOR_TEMPLATES } from "./templates/generated.js";
|
|
31
31
|
export { CAMUNDA_CONNECTOR_TEMPLATES } from "./templates/generated.js";
|
|
32
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";
|
|
33
|
+
import { buildPropertiesWithExampleOutput, buildZeebeLoopCharacteristics, findFlowElement, findSequenceFlow, getExampleOutputJson, getIoInput, getTaskHeader, parseCalledElement, parseZeebeError, parseZeebeEscalation, parseZeebeExtensions, parseZeebeLoopCharacteristics, parseZeebeMessage, parseZeebeScript, parseZeebeSignal, updateFlowElement, updateSequenceFlow, xmlLocalName, } from "./util.js";
|
|
34
34
|
/** Validates that a field value is valid JSON, or returns an error message. */
|
|
35
35
|
function validateJson(value) {
|
|
36
36
|
if (typeof value !== "string" || value.trim() === "")
|
|
@@ -358,11 +358,124 @@ const GENERAL_TYPES = [
|
|
|
358
358
|
"inclusiveGateway",
|
|
359
359
|
"eventBasedGateway",
|
|
360
360
|
"complexGateway",
|
|
361
|
-
"subProcess",
|
|
362
361
|
"transaction",
|
|
363
362
|
"manualTask",
|
|
364
363
|
"task",
|
|
365
364
|
];
|
|
365
|
+
// ── Sub-process schema (general + multi-instance) ─────────────────────────────
|
|
366
|
+
const SUB_PROCESS_SCHEMA = {
|
|
367
|
+
compact: [{ key: "name", label: "Name", type: "text", placeholder: "Sub-process name" }],
|
|
368
|
+
groups: [
|
|
369
|
+
{
|
|
370
|
+
id: "general",
|
|
371
|
+
label: "General",
|
|
372
|
+
fields: [
|
|
373
|
+
{ key: "name", label: "Name", type: "text", placeholder: "Sub-process name" },
|
|
374
|
+
{
|
|
375
|
+
key: "documentation",
|
|
376
|
+
label: "Documentation",
|
|
377
|
+
type: "textarea",
|
|
378
|
+
placeholder: "Add notes or documentation for this element…",
|
|
379
|
+
},
|
|
380
|
+
],
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
id: "multi-instance",
|
|
384
|
+
label: "Multi-instance",
|
|
385
|
+
fields: [
|
|
386
|
+
// Guided setup button — visible only when no loop is configured
|
|
387
|
+
{
|
|
388
|
+
key: "_setupForEach",
|
|
389
|
+
label: "Process each item in a list",
|
|
390
|
+
type: "action",
|
|
391
|
+
hint: "Configure this sub-process to run once for each item in a collection.",
|
|
392
|
+
condition: (v) => v.multiInstanceMode === "none",
|
|
393
|
+
onClick: (_values, setValue) => {
|
|
394
|
+
setValue("multiInstanceMode", "parallel");
|
|
395
|
+
setValue("elementVariable", "item");
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
key: "multiInstanceMode",
|
|
400
|
+
label: "Loop type",
|
|
401
|
+
type: "select",
|
|
402
|
+
options: [
|
|
403
|
+
{ value: "none", label: "None" },
|
|
404
|
+
{ value: "parallel", label: "Parallel (for each, all at once)" },
|
|
405
|
+
{ value: "sequential", label: "Sequential (for each, one at a time)" },
|
|
406
|
+
],
|
|
407
|
+
condition: (v) => v.multiInstanceMode !== "none",
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
key: "collection",
|
|
411
|
+
label: "Collection",
|
|
412
|
+
type: "feel-expression",
|
|
413
|
+
placeholder: "= emails",
|
|
414
|
+
hint: "FEEL expression that returns the array to iterate over.",
|
|
415
|
+
condition: (v) => v.multiInstanceMode !== "none",
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
key: "elementVariable",
|
|
419
|
+
label: "Element variable",
|
|
420
|
+
type: "text",
|
|
421
|
+
placeholder: "item",
|
|
422
|
+
hint: "Variable name for the current iteration item. Available inside the sub-process as a process variable.",
|
|
423
|
+
condition: (v) => v.multiInstanceMode !== "none",
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
},
|
|
427
|
+
],
|
|
428
|
+
};
|
|
429
|
+
const SUB_PROCESS_ADAPTER = {
|
|
430
|
+
read(defs, id) {
|
|
431
|
+
const el = findFlowElement(defs, id);
|
|
432
|
+
if (!el)
|
|
433
|
+
return {};
|
|
434
|
+
const lc = "loopCharacteristics" in el ? el.loopCharacteristics : undefined;
|
|
435
|
+
const loop = parseZeebeLoopCharacteristics(lc?.extensionElements ?? []);
|
|
436
|
+
let multiInstanceMode = "none";
|
|
437
|
+
if (lc) {
|
|
438
|
+
multiInstanceMode = lc.isSequential ? "sequential" : "parallel";
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
name: el.name ?? "",
|
|
442
|
+
documentation: el.documentation ?? "",
|
|
443
|
+
multiInstanceMode,
|
|
444
|
+
collection: loop?.inputCollection ?? "",
|
|
445
|
+
elementVariable: loop?.inputElement ?? "",
|
|
446
|
+
};
|
|
447
|
+
},
|
|
448
|
+
write(defs, id, values) {
|
|
449
|
+
return updateFlowElement(defs, id, (el) => {
|
|
450
|
+
const mode = typeof values.multiInstanceMode === "string" ? values.multiInstanceMode : "none";
|
|
451
|
+
const collection = typeof values.collection === "string" ? values.collection : "";
|
|
452
|
+
const elementVariable = typeof values.elementVariable === "string" ? values.elementVariable : "";
|
|
453
|
+
let loopCharacteristics;
|
|
454
|
+
if (mode !== "none") {
|
|
455
|
+
const extEls = collection
|
|
456
|
+
? [
|
|
457
|
+
buildZeebeLoopCharacteristics({
|
|
458
|
+
inputCollection: collection,
|
|
459
|
+
inputElement: elementVariable,
|
|
460
|
+
}),
|
|
461
|
+
]
|
|
462
|
+
: [];
|
|
463
|
+
loopCharacteristics = {
|
|
464
|
+
isSequential: mode === "sequential" ? true : undefined,
|
|
465
|
+
extensionElements: extEls,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
return {
|
|
469
|
+
...el,
|
|
470
|
+
name: typeof values.name === "string" ? values.name : el.name,
|
|
471
|
+
documentation: typeof values.documentation === "string"
|
|
472
|
+
? values.documentation || undefined
|
|
473
|
+
: el.documentation,
|
|
474
|
+
loopCharacteristics,
|
|
475
|
+
};
|
|
476
|
+
});
|
|
477
|
+
},
|
|
478
|
+
};
|
|
366
479
|
// ── User task schema (formId) ─────────────────────────────────────────────────
|
|
367
480
|
function makeUserTaskSchema() {
|
|
368
481
|
return {
|
|
@@ -436,7 +549,7 @@ const USER_TASK_ADAPTER = {
|
|
|
436
549
|
},
|
|
437
550
|
};
|
|
438
551
|
// ── Business rule task schema (decisionId + resultVariable) ──────────────────
|
|
439
|
-
function makeBusinessRuleTaskSchema() {
|
|
552
|
+
function makeBusinessRuleTaskSchema(onOpenDmn) {
|
|
440
553
|
return {
|
|
441
554
|
compact: [{ key: "name", label: "Name", type: "text", placeholder: "Task name" }],
|
|
442
555
|
groups: [
|
|
@@ -452,6 +565,18 @@ function makeBusinessRuleTaskSchema() {
|
|
|
452
565
|
placeholder: "e.g. Decision_1m0rvzp",
|
|
453
566
|
hint: "ID of the DMN decision to evaluate.",
|
|
454
567
|
},
|
|
568
|
+
{
|
|
569
|
+
key: "_openDmn",
|
|
570
|
+
label: "Open DMN",
|
|
571
|
+
type: "action",
|
|
572
|
+
hint: "Open the referenced DMN decision in the editor.",
|
|
573
|
+
condition: (values) => typeof values.decisionId === "string" && values.decisionId !== "",
|
|
574
|
+
onClick: (values) => {
|
|
575
|
+
const decId = values.decisionId;
|
|
576
|
+
if (decId)
|
|
577
|
+
onOpenDmn?.(decId);
|
|
578
|
+
},
|
|
579
|
+
},
|
|
455
580
|
{
|
|
456
581
|
key: "resultVariable",
|
|
457
582
|
label: "Result variable",
|
|
@@ -1192,19 +1317,6 @@ function eventDefToRegistration(defType) {
|
|
|
1192
1317
|
return null;
|
|
1193
1318
|
}
|
|
1194
1319
|
}
|
|
1195
|
-
const START_EVENT_ADAPTER = {
|
|
1196
|
-
read: GENERAL_ADAPTER.read,
|
|
1197
|
-
write: GENERAL_ADAPTER.write,
|
|
1198
|
-
resolve(defs, id) {
|
|
1199
|
-
const el = findFlowElement(defs, id);
|
|
1200
|
-
if (!el || el.type !== "startEvent")
|
|
1201
|
-
return null;
|
|
1202
|
-
const defType = el.eventDefinitions[0]?.type;
|
|
1203
|
-
if (!defType)
|
|
1204
|
-
return null;
|
|
1205
|
-
return eventDefToRegistration(defType);
|
|
1206
|
-
},
|
|
1207
|
-
};
|
|
1208
1320
|
const END_EVENT_ADAPTER = {
|
|
1209
1321
|
read: GENERAL_ADAPTER.read,
|
|
1210
1322
|
write: GENERAL_ADAPTER.write,
|
|
@@ -1257,6 +1369,400 @@ const BOUNDARY_EVENT_ADAPTER = {
|
|
|
1257
1369
|
return eventDefToRegistration(defType);
|
|
1258
1370
|
},
|
|
1259
1371
|
};
|
|
1372
|
+
// ── Input validation wizard modal ─────────────────────────────────────────────
|
|
1373
|
+
const VALIDATION_MODAL_CSS = `
|
|
1374
|
+
.bpmnkit-val-overlay {
|
|
1375
|
+
position: fixed; inset: 0; background: rgba(0,0,0,0.6); backdrop-filter: blur(4px);
|
|
1376
|
+
display: flex; align-items: center; justify-content: center; z-index: 99999;
|
|
1377
|
+
}
|
|
1378
|
+
.bpmnkit-val-dialog {
|
|
1379
|
+
background: var(--bpmnkit-surface, #161626);
|
|
1380
|
+
border: 1px solid var(--bpmnkit-border, #2a2a42);
|
|
1381
|
+
border-radius: 10px; padding: 20px; width: 680px; max-width: 95vw; max-height: 85vh;
|
|
1382
|
+
display: flex; flex-direction: column; gap: 14px;
|
|
1383
|
+
font-family: var(--bpmnkit-font, system-ui, sans-serif);
|
|
1384
|
+
font-size: 13px; color: var(--bpmnkit-fg, #cdd6f4);
|
|
1385
|
+
box-shadow: 0 24px 64px rgba(0,0,0,0.7);
|
|
1386
|
+
}
|
|
1387
|
+
.bpmnkit-val-dialog h2 {
|
|
1388
|
+
font-size: 14px; font-weight: 600; margin: 0;
|
|
1389
|
+
color: var(--bpmnkit-fg, #cdd6f4);
|
|
1390
|
+
}
|
|
1391
|
+
.bpmnkit-val-dialog p.hint {
|
|
1392
|
+
font-size: 12px; color: var(--bpmnkit-fg-muted, #8888a8); margin: 0;
|
|
1393
|
+
}
|
|
1394
|
+
.bpmnkit-val-table { overflow-y: auto; flex: 1; }
|
|
1395
|
+
.bpmnkit-val-table table {
|
|
1396
|
+
width: 100%; border-collapse: collapse; font-size: 12px;
|
|
1397
|
+
}
|
|
1398
|
+
.bpmnkit-val-table th {
|
|
1399
|
+
text-align: left; padding: 6px 8px; font-weight: 500;
|
|
1400
|
+
color: var(--bpmnkit-fg-muted, #8888a8); font-size: 11px; text-transform: uppercase;
|
|
1401
|
+
border-bottom: 1px solid var(--bpmnkit-border, #2a2a42);
|
|
1402
|
+
}
|
|
1403
|
+
.bpmnkit-val-table td { padding: 4px 4px; vertical-align: middle; }
|
|
1404
|
+
.bpmnkit-val-table input[type=text], .bpmnkit-val-table input[type=number], .bpmnkit-val-table select {
|
|
1405
|
+
background: var(--bpmnkit-surface-2, #1e1e2e);
|
|
1406
|
+
border: 1px solid var(--bpmnkit-border, #2a2a42);
|
|
1407
|
+
border-radius: 4px; padding: 4px 7px; font-size: 12px;
|
|
1408
|
+
color: var(--bpmnkit-fg, #cdd6f4); width: 100%; box-sizing: border-box;
|
|
1409
|
+
}
|
|
1410
|
+
.bpmnkit-val-table input[type=text]:focus, .bpmnkit-val-table input[type=number]:focus,
|
|
1411
|
+
.bpmnkit-val-table select:focus {
|
|
1412
|
+
outline: none; border-color: var(--bpmnkit-accent, #6b9df7);
|
|
1413
|
+
}
|
|
1414
|
+
.bpmnkit-val-table input:disabled, .bpmnkit-val-table select:disabled {
|
|
1415
|
+
opacity: 0.35; cursor: not-allowed;
|
|
1416
|
+
}
|
|
1417
|
+
.bpmnkit-val-chk { display: flex; align-items: center; justify-content: center; }
|
|
1418
|
+
.bpmnkit-val-chk input[type=checkbox] { width: 14px; height: 14px; cursor: pointer; }
|
|
1419
|
+
.bpmnkit-val-del {
|
|
1420
|
+
background: none; border: none; cursor: pointer; padding: 2px 6px; border-radius: 4px;
|
|
1421
|
+
color: var(--bpmnkit-fg-muted, #8888a8); font-size: 14px; line-height: 1;
|
|
1422
|
+
}
|
|
1423
|
+
.bpmnkit-val-del:hover { color: var(--bpmnkit-danger, #f87171); background: var(--bpmnkit-surface-2, #1e1e2e); }
|
|
1424
|
+
.bpmnkit-val-add {
|
|
1425
|
+
background: none; border: 1px dashed var(--bpmnkit-border, #2a2a42); border-radius: 6px;
|
|
1426
|
+
padding: 6px 12px; font-size: 12px; cursor: pointer; width: 100%;
|
|
1427
|
+
color: var(--bpmnkit-fg-muted, #8888a8); margin-top: 4px;
|
|
1428
|
+
}
|
|
1429
|
+
.bpmnkit-val-add:hover { border-color: var(--bpmnkit-accent, #6b9df7); color: var(--bpmnkit-accent, #6b9df7); }
|
|
1430
|
+
.bpmnkit-val-actions { display: flex; justify-content: flex-end; gap: 8px; }
|
|
1431
|
+
.bpmnkit-val-btn {
|
|
1432
|
+
padding: 6px 16px; border-radius: 6px; font-size: 12px; font-weight: 500;
|
|
1433
|
+
cursor: pointer; border: 1px solid var(--bpmnkit-border, #2a2a42);
|
|
1434
|
+
background: var(--bpmnkit-surface-2, #1e1e2e); color: var(--bpmnkit-fg, #cdd6f4);
|
|
1435
|
+
}
|
|
1436
|
+
.bpmnkit-val-btn:hover { background: var(--bpmnkit-accent-subtle, rgba(107,157,247,0.15)); }
|
|
1437
|
+
.bpmnkit-val-btn--primary {
|
|
1438
|
+
background: var(--bpmnkit-accent, #6b9df7); color: #fff; border-color: var(--bpmnkit-accent, #6b9df7);
|
|
1439
|
+
}
|
|
1440
|
+
.bpmnkit-val-btn--primary:hover { filter: brightness(1.1); }
|
|
1441
|
+
.bpmnkit-val-summary {
|
|
1442
|
+
background: var(--bpmnkit-surface-2, #1e1e2e); border-radius: 6px; padding: 8px 10px;
|
|
1443
|
+
font-size: 12px; display: flex; flex-direction: column; gap: 3px;
|
|
1444
|
+
}
|
|
1445
|
+
.bpmnkit-val-summary-row { display: flex; gap: 6px; align-items: baseline; }
|
|
1446
|
+
.bpmnkit-val-summary-name { color: var(--bpmnkit-accent-bright, #89b4fa); font-weight: 500; }
|
|
1447
|
+
.bpmnkit-val-summary-meta { color: var(--bpmnkit-fg-muted, #8888a8); }
|
|
1448
|
+
.bpmnkit-val-summary-badge {
|
|
1449
|
+
font-size: 10px; padding: 1px 5px; border-radius: 3px; font-weight: 500;
|
|
1450
|
+
background: var(--bpmnkit-accent-subtle, rgba(107,157,247,0.15));
|
|
1451
|
+
color: var(--bpmnkit-accent-bright, #89b4fa);
|
|
1452
|
+
}
|
|
1453
|
+
.bpmnkit-val-summary-badge--req {
|
|
1454
|
+
background: rgba(249,115,22,0.15); color: #fb923c;
|
|
1455
|
+
}
|
|
1456
|
+
`;
|
|
1457
|
+
function injectValidationModalCss() {
|
|
1458
|
+
const id = "bpmnkit-validation-modal-css";
|
|
1459
|
+
if (document.getElementById(id))
|
|
1460
|
+
return;
|
|
1461
|
+
const style = document.createElement("style");
|
|
1462
|
+
style.id = id;
|
|
1463
|
+
style.textContent = VALIDATION_MODAL_CSS;
|
|
1464
|
+
document.head.appendChild(style);
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Opens the input validation wizard modal.
|
|
1468
|
+
* Resolves with the variable definitions on confirm, or null on cancel.
|
|
1469
|
+
*/
|
|
1470
|
+
function openValidationWizard() {
|
|
1471
|
+
injectValidationModalCss();
|
|
1472
|
+
return new Promise((resolve) => {
|
|
1473
|
+
const rows = [
|
|
1474
|
+
{
|
|
1475
|
+
name: "",
|
|
1476
|
+
type: "string",
|
|
1477
|
+
required: true,
|
|
1478
|
+
min: "",
|
|
1479
|
+
max: "",
|
|
1480
|
+
minLength: "",
|
|
1481
|
+
maxLength: "",
|
|
1482
|
+
pattern: "",
|
|
1483
|
+
},
|
|
1484
|
+
];
|
|
1485
|
+
const overlay = document.createElement("div");
|
|
1486
|
+
overlay.className = "bpmnkit-val-overlay";
|
|
1487
|
+
const dialog = document.createElement("div");
|
|
1488
|
+
dialog.className = "bpmnkit-val-dialog";
|
|
1489
|
+
overlay.appendChild(dialog);
|
|
1490
|
+
const title = document.createElement("h2");
|
|
1491
|
+
title.textContent = "Add Input Validation";
|
|
1492
|
+
dialog.appendChild(title);
|
|
1493
|
+
const hint = document.createElement("p");
|
|
1494
|
+
hint.className = "hint";
|
|
1495
|
+
hint.textContent =
|
|
1496
|
+
"Define the variables this process expects. A validation DMN table and wiring will be inserted after the start event.";
|
|
1497
|
+
dialog.appendChild(hint);
|
|
1498
|
+
const tableWrap = document.createElement("div");
|
|
1499
|
+
tableWrap.className = "bpmnkit-val-table";
|
|
1500
|
+
dialog.appendChild(tableWrap);
|
|
1501
|
+
function renderTable() {
|
|
1502
|
+
tableWrap.innerHTML = "";
|
|
1503
|
+
const table = document.createElement("table");
|
|
1504
|
+
const thead = document.createElement("thead");
|
|
1505
|
+
thead.innerHTML = `<tr>
|
|
1506
|
+
<th style="width:22%">Name</th>
|
|
1507
|
+
<th style="width:14%">Type</th>
|
|
1508
|
+
<th style="width:8%;text-align:center">Req.</th>
|
|
1509
|
+
<th style="width:10%">Min</th>
|
|
1510
|
+
<th style="width:10%">Max</th>
|
|
1511
|
+
<th style="width:10%">MinLen</th>
|
|
1512
|
+
<th style="width:10%">MaxLen</th>
|
|
1513
|
+
<th style="width:10%">Pattern</th>
|
|
1514
|
+
<th style="width:6%"></th>
|
|
1515
|
+
</tr>`;
|
|
1516
|
+
table.appendChild(thead);
|
|
1517
|
+
const tbody = document.createElement("tbody");
|
|
1518
|
+
for (let i = 0; i < rows.length; i++) {
|
|
1519
|
+
const row = rows[i];
|
|
1520
|
+
if (!row)
|
|
1521
|
+
continue;
|
|
1522
|
+
const tr = document.createElement("tr");
|
|
1523
|
+
const isNum = row.type === "number";
|
|
1524
|
+
const isStr = row.type === "string";
|
|
1525
|
+
tr.innerHTML = `
|
|
1526
|
+
<td><input type="text" class="v-name" value="${escHtml(row.name)}" placeholder="variableName"/></td>
|
|
1527
|
+
<td><select class="v-type">
|
|
1528
|
+
<option value="string"${row.type === "string" ? " selected" : ""}>string</option>
|
|
1529
|
+
<option value="number"${row.type === "number" ? " selected" : ""}>number</option>
|
|
1530
|
+
<option value="boolean"${row.type === "boolean" ? " selected" : ""}>boolean</option>
|
|
1531
|
+
<option value="context"${row.type === "context" ? " selected" : ""}>context</option>
|
|
1532
|
+
<option value="list"${row.type === "list" ? " selected" : ""}>list</option>
|
|
1533
|
+
<option value="any"${row.type === "any" ? " selected" : ""}>any</option>
|
|
1534
|
+
</select></td>
|
|
1535
|
+
<td class="bpmnkit-val-chk"><input type="checkbox" class="v-req"${row.required ? " checked" : ""}/></td>
|
|
1536
|
+
<td><input type="number" class="v-min" value="${escHtml(row.min)}" placeholder="—"${!isNum ? " disabled" : ""}/></td>
|
|
1537
|
+
<td><input type="number" class="v-max" value="${escHtml(row.max)}" placeholder="—"${!isNum ? " disabled" : ""}/></td>
|
|
1538
|
+
<td><input type="number" class="v-minlen" value="${escHtml(row.minLength)}" placeholder="—"${!isStr ? " disabled" : ""}/></td>
|
|
1539
|
+
<td><input type="number" class="v-maxlen" value="${escHtml(row.maxLength)}" placeholder="—"${!isStr ? " disabled" : ""}/></td>
|
|
1540
|
+
<td><input type="text" class="v-pattern" value="${escHtml(row.pattern)}" placeholder="regex"${!isStr ? " disabled" : ""}/></td>
|
|
1541
|
+
<td><button class="bpmnkit-val-del v-del" title="Remove">✕</button></td>
|
|
1542
|
+
`;
|
|
1543
|
+
const readRow = (idx) => {
|
|
1544
|
+
const r = rows[idx];
|
|
1545
|
+
if (!r)
|
|
1546
|
+
return;
|
|
1547
|
+
r.name = (tr.querySelector(".v-name")?.value ?? "").trim();
|
|
1548
|
+
r.type = (tr.querySelector(".v-type")?.value ??
|
|
1549
|
+
"string");
|
|
1550
|
+
r.required = tr.querySelector(".v-req")?.checked ?? false;
|
|
1551
|
+
r.min = tr.querySelector(".v-min")?.value ?? "";
|
|
1552
|
+
r.max = tr.querySelector(".v-max")?.value ?? "";
|
|
1553
|
+
r.minLength = tr.querySelector(".v-minlen")?.value ?? "";
|
|
1554
|
+
r.maxLength = tr.querySelector(".v-maxlen")?.value ?? "";
|
|
1555
|
+
r.pattern = tr.querySelector(".v-pattern")?.value ?? "";
|
|
1556
|
+
};
|
|
1557
|
+
tr.querySelector(".v-type")?.addEventListener("change", () => {
|
|
1558
|
+
readRow(i);
|
|
1559
|
+
renderTable();
|
|
1560
|
+
});
|
|
1561
|
+
tr.querySelector(".v-name")?.addEventListener("input", () => readRow(i));
|
|
1562
|
+
tr.querySelector(".v-req")?.addEventListener("change", () => readRow(i));
|
|
1563
|
+
tr.querySelector(".v-min")?.addEventListener("input", () => readRow(i));
|
|
1564
|
+
tr.querySelector(".v-max")?.addEventListener("input", () => readRow(i));
|
|
1565
|
+
tr.querySelector(".v-minlen")?.addEventListener("input", () => readRow(i));
|
|
1566
|
+
tr.querySelector(".v-maxlen")?.addEventListener("input", () => readRow(i));
|
|
1567
|
+
tr.querySelector(".v-pattern")?.addEventListener("input", () => readRow(i));
|
|
1568
|
+
tr.querySelector(".v-del")?.addEventListener("click", () => {
|
|
1569
|
+
readRow(i);
|
|
1570
|
+
rows.splice(i, 1);
|
|
1571
|
+
renderTable();
|
|
1572
|
+
});
|
|
1573
|
+
tbody.appendChild(tr);
|
|
1574
|
+
}
|
|
1575
|
+
table.appendChild(tbody);
|
|
1576
|
+
tableWrap.appendChild(table);
|
|
1577
|
+
const addBtn = document.createElement("button");
|
|
1578
|
+
addBtn.className = "bpmnkit-val-add";
|
|
1579
|
+
addBtn.textContent = "+ Add variable";
|
|
1580
|
+
addBtn.addEventListener("click", () => {
|
|
1581
|
+
rows.push({
|
|
1582
|
+
name: "",
|
|
1583
|
+
type: "string",
|
|
1584
|
+
required: true,
|
|
1585
|
+
min: "",
|
|
1586
|
+
max: "",
|
|
1587
|
+
minLength: "",
|
|
1588
|
+
maxLength: "",
|
|
1589
|
+
pattern: "",
|
|
1590
|
+
});
|
|
1591
|
+
renderTable();
|
|
1592
|
+
const inputs = tableWrap.querySelectorAll(".v-name");
|
|
1593
|
+
inputs[inputs.length - 1]?.focus();
|
|
1594
|
+
});
|
|
1595
|
+
tableWrap.appendChild(addBtn);
|
|
1596
|
+
}
|
|
1597
|
+
renderTable();
|
|
1598
|
+
const actions = document.createElement("div");
|
|
1599
|
+
actions.className = "bpmnkit-val-actions";
|
|
1600
|
+
const cancelBtn = document.createElement("button");
|
|
1601
|
+
cancelBtn.className = "bpmnkit-val-btn";
|
|
1602
|
+
cancelBtn.textContent = "Cancel";
|
|
1603
|
+
cancelBtn.addEventListener("click", () => {
|
|
1604
|
+
overlay.remove();
|
|
1605
|
+
resolve(null);
|
|
1606
|
+
});
|
|
1607
|
+
const generateBtn = document.createElement("button");
|
|
1608
|
+
generateBtn.className = "bpmnkit-val-btn bpmnkit-val-btn--primary";
|
|
1609
|
+
generateBtn.textContent = "Generate Validation";
|
|
1610
|
+
generateBtn.addEventListener("click", () => {
|
|
1611
|
+
const defs = rows
|
|
1612
|
+
.filter((r) => r.name)
|
|
1613
|
+
.map((r) => ({
|
|
1614
|
+
name: r.name,
|
|
1615
|
+
type: r.type,
|
|
1616
|
+
required: r.required,
|
|
1617
|
+
...(r.type === "number" && r.min !== "" ? { min: Number(r.min) } : {}),
|
|
1618
|
+
...(r.type === "number" && r.max !== "" ? { max: Number(r.max) } : {}),
|
|
1619
|
+
...(r.type === "string" && r.minLength !== ""
|
|
1620
|
+
? { minLength: Number(r.minLength) }
|
|
1621
|
+
: {}),
|
|
1622
|
+
...(r.type === "string" && r.maxLength !== ""
|
|
1623
|
+
? { maxLength: Number(r.maxLength) }
|
|
1624
|
+
: {}),
|
|
1625
|
+
...(r.type === "string" && r.pattern ? { pattern: r.pattern } : {}),
|
|
1626
|
+
}));
|
|
1627
|
+
overlay.remove();
|
|
1628
|
+
resolve(defs.length > 0 ? defs : null);
|
|
1629
|
+
});
|
|
1630
|
+
actions.appendChild(cancelBtn);
|
|
1631
|
+
actions.appendChild(generateBtn);
|
|
1632
|
+
dialog.appendChild(actions);
|
|
1633
|
+
overlay.addEventListener("click", (e) => {
|
|
1634
|
+
if (e.target === overlay) {
|
|
1635
|
+
overlay.remove();
|
|
1636
|
+
resolve(null);
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
document.body.appendChild(overlay);
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
function escHtml(s) {
|
|
1643
|
+
return s
|
|
1644
|
+
.replace(/&/g, "&")
|
|
1645
|
+
.replace(/"/g, """)
|
|
1646
|
+
.replace(/</g, "<")
|
|
1647
|
+
.replace(/>/g, ">");
|
|
1648
|
+
}
|
|
1649
|
+
function makeStartEventSchema(callbacks) {
|
|
1650
|
+
return {
|
|
1651
|
+
compact: [{ key: "name", label: "Name", type: "text", placeholder: "Event name" }],
|
|
1652
|
+
groups: [
|
|
1653
|
+
{
|
|
1654
|
+
id: "general",
|
|
1655
|
+
label: "General",
|
|
1656
|
+
fields: [
|
|
1657
|
+
{ key: "name", label: "Name", type: "text", placeholder: "Event name" },
|
|
1658
|
+
{
|
|
1659
|
+
key: "documentation",
|
|
1660
|
+
label: "Documentation",
|
|
1661
|
+
type: "textarea",
|
|
1662
|
+
placeholder: "Add notes or documentation for this element…",
|
|
1663
|
+
},
|
|
1664
|
+
],
|
|
1665
|
+
},
|
|
1666
|
+
{
|
|
1667
|
+
id: "input-validation",
|
|
1668
|
+
label: "Input Validation",
|
|
1669
|
+
fields: [
|
|
1670
|
+
// Shown when no validation is configured
|
|
1671
|
+
{
|
|
1672
|
+
key: "_addValidation",
|
|
1673
|
+
label: "Add Input Validation",
|
|
1674
|
+
type: "action",
|
|
1675
|
+
hint: "Generate a DMN validation table and error path after this start event.",
|
|
1676
|
+
condition: (values) => values._hasValidation !== true,
|
|
1677
|
+
onClick: (_values, setValue) => {
|
|
1678
|
+
void openValidationWizard().then(async (vars) => {
|
|
1679
|
+
if (!vars || vars.length === 0)
|
|
1680
|
+
return;
|
|
1681
|
+
const startEventId = _values._elementId;
|
|
1682
|
+
if (!startEventId)
|
|
1683
|
+
return;
|
|
1684
|
+
const decId = validationDecisionId(startEventId);
|
|
1685
|
+
const dmnXml = buildValidationDmn(startEventId, vars);
|
|
1686
|
+
const fileName = `${startEventId}_validation.dmn`;
|
|
1687
|
+
callbacks.onCreateValidationDmn?.(dmnXml, fileName, decId);
|
|
1688
|
+
callbacks.applyChange?.((defs) => insertValidationStructure(defs, startEventId, decId));
|
|
1689
|
+
setValue("_hasValidation", true);
|
|
1690
|
+
setValue("_decisionId", decId);
|
|
1691
|
+
});
|
|
1692
|
+
},
|
|
1693
|
+
},
|
|
1694
|
+
// Shown when validation is already configured
|
|
1695
|
+
{
|
|
1696
|
+
key: "_decisionId",
|
|
1697
|
+
label: "Decision ID",
|
|
1698
|
+
type: "text",
|
|
1699
|
+
condition: (values) => values._hasValidation === true,
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
key: "_editValidation",
|
|
1703
|
+
label: "Edit Validation DMN",
|
|
1704
|
+
type: "action",
|
|
1705
|
+
hint: "Open the validation decision table in the DMN editor.",
|
|
1706
|
+
condition: (values) => values._hasValidation === true,
|
|
1707
|
+
onClick: (values) => {
|
|
1708
|
+
const decId = values._decisionId;
|
|
1709
|
+
if (decId)
|
|
1710
|
+
callbacks.onEditValidationDmn?.(decId);
|
|
1711
|
+
},
|
|
1712
|
+
},
|
|
1713
|
+
{
|
|
1714
|
+
key: "_removeValidation",
|
|
1715
|
+
label: "Remove Validation",
|
|
1716
|
+
type: "action",
|
|
1717
|
+
hint: "Delete the validation Business Rule Task, gateway, and error end event.",
|
|
1718
|
+
condition: (values) => values._hasValidation === true,
|
|
1719
|
+
onClick: (values, setValue) => {
|
|
1720
|
+
const startEventId = values._elementId;
|
|
1721
|
+
if (!startEventId)
|
|
1722
|
+
return;
|
|
1723
|
+
callbacks.applyChange?.((defs) => removeValidationStructure(defs, startEventId));
|
|
1724
|
+
setValue("_hasValidation", false);
|
|
1725
|
+
setValue("_decisionId", "");
|
|
1726
|
+
},
|
|
1727
|
+
},
|
|
1728
|
+
],
|
|
1729
|
+
},
|
|
1730
|
+
],
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
function makeStartEventAdapter(callbacks) {
|
|
1734
|
+
return {
|
|
1735
|
+
read(defs, id) {
|
|
1736
|
+
const el = findFlowElement(defs, id);
|
|
1737
|
+
const structure = findValidationStructure(defs, id);
|
|
1738
|
+
return {
|
|
1739
|
+
name: el?.name ?? "",
|
|
1740
|
+
documentation: el?.documentation ?? "",
|
|
1741
|
+
_elementId: id,
|
|
1742
|
+
_hasValidation: structure !== null,
|
|
1743
|
+
_decisionId: structure?.decisionId ?? "",
|
|
1744
|
+
};
|
|
1745
|
+
},
|
|
1746
|
+
write(defs, id, values) {
|
|
1747
|
+
return updateFlowElement(defs, id, (el) => ({
|
|
1748
|
+
...el,
|
|
1749
|
+
name: typeof values.name === "string" ? values.name : el.name,
|
|
1750
|
+
documentation: typeof values.documentation === "string"
|
|
1751
|
+
? values.documentation || undefined
|
|
1752
|
+
: el.documentation,
|
|
1753
|
+
}));
|
|
1754
|
+
},
|
|
1755
|
+
resolve(defs, id) {
|
|
1756
|
+
const el = findFlowElement(defs, id);
|
|
1757
|
+
if (!el || el.type !== "startEvent")
|
|
1758
|
+
return null;
|
|
1759
|
+
const defType = el.eventDefinitions[0]?.type;
|
|
1760
|
+
if (!defType)
|
|
1761
|
+
return null;
|
|
1762
|
+
return eventDefToRegistration(defType);
|
|
1763
|
+
},
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1260
1766
|
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
1261
1767
|
/**
|
|
1262
1768
|
* Creates the BPMN config panel extension plugin.
|
|
@@ -1272,10 +1778,17 @@ const BOUNDARY_EVENT_ADAPTER = {
|
|
|
1272
1778
|
*/
|
|
1273
1779
|
export function createConfigPanelBpmnPlugin(configPanel, options = {}) {
|
|
1274
1780
|
const userTaskSchema = makeUserTaskSchema();
|
|
1275
|
-
const businessRuleTaskSchema = makeBusinessRuleTaskSchema();
|
|
1781
|
+
const businessRuleTaskSchema = makeBusinessRuleTaskSchema(options.onEditValidationDmn);
|
|
1276
1782
|
const callActivitySchema = makeCallActivitySchema();
|
|
1277
1783
|
const scriptTaskSchema = makeScriptTaskSchema(options.openFeelPlayground);
|
|
1278
1784
|
const sequenceFlowSchema = makeSequenceFlowSchema(options.openFeelPlayground);
|
|
1785
|
+
const validationCallbacks = {
|
|
1786
|
+
applyChange: options.applyChange,
|
|
1787
|
+
onCreateValidationDmn: options.onCreateValidationDmn,
|
|
1788
|
+
onEditValidationDmn: options.onEditValidationDmn,
|
|
1789
|
+
};
|
|
1790
|
+
const startEventSchema = makeStartEventSchema(validationCallbacks);
|
|
1791
|
+
const startEventAdapter = makeStartEventAdapter(validationCallbacks);
|
|
1279
1792
|
return {
|
|
1280
1793
|
name: "config-panel-bpmn",
|
|
1281
1794
|
install() {
|
|
@@ -1284,7 +1797,7 @@ export function createConfigPanelBpmnPlugin(configPanel, options = {}) {
|
|
|
1284
1797
|
configPanel.registerSchema(type, GENERAL_SCHEMA, GENERAL_ADAPTER);
|
|
1285
1798
|
}
|
|
1286
1799
|
// Events: dispatcher adapters resolve to event-definition-specific schemas
|
|
1287
|
-
configPanel.registerSchema("startEvent",
|
|
1800
|
+
configPanel.registerSchema("startEvent", startEventSchema, startEventAdapter);
|
|
1288
1801
|
configPanel.registerSchema("endEvent", GENERAL_SCHEMA, END_EVENT_ADAPTER);
|
|
1289
1802
|
configPanel.registerSchema("intermediateCatchEvent", GENERAL_SCHEMA, CATCH_EVENT_ADAPTER);
|
|
1290
1803
|
configPanel.registerSchema("intermediateThrowEvent", GENERAL_SCHEMA, THROW_EVENT_ADAPTER);
|
|
@@ -1297,6 +1810,8 @@ export function createConfigPanelBpmnPlugin(configPanel, options = {}) {
|
|
|
1297
1810
|
configPanel.registerSchema("businessRuleTask", businessRuleTaskSchema, BUSINESS_RULE_TASK_ADAPTER);
|
|
1298
1811
|
// Service task: template-aware adapter
|
|
1299
1812
|
configPanel.registerSchema("serviceTask", GENERIC_SERVICE_TASK_SCHEMA, SERVICE_TASK_ADAPTER);
|
|
1813
|
+
// Sub-process: general fields + multi-instance configuration
|
|
1814
|
+
configPanel.registerSchema("subProcess", SUB_PROCESS_SCHEMA, SUB_PROCESS_ADAPTER);
|
|
1300
1815
|
// Ad-hoc subprocess: template-aware adapter (AI Agent pattern)
|
|
1301
1816
|
configPanel.registerSchema("adHocSubProcess", GENERIC_ADHOC_SCHEMA, ADHOC_SUBPROCESS_ADAPTER);
|
|
1302
1817
|
// Script task: FEEL expression + result variable
|
|
@@ -5,6 +5,20 @@ export declare function xmlLocalName(qname: string): string;
|
|
|
5
5
|
export declare function findFlowElement(defs: BpmnDefinitions, id: string): BpmnFlowElement | undefined;
|
|
6
6
|
export declare function updateFlowElement(defs: BpmnDefinitions, id: string, fn: (el: BpmnFlowElement) => BpmnFlowElement): BpmnDefinitions;
|
|
7
7
|
export declare function parseZeebeExtensions(extensionElements: XmlElement[]): ZeebeExtensions;
|
|
8
|
+
/** Parsed `zeebe:loopCharacteristics` extension element from a multi-instance loop. */
|
|
9
|
+
export interface ZeebeLoopCharacteristics {
|
|
10
|
+
inputCollection: string;
|
|
11
|
+
inputElement: string;
|
|
12
|
+
outputCollection?: string;
|
|
13
|
+
outputElement?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Read `zeebe:loopCharacteristics` from a `multiInstanceLoopCharacteristics` extensionElements list. */
|
|
16
|
+
export declare function parseZeebeLoopCharacteristics(extensionElements: XmlElement[]): ZeebeLoopCharacteristics | undefined;
|
|
17
|
+
/** Build a `zeebe:loopCharacteristics` XmlElement from collection/element options. */
|
|
18
|
+
export declare function buildZeebeLoopCharacteristics(opts: {
|
|
19
|
+
inputCollection: string;
|
|
20
|
+
inputElement: string;
|
|
21
|
+
}): XmlElement;
|
|
8
22
|
/** Read the example output JSON string from parsed zeebe extensions. */
|
|
9
23
|
export declare function getExampleOutputJson(ext: ZeebeExtensions): string;
|
|
10
24
|
/**
|
|
@@ -101,6 +101,27 @@ export function parseZeebeExtensions(extensionElements) {
|
|
|
101
101
|
}
|
|
102
102
|
return ext;
|
|
103
103
|
}
|
|
104
|
+
/** Read `zeebe:loopCharacteristics` from a `multiInstanceLoopCharacteristics` extensionElements list. */
|
|
105
|
+
export function parseZeebeLoopCharacteristics(extensionElements) {
|
|
106
|
+
const loopEl = extensionElements.find((e) => xmlLocalName(e.name) === "loopCharacteristics");
|
|
107
|
+
if (!loopEl)
|
|
108
|
+
return undefined;
|
|
109
|
+
return {
|
|
110
|
+
inputCollection: loopEl.attributes.inputCollection ?? "",
|
|
111
|
+
inputElement: loopEl.attributes.inputElement ?? "",
|
|
112
|
+
outputCollection: loopEl.attributes.outputCollection,
|
|
113
|
+
outputElement: loopEl.attributes.outputElement,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** Build a `zeebe:loopCharacteristics` XmlElement from collection/element options. */
|
|
117
|
+
export function buildZeebeLoopCharacteristics(opts) {
|
|
118
|
+
const attrs = {};
|
|
119
|
+
if (opts.inputCollection)
|
|
120
|
+
attrs.inputCollection = opts.inputCollection;
|
|
121
|
+
if (opts.inputElement)
|
|
122
|
+
attrs.inputElement = opts.inputElement;
|
|
123
|
+
return { name: "zeebe:loopCharacteristics", attributes: attrs, children: [] };
|
|
124
|
+
}
|
|
104
125
|
const EXAMPLE_OUTPUT_JSON_KEY = "camundaModeler:exampleOutputJson";
|
|
105
126
|
/** Read the example output JSON string from parsed zeebe extensions. */
|
|
106
127
|
export function getExampleOutputJson(ext) {
|