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,390 +0,0 @@
|
|
|
1
|
-
import { diffModels } from './model-differ.js';
|
|
2
|
-
import { resolvePropertyConflict, isLayoutOnlyChange, categorizeFieldChanges, } from './merge-rules.js';
|
|
3
|
-
function groupChangesByEntity(diff) {
|
|
4
|
-
const entityChanges = new Map();
|
|
5
|
-
const flowChanges = new Map();
|
|
6
|
-
for (const change of diff.entityChanges) {
|
|
7
|
-
entityChanges.set(change.entityId, change);
|
|
8
|
-
}
|
|
9
|
-
for (const change of diff.flowChanges) {
|
|
10
|
-
flowChanges.set(change.flowId, change);
|
|
11
|
-
}
|
|
12
|
-
return { entityChanges, flowChanges };
|
|
13
|
-
}
|
|
14
|
-
// Check if two entity changes are semantically identical
|
|
15
|
-
function changesAreIdentical(a, b) {
|
|
16
|
-
if (a.changeType !== b.changeType)
|
|
17
|
-
return false;
|
|
18
|
-
if (a.entityType !== b.entityType)
|
|
19
|
-
return false;
|
|
20
|
-
if (a.changeType === 'added' || a.changeType === 'removed') {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
const aProps = JSON.stringify(a.propertyChanges ?? []);
|
|
24
|
-
const bProps = JSON.stringify(b.propertyChanges ?? []);
|
|
25
|
-
const aFields = JSON.stringify(a.fieldChanges ?? []);
|
|
26
|
-
const bFields = JSON.stringify(b.fieldChanges ?? []);
|
|
27
|
-
return aProps === bProps && aFields === bFields;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Analyze two modifications and determine conflicts.
|
|
31
|
-
*/
|
|
32
|
-
function analyzeModificationConflicts(oursChange, theirsChange, strategy) {
|
|
33
|
-
const conflicts = [];
|
|
34
|
-
const autoResolved = [];
|
|
35
|
-
let hasHardConflict = false;
|
|
36
|
-
const entityType = oursChange.entityType;
|
|
37
|
-
const entityId = oursChange.entityId;
|
|
38
|
-
const entityName = oursChange.entityName;
|
|
39
|
-
// Analyze property conflicts
|
|
40
|
-
const oursProps = new Map((oursChange.propertyChanges ?? []).map(p => [p.property, p]));
|
|
41
|
-
const theirsProps = new Map((theirsChange.propertyChanges ?? []).map(p => [p.property, p]));
|
|
42
|
-
// Properties only in ours or theirs: auto-merge
|
|
43
|
-
for (const [prop, change] of oursProps) {
|
|
44
|
-
if (!theirsProps.has(prop)) {
|
|
45
|
-
autoResolved.push({
|
|
46
|
-
entityType,
|
|
47
|
-
entityId,
|
|
48
|
-
entityName,
|
|
49
|
-
action: `property-${prop}-from-ours`,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
for (const [prop, change] of theirsProps) {
|
|
54
|
-
if (!oursProps.has(prop)) {
|
|
55
|
-
autoResolved.push({
|
|
56
|
-
entityType,
|
|
57
|
-
entityId,
|
|
58
|
-
entityName,
|
|
59
|
-
action: `property-${prop}-from-theirs`,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
// Properties in both: check for conflicts
|
|
64
|
-
for (const [prop, oursProp] of oursProps) {
|
|
65
|
-
const theirsProp = theirsProps.get(prop);
|
|
66
|
-
if (!theirsProp)
|
|
67
|
-
continue;
|
|
68
|
-
const resolution = resolvePropertyConflict(prop, oursProp.newValue, theirsProp.newValue, entityType);
|
|
69
|
-
if (resolution.type === 'auto') {
|
|
70
|
-
autoResolved.push({
|
|
71
|
-
entityType,
|
|
72
|
-
entityId,
|
|
73
|
-
entityName,
|
|
74
|
-
action: resolution.action,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
else if (resolution.type === 'hard') {
|
|
78
|
-
if (strategy) {
|
|
79
|
-
autoResolved.push({
|
|
80
|
-
entityType,
|
|
81
|
-
entityId,
|
|
82
|
-
entityName,
|
|
83
|
-
action: `${prop}-resolved-${strategy}`,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
hasHardConflict = true;
|
|
88
|
-
conflicts.push({
|
|
89
|
-
entityType,
|
|
90
|
-
entityId,
|
|
91
|
-
entityName,
|
|
92
|
-
reason: resolution.reason,
|
|
93
|
-
property: prop,
|
|
94
|
-
oursValue: oursProp.newValue,
|
|
95
|
-
theirsValue: theirsProp.newValue,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// Analyze field conflicts
|
|
101
|
-
const oursFields = oursChange.fieldChanges ?? [];
|
|
102
|
-
const theirsFields = theirsChange.fieldChanges ?? [];
|
|
103
|
-
if (oursFields.length > 0 || theirsFields.length > 0) {
|
|
104
|
-
const { oursOnly, theirsOnly, conflicts: fieldConflicts } = categorizeFieldChanges(oursFields, theirsFields);
|
|
105
|
-
// Fields only in one branch: auto-merge
|
|
106
|
-
for (const field of oursOnly) {
|
|
107
|
-
autoResolved.push({
|
|
108
|
-
entityType,
|
|
109
|
-
entityId,
|
|
110
|
-
entityName,
|
|
111
|
-
action: `field-${field.fieldPath}-from-ours`,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
for (const field of theirsOnly) {
|
|
115
|
-
autoResolved.push({
|
|
116
|
-
entityType,
|
|
117
|
-
entityId,
|
|
118
|
-
entityName,
|
|
119
|
-
action: `field-${field.fieldPath}-from-theirs`,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
// Field conflicts
|
|
123
|
-
for (const { ours, theirs, resolution } of fieldConflicts) {
|
|
124
|
-
if (resolution.type === 'auto') {
|
|
125
|
-
autoResolved.push({
|
|
126
|
-
entityType,
|
|
127
|
-
entityId,
|
|
128
|
-
entityName,
|
|
129
|
-
action: `field-${ours.fieldPath}-${resolution.action}`,
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
else if (resolution.type === 'hard') {
|
|
133
|
-
if (strategy) {
|
|
134
|
-
autoResolved.push({
|
|
135
|
-
entityType,
|
|
136
|
-
entityId,
|
|
137
|
-
entityName,
|
|
138
|
-
action: `field-${ours.fieldPath}-resolved-${strategy}`,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
hasHardConflict = true;
|
|
143
|
-
conflicts.push({
|
|
144
|
-
entityType,
|
|
145
|
-
entityId,
|
|
146
|
-
entityName,
|
|
147
|
-
reason: resolution.reason,
|
|
148
|
-
property: `field:${ours.fieldPath}`,
|
|
149
|
-
oursValue: ours.newValue ?? ours.oldValue,
|
|
150
|
-
theirsValue: theirs.newValue ?? theirs.oldValue,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return { conflicts, autoResolved, hasHardConflict };
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Perform a three-way merge of event models.
|
|
160
|
-
*/
|
|
161
|
-
export function threeWayMerge(baseModel, oursModel, theirsModel, baseEvents, oursEvents, theirsEvents, strategy) {
|
|
162
|
-
const conflicts = [];
|
|
163
|
-
const autoResolved = [];
|
|
164
|
-
// Compute diffs from base
|
|
165
|
-
const oursDiff = diffModels(baseModel, oursModel);
|
|
166
|
-
const theirsDiff = diffModels(baseModel, theirsModel);
|
|
167
|
-
const oursChanges = groupChangesByEntity(oursDiff);
|
|
168
|
-
const theirsChanges = groupChangesByEntity(theirsDiff);
|
|
169
|
-
const baseTimestamp = baseEvents.length > 0
|
|
170
|
-
? Math.max(...baseEvents.map((e) => e.data.timestamp))
|
|
171
|
-
: 0;
|
|
172
|
-
const oursNewEvents = oursEvents.filter((e) => e.data.timestamp > baseTimestamp);
|
|
173
|
-
const theirsNewEvents = theirsEvents.filter((e) => e.data.timestamp > baseTimestamp);
|
|
174
|
-
const conflictingEntityIds = new Set();
|
|
175
|
-
const conflictingFlowIds = new Set();
|
|
176
|
-
// Analyze entity changes
|
|
177
|
-
const allEntityIds = new Set([
|
|
178
|
-
...oursChanges.entityChanges.keys(),
|
|
179
|
-
...theirsChanges.entityChanges.keys(),
|
|
180
|
-
]);
|
|
181
|
-
for (const entityId of allEntityIds) {
|
|
182
|
-
const oursChange = oursChanges.entityChanges.get(entityId);
|
|
183
|
-
const theirsChange = theirsChanges.entityChanges.get(entityId);
|
|
184
|
-
// Only one side changed - no conflict
|
|
185
|
-
if (!oursChange || !theirsChange) {
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
// Both sides made the same change
|
|
189
|
-
if (changesAreIdentical(oursChange, theirsChange)) {
|
|
190
|
-
autoResolved.push({
|
|
191
|
-
entityType: oursChange.entityType,
|
|
192
|
-
entityId,
|
|
193
|
-
entityName: oursChange.entityName,
|
|
194
|
-
action: `both-${oursChange.changeType}-identically`,
|
|
195
|
-
});
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
// Layout-only changes: auto-resolve with theirs
|
|
199
|
-
if (isLayoutOnlyChange(oursChange) && isLayoutOnlyChange(theirsChange)) {
|
|
200
|
-
autoResolved.push({
|
|
201
|
-
entityType: oursChange.entityType,
|
|
202
|
-
entityId,
|
|
203
|
-
entityName: oursChange.entityName,
|
|
204
|
-
action: 'layout-only-use-theirs',
|
|
205
|
-
});
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
// One removed, other modified
|
|
209
|
-
if ((oursChange.changeType === 'removed' && theirsChange.changeType === 'modified') ||
|
|
210
|
-
(oursChange.changeType === 'modified' && theirsChange.changeType === 'removed')) {
|
|
211
|
-
if (strategy) {
|
|
212
|
-
autoResolved.push({
|
|
213
|
-
entityType: oursChange.entityType,
|
|
214
|
-
entityId,
|
|
215
|
-
entityName: oursChange.entityName,
|
|
216
|
-
action: `delete-vs-modify-resolved-${strategy}`,
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
conflictingEntityIds.add(entityId);
|
|
221
|
-
conflicts.push({
|
|
222
|
-
entityType: oursChange.entityType,
|
|
223
|
-
entityId,
|
|
224
|
-
entityName: oursChange.entityName,
|
|
225
|
-
reason: oursChange.changeType === 'removed'
|
|
226
|
-
? 'Ours deleted, theirs modified'
|
|
227
|
-
: 'Ours modified, theirs deleted',
|
|
228
|
-
oursValue: oursChange.changeType,
|
|
229
|
-
theirsValue: theirsChange.changeType,
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
// Both modified - detailed analysis
|
|
235
|
-
if (oursChange.changeType === 'modified' && theirsChange.changeType === 'modified') {
|
|
236
|
-
const analysis = analyzeModificationConflicts(oursChange, theirsChange, strategy);
|
|
237
|
-
autoResolved.push(...analysis.autoResolved);
|
|
238
|
-
conflicts.push(...analysis.conflicts);
|
|
239
|
-
if (analysis.hasHardConflict) {
|
|
240
|
-
conflictingEntityIds.add(entityId);
|
|
241
|
-
}
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
// Both added with same ID
|
|
245
|
-
if (oursChange.changeType === 'added' && theirsChange.changeType === 'added') {
|
|
246
|
-
if (strategy) {
|
|
247
|
-
autoResolved.push({
|
|
248
|
-
entityType: oursChange.entityType,
|
|
249
|
-
entityId,
|
|
250
|
-
entityName: oursChange.entityName,
|
|
251
|
-
action: `both-added-resolved-${strategy}`,
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
conflictingEntityIds.add(entityId);
|
|
256
|
-
conflicts.push({
|
|
257
|
-
entityType: oursChange.entityType,
|
|
258
|
-
entityId,
|
|
259
|
-
entityName: oursChange.entityName,
|
|
260
|
-
reason: 'Both added entity with same ID',
|
|
261
|
-
oursValue: oursChange.entityName,
|
|
262
|
-
theirsValue: theirsChange.entityName,
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
// Analyze flow changes
|
|
268
|
-
const allFlowIds = new Set([
|
|
269
|
-
...oursChanges.flowChanges.keys(),
|
|
270
|
-
...theirsChanges.flowChanges.keys(),
|
|
271
|
-
]);
|
|
272
|
-
for (const flowId of allFlowIds) {
|
|
273
|
-
const oursChange = oursChanges.flowChanges.get(flowId);
|
|
274
|
-
const theirsChange = theirsChanges.flowChanges.get(flowId);
|
|
275
|
-
if (!oursChange || !theirsChange)
|
|
276
|
-
continue;
|
|
277
|
-
// Both added same flow
|
|
278
|
-
if (oursChange.changeType === 'added' && theirsChange.changeType === 'added') {
|
|
279
|
-
autoResolved.push({
|
|
280
|
-
entityType: 'flow',
|
|
281
|
-
entityId: flowId,
|
|
282
|
-
entityName: `${oursChange.sourceName} → ${oursChange.targetName}`,
|
|
283
|
-
action: 'both-added-flow-keep-one',
|
|
284
|
-
});
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
// Both removed
|
|
288
|
-
if (oursChange.changeType === 'removed' && theirsChange.changeType === 'removed') {
|
|
289
|
-
autoResolved.push({
|
|
290
|
-
entityType: 'flow',
|
|
291
|
-
entityId: flowId,
|
|
292
|
-
entityName: `${oursChange.sourceName} → ${oursChange.targetName}`,
|
|
293
|
-
action: 'both-removed-identically',
|
|
294
|
-
});
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
// One removed, other modified
|
|
298
|
-
if ((oursChange.changeType === 'removed' && theirsChange.changeType === 'modified') ||
|
|
299
|
-
(oursChange.changeType === 'modified' && theirsChange.changeType === 'removed')) {
|
|
300
|
-
if (strategy) {
|
|
301
|
-
autoResolved.push({
|
|
302
|
-
entityType: 'flow',
|
|
303
|
-
entityId: flowId,
|
|
304
|
-
entityName: `${oursChange.sourceName} → ${oursChange.targetName}`,
|
|
305
|
-
action: `flow-delete-vs-modify-resolved-${strategy}`,
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
conflictingFlowIds.add(flowId);
|
|
310
|
-
conflicts.push({
|
|
311
|
-
entityType: 'flow',
|
|
312
|
-
entityId: flowId,
|
|
313
|
-
entityName: `${oursChange.sourceName} → ${oursChange.targetName}`,
|
|
314
|
-
reason: oursChange.changeType === 'removed'
|
|
315
|
-
? 'Ours removed flow, theirs modified mappings'
|
|
316
|
-
: 'Ours modified mappings, theirs removed flow',
|
|
317
|
-
oursValue: oursChange.changeType,
|
|
318
|
-
theirsValue: theirsChange.changeType,
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
continue;
|
|
322
|
-
}
|
|
323
|
-
// Both modified mappings
|
|
324
|
-
if (oursChange.changeType === 'modified' && theirsChange.changeType === 'modified') {
|
|
325
|
-
if (strategy) {
|
|
326
|
-
autoResolved.push({
|
|
327
|
-
entityType: 'flow',
|
|
328
|
-
entityId: flowId,
|
|
329
|
-
entityName: `${oursChange.sourceName} → ${oursChange.targetName}`,
|
|
330
|
-
action: `flow-mappings-resolved-${strategy}`,
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
conflictingFlowIds.add(flowId);
|
|
335
|
-
conflicts.push({
|
|
336
|
-
entityType: 'flow',
|
|
337
|
-
entityId: flowId,
|
|
338
|
-
entityName: `${oursChange.sourceName} → ${oursChange.targetName}`,
|
|
339
|
-
reason: 'Both modified flow field mappings',
|
|
340
|
-
oursValue: oursChange.mappingChanges,
|
|
341
|
-
theirsValue: theirsChange.mappingChanges,
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
// Build merged events
|
|
347
|
-
const mergedEvents = [...baseEvents];
|
|
348
|
-
const affectsConflictingEntity = (event) => {
|
|
349
|
-
const idFields = [
|
|
350
|
-
'commandStickyId', 'eventStickyId', 'readModelStickyId',
|
|
351
|
-
'screenId', 'processorId', 'sliceId', 'chapterId',
|
|
352
|
-
'aggregateId', 'actorId', 'scenarioId', 'flowId',
|
|
353
|
-
];
|
|
354
|
-
for (const field of idFields) {
|
|
355
|
-
const id = event.data[field];
|
|
356
|
-
if (id && (conflictingEntityIds.has(id) || conflictingFlowIds.has(id))) {
|
|
357
|
-
return true;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return false;
|
|
361
|
-
};
|
|
362
|
-
// Add non-conflicting events from ours
|
|
363
|
-
for (const event of oursNewEvents) {
|
|
364
|
-
if (!affectsConflictingEntity(event)) {
|
|
365
|
-
mergedEvents.push(event);
|
|
366
|
-
}
|
|
367
|
-
else if (strategy === 'ours') {
|
|
368
|
-
mergedEvents.push(event);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
// Add non-conflicting events from theirs
|
|
372
|
-
const oursEventKeys = new Set(oursNewEvents.map(e => `${e.type}:${JSON.stringify(e)}`));
|
|
373
|
-
for (const event of theirsNewEvents) {
|
|
374
|
-
const eventKey = `${event.type}:${JSON.stringify(event)}`;
|
|
375
|
-
if (oursEventKeys.has(eventKey))
|
|
376
|
-
continue;
|
|
377
|
-
if (!affectsConflictingEntity(event)) {
|
|
378
|
-
mergedEvents.push(event);
|
|
379
|
-
}
|
|
380
|
-
else if (strategy === 'theirs') {
|
|
381
|
-
mergedEvents.push(event);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
return {
|
|
385
|
-
success: conflicts.length === 0,
|
|
386
|
-
conflicts,
|
|
387
|
-
autoResolved,
|
|
388
|
-
mergedEvents,
|
|
389
|
-
};
|
|
390
|
-
}
|
package/dist/lib/diff/types.d.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import type { RawEvent, Field } from '../../types.js';
|
|
2
|
-
export type EntityType = 'command' | 'event' | 'readModel' | 'screen' | 'processor' | 'slice' | 'chapter' | 'aggregate' | 'actor' | 'scenario' | 'flow';
|
|
3
|
-
export type ChangeType = 'added' | 'removed' | 'modified';
|
|
4
|
-
export interface FieldChange {
|
|
5
|
-
fieldPath: string;
|
|
6
|
-
changeType: ChangeType;
|
|
7
|
-
oldValue?: Field;
|
|
8
|
-
newValue?: Field;
|
|
9
|
-
}
|
|
10
|
-
export interface EntityChange {
|
|
11
|
-
entityType: EntityType;
|
|
12
|
-
entityId: string;
|
|
13
|
-
entityName: string;
|
|
14
|
-
canonicalId?: string;
|
|
15
|
-
changeType: ChangeType;
|
|
16
|
-
propertyChanges?: PropertyChange[];
|
|
17
|
-
fieldChanges?: FieldChange[];
|
|
18
|
-
}
|
|
19
|
-
export interface PropertyChange {
|
|
20
|
-
property: string;
|
|
21
|
-
oldValue?: unknown;
|
|
22
|
-
newValue?: unknown;
|
|
23
|
-
}
|
|
24
|
-
export interface FlowChange {
|
|
25
|
-
flowId: string;
|
|
26
|
-
flowType: string;
|
|
27
|
-
changeType: ChangeType;
|
|
28
|
-
sourceName?: string;
|
|
29
|
-
targetName?: string;
|
|
30
|
-
mappingChanges?: FieldMappingChange[];
|
|
31
|
-
}
|
|
32
|
-
export interface FieldMappingChange {
|
|
33
|
-
changeType: ChangeType;
|
|
34
|
-
sourceFieldName?: string;
|
|
35
|
-
targetFieldName?: string;
|
|
36
|
-
}
|
|
37
|
-
export interface DiffResult {
|
|
38
|
-
summary: {
|
|
39
|
-
added: number;
|
|
40
|
-
removed: number;
|
|
41
|
-
modified: number;
|
|
42
|
-
};
|
|
43
|
-
entityChanges: EntityChange[];
|
|
44
|
-
flowChanges: FlowChange[];
|
|
45
|
-
}
|
|
46
|
-
export interface MergeConflict {
|
|
47
|
-
entityType: EntityType;
|
|
48
|
-
entityId: string;
|
|
49
|
-
entityName: string;
|
|
50
|
-
reason: string;
|
|
51
|
-
property?: string;
|
|
52
|
-
baseValue?: unknown;
|
|
53
|
-
oursValue?: unknown;
|
|
54
|
-
theirsValue?: unknown;
|
|
55
|
-
}
|
|
56
|
-
export interface MergeResult {
|
|
57
|
-
success: boolean;
|
|
58
|
-
conflicts: MergeConflict[];
|
|
59
|
-
autoResolved: AutoResolution[];
|
|
60
|
-
mergedEvents: RawEvent[];
|
|
61
|
-
}
|
|
62
|
-
export interface AutoResolution {
|
|
63
|
-
entityType: EntityType;
|
|
64
|
-
entityId: string;
|
|
65
|
-
entityName: string;
|
|
66
|
-
action: string;
|
|
67
|
-
}
|
|
68
|
-
export interface MergeOptions {
|
|
69
|
-
basePath: string;
|
|
70
|
-
oursPath: string;
|
|
71
|
-
theirsPath: string;
|
|
72
|
-
outputPath: string;
|
|
73
|
-
strategy?: 'ours' | 'theirs';
|
|
74
|
-
dryRun?: boolean;
|
|
75
|
-
}
|
package/dist/lib/diff/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Element lookup utilities for CLI commands.
|
|
3
|
-
* Provides fuzzy lookup with UUID disambiguation for ambiguous matches.
|
|
4
|
-
*/
|
|
5
|
-
export type LookupResult<T> = {
|
|
6
|
-
success: true;
|
|
7
|
-
element: T;
|
|
8
|
-
} | {
|
|
9
|
-
success: false;
|
|
10
|
-
error: 'not_found' | 'ambiguous';
|
|
11
|
-
matches: T[];
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* Filter out linked copies from a collection of elements.
|
|
15
|
-
* Linked copies have `originalNodeId` set and should not be considered
|
|
16
|
-
* as separate elements for lookup - they're UI conveniences only.
|
|
17
|
-
*/
|
|
18
|
-
export declare function excludeLinkedCopies<T extends {
|
|
19
|
-
id: string;
|
|
20
|
-
originalNodeId?: string;
|
|
21
|
-
}>(elements: Map<string, T> | T[]): T[];
|
|
22
|
-
/**
|
|
23
|
-
* Find an element by name (fuzzy) or UUID.
|
|
24
|
-
* - If search starts with "id:", treats rest as UUID/UUID prefix
|
|
25
|
-
* - Otherwise, performs fuzzy name matching:
|
|
26
|
-
* 1. Exact match (case-insensitive, normalized spaces)
|
|
27
|
-
* 2. Partial match (search is contained in name)
|
|
28
|
-
* - Returns error if multiple elements match
|
|
29
|
-
*/
|
|
30
|
-
export declare function findElement<T extends {
|
|
31
|
-
id: string;
|
|
32
|
-
name: string;
|
|
33
|
-
}>(elements: Map<string, T> | T[], search: string): LookupResult<T>;
|
|
34
|
-
/**
|
|
35
|
-
* Format an element name with its ID for display
|
|
36
|
-
*/
|
|
37
|
-
export declare function formatElementWithId<T extends {
|
|
38
|
-
id: string;
|
|
39
|
-
name: string;
|
|
40
|
-
}>(element: T, truncateId?: boolean): string;
|
|
41
|
-
/**
|
|
42
|
-
* Print error message for lookup failures and exit
|
|
43
|
-
*/
|
|
44
|
-
export declare function handleLookupError<T extends {
|
|
45
|
-
id: string;
|
|
46
|
-
name: string;
|
|
47
|
-
}>(search: string, elementType: string, result: {
|
|
48
|
-
success: false;
|
|
49
|
-
error: 'not_found' | 'ambiguous';
|
|
50
|
-
matches: T[];
|
|
51
|
-
}, allElements: Map<string, T> | T[]): never;
|
|
52
|
-
/**
|
|
53
|
-
* Convenience function: find element or exit with error
|
|
54
|
-
*/
|
|
55
|
-
export declare function findElementOrExit<T extends {
|
|
56
|
-
id: string;
|
|
57
|
-
name: string;
|
|
58
|
-
}>(elements: Map<string, T> | T[], search: string, elementType: string): T;
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Element lookup utilities for CLI commands.
|
|
3
|
-
* Provides fuzzy lookup with UUID disambiguation for ambiguous matches.
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Filter out linked copies from a collection of elements.
|
|
7
|
-
* Linked copies have `originalNodeId` set and should not be considered
|
|
8
|
-
* as separate elements for lookup - they're UI conveniences only.
|
|
9
|
-
*/
|
|
10
|
-
export function excludeLinkedCopies(elements) {
|
|
11
|
-
const elementArray = Array.isArray(elements) ? elements : [...elements.values()];
|
|
12
|
-
return elementArray.filter(e => !e.originalNodeId);
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Normalize a string for fuzzy matching:
|
|
16
|
-
* - lowercase
|
|
17
|
-
* - collapse multiple spaces to single space
|
|
18
|
-
* - trim whitespace
|
|
19
|
-
*/
|
|
20
|
-
function normalize(str) {
|
|
21
|
-
return str.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Find an element by name (fuzzy) or UUID.
|
|
25
|
-
* - If search starts with "id:", treats rest as UUID/UUID prefix
|
|
26
|
-
* - Otherwise, performs fuzzy name matching:
|
|
27
|
-
* 1. Exact match (case-insensitive, normalized spaces)
|
|
28
|
-
* 2. Partial match (search is contained in name)
|
|
29
|
-
* - Returns error if multiple elements match
|
|
30
|
-
*/
|
|
31
|
-
export function findElement(elements, search) {
|
|
32
|
-
const elementArray = Array.isArray(elements) ? elements : [...elements.values()];
|
|
33
|
-
// Check for UUID lookup (id:prefix or full UUID format)
|
|
34
|
-
if (search.startsWith('id:')) {
|
|
35
|
-
const idSearch = search.slice(3).toLowerCase();
|
|
36
|
-
const match = elementArray.find(e => e.id.toLowerCase().startsWith(idSearch));
|
|
37
|
-
if (match) {
|
|
38
|
-
return { success: true, element: match };
|
|
39
|
-
}
|
|
40
|
-
return { success: false, error: 'not_found', matches: [] };
|
|
41
|
-
}
|
|
42
|
-
// Check if the search looks like a UUID (contains dashes in UUID pattern)
|
|
43
|
-
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
44
|
-
if (uuidPattern.test(search)) {
|
|
45
|
-
const match = elementArray.find(e => e.id.toLowerCase() === search.toLowerCase());
|
|
46
|
-
if (match) {
|
|
47
|
-
return { success: true, element: match };
|
|
48
|
-
}
|
|
49
|
-
return { success: false, error: 'not_found', matches: [] };
|
|
50
|
-
}
|
|
51
|
-
const searchNorm = normalize(search);
|
|
52
|
-
// 1. Try exact match first (case-insensitive, normalized)
|
|
53
|
-
const exactMatches = elementArray.filter(e => normalize(e.name) === searchNorm);
|
|
54
|
-
if (exactMatches.length === 1) {
|
|
55
|
-
return { success: true, element: exactMatches[0] };
|
|
56
|
-
}
|
|
57
|
-
if (exactMatches.length > 1) {
|
|
58
|
-
return { success: false, error: 'ambiguous', matches: exactMatches };
|
|
59
|
-
}
|
|
60
|
-
// 2. Try partial match (search contained in name)
|
|
61
|
-
const partialMatches = elementArray.filter(e => normalize(e.name).includes(searchNorm));
|
|
62
|
-
if (partialMatches.length === 1) {
|
|
63
|
-
return { success: true, element: partialMatches[0] };
|
|
64
|
-
}
|
|
65
|
-
if (partialMatches.length > 1) {
|
|
66
|
-
return { success: false, error: 'ambiguous', matches: partialMatches };
|
|
67
|
-
}
|
|
68
|
-
// 3. Try word-based match (all search words appear in name)
|
|
69
|
-
const searchWords = searchNorm.split(' ').filter(w => w.length > 0);
|
|
70
|
-
const wordMatches = elementArray.filter(e => {
|
|
71
|
-
const nameNorm = normalize(e.name);
|
|
72
|
-
return searchWords.every(word => nameNorm.includes(word));
|
|
73
|
-
});
|
|
74
|
-
if (wordMatches.length === 1) {
|
|
75
|
-
return { success: true, element: wordMatches[0] };
|
|
76
|
-
}
|
|
77
|
-
if (wordMatches.length > 1) {
|
|
78
|
-
return { success: false, error: 'ambiguous', matches: wordMatches };
|
|
79
|
-
}
|
|
80
|
-
return { success: false, error: 'not_found', matches: [] };
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Format an element name with its ID for display
|
|
84
|
-
*/
|
|
85
|
-
export function formatElementWithId(element, truncateId = true) {
|
|
86
|
-
const id = truncateId ? element.id.slice(0, 8) : element.id;
|
|
87
|
-
return `"${element.name}" (id: ${id})`;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Print error message for lookup failures and exit
|
|
91
|
-
*/
|
|
92
|
-
export function handleLookupError(search, elementType, result, allElements) {
|
|
93
|
-
const elementArray = Array.isArray(allElements) ? allElements : [...allElements.values()];
|
|
94
|
-
if (result.error === 'ambiguous') {
|
|
95
|
-
console.error(`Error: Multiple ${elementType}s found with name "${search}"`);
|
|
96
|
-
console.error('Please specify using the element ID:');
|
|
97
|
-
for (const el of result.matches) {
|
|
98
|
-
console.error(` - ${formatElementWithId(el, false)}`);
|
|
99
|
-
}
|
|
100
|
-
console.error('');
|
|
101
|
-
console.error(`Usage: eventmodeler <command> "id:${result.matches[0].id.slice(0, 8)}"`);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
console.error(`Error: ${elementType.charAt(0).toUpperCase() + elementType.slice(1)} not found: "${search}"`);
|
|
105
|
-
if (elementArray.length > 0) {
|
|
106
|
-
console.error(`Available ${elementType}s:`);
|
|
107
|
-
for (const el of elementArray) {
|
|
108
|
-
console.error(` - ${formatElementWithId(el)}`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
console.error(`No ${elementType}s exist in the model.`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
process.exit(1);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Convenience function: find element or exit with error
|
|
119
|
-
*/
|
|
120
|
-
export function findElementOrExit(elements, search, elementType) {
|
|
121
|
-
const result = findElement(elements, search);
|
|
122
|
-
if (!result.success) {
|
|
123
|
-
handleLookupError(search, elementType, result, elements);
|
|
124
|
-
}
|
|
125
|
-
return result.element;
|
|
126
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { EventModel, RawEvent } from '../types.js';
|
|
2
|
-
export declare function promptForFile(files: string[]): Promise<string>;
|
|
3
|
-
export declare function findEventModelFile(): Promise<string | null>;
|
|
4
|
-
export declare function loadModel(filePath: string): EventModel;
|
|
5
|
-
export declare function appendEvent(filePath: string, event: RawEvent): void;
|
|
6
|
-
export declare function loadRawEvents(filePath: string): RawEvent[];
|
|
7
|
-
export declare function writeEvents(filePath: string, events: RawEvent[]): void;
|
|
8
|
-
export declare function loadModelFromContent(content: string): EventModel;
|