eventmodeler 0.3.4 → 0.3.6
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 +1274 -5
- package/dist/projection.js +99 -9
- package/dist/slices/map-fields/index.js +59 -8
- package/package.json +1 -1
package/dist/projection.js
CHANGED
|
@@ -115,14 +115,32 @@ function applyEvent(model, event) {
|
|
|
115
115
|
}
|
|
116
116
|
case 'EventFieldAdded': {
|
|
117
117
|
const evt = model.events.get(event.eventStickyId);
|
|
118
|
-
if (evt)
|
|
118
|
+
if (evt) {
|
|
119
119
|
evt.fields.push(event.field);
|
|
120
|
+
// Sync field to all linked copies with the same canonicalId
|
|
121
|
+
if (evt.canonicalId) {
|
|
122
|
+
for (const otherEvt of model.events.values()) {
|
|
123
|
+
if (otherEvt.id !== evt.id && otherEvt.canonicalId === evt.canonicalId) {
|
|
124
|
+
otherEvt.fields.push(event.field);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
120
129
|
break;
|
|
121
130
|
}
|
|
122
131
|
case 'EventFieldRemoved': {
|
|
123
132
|
const evt = model.events.get(event.eventStickyId);
|
|
124
|
-
if (evt)
|
|
133
|
+
if (evt) {
|
|
125
134
|
evt.fields = evt.fields.filter(f => f.id !== event.fieldId);
|
|
135
|
+
// Sync removal to all linked copies with the same canonicalId
|
|
136
|
+
if (evt.canonicalId) {
|
|
137
|
+
for (const otherEvt of model.events.values()) {
|
|
138
|
+
if (otherEvt.id !== evt.id && otherEvt.canonicalId === evt.canonicalId) {
|
|
139
|
+
otherEvt.fields = otherEvt.fields.filter(f => f.id !== event.fieldId);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
126
144
|
break;
|
|
127
145
|
}
|
|
128
146
|
case 'EventFieldAdjusted': {
|
|
@@ -130,7 +148,19 @@ function applyEvent(model, event) {
|
|
|
130
148
|
if (evt) {
|
|
131
149
|
const idx = evt.fields.findIndex(f => f.id === event.fieldId);
|
|
132
150
|
if (idx !== -1) {
|
|
133
|
-
|
|
151
|
+
const adjustedField = { ...event.field, id: event.fieldId };
|
|
152
|
+
evt.fields[idx] = adjustedField;
|
|
153
|
+
// Sync adjustment to all linked copies with the same canonicalId
|
|
154
|
+
if (evt.canonicalId) {
|
|
155
|
+
for (const otherEvt of model.events.values()) {
|
|
156
|
+
if (otherEvt.id !== evt.id && otherEvt.canonicalId === evt.canonicalId) {
|
|
157
|
+
const otherIdx = otherEvt.fields.findIndex(f => f.id === event.fieldId);
|
|
158
|
+
if (otherIdx !== -1) {
|
|
159
|
+
otherEvt.fields[otherIdx] = adjustedField;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
134
164
|
}
|
|
135
165
|
}
|
|
136
166
|
break;
|
|
@@ -190,14 +220,32 @@ function applyEvent(model, event) {
|
|
|
190
220
|
}
|
|
191
221
|
case 'ReadModelFieldAdded': {
|
|
192
222
|
const rm = model.readModels.get(event.readModelStickyId);
|
|
193
|
-
if (rm)
|
|
223
|
+
if (rm) {
|
|
194
224
|
rm.fields.push(event.field);
|
|
225
|
+
// Sync field to all linked copies with the same canonicalId
|
|
226
|
+
if (rm.canonicalId) {
|
|
227
|
+
for (const otherRm of model.readModels.values()) {
|
|
228
|
+
if (otherRm.id !== rm.id && otherRm.canonicalId === rm.canonicalId) {
|
|
229
|
+
otherRm.fields.push(event.field);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
195
234
|
break;
|
|
196
235
|
}
|
|
197
236
|
case 'ReadModelFieldRemoved': {
|
|
198
237
|
const rm = model.readModels.get(event.readModelStickyId);
|
|
199
|
-
if (rm)
|
|
238
|
+
if (rm) {
|
|
200
239
|
rm.fields = rm.fields.filter(f => f.id !== event.fieldId);
|
|
240
|
+
// Sync removal to all linked copies with the same canonicalId
|
|
241
|
+
if (rm.canonicalId) {
|
|
242
|
+
for (const otherRm of model.readModels.values()) {
|
|
243
|
+
if (otherRm.id !== rm.id && otherRm.canonicalId === rm.canonicalId) {
|
|
244
|
+
otherRm.fields = otherRm.fields.filter(f => f.id !== event.fieldId);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
201
249
|
break;
|
|
202
250
|
}
|
|
203
251
|
case 'ReadModelFieldAdjusted': {
|
|
@@ -205,7 +253,19 @@ function applyEvent(model, event) {
|
|
|
205
253
|
if (rm) {
|
|
206
254
|
const idx = rm.fields.findIndex(f => f.id === event.fieldId);
|
|
207
255
|
if (idx !== -1) {
|
|
208
|
-
|
|
256
|
+
const adjustedField = { ...event.field, id: event.fieldId };
|
|
257
|
+
rm.fields[idx] = adjustedField;
|
|
258
|
+
// Sync adjustment to all linked copies with the same canonicalId
|
|
259
|
+
if (rm.canonicalId) {
|
|
260
|
+
for (const otherRm of model.readModels.values()) {
|
|
261
|
+
if (otherRm.id !== rm.id && otherRm.canonicalId === rm.canonicalId) {
|
|
262
|
+
const otherIdx = otherRm.fields.findIndex(f => f.id === event.fieldId);
|
|
263
|
+
if (otherIdx !== -1) {
|
|
264
|
+
otherRm.fields[otherIdx] = adjustedField;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
209
269
|
}
|
|
210
270
|
}
|
|
211
271
|
break;
|
|
@@ -264,14 +324,32 @@ function applyEvent(model, event) {
|
|
|
264
324
|
}
|
|
265
325
|
case 'ScreenFieldAdded': {
|
|
266
326
|
const scr = model.screens.get(event.screenId);
|
|
267
|
-
if (scr)
|
|
327
|
+
if (scr) {
|
|
268
328
|
scr.fields.push(event.field);
|
|
329
|
+
// Sync field to all linked copies with the same canonicalId
|
|
330
|
+
if (scr.canonicalId) {
|
|
331
|
+
for (const otherScr of model.screens.values()) {
|
|
332
|
+
if (otherScr.id !== scr.id && otherScr.canonicalId === scr.canonicalId) {
|
|
333
|
+
otherScr.fields.push(event.field);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
269
338
|
break;
|
|
270
339
|
}
|
|
271
340
|
case 'ScreenFieldRemoved': {
|
|
272
341
|
const scr = model.screens.get(event.screenId);
|
|
273
|
-
if (scr)
|
|
342
|
+
if (scr) {
|
|
274
343
|
scr.fields = scr.fields.filter(f => f.id !== event.fieldId);
|
|
344
|
+
// Sync removal to all linked copies with the same canonicalId
|
|
345
|
+
if (scr.canonicalId) {
|
|
346
|
+
for (const otherScr of model.screens.values()) {
|
|
347
|
+
if (otherScr.id !== scr.id && otherScr.canonicalId === scr.canonicalId) {
|
|
348
|
+
otherScr.fields = otherScr.fields.filter(f => f.id !== event.fieldId);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
275
353
|
break;
|
|
276
354
|
}
|
|
277
355
|
case 'ScreenFieldAdjusted': {
|
|
@@ -279,7 +357,19 @@ function applyEvent(model, event) {
|
|
|
279
357
|
if (scr) {
|
|
280
358
|
const idx = scr.fields.findIndex(f => f.id === event.fieldId);
|
|
281
359
|
if (idx !== -1) {
|
|
282
|
-
|
|
360
|
+
const adjustedField = { ...event.field, id: event.fieldId };
|
|
361
|
+
scr.fields[idx] = adjustedField;
|
|
362
|
+
// Sync adjustment to all linked copies with the same canonicalId
|
|
363
|
+
if (scr.canonicalId) {
|
|
364
|
+
for (const otherScr of model.screens.values()) {
|
|
365
|
+
if (otherScr.id !== scr.id && otherScr.canonicalId === scr.canonicalId) {
|
|
366
|
+
const otherIdx = otherScr.fields.findIndex(f => f.id === event.fieldId);
|
|
367
|
+
if (otherIdx !== -1) {
|
|
368
|
+
otherScr.fields[otherIdx] = adjustedField;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
283
373
|
}
|
|
284
374
|
}
|
|
285
375
|
break;
|
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
import { appendEvent } from '../../lib/file-loader.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves an entity ID to its canonical original.
|
|
4
|
+
* If the entity is a linked copy, returns the original's ID.
|
|
5
|
+
* Otherwise returns the entity ID unchanged.
|
|
6
|
+
*/
|
|
7
|
+
function resolveToOriginal(model, entityId) {
|
|
8
|
+
const event = model.events.get(entityId);
|
|
9
|
+
if (event?.originalNodeId)
|
|
10
|
+
return event.originalNodeId;
|
|
11
|
+
const readModel = model.readModels.get(entityId);
|
|
12
|
+
if (readModel?.originalNodeId)
|
|
13
|
+
return readModel.originalNodeId;
|
|
14
|
+
const screen = model.screens.get(entityId);
|
|
15
|
+
if (screen?.originalNodeId)
|
|
16
|
+
return screen.originalNodeId;
|
|
17
|
+
return entityId;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Checks if an entity is a linked copy (has originalNodeId set).
|
|
21
|
+
*/
|
|
22
|
+
function isLinkedCopy(model, entityId) {
|
|
23
|
+
const event = model.events.get(entityId);
|
|
24
|
+
if (event?.originalNodeId)
|
|
25
|
+
return true;
|
|
26
|
+
const readModel = model.readModels.get(entityId);
|
|
27
|
+
if (readModel?.originalNodeId)
|
|
28
|
+
return true;
|
|
29
|
+
const screen = model.screens.get(entityId);
|
|
30
|
+
if (screen?.originalNodeId)
|
|
31
|
+
return true;
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
2
34
|
function flattenFields(fields, prefix = '') {
|
|
3
35
|
const result = [];
|
|
4
36
|
for (const field of fields) {
|
|
@@ -64,7 +96,8 @@ export function mapFields(model, filePath, flowIdentifier, input) {
|
|
|
64
96
|
if (arrowMatch) {
|
|
65
97
|
const sourceName = arrowMatch[1].trim().toLowerCase();
|
|
66
98
|
const targetName = arrowMatch[2].trim().toLowerCase();
|
|
67
|
-
// Find matching flow
|
|
99
|
+
// Find matching flow, preferring flows that don't involve linked copies
|
|
100
|
+
let fallbackFlow = undefined;
|
|
68
101
|
for (const f of model.flows.values()) {
|
|
69
102
|
let sourceEntityName = '';
|
|
70
103
|
let targetEntityName = '';
|
|
@@ -104,16 +137,32 @@ export function mapFields(model, filePath, flowIdentifier, input) {
|
|
|
104
137
|
const sourceMatches = sourceEntityName === sourceName || sourceEntityName.includes(sourceName);
|
|
105
138
|
const targetMatches = targetEntityName === targetName || targetEntityName.includes(targetName);
|
|
106
139
|
if (sourceMatches && targetMatches) {
|
|
107
|
-
|
|
108
|
-
|
|
140
|
+
// Prefer flows that don't involve linked copies
|
|
141
|
+
const involvesLinkedCopy = isLinkedCopy(model, f.sourceId) || isLinkedCopy(model, f.targetId);
|
|
142
|
+
if (!involvesLinkedCopy) {
|
|
143
|
+
flow = f;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
else if (!fallbackFlow) {
|
|
147
|
+
// Keep as fallback in case no non-copy flow is found
|
|
148
|
+
fallbackFlow = f;
|
|
149
|
+
}
|
|
109
150
|
}
|
|
110
151
|
}
|
|
152
|
+
// Use fallback if no non-copy flow was found
|
|
153
|
+
if (!flow && fallbackFlow) {
|
|
154
|
+
flow = fallbackFlow;
|
|
155
|
+
}
|
|
111
156
|
}
|
|
112
157
|
}
|
|
113
158
|
if (!flow) {
|
|
114
159
|
console.error(`Error: Flow not found: ${flowIdentifier}`);
|
|
115
160
|
console.error('Available flows:');
|
|
116
161
|
for (const f of model.flows.values()) {
|
|
162
|
+
// Skip flows where source or target is a linked copy (UI-only elements)
|
|
163
|
+
if (isLinkedCopy(model, f.sourceId) || isLinkedCopy(model, f.targetId)) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
117
166
|
const source = getEntityName(model, f.sourceId);
|
|
118
167
|
const target = getEntityName(model, f.targetId);
|
|
119
168
|
console.error(` - ${source}→${target} (${f.id})`);
|
|
@@ -197,19 +246,21 @@ function getEntityName(model, entityId) {
|
|
|
197
246
|
return entityId;
|
|
198
247
|
}
|
|
199
248
|
function getEntityFields(model, entityId) {
|
|
200
|
-
|
|
249
|
+
// Resolve linked copies to their originals to get canonical fields
|
|
250
|
+
const resolvedId = resolveToOriginal(model, entityId);
|
|
251
|
+
const event = model.events.get(resolvedId);
|
|
201
252
|
if (event)
|
|
202
253
|
return event.fields;
|
|
203
|
-
const readModel = model.readModels.get(
|
|
254
|
+
const readModel = model.readModels.get(resolvedId);
|
|
204
255
|
if (readModel)
|
|
205
256
|
return readModel.fields;
|
|
206
|
-
const command = model.commands.get(
|
|
257
|
+
const command = model.commands.get(resolvedId);
|
|
207
258
|
if (command)
|
|
208
259
|
return command.fields;
|
|
209
|
-
const screen = model.screens.get(
|
|
260
|
+
const screen = model.screens.get(resolvedId);
|
|
210
261
|
if (screen)
|
|
211
262
|
return screen.fields;
|
|
212
|
-
const processor = model.processors.get(
|
|
263
|
+
const processor = model.processors.get(resolvedId);
|
|
213
264
|
if (processor)
|
|
214
265
|
return processor.fields;
|
|
215
266
|
return [];
|