@bpmn-nova/studio 0.3.0-preview → 0.3.2-preview

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 (63) hide show
  1. package/README.md +245 -20
  2. package/dist/canvas.js +7 -4
  3. package/dist/context-menu.js +2 -2
  4. package/dist/controller.js +84 -6
  5. package/dist/index.d.ts +138 -38
  6. package/dist/index.js +12 -11
  7. package/dist/interactions.js +1 -1
  8. package/dist/modules/bpmn-model/index.d.ts +11 -0
  9. package/dist/modules/bpmn-model/index.js +703 -0
  10. package/dist/modules/core/containment.js +590 -0
  11. package/dist/modules/core/gateway.js +72 -0
  12. package/dist/modules/core/history.js +32 -0
  13. package/dist/modules/core/index.d.ts +268 -0
  14. package/dist/modules/core/index.js +7 -0
  15. package/dist/modules/core/layout.js +644 -0
  16. package/dist/modules/core/model.js +287 -0
  17. package/dist/modules/core/runtime-transition-route.js +360 -0
  18. package/dist/modules/core/scope.js +99 -0
  19. package/dist/modules/designer/index.d.ts +79 -0
  20. package/dist/modules/designer/index.js +607 -0
  21. package/dist/modules/engine-activiti/index.d.ts +19 -0
  22. package/dist/modules/engine-activiti/index.js +160 -0
  23. package/dist/modules/engine-flowable/index.d.ts +19 -0
  24. package/dist/modules/engine-flowable/index.js +160 -0
  25. package/dist/modules/export-svg/index.d.ts +112 -0
  26. package/dist/modules/export-svg/index.js +2 -0
  27. package/dist/modules/export-svg/preview.js +327 -0
  28. package/dist/modules/export-svg/render.js +718 -0
  29. package/dist/modules/icons/index.d.ts +24 -0
  30. package/dist/modules/icons/index.js +264 -0
  31. package/dist/modules/palette/index.d.ts +74 -0
  32. package/dist/modules/palette/index.js +99 -0
  33. package/dist/modules/palette/panel.js +99 -0
  34. package/dist/modules/properties/index.d.ts +20 -0
  35. package/dist/modules/properties/index.js +19 -0
  36. package/dist/modules/properties-activiti/index.d.ts +3 -0
  37. package/dist/modules/properties-activiti/index.js +97 -0
  38. package/dist/modules/properties-bpmn/index.d.ts +3 -0
  39. package/dist/modules/properties-bpmn/index.js +518 -0
  40. package/dist/modules/properties-core/index.d.ts +124 -0
  41. package/dist/modules/properties-core/index.js +312 -0
  42. package/dist/modules/properties-flowable/index.d.ts +3 -0
  43. package/dist/modules/properties-flowable/index.js +114 -0
  44. package/dist/modules/properties-renderer/index.d.ts +25 -0
  45. package/dist/modules/properties-renderer/index.js +491 -0
  46. package/dist/modules/renderer-svg/index.d.ts +118 -0
  47. package/dist/modules/renderer-svg/index.js +1460 -0
  48. package/dist/modules/runtime/index.d.ts +169 -0
  49. package/dist/modules/runtime/index.js +535 -0
  50. package/dist/modules/theme/index.d.ts +95 -0
  51. package/dist/modules/theme/index.js +368 -0
  52. package/dist/modules/viewer/index.d.ts +265 -0
  53. package/dist/modules/viewer/index.js +1011 -0
  54. package/dist/modules/viewer/runtime-content.js +123 -0
  55. package/dist/modules/viewer/runtime-details-motion.js +228 -0
  56. package/dist/modules/viewer/runtime-trace.js +574 -0
  57. package/dist/modules/viewer/timeline.js +276 -0
  58. package/dist/selection-layout.js +1 -1
  59. package/dist/shell.js +210 -26
  60. package/dist/styles.css +116 -7
  61. package/llms-full.txt +3082 -0
  62. package/llms.txt +225 -0
  63. package/package.json +39 -16
