eventmodeler 0.3.0 → 0.3.2
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 +17 -6
- package/dist/lib/flow-utils.js +28 -1
- package/dist/slices/add-field/index.js +2 -0
- package/dist/slices/codegen-slice/index.js +34 -4
- package/dist/slices/show-slice/index.js +5 -13
- package/dist/slices/update-field/index.d.ts +1 -0
- package/dist/slices/update-field/index.js +4 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -91,7 +91,7 @@ COMMANDS:
|
|
|
91
91
|
map fields --flow <source→target> --json|--xml <mappings>
|
|
92
92
|
Set field mappings on a flow
|
|
93
93
|
|
|
94
|
-
update field --command|--event|--read-model|--screen|--processor <name> --field <name> [--optional true|false] [--generated true|false]
|
|
94
|
+
update field --command|--event|--read-model|--screen|--processor <name> --field <name> [--optional true|false] [--generated true|false] [--user-input true|false (screen only)]
|
|
95
95
|
Update field properties
|
|
96
96
|
|
|
97
97
|
create state-change-slice --xml <data>
|
|
@@ -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
|
|
287
|
-
|
|
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 =
|
|
294
|
+
const sliceName = allArgs.slice(0, -1).join(' ');
|
|
293
295
|
const status = lastArg;
|
|
294
296
|
markSliceStatus(model, filePath, sliceName, status);
|
|
295
297
|
break;
|
|
@@ -421,10 +423,16 @@ async function main() {
|
|
|
421
423
|
const fieldArg = getNamedArg(filteredArgs, '--field');
|
|
422
424
|
const optionalArg = getNamedArg(filteredArgs, '--optional');
|
|
423
425
|
const generatedArg = getNamedArg(filteredArgs, '--generated');
|
|
426
|
+
const userInputArg = getNamedArg(filteredArgs, '--user-input');
|
|
424
427
|
const typeArg = getNamedArg(filteredArgs, '--type');
|
|
425
428
|
if (!fieldArg) {
|
|
426
429
|
console.error('Error: --field is required');
|
|
427
|
-
console.error('Usage: eventmodeler update field --command|--event|--read-model|--screen|--processor <name> --field <field-name> [--optional true|false] [--generated true|false]');
|
|
430
|
+
console.error('Usage: eventmodeler update field --command|--event|--read-model|--screen|--processor <name> --field <field-name> [--optional true|false] [--generated true|false] [--user-input true|false (screen only)]');
|
|
431
|
+
process.exit(1);
|
|
432
|
+
}
|
|
433
|
+
// --user-input is only valid for screens
|
|
434
|
+
if (userInputArg !== undefined && !screenArg) {
|
|
435
|
+
console.error('Error: --user-input can only be used with --screen');
|
|
428
436
|
process.exit(1);
|
|
429
437
|
}
|
|
430
438
|
const updates = {};
|
|
@@ -434,11 +442,14 @@ async function main() {
|
|
|
434
442
|
if (generatedArg !== undefined) {
|
|
435
443
|
updates.generated = generatedArg === 'true';
|
|
436
444
|
}
|
|
445
|
+
if (userInputArg !== undefined) {
|
|
446
|
+
updates.userInput = userInputArg === 'true';
|
|
447
|
+
}
|
|
437
448
|
if (typeArg !== undefined) {
|
|
438
449
|
updates.type = typeArg;
|
|
439
450
|
}
|
|
440
451
|
if (Object.keys(updates).length === 0) {
|
|
441
|
-
console.error('Error: Must specify at least one update (--optional, --generated, or --type)');
|
|
452
|
+
console.error('Error: Must specify at least one update (--optional, --generated, --user-input, or --type)');
|
|
442
453
|
process.exit(1);
|
|
443
454
|
}
|
|
444
455
|
updateField(model, filePath, { command: commandArg, event: eventArg, readModel: readModelArg, screen: screenArg, processor: processorArg }, fieldArg, updates);
|
package/dist/lib/flow-utils.js
CHANGED
|
@@ -61,12 +61,16 @@ function isElementInSlice(slice, position, width, height) {
|
|
|
61
61
|
centerY >= slice.position.y &&
|
|
62
62
|
centerY <= slice.position.y + slice.size.height);
|
|
63
63
|
}
|
|
64
|
-
// Get all component IDs in a slice
|
|
64
|
+
// Get all component IDs in a slice (including canonical group members for linked copies)
|
|
65
65
|
export function getSliceComponentIds(model, slice) {
|
|
66
66
|
const ids = new Set();
|
|
67
|
+
const canonicalIds = new Set();
|
|
68
|
+
// First pass: collect IDs and canonical IDs of elements in the slice
|
|
67
69
|
for (const screen of model.screens.values()) {
|
|
68
70
|
if (isElementInSlice(slice, screen.position, screen.width, screen.height)) {
|
|
69
71
|
ids.add(screen.id);
|
|
72
|
+
if (screen.canonicalId)
|
|
73
|
+
canonicalIds.add(screen.canonicalId);
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
76
|
for (const command of model.commands.values()) {
|
|
@@ -77,11 +81,15 @@ export function getSliceComponentIds(model, slice) {
|
|
|
77
81
|
for (const event of model.events.values()) {
|
|
78
82
|
if (isElementInSlice(slice, event.position, event.width, event.height)) {
|
|
79
83
|
ids.add(event.id);
|
|
84
|
+
if (event.canonicalId)
|
|
85
|
+
canonicalIds.add(event.canonicalId);
|
|
80
86
|
}
|
|
81
87
|
}
|
|
82
88
|
for (const readModel of model.readModels.values()) {
|
|
83
89
|
if (isElementInSlice(slice, readModel.position, readModel.width, readModel.height)) {
|
|
84
90
|
ids.add(readModel.id);
|
|
91
|
+
if (readModel.canonicalId)
|
|
92
|
+
canonicalIds.add(readModel.canonicalId);
|
|
85
93
|
}
|
|
86
94
|
}
|
|
87
95
|
for (const processor of model.processors.values()) {
|
|
@@ -89,6 +97,25 @@ export function getSliceComponentIds(model, slice) {
|
|
|
89
97
|
ids.add(processor.id);
|
|
90
98
|
}
|
|
91
99
|
}
|
|
100
|
+
// Second pass: add all elements that share a canonical ID with elements in the slice
|
|
101
|
+
// This ensures flows targeting any element in a canonical group are detected
|
|
102
|
+
if (canonicalIds.size > 0) {
|
|
103
|
+
for (const screen of model.screens.values()) {
|
|
104
|
+
if (screen.canonicalId && canonicalIds.has(screen.canonicalId)) {
|
|
105
|
+
ids.add(screen.id);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const event of model.events.values()) {
|
|
109
|
+
if (event.canonicalId && canonicalIds.has(event.canonicalId)) {
|
|
110
|
+
ids.add(event.id);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
for (const readModel of model.readModels.values()) {
|
|
114
|
+
if (readModel.canonicalId && canonicalIds.has(readModel.canonicalId)) {
|
|
115
|
+
ids.add(readModel.id);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
92
119
|
return ids;
|
|
93
120
|
}
|
|
94
121
|
// Find which slice contains a component
|
|
@@ -36,6 +36,7 @@ function parseXmlInput(input) {
|
|
|
36
36
|
isList: getBoolAttr(fieldMatch[1], 'isList'),
|
|
37
37
|
isGenerated: getBoolAttr(fieldMatch[1], 'isGenerated'),
|
|
38
38
|
isOptional: getBoolAttr(fieldMatch[1], 'isOptional'),
|
|
39
|
+
isUserInput: getBoolAttr(fieldMatch[1], 'isUserInput'),
|
|
39
40
|
};
|
|
40
41
|
// Parse nested subfields for Custom type
|
|
41
42
|
if (type === 'Custom' && fieldMatch[2]) {
|
|
@@ -63,6 +64,7 @@ function createFieldFromInput(input) {
|
|
|
63
64
|
isList: input.isList ?? false,
|
|
64
65
|
isGenerated: input.isGenerated ?? false,
|
|
65
66
|
isOptional: input.isOptional,
|
|
67
|
+
isUserInput: input.isUserInput,
|
|
66
68
|
subfields: input.subfields?.map(createFieldFromInput),
|
|
67
69
|
};
|
|
68
70
|
}
|
|
@@ -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 =>
|
|
299
|
-
|
|
300
|
-
|
|
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, } from '../../lib/flow-utils.js';
|
|
5
5
|
function formatFieldValues(values) {
|
|
6
6
|
if (!values || Object.keys(values).length === 0)
|
|
7
7
|
return '';
|
|
@@ -189,12 +189,8 @@ function formatSliceXml(model, slice) {
|
|
|
189
189
|
const components = getSliceComponents(model, slice);
|
|
190
190
|
const scenarios = [...model.scenarios.values()].filter(s => s.sliceId === slice.id);
|
|
191
191
|
const chapter = findChapterForSlice(model, slice);
|
|
192
|
-
|
|
193
|
-
|
|
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));
|
|
192
|
+
// Use shared function that handles canonical groups for linked copies
|
|
193
|
+
const componentIds = getSliceComponentIds(model, slice);
|
|
198
194
|
const flows = [...model.flows.values()].filter(f => componentIds.has(f.sourceId) || componentIds.has(f.targetId));
|
|
199
195
|
const internalFlows = flows.filter(f => componentIds.has(f.sourceId) && componentIds.has(f.targetId));
|
|
200
196
|
// Get inbound and outbound flows for the slice
|
|
@@ -430,12 +426,8 @@ function formatSliceJson(model, slice) {
|
|
|
430
426
|
const components = getSliceComponents(model, slice);
|
|
431
427
|
const scenarios = [...model.scenarios.values()].filter(s => s.sliceId === slice.id);
|
|
432
428
|
const chapter = findChapterForSlice(model, slice);
|
|
433
|
-
|
|
434
|
-
|
|
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));
|
|
429
|
+
// Use shared function that handles canonical groups for linked copies
|
|
430
|
+
const componentIds = getSliceComponentIds(model, slice);
|
|
439
431
|
const flows = [...model.flows.values()].filter(f => componentIds.has(f.sourceId) || componentIds.has(f.targetId));
|
|
440
432
|
const internalFlows = flows.filter(f => componentIds.has(f.sourceId) && componentIds.has(f.targetId));
|
|
441
433
|
// Get inbound and outbound flows for the slice
|
|
@@ -28,6 +28,7 @@ function createUpdatedField(original, updates) {
|
|
|
28
28
|
...original,
|
|
29
29
|
isOptional: updates.optional !== undefined ? updates.optional : original.isOptional,
|
|
30
30
|
isGenerated: updates.generated !== undefined ? updates.generated : original.isGenerated,
|
|
31
|
+
isUserInput: updates.userInput !== undefined ? updates.userInput : original.isUserInput,
|
|
31
32
|
fieldType: updates.type !== undefined ? updates.type : original.fieldType,
|
|
32
33
|
};
|
|
33
34
|
}
|
|
@@ -185,6 +186,9 @@ function logUpdates(updates) {
|
|
|
185
186
|
if (updates.generated !== undefined) {
|
|
186
187
|
console.log(` isGenerated: ${updates.generated}`);
|
|
187
188
|
}
|
|
189
|
+
if (updates.userInput !== undefined) {
|
|
190
|
+
console.log(` isUserInput: ${updates.userInput}`);
|
|
191
|
+
}
|
|
188
192
|
if (updates.type !== undefined) {
|
|
189
193
|
console.log(` fieldType: ${updates.type}`);
|
|
190
194
|
}
|