eventmodeler 0.3.1 → 0.3.3

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 CHANGED
@@ -282,14 +282,16 @@ async function main() {
282
282
  break;
283
283
  case 'mark': {
284
284
  // mark <slice-name> <status> - status is last arg, slice name is everything before
285
+ // Note: subcommand contains first part of slice name, remainingArgs has the rest
285
286
  const validStatuses = ['created', 'in-progress', 'blocked', 'done'];
286
- const lastArg = remainingArgs[remainingArgs.length - 1];
287
- if (remainingArgs.length < 2 || !validStatuses.includes(lastArg)) {
287
+ const allArgs = subcommand ? [subcommand, ...remainingArgs] : remainingArgs;
288
+ const lastArg = allArgs[allArgs.length - 1];
289
+ if (allArgs.length < 2 || !validStatuses.includes(lastArg)) {
288
290
  console.error('Usage: eventmodeler mark <slice-name> <status>');
289
291
  console.error('Valid statuses: created, in-progress, blocked, done');
290
292
  process.exit(1);
291
293
  }
292
- const sliceName = remainingArgs.slice(0, -1).join(' ');
294
+ const sliceName = allArgs.slice(0, -1).join(' ');
293
295
  const status = lastArg;
294
296
  markSliceStatus(model, filePath, sliceName, status);
295
297
  break;
@@ -26,6 +26,7 @@ export interface SliceFlowInfo extends FlowInfo {
26
26
  name: string;
27
27
  };
28
28
  }
29
+ export declare function resolveToCanonical(model: EventModel, id: string): string;
29
30
  export declare function getElementInfo(model: EventModel, id: string): ElementInfo | null;
30
31
  export declare function getSliceComponentIds(model: EventModel, slice: Slice): Set<string>;
31
32
  export declare function findSliceForComponent(model: EventModel, componentId: string): Slice | null;
@@ -1,3 +1,16 @@
1
+ // Resolve a linked copy to its canonical original, or return the ID unchanged if not a copy
2
+ export function resolveToCanonical(model, id) {
3
+ const screen = model.screens.get(id);
4
+ if (screen?.originalNodeId)
5
+ return screen.originalNodeId;
6
+ const event = model.events.get(id);
7
+ if (event?.originalNodeId)
8
+ return event.originalNodeId;
9
+ const readModel = model.readModels.get(id);
10
+ if (readModel?.originalNodeId)
11
+ return readModel.originalNodeId;
12
+ return id;
13
+ }
1
14
  // Get element info (name and type) by ID
2
15
  export function getElementInfo(model, id) {
3
16
  const screen = model.screens.get(id);
@@ -61,11 +74,12 @@ function isElementInSlice(slice, position, width, height) {
61
74
  centerY >= slice.position.y &&
62
75
  centerY <= slice.position.y + slice.size.height);
63
76
  }
64
- // Get all component IDs in a slice
77
+ // Get all component IDs in a slice (excludes linked copies - they are UI-only)
65
78
  export function getSliceComponentIds(model, slice) {
66
79
  const ids = new Set();
80
+ // Only include canonical elements (not linked copies) that are spatially in the slice
67
81
  for (const screen of model.screens.values()) {
68
- if (isElementInSlice(slice, screen.position, screen.width, screen.height)) {
82
+ if (!screen.originalNodeId && isElementInSlice(slice, screen.position, screen.width, screen.height)) {
69
83
  ids.add(screen.id);
70
84
  }
71
85
  }
@@ -75,12 +89,12 @@ export function getSliceComponentIds(model, slice) {
75
89
  }
76
90
  }
77
91
  for (const event of model.events.values()) {
78
- if (isElementInSlice(slice, event.position, event.width, event.height)) {
92
+ if (!event.originalNodeId && isElementInSlice(slice, event.position, event.width, event.height)) {
79
93
  ids.add(event.id);
80
94
  }
81
95
  }
82
96
  for (const readModel of model.readModels.values()) {
83
- if (isElementInSlice(slice, readModel.position, readModel.width, readModel.height)) {
97
+ if (!readModel.originalNodeId && isElementInSlice(slice, readModel.position, readModel.width, readModel.height)) {
84
98
  ids.add(readModel.id);
85
99
  }
86
100
  }
@@ -136,10 +150,13 @@ export function findSliceForComponent(model, componentId) {
136
150
  }
137
151
  return null;
138
152
  }
139
- // Build FlowInfo from a Flow
153
+ // Build FlowInfo from a Flow (resolves linked copies to their canonical originals)
140
154
  function buildFlowInfo(model, flow) {
141
- const source = getElementInfo(model, flow.sourceId);
142
- const target = getElementInfo(model, flow.targetId);
155
+ // Resolve linked copies to canonical originals
156
+ const canonicalSourceId = resolveToCanonical(model, flow.sourceId);
157
+ const canonicalTargetId = resolveToCanonical(model, flow.targetId);
158
+ const source = getElementInfo(model, canonicalSourceId);
159
+ const target = getElementInfo(model, canonicalTargetId);
143
160
  if (!source || !target)
144
161
  return null;
145
162
  return {
@@ -150,13 +167,16 @@ function buildFlowInfo(model, flow) {
150
167
  fieldMappings: enrichFieldMappings(model, flow),
151
168
  };
152
169
  }
153
- // Build SliceFlowInfo from a Flow (includes slice info for source/target)
170
+ // Build SliceFlowInfo from a Flow (includes slice info for source/target, resolves linked copies)
154
171
  function buildSliceFlowInfo(model, flow) {
155
172
  const base = buildFlowInfo(model, flow);
156
173
  if (!base)
157
174
  return null;
158
- const sourceSlice = findSliceForComponent(model, flow.sourceId);
159
- const targetSlice = findSliceForComponent(model, flow.targetId);
175
+ // Resolve linked copies to canonical originals for slice lookup
176
+ const canonicalSourceId = resolveToCanonical(model, flow.sourceId);
177
+ const canonicalTargetId = resolveToCanonical(model, flow.targetId);
178
+ const sourceSlice = findSliceForComponent(model, canonicalSourceId);
179
+ const targetSlice = findSliceForComponent(model, canonicalTargetId);
160
180
  return {
161
181
  ...base,
162
182
  sourceSlice: sourceSlice ? { id: sourceSlice.id, name: sourceSlice.name } : undefined,
@@ -168,7 +188,10 @@ export function getInboundFlows(model, slice) {
168
188
  const componentIds = getSliceComponentIds(model, slice);
169
189
  const flows = [];
170
190
  for (const flow of model.flows.values()) {
171
- if (componentIds.has(flow.targetId) && !componentIds.has(flow.sourceId)) {
191
+ // Resolve linked copies to canonical originals for containment check
192
+ const canonicalSourceId = resolveToCanonical(model, flow.sourceId);
193
+ const canonicalTargetId = resolveToCanonical(model, flow.targetId);
194
+ if (componentIds.has(canonicalTargetId) && !componentIds.has(canonicalSourceId)) {
172
195
  const flowInfo = buildSliceFlowInfo(model, flow);
173
196
  if (flowInfo)
174
197
  flows.push(flowInfo);
@@ -181,7 +204,10 @@ export function getOutboundFlows(model, slice) {
181
204
  const componentIds = getSliceComponentIds(model, slice);
182
205
  const flows = [];
183
206
  for (const flow of model.flows.values()) {
184
- if (componentIds.has(flow.sourceId) && !componentIds.has(flow.targetId)) {
207
+ // Resolve linked copies to canonical originals for containment check
208
+ const canonicalSourceId = resolveToCanonical(model, flow.sourceId);
209
+ const canonicalTargetId = resolveToCanonical(model, flow.targetId);
210
+ if (componentIds.has(canonicalSourceId) && !componentIds.has(canonicalTargetId)) {
185
211
  const flowInfo = buildSliceFlowInfo(model, flow);
186
212
  if (flowInfo)
187
213
  flows.push(flowInfo);
@@ -194,7 +220,10 @@ export function getInternalFlows(model, slice) {
194
220
  const componentIds = getSliceComponentIds(model, slice);
195
221
  const flows = [];
196
222
  for (const flow of model.flows.values()) {
197
- if (componentIds.has(flow.sourceId) && componentIds.has(flow.targetId)) {
223
+ // Resolve linked copies to canonical originals for containment check
224
+ const canonicalSourceId = resolveToCanonical(model, flow.sourceId);
225
+ const canonicalTargetId = resolveToCanonical(model, flow.targetId);
226
+ if (componentIds.has(canonicalSourceId) && componentIds.has(canonicalTargetId)) {
198
227
  const flowInfo = buildFlowInfo(model, flow);
199
228
  if (flowInfo)
200
229
  flows.push(flowInfo);
@@ -202,17 +231,22 @@ export function getInternalFlows(model, slice) {
202
231
  }
203
232
  return flows;
204
233
  }
205
- // Get all flows for a specific element (both directions)
234
+ // Get all flows for a specific element (both directions, resolves linked copies)
206
235
  export function getFlowsForElement(model, elementId) {
207
236
  const incoming = [];
208
237
  const outgoing = [];
238
+ // Resolve element ID to canonical if it's a linked copy
239
+ const canonicalElementId = resolveToCanonical(model, elementId);
209
240
  for (const flow of model.flows.values()) {
210
- if (flow.targetId === elementId) {
241
+ // Resolve flow endpoints to canonical originals
242
+ const canonicalTargetId = resolveToCanonical(model, flow.targetId);
243
+ const canonicalSourceId = resolveToCanonical(model, flow.sourceId);
244
+ if (canonicalTargetId === canonicalElementId) {
211
245
  const flowInfo = buildFlowInfo(model, flow);
212
246
  if (flowInfo)
213
247
  incoming.push(flowInfo);
214
248
  }
215
- if (flow.sourceId === elementId) {
249
+ if (canonicalSourceId === canonicalElementId) {
216
250
  const flowInfo = buildFlowInfo(model, flow);
217
251
  if (flowInfo)
218
252
  outgoing.push(flowInfo);
@@ -230,13 +264,16 @@ export function findSliceToSliceFlows(model, slices) {
230
264
  // Group flows by slice pair
231
265
  const flowsBySlicePair = new Map();
232
266
  for (const flow of model.flows.values()) {
267
+ // Resolve linked copies to canonical originals
268
+ const canonicalSourceId = resolveToCanonical(model, flow.sourceId);
269
+ const canonicalTargetId = resolveToCanonical(model, flow.targetId);
233
270
  let sourceSliceId = null;
234
271
  let targetSliceId = null;
235
272
  // Find which slice contains source and target
236
273
  for (const [sliceId, componentIds] of sliceComponentMap.entries()) {
237
- if (componentIds.has(flow.sourceId))
274
+ if (componentIds.has(canonicalSourceId))
238
275
  sourceSliceId = sliceId;
239
- if (componentIds.has(flow.targetId))
276
+ if (componentIds.has(canonicalTargetId))
240
277
  targetSliceId = sliceId;
241
278
  }
242
279
  // Only include flows between different slices in our set
@@ -277,7 +314,10 @@ export function findChapterInboundFlows(model, slices) {
277
314
  }
278
315
  const flows = [];
279
316
  for (const flow of model.flows.values()) {
280
- if (chapterComponentIds.has(flow.targetId) && !chapterComponentIds.has(flow.sourceId)) {
317
+ // Resolve linked copies to canonical originals
318
+ const canonicalSourceId = resolveToCanonical(model, flow.sourceId);
319
+ const canonicalTargetId = resolveToCanonical(model, flow.targetId);
320
+ if (chapterComponentIds.has(canonicalTargetId) && !chapterComponentIds.has(canonicalSourceId)) {
281
321
  const flowInfo = buildSliceFlowInfo(model, flow);
282
322
  if (flowInfo)
283
323
  flows.push(flowInfo);
@@ -295,7 +335,10 @@ export function findChapterOutboundFlows(model, slices) {
295
335
  }
296
336
  const flows = [];
297
337
  for (const flow of model.flows.values()) {
298
- if (chapterComponentIds.has(flow.sourceId) && !chapterComponentIds.has(flow.targetId)) {
338
+ // Resolve linked copies to canonical originals
339
+ const canonicalSourceId = resolveToCanonical(model, flow.sourceId);
340
+ const canonicalTargetId = resolveToCanonical(model, flow.targetId);
341
+ if (chapterComponentIds.has(canonicalSourceId) && !chapterComponentIds.has(canonicalTargetId)) {
299
342
  const flowInfo = buildSliceFlowInfo(model, flow);
300
343
  if (flowInfo)
301
344
  flows.push(flowInfo);
@@ -292,12 +292,42 @@ export function codegenSlice(model, sliceName) {
292
292
  const slice = findElementOrExit(model.slices, sliceName, 'slice');
293
293
  // 2. Get components inside slice
294
294
  const components = getSliceComponents(model, slice);
295
- // 3. Build component ID set
295
+ // 3. Build component ID set (including canonical group members for linked copies)
296
296
  const componentIds = new Set();
297
297
  components.commands.forEach(c => componentIds.add(c.id));
298
- components.events.forEach(e => componentIds.add(e.id));
299
- components.readModels.forEach(rm => componentIds.add(rm.id));
300
- components.screens.forEach(s => componentIds.add(s.id));
298
+ components.events.forEach(e => {
299
+ componentIds.add(e.id);
300
+ // If this is a linked copy, also add all elements in the same canonical group
301
+ if (e.canonicalId) {
302
+ for (const evt of model.events.values()) {
303
+ if (evt.canonicalId === e.canonicalId) {
304
+ componentIds.add(evt.id);
305
+ }
306
+ }
307
+ }
308
+ });
309
+ components.readModels.forEach(rm => {
310
+ componentIds.add(rm.id);
311
+ // If this is a linked copy, also add all elements in the same canonical group
312
+ if (rm.canonicalId) {
313
+ for (const r of model.readModels.values()) {
314
+ if (r.canonicalId === rm.canonicalId) {
315
+ componentIds.add(r.id);
316
+ }
317
+ }
318
+ }
319
+ });
320
+ components.screens.forEach(s => {
321
+ componentIds.add(s.id);
322
+ // If this is a linked copy, also add all elements in the same canonical group
323
+ if (s.canonicalId) {
324
+ for (const scr of model.screens.values()) {
325
+ if (scr.canonicalId === s.canonicalId) {
326
+ componentIds.add(scr.id);
327
+ }
328
+ }
329
+ }
330
+ });
301
331
  components.processors.forEach(p => componentIds.add(p.id));
302
332
  // 4. Determine slice type
303
333
  const sliceType = determineSliceType(components.processors.length > 0, components.commands.length > 0, components.events.length > 0, components.readModels.length > 0, components.screens.length > 0);
@@ -1,7 +1,7 @@
1
1
  import { escapeXml, escapeXmlText, outputJson } from '../../lib/format.js';
2
2
  import { findElementOrExit } from '../../lib/element-lookup.js';
3
3
  import { findChapterForSlice, getChapterHierarchy } from '../../lib/chapter-utils.js';
4
- import { getInboundFlows, getOutboundFlows, getFlowsForElement, } from '../../lib/flow-utils.js';
4
+ import { getInboundFlows, getOutboundFlows, getFlowsForElement, getSliceComponentIds, resolveToCanonical, } from '../../lib/flow-utils.js';
5
5
  function formatFieldValues(values) {
6
6
  if (!values || Object.keys(values).length === 0)
7
7
  return '';
@@ -46,36 +46,16 @@ function getSliceComponents(model, slice) {
46
46
  const centerY = pos.y + height / 2;
47
47
  return centerX >= bounds.left && centerX <= bounds.right && centerY >= bounds.top && centerY <= bounds.bottom;
48
48
  }
49
- // Include all elements in the slice (including linked copies)
50
- // Linked copies will be marked as such in the output
49
+ // Only include canonical elements - linked copies are UI-only conveniences
50
+ // and should not appear as slice contents in CLI output
51
51
  return {
52
52
  commands: [...model.commands.values()].filter(c => isInSlice(c.position, c.width, c.height)),
53
- events: [...model.events.values()].filter(e => isInSlice(e.position, e.width, e.height)),
54
- readModels: [...model.readModels.values()].filter(rm => isInSlice(rm.position, rm.width, rm.height)),
55
- screens: [...model.screens.values()].filter(s => isInSlice(s.position, s.width, s.height)),
53
+ events: [...model.events.values()].filter(e => !e.originalNodeId && isInSlice(e.position, e.width, e.height)),
54
+ readModels: [...model.readModels.values()].filter(rm => !rm.originalNodeId && isInSlice(rm.position, rm.width, rm.height)),
55
+ screens: [...model.screens.values()].filter(s => !s.originalNodeId && isInSlice(s.position, s.width, s.height)),
56
56
  processors: [...model.processors.values()].filter(p => isInSlice(p.position, p.width, p.height)),
57
57
  };
58
58
  }
59
- // Find which slice contains the original of a linked copy
60
- function findSliceForNode(model, nodeId) {
61
- const event = model.events.get(nodeId);
62
- const readModel = model.readModels.get(nodeId);
63
- const screen = model.screens.get(nodeId);
64
- const node = event ?? readModel ?? screen;
65
- if (!node)
66
- return null;
67
- for (const slice of model.slices.values()) {
68
- const centerX = node.position.x + node.width / 2;
69
- const centerY = node.position.y + node.height / 2;
70
- if (centerX >= slice.position.x &&
71
- centerX <= slice.position.x + slice.size.width &&
72
- centerY >= slice.position.y &&
73
- centerY <= slice.position.y + slice.size.height) {
74
- return slice;
75
- }
76
- }
77
- return null;
78
- }
79
59
  // Find which aggregate an event belongs to (center point inside aggregate bounds)
80
60
  function findAggregateForEvent(model, event) {
81
61
  const centerX = event.position.x + event.width / 2;
@@ -189,14 +169,19 @@ function formatSliceXml(model, slice) {
189
169
  const components = getSliceComponents(model, slice);
190
170
  const scenarios = [...model.scenarios.values()].filter(s => s.sliceId === slice.id);
191
171
  const chapter = findChapterForSlice(model, slice);
192
- const componentIds = new Set();
193
- components.commands.forEach(c => componentIds.add(c.id));
194
- components.events.forEach(e => componentIds.add(e.id));
195
- components.readModels.forEach(rm => componentIds.add(rm.id));
196
- components.screens.forEach(s => componentIds.add(s.id));
197
- components.processors.forEach(p => componentIds.add(p.id));
198
- const flows = [...model.flows.values()].filter(f => componentIds.has(f.sourceId) || componentIds.has(f.targetId));
199
- const internalFlows = flows.filter(f => componentIds.has(f.sourceId) && componentIds.has(f.targetId));
172
+ // Use shared function that handles canonical groups for linked copies
173
+ const componentIds = getSliceComponentIds(model, slice);
174
+ // Resolve linked copies to canonical originals when checking flow containment
175
+ const flows = [...model.flows.values()].filter(f => {
176
+ const canonicalSourceId = resolveToCanonical(model, f.sourceId);
177
+ const canonicalTargetId = resolveToCanonical(model, f.targetId);
178
+ return componentIds.has(canonicalSourceId) || componentIds.has(canonicalTargetId);
179
+ });
180
+ const internalFlows = flows.filter(f => {
181
+ const canonicalSourceId = resolveToCanonical(model, f.sourceId);
182
+ const canonicalTargetId = resolveToCanonical(model, f.targetId);
183
+ return componentIds.has(canonicalSourceId) && componentIds.has(canonicalTargetId);
184
+ });
200
185
  // Get inbound and outbound flows for the slice
201
186
  const inboundFlows = getInboundFlows(model, slice);
202
187
  const outboundFlows = getOutboundFlows(model, slice);
@@ -214,19 +199,10 @@ function formatSliceXml(model, slice) {
214
199
  }
215
200
  xml += ' <components>\n';
216
201
  for (const screen of components.screens) {
217
- // Check if this is a linked copy
218
- const copyAttr = screen.originalNodeId ? ' linked-copy="true"' : '';
219
- let originAttr = '';
220
- if (screen.originalNodeId) {
221
- const originSlice = findSliceForNode(model, screen.originalNodeId);
222
- if (originSlice) {
223
- originAttr = ` origin-slice="${escapeXml(originSlice.name)}"`;
224
- }
225
- }
226
202
  // Check which actor this screen belongs to
227
203
  const actor = findActorForScreen(model, screen);
228
204
  const actorAttr = actor ? ` actor="${escapeXml(actor.name)}"` : '';
229
- xml += ` <screen id="${screen.id}" name="${escapeXml(screen.name)}"${copyAttr}${originAttr}${actorAttr}>\n`;
205
+ xml += ` <screen id="${screen.id}" name="${escapeXml(screen.name)}"${actorAttr}>\n`;
230
206
  // Add flow annotations
231
207
  const screenFlows = getFlowsForElement(model, screen.id);
232
208
  xml += formatFlowAnnotationsXml(screen.id, screenFlows.incoming, screenFlows.outgoing, componentIds, ' ');
@@ -262,19 +238,10 @@ function formatSliceXml(model, slice) {
262
238
  xml += ' </command>\n';
263
239
  }
264
240
  for (const event of components.events) {
265
- // Check if this is a linked copy
266
- const copyAttr = event.originalNodeId ? ' linked-copy="true"' : '';
267
- let originAttr = '';
268
- if (event.originalNodeId) {
269
- const originSlice = findSliceForNode(model, event.originalNodeId);
270
- if (originSlice) {
271
- originAttr = ` origin-slice="${escapeXml(originSlice.name)}"`;
272
- }
273
- }
274
241
  // Check which aggregate this event belongs to
275
242
  const aggregate = findAggregateForEvent(model, event);
276
243
  const aggregateAttr = aggregate ? ` aggregate="${escapeXml(aggregate.name)}"` : '';
277
- xml += ` <event id="${event.id}" name="${escapeXml(event.name)}"${copyAttr}${originAttr}${aggregateAttr}>\n`;
244
+ xml += ` <event id="${event.id}" name="${escapeXml(event.name)}"${aggregateAttr}>\n`;
278
245
  // Add flow annotations
279
246
  const eventFlows = getFlowsForElement(model, event.id);
280
247
  xml += formatFlowAnnotationsXml(event.id, eventFlows.incoming, eventFlows.outgoing, componentIds, ' ');
@@ -299,16 +266,7 @@ function formatSliceXml(model, slice) {
299
266
  xml += ' </event>\n';
300
267
  }
301
268
  for (const readModel of components.readModels) {
302
- // Check if this is a linked copy
303
- const copyAttr = readModel.originalNodeId ? ' linked-copy="true"' : '';
304
- let originAttr = '';
305
- if (readModel.originalNodeId) {
306
- const originSlice = findSliceForNode(model, readModel.originalNodeId);
307
- if (originSlice) {
308
- originAttr = ` origin-slice="${escapeXml(originSlice.name)}"`;
309
- }
310
- }
311
- xml += ` <read-model id="${readModel.id}" name="${escapeXml(readModel.name)}"${copyAttr}${originAttr}>\n`;
269
+ xml += ` <read-model id="${readModel.id}" name="${escapeXml(readModel.name)}">\n`;
312
270
  // Add flow annotations
313
271
  const rmFlows = getFlowsForElement(model, readModel.id);
314
272
  xml += formatFlowAnnotationsXml(readModel.id, rmFlows.incoming, rmFlows.outgoing, componentIds, ' ');
@@ -430,14 +388,19 @@ function formatSliceJson(model, slice) {
430
388
  const components = getSliceComponents(model, slice);
431
389
  const scenarios = [...model.scenarios.values()].filter(s => s.sliceId === slice.id);
432
390
  const chapter = findChapterForSlice(model, slice);
433
- const componentIds = new Set();
434
- components.commands.forEach(c => componentIds.add(c.id));
435
- components.events.forEach(e => componentIds.add(e.id));
436
- components.readModels.forEach(rm => componentIds.add(rm.id));
437
- components.screens.forEach(s => componentIds.add(s.id));
438
- components.processors.forEach(p => componentIds.add(p.id));
439
- const flows = [...model.flows.values()].filter(f => componentIds.has(f.sourceId) || componentIds.has(f.targetId));
440
- const internalFlows = flows.filter(f => componentIds.has(f.sourceId) && componentIds.has(f.targetId));
391
+ // Use shared function that handles canonical groups for linked copies
392
+ const componentIds = getSliceComponentIds(model, slice);
393
+ // Resolve linked copies to canonical originals when checking flow containment
394
+ const flows = [...model.flows.values()].filter(f => {
395
+ const canonicalSourceId = resolveToCanonical(model, f.sourceId);
396
+ const canonicalTargetId = resolveToCanonical(model, f.targetId);
397
+ return componentIds.has(canonicalSourceId) || componentIds.has(canonicalTargetId);
398
+ });
399
+ const internalFlows = flows.filter(f => {
400
+ const canonicalSourceId = resolveToCanonical(model, f.sourceId);
401
+ const canonicalTargetId = resolveToCanonical(model, f.targetId);
402
+ return componentIds.has(canonicalSourceId) && componentIds.has(canonicalTargetId);
403
+ });
441
404
  // Get inbound and outbound flows for the slice
442
405
  const inboundFlows = getInboundFlows(model, slice);
443
406
  const outboundFlows = getOutboundFlows(model, slice);
@@ -461,12 +424,6 @@ function formatSliceJson(model, slice) {
461
424
  name: screen.name,
462
425
  fields: screen.fields.map(fieldToJson)
463
426
  };
464
- if (screen.originalNodeId) {
465
- screenObj.linkedCopy = true;
466
- const originSlice = findSliceForNode(model, screen.originalNodeId);
467
- if (originSlice)
468
- screenObj.originSlice = originSlice.name;
469
- }
470
427
  const actor = findActorForScreen(model, screen);
471
428
  if (actor)
472
429
  screenObj.actor = actor.name;
@@ -517,12 +474,6 @@ function formatSliceJson(model, slice) {
517
474
  name: event.name,
518
475
  fields: event.fields.map(fieldToJson)
519
476
  };
520
- if (event.originalNodeId) {
521
- eventObj.linkedCopy = true;
522
- const originSlice = findSliceForNode(model, event.originalNodeId);
523
- if (originSlice)
524
- eventObj.originSlice = originSlice.name;
525
- }
526
477
  const aggregate = findAggregateForEvent(model, event);
527
478
  if (aggregate)
528
479
  eventObj.aggregate = aggregate.name;
@@ -550,12 +501,6 @@ function formatSliceJson(model, slice) {
550
501
  name: rm.name,
551
502
  fields: rm.fields.map(fieldToJson)
552
503
  };
553
- if (rm.originalNodeId) {
554
- rmObj.linkedCopy = true;
555
- const originSlice = findSliceForNode(model, rm.originalNodeId);
556
- if (originSlice)
557
- rmObj.originSlice = originSlice.name;
558
- }
559
504
  // Add flow annotations
560
505
  const rmFlows = getFlowsForElement(model, rm.id);
561
506
  if (rmFlows.incoming.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eventmodeler",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "CLI tool for interacting with Event Model files - query, update, and export event models from the terminal",
5
5
  "type": "module",
6
6
  "bin": {