eventmodeler 0.5.0 → 0.6.1
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/dist/index.js +6776 -2132
- package/package.json +11 -5
- package/dist/api/index.d.ts +0 -285
- package/dist/api/index.js +0 -323
- package/dist/cloud/slices/index.d.ts +0 -276
- package/dist/cloud/slices/index.js +0 -406
- package/dist/eventmodeler.js +0 -5646
- package/dist/formatters.d.ts +0 -17
- package/dist/formatters.js +0 -482
- package/dist/index.d.ts +0 -2
- package/dist/lib/auth.d.ts +0 -24
- package/dist/lib/auth.js +0 -331
- package/dist/lib/backend.d.ts +0 -43
- package/dist/lib/backend.js +0 -73
- package/dist/lib/chapter-utils.d.ts +0 -13
- package/dist/lib/chapter-utils.js +0 -71
- package/dist/lib/cloud-client.d.ts +0 -69
- package/dist/lib/cloud-client.js +0 -364
- package/dist/lib/config.d.ts +0 -30
- package/dist/lib/config.js +0 -95
- package/dist/lib/diff/merge-rules.d.ts +0 -45
- package/dist/lib/diff/merge-rules.js +0 -210
- package/dist/lib/diff/model-differ.d.ts +0 -8
- package/dist/lib/diff/model-differ.js +0 -568
- package/dist/lib/diff/three-way-merge.d.ts +0 -7
- package/dist/lib/diff/three-way-merge.js +0 -390
- package/dist/lib/diff/types.d.ts +0 -75
- package/dist/lib/diff/types.js +0 -1
- package/dist/lib/element-lookup.d.ts +0 -58
- package/dist/lib/element-lookup.js +0 -126
- package/dist/lib/file-loader.d.ts +0 -8
- package/dist/lib/file-loader.js +0 -108
- package/dist/lib/flow-utils.d.ts +0 -53
- package/dist/lib/flow-utils.js +0 -348
- package/dist/lib/format.d.ts +0 -10
- package/dist/lib/format.js +0 -23
- package/dist/lib/project-config.d.ts +0 -27
- package/dist/lib/project-config.js +0 -83
- package/dist/lib/slice-utils.d.ts +0 -59
- package/dist/lib/slice-utils.js +0 -140
- package/dist/local/slices/index.d.ts +0 -11
- package/dist/local/slices/index.js +0 -13
- package/dist/projection.d.ts +0 -3
- package/dist/projection.js +0 -828
- package/dist/slices/add-field/index.d.ts +0 -8
- package/dist/slices/add-field/index.js +0 -211
- package/dist/slices/add-scenario/index.d.ts +0 -27
- package/dist/slices/add-scenario/index.js +0 -307
- package/dist/slices/codegen-chapter-events/index.d.ts +0 -2
- package/dist/slices/codegen-chapter-events/index.js +0 -145
- package/dist/slices/codegen-slice/index.d.ts +0 -2
- package/dist/slices/codegen-slice/index.js +0 -448
- package/dist/slices/create-automation-slice/index.d.ts +0 -2
- package/dist/slices/create-automation-slice/index.js +0 -304
- package/dist/slices/create-flow/index.d.ts +0 -2
- package/dist/slices/create-flow/index.js +0 -183
- package/dist/slices/create-state-change-slice/index.d.ts +0 -2
- package/dist/slices/create-state-change-slice/index.js +0 -263
- package/dist/slices/create-state-view-slice/index.d.ts +0 -2
- package/dist/slices/create-state-view-slice/index.js +0 -128
- package/dist/slices/diff/index.d.ts +0 -11
- package/dist/slices/diff/index.js +0 -293
- package/dist/slices/export-eventmodel-to-json/index.d.ts +0 -2
- package/dist/slices/export-eventmodel-to-json/index.js +0 -355
- package/dist/slices/git/index.d.ts +0 -2
- package/dist/slices/git/index.js +0 -125
- package/dist/slices/guide/guides/codegen.d.ts +0 -5
- package/dist/slices/guide/guides/codegen.js +0 -339
- package/dist/slices/guide/guides/connect-slices.d.ts +0 -5
- package/dist/slices/guide/guides/connect-slices.js +0 -202
- package/dist/slices/guide/guides/create-slices.d.ts +0 -5
- package/dist/slices/guide/guides/create-slices.js +0 -303
- package/dist/slices/guide/guides/explore.d.ts +0 -5
- package/dist/slices/guide/guides/explore.js +0 -251
- package/dist/slices/guide/guides/information-flow.d.ts +0 -5
- package/dist/slices/guide/guides/information-flow.js +0 -318
- package/dist/slices/guide/guides/scenarios.d.ts +0 -5
- package/dist/slices/guide/guides/scenarios.js +0 -269
- package/dist/slices/guide/index.d.ts +0 -1
- package/dist/slices/guide/index.js +0 -40
- package/dist/slices/import/index.d.ts +0 -8
- package/dist/slices/import/index.js +0 -63
- package/dist/slices/init/index.d.ts +0 -5
- package/dist/slices/init/index.js +0 -80
- package/dist/slices/list-chapters/index.d.ts +0 -3
- package/dist/slices/list-chapters/index.js +0 -21
- package/dist/slices/list-commands/index.d.ts +0 -3
- package/dist/slices/list-commands/index.js +0 -20
- package/dist/slices/list-events/index.d.ts +0 -3
- package/dist/slices/list-events/index.js +0 -98
- package/dist/slices/list-processors/index.d.ts +0 -3
- package/dist/slices/list-processors/index.js +0 -20
- package/dist/slices/list-readmodels/index.d.ts +0 -3
- package/dist/slices/list-readmodels/index.js +0 -21
- package/dist/slices/list-scenarios/index.d.ts +0 -3
- package/dist/slices/list-scenarios/index.js +0 -35
- package/dist/slices/list-screens/index.d.ts +0 -3
- package/dist/slices/list-screens/index.js +0 -47
- package/dist/slices/list-slices/index.d.ts +0 -3
- package/dist/slices/list-slices/index.js +0 -35
- package/dist/slices/login/index.d.ts +0 -1
- package/dist/slices/login/index.js +0 -20
- package/dist/slices/logout/index.d.ts +0 -1
- package/dist/slices/logout/index.js +0 -14
- package/dist/slices/map-fields/index.d.ts +0 -2
- package/dist/slices/map-fields/index.js +0 -269
- package/dist/slices/mark-slice-status/index.d.ts +0 -2
- package/dist/slices/mark-slice-status/index.js +0 -31
- package/dist/slices/merge/index.d.ts +0 -19
- package/dist/slices/merge/index.js +0 -147
- package/dist/slices/open-app/index.d.ts +0 -1
- package/dist/slices/open-app/index.js +0 -36
- package/dist/slices/remove-field/index.d.ts +0 -8
- package/dist/slices/remove-field/index.js +0 -167
- package/dist/slices/remove-scenario/index.d.ts +0 -2
- package/dist/slices/remove-scenario/index.js +0 -77
- package/dist/slices/search/index.d.ts +0 -3
- package/dist/slices/search/index.js +0 -302
- package/dist/slices/show-actor/index.d.ts +0 -4
- package/dist/slices/show-actor/index.js +0 -115
- package/dist/slices/show-aggregate/index.d.ts +0 -3
- package/dist/slices/show-aggregate/index.js +0 -108
- package/dist/slices/show-aggregate-completeness/index.d.ts +0 -4
- package/dist/slices/show-aggregate-completeness/index.js +0 -181
- package/dist/slices/show-chapter/index.d.ts +0 -3
- package/dist/slices/show-chapter/index.js +0 -195
- package/dist/slices/show-command/index.d.ts +0 -3
- package/dist/slices/show-command/index.js +0 -133
- package/dist/slices/show-completeness/index.d.ts +0 -4
- package/dist/slices/show-completeness/index.js +0 -731
- package/dist/slices/show-event/index.d.ts +0 -3
- package/dist/slices/show-event/index.js +0 -118
- package/dist/slices/show-model-summary/index.d.ts +0 -3
- package/dist/slices/show-model-summary/index.js +0 -31
- package/dist/slices/show-processor/index.d.ts +0 -3
- package/dist/slices/show-processor/index.js +0 -111
- package/dist/slices/show-readmodel/index.d.ts +0 -3
- package/dist/slices/show-readmodel/index.js +0 -158
- package/dist/slices/show-scenario/index.d.ts +0 -3
- package/dist/slices/show-scenario/index.js +0 -196
- package/dist/slices/show-screen/index.d.ts +0 -3
- package/dist/slices/show-screen/index.js +0 -139
- package/dist/slices/show-slice/index.d.ts +0 -3
- package/dist/slices/show-slice/index.js +0 -696
- package/dist/slices/update-field/index.d.ts +0 -15
- package/dist/slices/update-field/index.js +0 -208
- package/dist/slices/whoami/index.d.ts +0 -2
- package/dist/slices/whoami/index.js +0 -44
- package/dist/types.d.ts +0 -195
- package/dist/types.js +0 -1
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { escapeXml, outputJson } from '../../lib/format.js';
|
|
2
|
-
import { findElementOrExit } from '../../lib/element-lookup.js';
|
|
3
|
-
// Get screens whose center point is inside the actor bounds
|
|
4
|
-
function getScreensInActor(model, actor) {
|
|
5
|
-
const bounds = {
|
|
6
|
-
left: actor.position.x,
|
|
7
|
-
right: actor.position.x + actor.size.width,
|
|
8
|
-
top: actor.position.y,
|
|
9
|
-
bottom: actor.position.y + actor.size.height,
|
|
10
|
-
};
|
|
11
|
-
function isInActor(pos, width, height) {
|
|
12
|
-
const centerX = pos.x + width / 2;
|
|
13
|
-
const centerY = pos.y + height / 2;
|
|
14
|
-
return centerX >= bounds.left && centerX <= bounds.right && centerY >= bounds.top && centerY <= bounds.bottom;
|
|
15
|
-
}
|
|
16
|
-
return [...model.screens.values()].filter(s => isInActor(s.position, s.width, s.height));
|
|
17
|
-
}
|
|
18
|
-
// Find which slice contains a screen
|
|
19
|
-
function findSliceForScreen(model, screen) {
|
|
20
|
-
for (const slice of model.slices.values()) {
|
|
21
|
-
const centerX = screen.position.x + screen.width / 2;
|
|
22
|
-
const centerY = screen.position.y + screen.height / 2;
|
|
23
|
-
if (centerX >= slice.position.x &&
|
|
24
|
-
centerX <= slice.position.x + slice.size.width &&
|
|
25
|
-
centerY >= slice.position.y &&
|
|
26
|
-
centerY <= slice.position.y + slice.size.height) {
|
|
27
|
-
return slice.name;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
export function showActor(model, actorName, format) {
|
|
33
|
-
// Find the actor
|
|
34
|
-
const actor = findElementOrExit(model.actors, actorName, 'actor');
|
|
35
|
-
// Get screens dynamically based on position (center point inside actor bounds)
|
|
36
|
-
const screensInActor = getScreensInActor(model, actor);
|
|
37
|
-
if (format === 'json') {
|
|
38
|
-
outputJson({
|
|
39
|
-
id: actor.id,
|
|
40
|
-
name: actor.name,
|
|
41
|
-
screensCount: screensInActor.length,
|
|
42
|
-
screens: screensInActor.map(screen => {
|
|
43
|
-
const result = {
|
|
44
|
-
id: screen.id,
|
|
45
|
-
name: screen.name,
|
|
46
|
-
fields: screen.fields.length
|
|
47
|
-
};
|
|
48
|
-
if (screen.originalNodeId) {
|
|
49
|
-
result.linkedCopy = true;
|
|
50
|
-
const original = model.screens.get(screen.originalNodeId);
|
|
51
|
-
const originSlice = original ? findSliceForScreen(model, original) : null;
|
|
52
|
-
if (originSlice)
|
|
53
|
-
result.originSlice = originSlice;
|
|
54
|
-
}
|
|
55
|
-
return result;
|
|
56
|
-
})
|
|
57
|
-
});
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
console.log(`<actor id="${actor.id}" name="${escapeXml(actor.name)}">`);
|
|
61
|
-
console.log(` <screens-count>${screensInActor.length}</screens-count>`);
|
|
62
|
-
if (screensInActor.length > 0) {
|
|
63
|
-
console.log(' <screens>');
|
|
64
|
-
for (const screen of screensInActor) {
|
|
65
|
-
const fieldCount = screen.fields.length;
|
|
66
|
-
if (screen.originalNodeId) {
|
|
67
|
-
// This is a linked copy - show origin info
|
|
68
|
-
const original = model.screens.get(screen.originalNodeId);
|
|
69
|
-
const originSlice = original ? findSliceForScreen(model, original) : null;
|
|
70
|
-
const originAttr = originSlice ? ` origin-slice="${escapeXml(originSlice)}"` : '';
|
|
71
|
-
console.log(` <screen id="${screen.id}" name="${escapeXml(screen.name)}" fields="${fieldCount}" linked-copy="true"${originAttr}/>`);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
console.log(` <screen id="${screen.id}" name="${escapeXml(screen.name)}" fields="${fieldCount}"/>`);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
console.log(' </screens>');
|
|
78
|
-
}
|
|
79
|
-
console.log('</actor>');
|
|
80
|
-
}
|
|
81
|
-
export function listActors(model, format) {
|
|
82
|
-
const actors = [...model.actors.values()];
|
|
83
|
-
if (format === 'json') {
|
|
84
|
-
outputJson({
|
|
85
|
-
actors: actors.map(actor => {
|
|
86
|
-
const screensInActor = getScreensInActor(model, actor);
|
|
87
|
-
const originalCount = screensInActor.filter(s => !s.originalNodeId).length;
|
|
88
|
-
const copyCount = screensInActor.filter(s => s.originalNodeId).length;
|
|
89
|
-
const result = {
|
|
90
|
-
id: actor.id,
|
|
91
|
-
name: actor.name,
|
|
92
|
-
screens: originalCount
|
|
93
|
-
};
|
|
94
|
-
if (copyCount > 0)
|
|
95
|
-
result.copies = copyCount;
|
|
96
|
-
return result;
|
|
97
|
-
})
|
|
98
|
-
});
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
if (actors.length === 0) {
|
|
102
|
-
console.log('<actors/>');
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
console.log('<actors>');
|
|
106
|
-
for (const actor of actors) {
|
|
107
|
-
// Calculate screens dynamically based on position
|
|
108
|
-
const screensInActor = getScreensInActor(model, actor);
|
|
109
|
-
const originalCount = screensInActor.filter(s => !s.originalNodeId).length;
|
|
110
|
-
const copyCount = screensInActor.filter(s => s.originalNodeId).length;
|
|
111
|
-
const copiesAttr = copyCount > 0 ? ` copies="${copyCount}"` : '';
|
|
112
|
-
console.log(` <actor id="${actor.id}" name="${escapeXml(actor.name)}" screens="${originalCount}"${copiesAttr}/>`);
|
|
113
|
-
}
|
|
114
|
-
console.log('</actors>');
|
|
115
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { escapeXml, outputJson } from '../../lib/format.js';
|
|
2
|
-
import { findElementOrExit } from '../../lib/element-lookup.js';
|
|
3
|
-
// Get events whose center point is inside the aggregate bounds
|
|
4
|
-
function getEventsInAggregate(model, aggregate) {
|
|
5
|
-
const bounds = {
|
|
6
|
-
left: aggregate.position.x,
|
|
7
|
-
right: aggregate.position.x + aggregate.size.width,
|
|
8
|
-
top: aggregate.position.y,
|
|
9
|
-
bottom: aggregate.position.y + aggregate.size.height,
|
|
10
|
-
};
|
|
11
|
-
function isInAggregate(pos, width, height) {
|
|
12
|
-
const centerX = pos.x + width / 2;
|
|
13
|
-
const centerY = pos.y + height / 2;
|
|
14
|
-
return centerX >= bounds.left && centerX <= bounds.right && centerY >= bounds.top && centerY <= bounds.bottom;
|
|
15
|
-
}
|
|
16
|
-
// Filter out linked copies
|
|
17
|
-
return [...model.events.values()].filter(e => !e.originalNodeId && isInAggregate(e.position, e.width, e.height));
|
|
18
|
-
}
|
|
19
|
-
// Check if an event has the aggregate's ID field
|
|
20
|
-
function hasIdField(event, aggregate) {
|
|
21
|
-
if (!aggregate.aggregateIdFieldName) {
|
|
22
|
-
return { has: false };
|
|
23
|
-
}
|
|
24
|
-
const field = event.fields.find(f => f.name === aggregate.aggregateIdFieldName);
|
|
25
|
-
if (!field) {
|
|
26
|
-
return { has: false };
|
|
27
|
-
}
|
|
28
|
-
// Check if field type matches (if aggregate has a configured type)
|
|
29
|
-
if (aggregate.aggregateIdFieldType && field.fieldType !== aggregate.aggregateIdFieldType) {
|
|
30
|
-
return { has: true, fieldType: field.fieldType };
|
|
31
|
-
}
|
|
32
|
-
return { has: true, fieldType: field.fieldType };
|
|
33
|
-
}
|
|
34
|
-
export function showAggregate(model, name, format) {
|
|
35
|
-
const aggregate = findElementOrExit(model.aggregates, name, 'aggregate');
|
|
36
|
-
const events = getEventsInAggregate(model, aggregate);
|
|
37
|
-
// Check ID field status for each event
|
|
38
|
-
const eventsWithStatus = events.map(event => {
|
|
39
|
-
const idStatus = hasIdField(event, aggregate);
|
|
40
|
-
return {
|
|
41
|
-
event,
|
|
42
|
-
hasIdField: idStatus.has,
|
|
43
|
-
fieldType: idStatus.fieldType
|
|
44
|
-
};
|
|
45
|
-
});
|
|
46
|
-
// Calculate overall ID field completeness
|
|
47
|
-
const hasIdFieldConfig = !!aggregate.aggregateIdFieldName;
|
|
48
|
-
const eventsWithId = eventsWithStatus.filter(e => e.hasIdField).length;
|
|
49
|
-
const completeness = hasIdFieldConfig && events.length > 0
|
|
50
|
-
? `${eventsWithId}/${events.length}`
|
|
51
|
-
: undefined;
|
|
52
|
-
if (format === 'json') {
|
|
53
|
-
const result = {
|
|
54
|
-
id: aggregate.id,
|
|
55
|
-
name: aggregate.name,
|
|
56
|
-
eventsCount: events.length
|
|
57
|
-
};
|
|
58
|
-
if (aggregate.aggregateIdFieldName) {
|
|
59
|
-
result.idField = {
|
|
60
|
-
name: aggregate.aggregateIdFieldName,
|
|
61
|
-
type: aggregate.aggregateIdFieldType ?? 'any'
|
|
62
|
-
};
|
|
63
|
-
result.idFieldCompleteness = completeness;
|
|
64
|
-
}
|
|
65
|
-
result.events = eventsWithStatus.map(({ event, hasIdField, fieldType }) => {
|
|
66
|
-
const eventResult = {
|
|
67
|
-
id: event.id,
|
|
68
|
-
name: event.name,
|
|
69
|
-
fieldsCount: event.fields.length
|
|
70
|
-
};
|
|
71
|
-
if (hasIdFieldConfig) {
|
|
72
|
-
eventResult.hasIdField = hasIdField;
|
|
73
|
-
if (hasIdField && fieldType && fieldType !== aggregate.aggregateIdFieldType) {
|
|
74
|
-
eventResult.idFieldType = fieldType;
|
|
75
|
-
eventResult.typeMatch = false;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return eventResult;
|
|
79
|
-
});
|
|
80
|
-
outputJson(result);
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
// XML output
|
|
84
|
-
console.log(`<aggregate id="${aggregate.id}" name="${escapeXml(aggregate.name)}">`);
|
|
85
|
-
console.log(` <events-count>${events.length}</events-count>`);
|
|
86
|
-
if (aggregate.aggregateIdFieldName) {
|
|
87
|
-
const typeAttr = aggregate.aggregateIdFieldType ? ` type="${aggregate.aggregateIdFieldType}"` : '';
|
|
88
|
-
console.log(` <id-field name="${escapeXml(aggregate.aggregateIdFieldName)}"${typeAttr}/>`);
|
|
89
|
-
if (completeness) {
|
|
90
|
-
console.log(` <id-field-completeness>${completeness}</id-field-completeness>`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (events.length > 0) {
|
|
94
|
-
console.log(' <events>');
|
|
95
|
-
for (const { event, hasIdField, fieldType } of eventsWithStatus) {
|
|
96
|
-
let attrs = `id="${event.id}" name="${escapeXml(event.name)}" fields="${event.fields.length}"`;
|
|
97
|
-
if (hasIdFieldConfig) {
|
|
98
|
-
attrs += ` has-id-field="${hasIdField}"`;
|
|
99
|
-
if (hasIdField && fieldType && fieldType !== aggregate.aggregateIdFieldType) {
|
|
100
|
-
attrs += ` id-field-type="${fieldType}" type-match="false"`;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
console.log(` <event ${attrs}/>`);
|
|
104
|
-
}
|
|
105
|
-
console.log(' </events>');
|
|
106
|
-
}
|
|
107
|
-
console.log('</aggregate>');
|
|
108
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { EventModel } from '../../types.js';
|
|
2
|
-
import { type OutputFormat } from '../../lib/format.js';
|
|
3
|
-
export declare function showAggregateCompleteness(model: EventModel, aggregateName: string, format: OutputFormat): void;
|
|
4
|
-
export declare function listAggregates(model: EventModel, format: OutputFormat): void;
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { escapeXml, outputJson } from '../../lib/format.js';
|
|
2
|
-
import { findElementOrExit } from '../../lib/element-lookup.js';
|
|
3
|
-
function findMatchingField(fields, targetName, targetType) {
|
|
4
|
-
for (const field of fields) {
|
|
5
|
-
// Check if name and type match
|
|
6
|
-
if (field.name.toLowerCase() === targetName.toLowerCase() && field.fieldType === targetType) {
|
|
7
|
-
return field;
|
|
8
|
-
}
|
|
9
|
-
// Check nested subfields
|
|
10
|
-
if (field.fieldType === 'Custom' && field.subfields) {
|
|
11
|
-
const nested = findMatchingField(field.subfields, targetName, targetType);
|
|
12
|
-
if (nested)
|
|
13
|
-
return nested;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return undefined;
|
|
17
|
-
}
|
|
18
|
-
// Get events whose center point is inside the aggregate bounds
|
|
19
|
-
// Only returns original events, not linked copies (linked copies have originalNodeId set)
|
|
20
|
-
function getEventsInAggregate(model, aggregate) {
|
|
21
|
-
const bounds = {
|
|
22
|
-
left: aggregate.position.x,
|
|
23
|
-
right: aggregate.position.x + aggregate.size.width,
|
|
24
|
-
top: aggregate.position.y,
|
|
25
|
-
bottom: aggregate.position.y + aggregate.size.height,
|
|
26
|
-
};
|
|
27
|
-
function isInAggregate(pos, width, height) {
|
|
28
|
-
const centerX = pos.x + width / 2;
|
|
29
|
-
const centerY = pos.y + height / 2;
|
|
30
|
-
return centerX >= bounds.left && centerX <= bounds.right && centerY >= bounds.top && centerY <= bounds.bottom;
|
|
31
|
-
}
|
|
32
|
-
// Filter out linked copies - only return original events
|
|
33
|
-
// Linked copies have originalNodeId set, originals do not
|
|
34
|
-
return [...model.events.values()].filter(e => !e.originalNodeId && isInAggregate(e.position, e.width, e.height));
|
|
35
|
-
}
|
|
36
|
-
export function showAggregateCompleteness(model, aggregateName, format) {
|
|
37
|
-
// Find the aggregate
|
|
38
|
-
const aggregate = findElementOrExit(model.aggregates, aggregateName, 'aggregate');
|
|
39
|
-
// Get events dynamically based on position (center point inside aggregate bounds)
|
|
40
|
-
const eventsInAggregate = getEventsInAggregate(model, aggregate);
|
|
41
|
-
// Check if aggregate has ID field configured
|
|
42
|
-
if (!aggregate.aggregateIdFieldName || !aggregate.aggregateIdFieldType) {
|
|
43
|
-
if (format === 'json') {
|
|
44
|
-
outputJson({
|
|
45
|
-
aggregate: aggregate.name,
|
|
46
|
-
status: 'unconfigured',
|
|
47
|
-
eventsCount: eventsInAggregate.length,
|
|
48
|
-
events: eventsInAggregate.map(e => ({ id: e.id, name: e.name })),
|
|
49
|
-
message: 'Aggregate ID field not configured. Set aggregateIdFieldName and aggregateIdFieldType.'
|
|
50
|
-
});
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
console.log(`<aggregate-completeness aggregate="${escapeXml(aggregate.name)}" status="unconfigured">`);
|
|
54
|
-
console.log(` <events-count>${eventsInAggregate.length}</events-count>`);
|
|
55
|
-
console.log(' <events>');
|
|
56
|
-
for (const event of eventsInAggregate) {
|
|
57
|
-
console.log(` <event id="${event.id}" name="${escapeXml(event.name)}"/>`);
|
|
58
|
-
}
|
|
59
|
-
console.log(' </events>');
|
|
60
|
-
console.log(' <message>Aggregate ID field not configured. Set aggregateIdFieldName and aggregateIdFieldType.</message>');
|
|
61
|
-
console.log('</aggregate-completeness>');
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const idFieldName = aggregate.aggregateIdFieldName;
|
|
65
|
-
const idFieldType = aggregate.aggregateIdFieldType;
|
|
66
|
-
if (eventsInAggregate.length === 0) {
|
|
67
|
-
if (format === 'json') {
|
|
68
|
-
outputJson({
|
|
69
|
-
aggregate: aggregate.name,
|
|
70
|
-
status: 'empty',
|
|
71
|
-
idField: { name: idFieldName, type: idFieldType },
|
|
72
|
-
message: 'No events in this aggregate.'
|
|
73
|
-
});
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
console.log(`<aggregate-completeness aggregate="${escapeXml(aggregate.name)}" status="empty">`);
|
|
77
|
-
console.log(` <id-field name="${escapeXml(idFieldName)}" type="${idFieldType}"/>`);
|
|
78
|
-
console.log(' <message>No events in this aggregate.</message>');
|
|
79
|
-
console.log('</aggregate-completeness>');
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
// Check each event for the ID field
|
|
83
|
-
const eventResults = [];
|
|
84
|
-
for (const event of eventsInAggregate) {
|
|
85
|
-
const matchingField = findMatchingField(event.fields, idFieldName, idFieldType);
|
|
86
|
-
eventResults.push({
|
|
87
|
-
eventId: event.id,
|
|
88
|
-
eventName: event.name,
|
|
89
|
-
hasIdField: !!matchingField,
|
|
90
|
-
matchingField: matchingField ? {
|
|
91
|
-
fieldId: matchingField.id,
|
|
92
|
-
fieldName: matchingField.name,
|
|
93
|
-
fieldType: matchingField.fieldType,
|
|
94
|
-
} : undefined,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
const completeEvents = eventResults.filter(e => e.hasIdField);
|
|
98
|
-
const incompleteEvents = eventResults.filter(e => !e.hasIdField);
|
|
99
|
-
const isComplete = incompleteEvents.length === 0;
|
|
100
|
-
const overallStatus = isComplete ? 'complete' : 'incomplete';
|
|
101
|
-
const completionPercent = eventResults.length > 0
|
|
102
|
-
? Math.round((completeEvents.length / eventResults.length) * 100)
|
|
103
|
-
: 100;
|
|
104
|
-
if (format === 'json') {
|
|
105
|
-
outputJson({
|
|
106
|
-
aggregate: aggregate.name,
|
|
107
|
-
status: overallStatus,
|
|
108
|
-
completion: `${completionPercent}%`,
|
|
109
|
-
idField: { name: idFieldName, type: idFieldType },
|
|
110
|
-
summary: {
|
|
111
|
-
total: eventResults.length,
|
|
112
|
-
complete: completeEvents.length,
|
|
113
|
-
incomplete: incompleteEvents.length
|
|
114
|
-
},
|
|
115
|
-
incompleteEvents: incompleteEvents.map(e => ({ id: e.eventId, name: e.eventName })),
|
|
116
|
-
completeEvents: completeEvents.map(e => ({
|
|
117
|
-
id: e.eventId,
|
|
118
|
-
name: e.eventName,
|
|
119
|
-
fieldId: e.matchingField?.fieldId
|
|
120
|
-
}))
|
|
121
|
-
});
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
console.log(`<aggregate-completeness aggregate="${escapeXml(aggregate.name)}" status="${overallStatus}" completion="${completionPercent}%">`);
|
|
125
|
-
console.log(` <id-field name="${escapeXml(idFieldName)}" type="${idFieldType}"/>`);
|
|
126
|
-
console.log(` <summary total="${eventResults.length}" complete="${completeEvents.length}" incomplete="${incompleteEvents.length}"/>`);
|
|
127
|
-
if (incompleteEvents.length > 0) {
|
|
128
|
-
console.log(' <incomplete-events>');
|
|
129
|
-
for (const event of incompleteEvents) {
|
|
130
|
-
console.log(` <event id="${event.eventId}" name="${escapeXml(event.eventName)}"/>`);
|
|
131
|
-
}
|
|
132
|
-
console.log(' </incomplete-events>');
|
|
133
|
-
}
|
|
134
|
-
if (completeEvents.length > 0) {
|
|
135
|
-
console.log(' <complete-events>');
|
|
136
|
-
for (const event of completeEvents) {
|
|
137
|
-
const fieldAttr = event.matchingField
|
|
138
|
-
? ` field-id="${event.matchingField.fieldId}"`
|
|
139
|
-
: '';
|
|
140
|
-
console.log(` <event id="${event.eventId}" name="${escapeXml(event.eventName)}"${fieldAttr}/>`);
|
|
141
|
-
}
|
|
142
|
-
console.log(' </complete-events>');
|
|
143
|
-
}
|
|
144
|
-
console.log('</aggregate-completeness>');
|
|
145
|
-
}
|
|
146
|
-
export function listAggregates(model, format) {
|
|
147
|
-
const aggregates = [...model.aggregates.values()];
|
|
148
|
-
if (format === 'json') {
|
|
149
|
-
outputJson({
|
|
150
|
-
aggregates: aggregates.map(aggregate => {
|
|
151
|
-
const eventsInAggregate = getEventsInAggregate(model, aggregate);
|
|
152
|
-
const result = {
|
|
153
|
-
id: aggregate.id,
|
|
154
|
-
name: aggregate.name,
|
|
155
|
-
events: eventsInAggregate.length
|
|
156
|
-
};
|
|
157
|
-
if (aggregate.aggregateIdFieldName) {
|
|
158
|
-
result.idField = aggregate.aggregateIdFieldName;
|
|
159
|
-
result.idType = aggregate.aggregateIdFieldType ?? 'Unknown';
|
|
160
|
-
}
|
|
161
|
-
return result;
|
|
162
|
-
})
|
|
163
|
-
});
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
if (aggregates.length === 0) {
|
|
167
|
-
console.log('<aggregates/>');
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
console.log('<aggregates>');
|
|
171
|
-
for (const aggregate of aggregates) {
|
|
172
|
-
// Calculate events dynamically based on position
|
|
173
|
-
const eventsInAggregate = getEventsInAggregate(model, aggregate);
|
|
174
|
-
const eventCount = eventsInAggregate.length;
|
|
175
|
-
const idFieldAttr = aggregate.aggregateIdFieldName
|
|
176
|
-
? ` idField="${escapeXml(aggregate.aggregateIdFieldName)}" idType="${aggregate.aggregateIdFieldType ?? 'Unknown'}"`
|
|
177
|
-
: '';
|
|
178
|
-
console.log(` <aggregate id="${aggregate.id}" name="${escapeXml(aggregate.name)}" events="${eventCount}"${idFieldAttr}/>`);
|
|
179
|
-
}
|
|
180
|
-
console.log('</aggregates>');
|
|
181
|
-
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { escapeXml, outputJson } from '../../lib/format.js';
|
|
2
|
-
import { findElementOrExit } from '../../lib/element-lookup.js';
|
|
3
|
-
import { getChapterHierarchy, findChapterForSlice } from '../../lib/chapter-utils.js';
|
|
4
|
-
import { findSliceToSliceFlows, findChapterInboundFlows, findChapterOutboundFlows, } from '../../lib/flow-utils.js';
|
|
5
|
-
function getSlicesUnderChapter(model, chapter) {
|
|
6
|
-
// A slice is "under" a chapter if its horizontal center falls within the chapter's x range
|
|
7
|
-
const chapterLeft = chapter.position.x;
|
|
8
|
-
const chapterRight = chapter.position.x + chapter.size.width;
|
|
9
|
-
return [...model.slices.values()].filter(slice => {
|
|
10
|
-
const sliceCenterX = slice.position.x + slice.size.width / 2;
|
|
11
|
-
return sliceCenterX >= chapterLeft && sliceCenterX <= chapterRight;
|
|
12
|
-
}).sort((a, b) => a.position.x - b.position.x);
|
|
13
|
-
}
|
|
14
|
-
function formatSliceFlowXml(sliceFlow) {
|
|
15
|
-
let xml = ` <slice-flow from="${escapeXml(sliceFlow.fromSlice.name)}" to="${escapeXml(sliceFlow.toSlice.name)}">\n`;
|
|
16
|
-
for (const flow of sliceFlow.flows) {
|
|
17
|
-
xml += ` <via type="${flow.flowType}">\n`;
|
|
18
|
-
xml += ` <source element="${escapeXml(flow.source.name)}" type="${flow.source.type}"/>\n`;
|
|
19
|
-
xml += ` <target element="${escapeXml(flow.target.name)}" type="${flow.target.type}"/>\n`;
|
|
20
|
-
if (flow.fieldMappings.length > 0) {
|
|
21
|
-
xml += ' <mappings>\n';
|
|
22
|
-
for (const mapping of flow.fieldMappings) {
|
|
23
|
-
xml += ` <map from="${escapeXml(mapping.from)}" to="${escapeXml(mapping.to)}"/>\n`;
|
|
24
|
-
}
|
|
25
|
-
xml += ' </mappings>\n';
|
|
26
|
-
}
|
|
27
|
-
xml += ' </via>\n';
|
|
28
|
-
}
|
|
29
|
-
xml += ' </slice-flow>\n';
|
|
30
|
-
return xml;
|
|
31
|
-
}
|
|
32
|
-
function formatExternalFlowXml(flow, direction, model) {
|
|
33
|
-
let xml = ` <flow ${direction === 'inbound' ? `to-slice="${escapeXml(flow.targetSlice?.name ?? 'unknown')}"` : `from-slice="${escapeXml(flow.sourceSlice?.name ?? 'unknown')}"`} type="${flow.flowType}">\n`;
|
|
34
|
-
// For inbound: source is external, for outbound: target is external
|
|
35
|
-
if (direction === 'inbound') {
|
|
36
|
-
// Find the chapter of the external source
|
|
37
|
-
const sourceSlice = flow.sourceSlice;
|
|
38
|
-
let chapterInfo = '';
|
|
39
|
-
if (sourceSlice) {
|
|
40
|
-
const sourceSliceObj = model.slices.get(sourceSlice.id);
|
|
41
|
-
if (sourceSliceObj) {
|
|
42
|
-
const sourceChapter = findChapterForSlice(model, sourceSliceObj);
|
|
43
|
-
if (sourceChapter) {
|
|
44
|
-
chapterInfo = ` chapter="${escapeXml(sourceChapter.name)}"`;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
xml += ` <from external="true"${chapterInfo}>${escapeXml(flow.source.name)} (${flow.source.type})</from>\n`;
|
|
49
|
-
xml += ` <to>${escapeXml(flow.target.name)} (${flow.target.type})</to>\n`;
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
xml += ` <from>${escapeXml(flow.source.name)} (${flow.source.type})</from>\n`;
|
|
53
|
-
// Find the chapter of the external target
|
|
54
|
-
const targetSlice = flow.targetSlice;
|
|
55
|
-
let chapterInfo = '';
|
|
56
|
-
if (targetSlice) {
|
|
57
|
-
const targetSliceObj = model.slices.get(targetSlice.id);
|
|
58
|
-
if (targetSliceObj) {
|
|
59
|
-
const targetChapter = findChapterForSlice(model, targetSliceObj);
|
|
60
|
-
if (targetChapter) {
|
|
61
|
-
chapterInfo = ` chapter="${escapeXml(targetChapter.name)}"`;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
xml += ` <to external="true"${chapterInfo}>${escapeXml(flow.target.name)} (${flow.target.type})</to>\n`;
|
|
66
|
-
}
|
|
67
|
-
if (flow.fieldMappings.length > 0) {
|
|
68
|
-
xml += ' <mappings>\n';
|
|
69
|
-
for (const mapping of flow.fieldMappings) {
|
|
70
|
-
xml += ` <map from="${escapeXml(mapping.from)}" to="${escapeXml(mapping.to)}"/>\n`;
|
|
71
|
-
}
|
|
72
|
-
xml += ' </mappings>\n';
|
|
73
|
-
}
|
|
74
|
-
xml += ' </flow>\n';
|
|
75
|
-
return xml;
|
|
76
|
-
}
|
|
77
|
-
export function showChapter(model, name, format) {
|
|
78
|
-
const chapter = findElementOrExit(model.chapters, name, 'chapter');
|
|
79
|
-
const slices = getSlicesUnderChapter(model, chapter);
|
|
80
|
-
// Get flow information
|
|
81
|
-
const sliceToSliceFlows = findSliceToSliceFlows(model, slices);
|
|
82
|
-
const chapterInboundFlows = findChapterInboundFlows(model, slices);
|
|
83
|
-
const chapterOutboundFlows = findChapterOutboundFlows(model, slices);
|
|
84
|
-
if (format === 'json') {
|
|
85
|
-
const result = {
|
|
86
|
-
id: chapter.id,
|
|
87
|
-
name: chapter.name,
|
|
88
|
-
parent: chapter ? getChapterHierarchy(model, chapter).parent : undefined,
|
|
89
|
-
slices: slices.map(s => ({ id: s.id, name: s.name, status: s.status }))
|
|
90
|
-
};
|
|
91
|
-
// Add flow graph if there are slice-to-slice flows
|
|
92
|
-
if (sliceToSliceFlows.length > 0) {
|
|
93
|
-
result.flowGraph = sliceToSliceFlows.map(sf => ({
|
|
94
|
-
from: sf.fromSlice.name,
|
|
95
|
-
to: sf.toSlice.name,
|
|
96
|
-
flows: sf.flows.map(f => ({
|
|
97
|
-
type: f.flowType,
|
|
98
|
-
source: { element: f.source.name, type: f.source.type },
|
|
99
|
-
target: { element: f.target.name, type: f.target.type },
|
|
100
|
-
...(f.fieldMappings.length > 0 && {
|
|
101
|
-
mappings: f.fieldMappings.map(m => ({ from: m.from, to: m.to }))
|
|
102
|
-
})
|
|
103
|
-
}))
|
|
104
|
-
}));
|
|
105
|
-
}
|
|
106
|
-
// Add external dependencies
|
|
107
|
-
if (chapterInboundFlows.length > 0 || chapterOutboundFlows.length > 0) {
|
|
108
|
-
result.externalDependencies = {
|
|
109
|
-
...(chapterInboundFlows.length > 0 && {
|
|
110
|
-
inbound: chapterInboundFlows.map(f => {
|
|
111
|
-
const sourceSliceObj = f.sourceSlice ? model.slices.get(f.sourceSlice.id) : null;
|
|
112
|
-
const sourceChapter = sourceSliceObj ? findChapterForSlice(model, sourceSliceObj) : null;
|
|
113
|
-
return {
|
|
114
|
-
toSlice: f.targetSlice?.name,
|
|
115
|
-
type: f.flowType,
|
|
116
|
-
from: {
|
|
117
|
-
element: f.source.name,
|
|
118
|
-
type: f.source.type,
|
|
119
|
-
slice: f.sourceSlice?.name,
|
|
120
|
-
chapter: sourceChapter?.name
|
|
121
|
-
},
|
|
122
|
-
to: { element: f.target.name, type: f.target.type },
|
|
123
|
-
...(f.fieldMappings.length > 0 && {
|
|
124
|
-
mappings: f.fieldMappings.map(m => ({ from: m.from, to: m.to }))
|
|
125
|
-
})
|
|
126
|
-
};
|
|
127
|
-
})
|
|
128
|
-
}),
|
|
129
|
-
...(chapterOutboundFlows.length > 0 && {
|
|
130
|
-
outbound: chapterOutboundFlows.map(f => {
|
|
131
|
-
const targetSliceObj = f.targetSlice ? model.slices.get(f.targetSlice.id) : null;
|
|
132
|
-
const targetChapter = targetSliceObj ? findChapterForSlice(model, targetSliceObj) : null;
|
|
133
|
-
return {
|
|
134
|
-
fromSlice: f.sourceSlice?.name,
|
|
135
|
-
type: f.flowType,
|
|
136
|
-
from: { element: f.source.name, type: f.source.type },
|
|
137
|
-
to: {
|
|
138
|
-
element: f.target.name,
|
|
139
|
-
type: f.target.type,
|
|
140
|
-
slice: f.targetSlice?.name,
|
|
141
|
-
chapter: targetChapter?.name
|
|
142
|
-
},
|
|
143
|
-
...(f.fieldMappings.length > 0 && {
|
|
144
|
-
mappings: f.fieldMappings.map(m => ({ from: m.from, to: m.to }))
|
|
145
|
-
})
|
|
146
|
-
};
|
|
147
|
-
})
|
|
148
|
-
})
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
outputJson(result);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
// XML format
|
|
155
|
-
console.log(`<chapter id="${chapter.id}" name="${escapeXml(chapter.name)}">`);
|
|
156
|
-
// Slices section
|
|
157
|
-
if (slices.length === 0) {
|
|
158
|
-
console.log(' <slices/>');
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
console.log(' <slices>');
|
|
162
|
-
for (const slice of slices) {
|
|
163
|
-
console.log(` <slice id="${slice.id}" name="${escapeXml(slice.name)}" status="${slice.status}"/>`);
|
|
164
|
-
}
|
|
165
|
-
console.log(' </slices>');
|
|
166
|
-
}
|
|
167
|
-
// Flow graph section
|
|
168
|
-
if (sliceToSliceFlows.length > 0) {
|
|
169
|
-
console.log(' <flow-graph>');
|
|
170
|
-
for (const sliceFlow of sliceToSliceFlows) {
|
|
171
|
-
process.stdout.write(formatSliceFlowXml(sliceFlow));
|
|
172
|
-
}
|
|
173
|
-
console.log(' </flow-graph>');
|
|
174
|
-
}
|
|
175
|
-
// External dependencies section
|
|
176
|
-
if (chapterInboundFlows.length > 0 || chapterOutboundFlows.length > 0) {
|
|
177
|
-
console.log(' <external-dependencies>');
|
|
178
|
-
if (chapterInboundFlows.length > 0) {
|
|
179
|
-
console.log(' <inbound>');
|
|
180
|
-
for (const flow of chapterInboundFlows) {
|
|
181
|
-
process.stdout.write(formatExternalFlowXml(flow, 'inbound', model));
|
|
182
|
-
}
|
|
183
|
-
console.log(' </inbound>');
|
|
184
|
-
}
|
|
185
|
-
if (chapterOutboundFlows.length > 0) {
|
|
186
|
-
console.log(' <outbound>');
|
|
187
|
-
for (const flow of chapterOutboundFlows) {
|
|
188
|
-
process.stdout.write(formatExternalFlowXml(flow, 'outbound', model));
|
|
189
|
-
}
|
|
190
|
-
console.log(' </outbound>');
|
|
191
|
-
}
|
|
192
|
-
console.log(' </external-dependencies>');
|
|
193
|
-
}
|
|
194
|
-
console.log('</chapter>');
|
|
195
|
-
}
|