@@ -0,0 +1,160 @@
1
+ const escapeXml = (value = '') => String(value).replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&apos;');
2
+
3
+ function implementationAttrs(prefix, item = {}) {
4
+ const type = item.implementationType || 'class';
5
+ const value = item.implementation || '';
6
+ return value ? ` ${type}="${escapeXml(value)}"` : '';
7
+ }
8
+
9
+ function parseImplementation(element, prefix, namespace) {
10
+ const get = (name) => element.getAttributeNS(namespace, name) || element.getAttribute(`${prefix}:${name}`) || '';
11
+ const found = [['class', get('class')], ['delegateExpression', get('delegateExpression')], ['expression', get('expression')]].find(([, value]) => value);
12
+ return found ? { implementationType: found[0], implementation: found[1] } : {};
13
+ }
14
+
15
+ function parseExtensionProperties(element, prefix, namespace) {
16
+ const properties = { executionListeners: [], taskListeners: [], fields: [] };
17
+ const ext = Array.from(element.children || []).find((child) => child.localName === 'extensionElements');
18
+ if (ext) {
19
+ for (const child of Array.from(ext.children || [])) {
20
+ if (child.localName === 'executionListener') {
21
+ properties.executionListeners.push({ event: child.getAttribute('event') || 'start', ...parseImplementation(child, prefix, namespace) });
22
+ } else if (child.localName === 'taskListener') {
23
+ properties.taskListeners.push({ event: child.getAttribute('event') || 'create', ...parseImplementation(child, prefix, namespace) });
24
+ } else if (child.localName === 'field') {
25
+ const string = Array.from(child.children || []).find((item) => item.localName === 'string');
26
+ const expression = Array.from(child.children || []).find((item) => item.localName === 'expression');
27
+ properties.fields.push({ name: child.getAttribute('name') || '', type: expression ? 'expression' : 'string', value: (expression || string)?.textContent || '' });
28
+ } else if (child.localName === 'failedJobRetryTimeCycle') {
29
+ properties.failedJobRetryTimeCycle = child.textContent?.trim() || '';
30
+ }
31
+ }
32
+ }
33
+ const inParameters = [];
34
+ const outParameters = [];
35
+ for (const child of Array.from(element.children || [])) {
36
+ if (child.localName !== 'in' && child.localName !== 'out') continue;
37
+ const row = { source: child.getAttribute('source') || '', sourceExpression: child.getAttribute('sourceExpression') || '', target: child.getAttribute('target') || '' };
38
+ (child.localName === 'in' ? inParameters : outParameters).push(row);
39
+ }
40
+ if (inParameters.length) properties.inParameters = inParameters;
41
+ if (outParameters.length) properties.outParameters = outParameters;
42
+ return properties;
43
+ }
44
+
45
+ function listenerXml(prefix, kind, listener) {
46
+ return `<${prefix}:${kind} event="${escapeXml(listener.event || (kind === 'taskListener' ? 'create' : 'start'))}"${implementationAttrs(prefix, listener)} />`;
47
+ }
48
+
49
+ export const activitiProfile = {
50
+ id: 'activiti',
51
+ label: 'Activiti',
52
+ prefix: 'activiti',
53
+ namespace: 'http://activiti.org/bpmn',
54
+
55
+ parseProcess(element) {
56
+ const get = (name) => element.getAttributeNS(this.namespace, name) || element.getAttribute(`${this.prefix}:${name}`) || '';
57
+ return {
58
+ candidateStarterUsers: get('candidateStarterUsers'),
59
+ candidateStarterGroups: get('candidateStarterGroups'),
60
+ ...parseExtensionProperties(element, this.prefix, this.namespace),
61
+ };
62
+ },
63
+
64
+ processAttributes(model) {
65
+ const p = model.properties || {};
66
+ return {
67
+ ...(p.candidateStarterUsers ? { [`${this.prefix}:candidateStarterUsers`]: p.candidateStarterUsers } : {}),
68
+ ...(p.candidateStarterGroups ? { [`${this.prefix}:candidateStarterGroups`]: p.candidateStarterGroups } : {}),
69
+ ...(p.extensionAttributes || {}),
70
+ };
71
+ },
72
+
73
+ parseNode(element) {
74
+ const get = (name) => element.getAttributeNS(this.namespace, name) || element.getAttribute(`${this.prefix}:${name}`) || '';
75
+ const local = element.localName;
76
+ const common = {
77
+ async: get('async') === 'true',
78
+ exclusive: get('exclusive') !== 'false',
79
+ asyncLeave: get('asyncLeave') === 'true',
80
+ skipExpression: get('skipExpression'),
81
+ ...parseExtensionProperties(element, this.prefix, this.namespace),
82
+ };
83
+ if (local === 'userTask') Object.assign(common, {
84
+ assignee: get('assignee'), owner: get('owner'), candidateUsers: get('candidateUsers'), candidateGroups: get('candidateGroups'), formKey: get('formKey'), dueDate: get('dueDate'), priority: get('priority'), category: get('category'),
85
+ });
86
+ if (['serviceTask', 'sendTask', 'businessRuleTask'].includes(local)) Object.assign(common, parseImplementation(element, this.prefix, this.namespace), { resultVariable: get('resultVariable') || get('resultVariableName') });
87
+ if (local === 'scriptTask') common.resultVariable = get('resultVariable') || '';
88
+ if (local === 'callActivity') Object.assign(common, { businessKey: get('businessKey'), inheritBusinessKey: get('inheritBusinessKey') === 'true', inheritVariables: get('inheritVariables') === 'true', sameDeployment: get('sameDeployment') === 'true', processInstanceName: get('processInstanceName') });
89
+ return common;
90
+ },
91
+
92
+ nodeAttributes(node) {
93
+ const p = node.properties || {};
94
+ const attrs = { ...(p.extensionAttributes || {}) };
95
+ if (p.async) attrs[`${this.prefix}:async`] = 'true';
96
+ if (p.exclusive === false) attrs[`${this.prefix}:exclusive`] = 'false';
97
+ if (p.asyncLeave) attrs[`${this.prefix}:asyncLeave`] = 'true';
98
+ if (p.skipExpression) attrs[`${this.prefix}:skipExpression`] = p.skipExpression;
99
+ if (node.type === 'userTask') Object.assign(attrs, {
100
+ ...(p.assignee ? { [`${this.prefix}:assignee`]: p.assignee } : {}),
101
+ ...(p.owner ? { [`${this.prefix}:owner`]: p.owner } : {}),
102
+ ...(p.candidateUsers ? { [`${this.prefix}:candidateUsers`]: p.candidateUsers } : {}),
103
+ ...(p.candidateGroups ? { [`${this.prefix}:candidateGroups`]: p.candidateGroups } : {}),
104
+ ...(p.formKey ? { [`${this.prefix}:formKey`]: p.formKey } : {}),
105
+ ...(p.dueDate ? { [`${this.prefix}:dueDate`]: p.dueDate } : {}),
106
+ ...(p.priority !== '' && p.priority != null ? { [`${this.prefix}:priority`]: p.priority } : {}),
107
+ ...(p.category ? { [`${this.prefix}:category`]: p.category } : {}),
108
+ });
109
+ if (['serviceTask', 'sendTask', 'businessRuleTask'].includes(node.type) && p.implementation) attrs[`${this.prefix}:${p.implementationType || 'class'}`] = p.implementation;
110
+ if (['serviceTask', 'sendTask', 'businessRuleTask', 'scriptTask'].includes(node.type) && p.resultVariable) attrs[`${this.prefix}:resultVariable`] = p.resultVariable;
111
+ if (node.type === 'callActivity') Object.assign(attrs, {
112
+ ...(p.businessKey ? { [`${this.prefix}:businessKey`]: p.businessKey } : {}),
113
+ ...(p.inheritBusinessKey ? { [`${this.prefix}:inheritBusinessKey`]: 'true' } : {}),
114
+ ...(p.inheritVariables ? { [`${this.prefix}:inheritVariables`]: 'true' } : {}),
115
+ ...(p.sameDeployment ? { [`${this.prefix}:sameDeployment`]: 'true' } : {}),
116
+ ...(p.processInstanceName ? { [`${this.prefix}:processInstanceName`]: p.processInstanceName } : {}),
117
+ });
118
+ return attrs;
119
+ },
120
+
121
+ processExtensionElements(model) {
122
+ return (model.properties?.executionListeners || []).filter((item) => item.implementation).map((item) => listenerXml(this.prefix, 'executionListener', item));
123
+ },
124
+
125
+ nodeExtensionElements(node) {
126
+ const p = node.properties || {};
127
+ const result = [];
128
+ for (const listener of p.executionListeners || []) if (listener.implementation) result.push(listenerXml(this.prefix, 'executionListener', listener));
129
+ for (const listener of p.taskListeners || []) if (listener.implementation) result.push(listenerXml(this.prefix, 'taskListener', listener));
130
+ for (const field of p.fields || []) {
131
+ if (!field.name) continue;
132
+ const tag = field.type === 'expression' ? 'expression' : 'string';
133
+ result.push(`<${this.prefix}:field name="${escapeXml(field.name)}"><${this.prefix}:${tag}>${escapeXml(field.value || '')}</${this.prefix}:${tag}></${this.prefix}:field>`);
134
+ }
135
+ if (p.failedJobRetryTimeCycle) result.push(`<${this.prefix}:failedJobRetryTimeCycle>${escapeXml(p.failedJobRetryTimeCycle)}</${this.prefix}:failedJobRetryTimeCycle>`);
136
+ return result;
137
+ },
138
+
139
+ nodeChildElements(node) {
140
+ if (node.type !== 'callActivity') return [];
141
+ const result = [];
142
+ for (const row of node.properties?.inParameters || []) {
143
+ const attrs = [row.source ? `source="${escapeXml(row.source)}"` : '', row.sourceExpression ? `sourceExpression="${escapeXml(row.sourceExpression)}"` : '', row.target ? `target="${escapeXml(row.target)}"` : ''].filter(Boolean).join(' ');
144
+ if (attrs) result.push(`<${this.prefix}:in ${attrs} />`);
145
+ }
146
+ for (const row of node.properties?.outParameters || []) {
147
+ const attrs = [row.source ? `source="${escapeXml(row.source)}"` : '', row.sourceExpression ? `sourceExpression="${escapeXml(row.sourceExpression)}"` : '', row.target ? `target="${escapeXml(row.target)}"` : ''].filter(Boolean).join(' ');
148
+ if (attrs) result.push(`<${this.prefix}:out ${attrs} />`);
149
+ }
150
+ return result;
151
+ },
152
+
153
+ parseEdge(element) {
154
+ return { ...parseExtensionProperties(element, this.prefix, this.namespace) };
155
+ },
156
+
157
+ edgeExtensionElements(edge) {
158
+ return (edge.properties?.executionListeners || []).filter((item) => item.implementation).map((item) => listenerXml(this.prefix, 'executionListener', item));
159
+ },
160
+ };
@@ -0,0 +1,19 @@
1
+ import type { BpmnEdge, BpmnNode, ProcessModel } from '../core/index.js'
2
+
3
+ export interface FlowableEngineProfile {
4
+ id: 'flowable'
5
+ label: 'Flowable'
6
+ prefix: 'flowable'
7
+ namespace: 'http://flowable.org/bpmn'
8
+ parseProcess(element: Element): Record<string, unknown>
9
+ processAttributes(model: ProcessModel): Record<string, unknown>
10
+ parseNode(element: Element): Record<string, unknown>
11
+ nodeAttributes(node: BpmnNode): Record<string, unknown>
12
+ processExtensionElements(model: ProcessModel): string[]
13
+ nodeExtensionElements(node: BpmnNode): string[]
14
+ nodeChildElements(node: BpmnNode): string[]
15
+ parseEdge(element: Element): Record<string, unknown>
16
+ edgeExtensionElements(edge: BpmnEdge): string[]
17
+ }
18
+
19
+ export const flowableProfile: FlowableEngineProfile
@@ -0,0 +1,160 @@
1
+ const escapeXml = (value = '') => String(value).replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&apos;');
2
+
3
+ function implementationAttrs(prefix, item = {}) {
4
+ const type = item.implementationType || 'class';
5
+ const value = item.implementation || '';
6
+ return value ? ` ${type}="${escapeXml(value)}"` : '';
7
+ }
8
+
9
+ function parseImplementation(element, prefix, namespace) {
10
+ const get = (name) => element.getAttributeNS(namespace, name) || element.getAttribute(`${prefix}:${name}`) || '';
11
+ const found = [['class', get('class')], ['delegateExpression', get('delegateExpression')], ['expression', get('expression')]].find(([, value]) => value);
12
+ return found ? { implementationType: found[0], implementation: found[1] } : {};
13
+ }
14
+
15
+ function parseExtensionProperties(element, prefix, namespace) {
16
+ const properties = { executionListeners: [], taskListeners: [], fields: [] };
17
+ const ext = Array.from(element.children || []).find((child) => child.localName === 'extensionElements');
18
+ if (ext) {
19
+ for (const child of Array.from(ext.children || [])) {
20
+ if (child.localName === 'executionListener') {
21
+ properties.executionListeners.push({ event: child.getAttribute('event') || 'start', ...parseImplementation(child, prefix, namespace) });
22
+ } else if (child.localName === 'taskListener') {
23
+ properties.taskListeners.push({ event: child.getAttribute('event') || 'create', ...parseImplementation(child, prefix, namespace) });
24
+ } else if (child.localName === 'field') {
25
+ const string = Array.from(child.children || []).find((item) => item.localName === 'string');
26
+ const expression = Array.from(child.children || []).find((item) => item.localName === 'expression');
27
+ properties.fields.push({ name: child.getAttribute('name') || '', type: expression ? 'expression' : 'string', value: (expression || string)?.textContent || '' });
28
+ } else if (child.localName === 'failedJobRetryTimeCycle') {
29
+ properties.failedJobRetryTimeCycle = child.textContent?.trim() || '';
30
+ }
31
+ }
32
+ }
33
+ const inParameters = [];
34
+ const outParameters = [];
35
+ for (const child of Array.from(element.children || [])) {
36
+ if (child.localName !== 'in' && child.localName !== 'out') continue;
37
+ const row = { source: child.getAttribute('source') || '', sourceExpression: child.getAttribute('sourceExpression') || '', target: child.getAttribute('target') || '' };
38
+ (child.localName === 'in' ? inParameters : outParameters).push(row);
39
+ }
40
+ if (inParameters.length) properties.inParameters = inParameters;
41
+ if (outParameters.length) properties.outParameters = outParameters;
42
+ return properties;
43
+ }
44
+
45
+ function listenerXml(prefix, kind, listener) {
46
+ return `<${prefix}:${kind} event="${escapeXml(listener.event || (kind === 'taskListener' ? 'create' : 'start'))}"${implementationAttrs(prefix, listener)} />`;
47
+ }
48
+
49
+ export const flowableProfile = {
50
+ id: 'flowable',
51
+ label: 'Flowable',
52
+ prefix: 'flowable',
53
+ namespace: 'http://flowable.org/bpmn',
54
+
55
+ parseProcess(element) {
56
+ const get = (name) => element.getAttributeNS(this.namespace, name) || element.getAttribute(`${this.prefix}:${name}`) || '';
57
+ return {
58
+ candidateStarterUsers: get('candidateStarterUsers'),
59
+ candidateStarterGroups: get('candidateStarterGroups'),
60
+ ...parseExtensionProperties(element, this.prefix, this.namespace),
61
+ };
62
+ },
63
+
64
+ processAttributes(model) {
65
+ const p = model.properties || {};
66
+ return {
67
+ ...(p.candidateStarterUsers ? { [`${this.prefix}:candidateStarterUsers`]: p.candidateStarterUsers } : {}),
68
+ ...(p.candidateStarterGroups ? { [`${this.prefix}:candidateStarterGroups`]: p.candidateStarterGroups } : {}),
69
+ ...(p.extensionAttributes || {}),
70
+ };
71
+ },
72
+
73
+ parseNode(element) {
74
+ const get = (name) => element.getAttributeNS(this.namespace, name) || element.getAttribute(`${this.prefix}:${name}`) || '';
75
+ const local = element.localName;
76
+ const common = {
77
+ async: get('async') === 'true',
78
+ exclusive: get('exclusive') !== 'false',
79
+ asyncLeave: get('asyncLeave') === 'true',
80
+ skipExpression: get('skipExpression'),
81
+ ...parseExtensionProperties(element, this.prefix, this.namespace),
82
+ };
83
+ if (local === 'userTask') Object.assign(common, {
84
+ assignee: get('assignee'), owner: get('owner'), candidateUsers: get('candidateUsers'), candidateGroups: get('candidateGroups'), formKey: get('formKey'), dueDate: get('dueDate'), priority: get('priority'), category: get('category'),
85
+ });
86
+ if (['serviceTask', 'sendTask', 'businessRuleTask'].includes(local)) Object.assign(common, parseImplementation(element, this.prefix, this.namespace), { resultVariable: get('resultVariable') || get('resultVariableName') });
87
+ if (local === 'scriptTask') common.resultVariable = get('resultVariable') || '';
88
+ if (local === 'callActivity') Object.assign(common, { businessKey: get('businessKey'), inheritBusinessKey: get('inheritBusinessKey') === 'true', inheritVariables: get('inheritVariables') === 'true', sameDeployment: get('sameDeployment') === 'true', processInstanceName: get('processInstanceName') });
89
+ return common;
90
+ },
91
+
92
+ nodeAttributes(node) {
93
+ const p = node.properties || {};
94
+ const attrs = { ...(p.extensionAttributes || {}) };
95
+ if (p.async) attrs[`${this.prefix}:async`] = 'true';
96
+ if (p.exclusive === false) attrs[`${this.prefix}:exclusive`] = 'false';
97
+ if (p.asyncLeave) attrs[`${this.prefix}:asyncLeave`] = 'true';
98
+ if (p.skipExpression) attrs[`${this.prefix}:skipExpression`] = p.skipExpression;
99
+ if (node.type === 'userTask') Object.assign(attrs, {
100
+ ...(p.assignee ? { [`${this.prefix}:assignee`]: p.assignee } : {}),
101
+ ...(p.owner ? { [`${this.prefix}:owner`]: p.owner } : {}),
102
+ ...(p.candidateUsers ? { [`${this.prefix}:candidateUsers`]: p.candidateUsers } : {}),
103
+ ...(p.candidateGroups ? { [`${this.prefix}:candidateGroups`]: p.candidateGroups } : {}),
104
+ ...(p.formKey ? { [`${this.prefix}:formKey`]: p.formKey } : {}),
105
+ ...(p.dueDate ? { [`${this.prefix}:dueDate`]: p.dueDate } : {}),
106
+ ...(p.priority !== '' && p.priority != null ? { [`${this.prefix}:priority`]: p.priority } : {}),
107
+ ...(p.category ? { [`${this.prefix}:category`]: p.category } : {}),
108
+ });
109
+ if (['serviceTask', 'sendTask', 'businessRuleTask'].includes(node.type) && p.implementation) attrs[`${this.prefix}:${p.implementationType || 'class'}`] = p.implementation;
110
+ if (['serviceTask', 'sendTask', 'businessRuleTask', 'scriptTask'].includes(node.type) && p.resultVariable) attrs[`${this.prefix}:resultVariable`] = p.resultVariable;
111
+ if (node.type === 'callActivity') Object.assign(attrs, {
112
+ ...(p.businessKey ? { [`${this.prefix}:businessKey`]: p.businessKey } : {}),
113
+ ...(p.inheritBusinessKey ? { [`${this.prefix}:inheritBusinessKey`]: 'true' } : {}),
114
+ ...(p.inheritVariables ? { [`${this.prefix}:inheritVariables`]: 'true' } : {}),
115
+ ...(p.sameDeployment ? { [`${this.prefix}:sameDeployment`]: 'true' } : {}),
116
+ ...(p.processInstanceName ? { [`${this.prefix}:processInstanceName`]: p.processInstanceName } : {}),
117
+ });
118
+ return attrs;
119
+ },
120
+
121
+ processExtensionElements(model) {
122
+ return (model.properties?.executionListeners || []).filter((item) => item.implementation).map((item) => listenerXml(this.prefix, 'executionListener', item));
123
+ },
124
+
125
+ nodeExtensionElements(node) {
126
+ const p = node.properties || {};
127
+ const result = [];
128
+ for (const listener of p.executionListeners || []) if (listener.implementation) result.push(listenerXml(this.prefix, 'executionListener', listener));
129
+ for (const listener of p.taskListeners || []) if (listener.implementation) result.push(listenerXml(this.prefix, 'taskListener', listener));
130
+ for (const field of p.fields || []) {
131
+ if (!field.name) continue;
132
+ const tag = field.type === 'expression' ? 'expression' : 'string';
133
+ result.push(`<${this.prefix}:field name="${escapeXml(field.name)}"><${this.prefix}:${tag}>${escapeXml(field.value || '')}</${this.prefix}:${tag}></${this.prefix}:field>`);
134
+ }
135
+ if (p.failedJobRetryTimeCycle) result.push(`<${this.prefix}:failedJobRetryTimeCycle>${escapeXml(p.failedJobRetryTimeCycle)}</${this.prefix}:failedJobRetryTimeCycle>`);
136
+ return result;
137
+ },
138
+
139
+ nodeChildElements(node) {
140
+ if (node.type !== 'callActivity') return [];
141
+ const result = [];
142
+ for (const row of node.properties?.inParameters || []) {
143
+ const attrs = [row.source ? `source="${escapeXml(row.source)}"` : '', row.sourceExpression ? `sourceExpression="${escapeXml(row.sourceExpression)}"` : '', row.target ? `target="${escapeXml(row.target)}"` : ''].filter(Boolean).join(' ');
144
+ if (attrs) result.push(`<${this.prefix}:in ${attrs} />`);
145
+ }
146
+ for (const row of node.properties?.outParameters || []) {
147
+ const attrs = [row.source ? `source="${escapeXml(row.source)}"` : '', row.sourceExpression ? `sourceExpression="${escapeXml(row.sourceExpression)}"` : '', row.target ? `target="${escapeXml(row.target)}"` : ''].filter(Boolean).join(' ');
148
+ if (attrs) result.push(`<${this.prefix}:out ${attrs} />`);
149
+ }
150
+ return result;
151
+ },
152
+
153
+ parseEdge(element) {
154
+ return { ...parseExtensionProperties(element, this.prefix, this.namespace) };
155
+ },
156
+
157
+ edgeExtensionElements(edge) {
158
+ return (edge.properties?.executionListeners || []).filter((item) => item.implementation).map((item) => listenerXml(this.prefix, 'executionListener', item));
159
+ },
160
+ };
@@ -0,0 +1,112 @@
1
+ import type { BpmnNode, ProcessModel } from '../core/index.js'
2
+ import type { IconRegistry, NodeVisualResolution } from '../icons/index.js'
3
+ import type { NodeRuntimePresentation, ProcessInstanceSnapshot, RuntimeApprovalActionPresentation, RuntimePresentation, RuntimeAssetRef } from '../runtime/index.js'
4
+ import type { NovaResolvedTheme, NovaThemeSnapshot, RuntimeAppearanceOptions, RuntimeAppearanceResolver, ThemeController } from '../theme/index.js'
5
+
6
+ export type SvgExportTheme = 'current' | NovaResolvedTheme
7
+ export interface SvgExportOptions {
8
+ theme?: SvgExportTheme
9
+ transparentBackground?: boolean
10
+ padding?: number
11
+ filename?: string
12
+ title?: string
13
+ signal?: AbortSignal
14
+ }
15
+ export interface SvgExportWarning {
16
+ code: string
17
+ message: string
18
+ elementId?: string
19
+ assetId?: string
20
+ }
21
+ export interface SvgExportArtifact {
22
+ svg: string
23
+ blob: Blob
24
+ filename: string
25
+ mimeType: 'image/svg+xml'
26
+ width: number
27
+ height: number
28
+ viewBox: { x: number; y: number; width: number; height: number }
29
+ warnings: readonly SvgExportWarning[]
30
+ }
31
+ export interface SvgNodeRendererContext {
32
+ container: SVGGElement
33
+ node: BpmnNode
34
+ definition: Record<string, unknown>
35
+ runtimePresentation: NodeRuntimePresentation | null
36
+ visual: NodeVisualResolution
37
+ model: ProcessModel
38
+ themeSnapshot: NovaThemeSnapshot
39
+ iconRegistry: IconRegistry
40
+ }
41
+ export type SvgNodeRenderer = (context: SvgNodeRendererContext) => void | boolean
42
+ export type RuntimeTimelineSvgRenderer = (context: {
43
+ container: SVGGElement
44
+ document: Document
45
+ projection: Record<string, unknown>
46
+ model: ProcessModel
47
+ runtime: ProcessInstanceSnapshot | null
48
+ themeSnapshot: NovaThemeSnapshot
49
+ appearance: RuntimeAppearanceResolver
50
+ resolveAsset?: SvgRuntimeAssetResolver
51
+ signal?: AbortSignal
52
+ }) => void | { height?: number } | Promise<void | { height?: number }>
53
+ export type SvgRuntimeAssetResolver = (asset: RuntimeAssetRef, purpose: 'export', action: RuntimeApprovalActionPresentation) => string | null | Promise<string | null>
54
+ export interface DiagramSvgExportContext {
55
+ document?: Document
56
+ root?: HTMLElement
57
+ model: ProcessModel
58
+ visualModel?: ProcessModel
59
+ runtime?: ProcessInstanceSnapshot | null
60
+ runtimePresentation?: RuntimePresentation
61
+ runtimeAppearance?: RuntimeAppearanceOptions | RuntimeAppearanceResolver | null
62
+ themeController?: ThemeController
63
+ themeSnapshot?: NovaThemeSnapshot
64
+ iconRegistry?: IconRegistry
65
+ nodeRenderers?: Record<string, SvgNodeRenderer>
66
+ htmlNodeRenderers?: Record<string, Function>
67
+ htmlNodeRenderer?: Function
68
+ mode?: 'design' | 'viewer' | 'instance'
69
+ label?: string
70
+ }
71
+ export interface RuntimeTimelineSvgExportContext {
72
+ document?: Document
73
+ root?: HTMLElement
74
+ model: ProcessModel
75
+ runtime: ProcessInstanceSnapshot | null
76
+ projection: { items: any[]; groups: any[]; links: any[] }
77
+ title?: string | null
78
+ description?: string | null
79
+ runtimeAppearance?: RuntimeAppearanceOptions | RuntimeAppearanceResolver | null
80
+ themeController?: ThemeController
81
+ themeSnapshot?: NovaThemeSnapshot
82
+ iconRegistry?: IconRegistry
83
+ resolveAsset?: SvgRuntimeAssetResolver
84
+ assetCache?: Map<string, string>
85
+ timelineRenderer?: RuntimeTimelineSvgRenderer
86
+ label?: string
87
+ }
88
+ export function sanitizeSvgFilename(value: string, fallback?: string): string
89
+ export function exportDiagramSvg(context: DiagramSvgExportContext, options?: SvgExportOptions): Promise<SvgExportArtifact>
90
+ export function exportRuntimeTimelineSvg(context: RuntimeTimelineSvgExportContext, options?: SvgExportOptions & { width?: number; description?: string }): Promise<SvgExportArtifact>
91
+ export function downloadSvg(artifact: SvgExportArtifact, options?: { document?: Document }): string
92
+
93
+ export interface SvgExportPreviewOptions {
94
+ container: HTMLElement
95
+ createArtifact(options: SvgExportOptions): Promise<SvgExportArtifact>
96
+ title?: string
97
+ initialTheme?: SvgExportTheme
98
+ onClose?: () => void
99
+ onDownload?: (artifact: SvgExportArtifact) => void
100
+ }
101
+ export class SvgExportPreviewController {
102
+ constructor(options: SvgExportPreviewOptions)
103
+ readonly artifact: SvgExportArtifact | null
104
+ readonly closed: boolean
105
+ setTheme(theme: SvgExportTheme): void
106
+ setTransparentBackground(value: boolean): void
107
+ fit(): void
108
+ actualSize(): void
109
+ focus(): void
110
+ close(options?: { immediate?: boolean }): void
111
+ }
112
+ export function openSvgExportPreview(options: SvgExportPreviewOptions): SvgExportPreviewController
@@ -0,0 +1,2 @@
1
+ export { downloadSvg, exportDiagramSvg, exportRuntimeTimelineSvg, sanitizeSvgFilename } from './render.js';
2
+ export { SvgExportPreviewController, openSvgExportPreview } from './preview.js';