eventmodeler 0.4.4 → 0.4.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.
@@ -1,6 +1,17 @@
1
- import * as crypto from 'node:crypto';
2
- import { appendEvent } from '../../lib/file-loader.js';
3
- import { findElementOrExit, excludeLinkedCopies } from '../../lib/element-lookup.js';
1
+ import { XMLParser } from 'fast-xml-parser';
2
+ const xmlParser = new XMLParser({
3
+ ignoreAttributes: false,
4
+ attributeNamePrefix: '@_',
5
+ allowBooleanAttributes: false,
6
+ parseAttributeValue: false,
7
+ isArray: (name) => name === 'event' || name === 'field',
8
+ trimValues: true,
9
+ });
10
+ function asArray(value) {
11
+ if (value === undefined || value === null)
12
+ return [];
13
+ return Array.isArray(value) ? value : [value];
14
+ }
4
15
  function asRecord(value, context) {
5
16
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
6
17
  throw new Error(`${context} must be an object`);
@@ -28,91 +39,62 @@ function parseFieldValue(value) {
28
39
  return trimmed;
29
40
  }
30
41
  }
31
- function stripTags(value) {
32
- return value.replace(/<[^>]*>/g, '');
33
- }
34
- function getAttr(attrs, attr) {
35
- const match = attrs.match(new RegExp(`${attr}="([^"]*)"`, 'i'));
36
- return match ? match[1] : undefined;
37
- }
38
- function extractTopLevelTagBlocks(xml, tagName) {
39
- const blocks = [];
40
- const regex = new RegExp(`<\\/?${tagName}\\b[^>]*>`, 'gi');
41
- let match;
42
- let depth = 0;
43
- let startIndex = -1;
44
- let startTagEnd = -1;
45
- let startAttrs = '';
46
- while ((match = regex.exec(xml)) !== null) {
47
- const token = match[0];
48
- const isClosing = /^<\//.test(token);
49
- if (!isClosing) {
50
- const selfClosing = /\/>$/.test(token.trim());
51
- if (depth === 0) {
52
- startIndex = match.index;
53
- startTagEnd = match.index + token.length;
54
- startAttrs = token.slice(tagName.length + 1, token.length - (selfClosing ? 2 : 1)).trim();
55
- if (selfClosing) {
56
- blocks.push({ attrs: startAttrs, inner: '', selfClosing: true });
57
- startIndex = -1;
58
- }
59
- else {
60
- depth = 1;
61
- }
42
+ function parseFieldValuesFromNodes(nodes) {
43
+ if (nodes.length === 0)
44
+ return undefined;
45
+ const result = {};
46
+ for (const node of nodes) {
47
+ const n = node;
48
+ const name = n['@_name'];
49
+ if (!name)
50
+ continue;
51
+ const childFields = asArray(n['field']);
52
+ let value;
53
+ if (childFields.length > 0) {
54
+ value = parseFieldValuesFromNodes(childFields);
55
+ }
56
+ else {
57
+ // Get text content (fast-xml-parser stores it as #text)
58
+ const text = n['#text'];
59
+ if (text === undefined || text === '') {
60
+ value = null;
62
61
  }
63
- else if (!selfClosing) {
64
- depth++;
62
+ else {
63
+ value = parseFieldValue(String(text));
65
64
  }
66
- continue;
67
65
  }
68
- if (depth === 0)
69
- continue;
70
- depth--;
71
- if (depth === 0 && startIndex >= 0) {
72
- blocks.push({
73
- attrs: startAttrs,
74
- inner: xml.slice(startTagEnd, match.index),
75
- selfClosing: false,
76
- });
77
- startIndex = -1;
66
+ // Handle duplicate keys (multiple values → array)
67
+ if (name in result) {
68
+ const current = result[name];
69
+ if (Array.isArray(current)) {
70
+ current.push(value);
71
+ }
72
+ else {
73
+ result[name] = [current, value];
74
+ }
75
+ }
76
+ else {
77
+ result[name] = value;
78
78
  }
79
79
  }
80
- return blocks;
81
- }
82
- function firstTagBlock(xml, tagName) {
83
- return extractTopLevelTagBlocks(xml, tagName)[0];
84
- }
85
- function assignFieldValue(target, key, value) {
86
- if (!(key in target)) {
87
- target[key] = value;
88
- return;
89
- }
90
- const current = target[key];
91
- if (Array.isArray(current)) {
92
- current.push(value);
93
- }
94
- else {
95
- target[key] = [current, value];
96
- }
80
+ return Object.keys(result).length > 0 ? result : undefined;
97
81
  }
98
- function parseFieldValuesFromXml(xml) {
99
- const fields = extractTopLevelTagBlocks(xml, 'field');
100
- if (fields.length === 0)
101
- return undefined;
102
- const result = {};
103
- for (const field of fields) {
104
- const name = getAttr(field.attrs, 'name');
105
- if (!name)
82
+ function parseEventInputsFromNodes(nodes) {
83
+ const result = [];
84
+ for (const node of nodes) {
85
+ const n = node;
86
+ const nameFromAttr = n['@_name'];
87
+ const textContent = n['#text'];
88
+ const eventName = nameFromAttr ?? textContent;
89
+ if (!eventName)
106
90
  continue;
107
- const nestedValues = parseFieldValuesFromXml(field.inner);
108
- const value = nestedValues && Object.keys(nestedValues).length > 0
109
- ? nestedValues
110
- : field.selfClosing
111
- ? null
112
- : parseFieldValue(stripTags(field.inner));
113
- assignFieldValue(result, name, value);
91
+ const childFields = asArray(n['field']);
92
+ result.push({
93
+ event: eventName,
94
+ fieldValues: parseFieldValuesFromNodes(childFields),
95
+ });
114
96
  }
115
- return Object.keys(result).length > 0 ? result : undefined;
97
+ return result;
116
98
  }
117
99
  function parseEventInput(value, context) {
118
100
  if (typeof value === 'string') {
@@ -214,55 +196,49 @@ function normalizeScenarioInput(rawValue) {
214
196
  function parseJsonInput(input) {
215
197
  return normalizeScenarioInput(JSON.parse(input));
216
198
  }
217
- function parseEventInputsFromXml(xml) {
218
- const result = [];
219
- const eventBlocks = extractTopLevelTagBlocks(xml, 'event');
220
- for (const eventBlock of eventBlocks) {
221
- const nameFromAttr = getAttr(eventBlock.attrs, 'name');
222
- const nameFromContent = eventBlock.inner.split('<', 1)[0].trim();
223
- const eventName = nameFromAttr ?? nameFromContent;
224
- if (!eventName)
225
- continue;
226
- result.push({
227
- event: eventName,
228
- fieldValues: parseFieldValuesFromXml(eventBlock.inner),
229
- });
230
- }
231
- return result;
232
- }
233
199
  function parseXmlInput(input) {
234
- const scenarioMatch = input.match(/<scenario([^>]*)>/);
235
- if (!scenarioMatch) {
200
+ const parsed = xmlParser.parse(input);
201
+ const scenario = parsed['scenario'];
202
+ if (!scenario) {
236
203
  throw new Error('Invalid XML: missing <scenario> tag');
237
204
  }
238
- const name = getAttr(scenarioMatch[1], 'name');
205
+ const name = scenario['@_name'];
239
206
  if (!name) {
240
207
  throw new Error('Invalid XML: scenario must have a name attribute');
241
208
  }
242
- const description = getAttr(scenarioMatch[1], 'description');
243
- const givenBlock = firstTagBlock(input, 'given');
244
- const given = givenBlock ? parseEventInputsFromXml(givenBlock.inner) : [];
209
+ const description = scenario['@_description'];
210
+ // Parse given events
211
+ const givenNode = scenario['given'];
212
+ const given = givenNode ? parseEventInputsFromNodes(asArray(givenNode['event'])) : [];
213
+ // Parse when
245
214
  let when;
246
- const whenBlock = firstTagBlock(input, 'when');
247
- if (whenBlock) {
248
- const commandBlock = firstTagBlock(whenBlock.inner, 'command');
249
- const whenEvents = parseEventInputsFromXml(whenBlock.inner);
250
- if (commandBlock || whenEvents.length > 0) {
251
- const commandName = commandBlock
252
- ? getAttr(commandBlock.attrs, 'name') ?? commandBlock.inner.split('<', 1)[0].trim()
253
- : undefined;
215
+ const whenNode = scenario['when'];
216
+ if (whenNode) {
217
+ const commandNodes = asArray(whenNode['command']);
218
+ const commandNode = commandNodes[0];
219
+ const whenEvents = parseEventInputsFromNodes(asArray(whenNode['event']));
220
+ if (commandNode || whenEvents.length > 0) {
221
+ let commandName;
222
+ let commandFieldValues;
223
+ if (commandNode) {
224
+ const nameFromAttr = commandNode['@_name'];
225
+ const textContent = commandNode['#text'];
226
+ commandName = nameFromAttr ?? textContent ?? undefined;
227
+ commandFieldValues = parseFieldValuesFromNodes(asArray(commandNode['field']));
228
+ }
254
229
  when = {
255
230
  command: commandName || undefined,
256
- commandFieldValues: commandBlock ? parseFieldValuesFromXml(commandBlock.inner) : undefined,
231
+ commandFieldValues,
257
232
  events: whenEvents.length > 0 ? whenEvents : undefined,
258
233
  };
259
234
  }
260
235
  }
261
- const thenMatch = input.match(/<then([^>]*?)(?:\/>|>([\s\S]*?)<\/then>)/);
262
- if (!thenMatch) {
236
+ // Parse then
237
+ const thenNode = scenario['then'];
238
+ if (!thenNode) {
263
239
  throw new Error('Invalid XML: missing <then> tag');
264
240
  }
265
- const thenType = getAttr(thenMatch[1], 'type');
241
+ const thenType = thenNode['@_type'];
266
242
  if (thenType !== 'error' &&
267
243
  thenType !== 'events' &&
268
244
  thenType !== 'readModelAssertion' &&
@@ -270,37 +246,47 @@ function parseXmlInput(input) {
270
246
  thenType !== 'noCommand') {
271
247
  throw new Error('Invalid XML: <then> must have a type attribute');
272
248
  }
273
- const thenBody = thenMatch[2] ?? '';
274
249
  const then = { type: thenType };
275
250
  if (thenType === 'error') {
276
- then.errorType = getAttr(thenMatch[1], 'errorType') ?? firstTagBlock(thenBody, 'errorType')?.inner.trim();
277
- then.errorMessage = firstTagBlock(thenBody, 'message')?.inner.trim() ?? stripTags(thenBody).trim();
251
+ then.errorType = thenNode['@_errorType'];
252
+ const errorTypeNode = thenNode['errorType'];
253
+ if (!then.errorType && errorTypeNode) {
254
+ then.errorType = typeof errorTypeNode === 'string' ? errorTypeNode : errorTypeNode['#text'];
255
+ }
256
+ const messageNode = thenNode['message'];
257
+ if (messageNode) {
258
+ then.errorMessage = typeof messageNode === 'string' ? messageNode : messageNode['#text'];
259
+ }
278
260
  }
279
261
  else if (thenType === 'events') {
280
- then.events = parseEventInputsFromXml(thenBody);
262
+ then.events = parseEventInputsFromNodes(asArray(thenNode['event']));
281
263
  }
282
264
  else if (thenType === 'command') {
283
- const commandBlock = firstTagBlock(thenBody, 'command');
284
- if (commandBlock) {
285
- then.command = getAttr(commandBlock.attrs, 'name') ?? commandBlock.inner.split('<', 1)[0].trim();
286
- then.commandFieldValues = parseFieldValuesFromXml(commandBlock.inner);
265
+ const commandNodes = asArray(thenNode['command']);
266
+ const commandNode = commandNodes[0];
267
+ if (commandNode) {
268
+ const nameFromAttr = commandNode['@_name'];
269
+ const textContent = commandNode['#text'];
270
+ then.command = nameFromAttr ?? textContent ?? undefined;
271
+ then.commandFieldValues = parseFieldValuesFromNodes(asArray(commandNode['field']));
287
272
  }
288
273
  }
289
274
  else if (thenType === 'readModelAssertion') {
290
- const readModelBlock = firstTagBlock(thenBody, 'read-model');
291
- if (readModelBlock) {
292
- then.readModel = getAttr(readModelBlock.attrs, 'name') ?? readModelBlock.inner.split('<', 1)[0].trim();
293
- const expectedBlock = firstTagBlock(readModelBlock.inner, 'expected');
294
- const directExpected = parseFieldValuesFromXml(readModelBlock.inner);
295
- const expectedFromBlock = expectedBlock ? parseFieldValuesFromXml(expectedBlock.inner) : undefined;
275
+ const readModelNode = thenNode['read-model'];
276
+ if (readModelNode) {
277
+ const nameFromAttr = readModelNode['@_name'];
278
+ const textContent = readModelNode['#text'];
279
+ then.readModel = nameFromAttr ?? textContent ?? undefined;
280
+ const expectedNode = readModelNode['expected'];
281
+ const directFields = parseFieldValuesFromNodes(asArray(readModelNode['field']));
282
+ const expectedFields = expectedNode ? parseFieldValuesFromNodes(asArray(expectedNode['field'])) : undefined;
296
283
  then.expected = {
297
- ...(directExpected ?? {}),
298
- ...(expectedFromBlock ?? {}),
284
+ ...(directFields ?? {}),
285
+ ...(expectedFields ?? {}),
299
286
  };
300
- const givenBlockInAssertion = firstTagBlock(readModelBlock.inner, 'given')
301
- ?? firstTagBlock(readModelBlock.inner, 'given-events');
302
- if (givenBlockInAssertion) {
303
- then.givenEvents = parseEventInputsFromXml(givenBlockInAssertion.inner);
287
+ const givenEventsNode = (readModelNode['given'] ?? readModelNode['given-events']);
288
+ if (givenEventsNode) {
289
+ then.givenEvents = parseEventInputsFromNodes(asArray(givenEventsNode['event']));
304
290
  }
305
291
  }
306
292
  }
@@ -319,187 +305,3 @@ export function parseScenarioInput(input) {
319
305
  }
320
306
  return parseJsonInput(trimmed);
321
307
  }
322
- export function addScenario(model, filePath, sliceName, input) {
323
- // Parse input
324
- let scenarioInput;
325
- try {
326
- scenarioInput = parseScenarioInput(input);
327
- }
328
- catch (err) {
329
- console.error(`Error: Invalid input format: ${err.message}`);
330
- process.exit(1);
331
- }
332
- // Find slice by name
333
- const slice = findElementOrExit(model.slices, sliceName, 'slice');
334
- // Resolve event references in given (exclude linked copies)
335
- const givenEvents = [];
336
- if (scenarioInput.given) {
337
- for (const g of scenarioInput.given) {
338
- const event = findElementOrExit(excludeLinkedCopies(model.events), g.event, 'event');
339
- givenEvents.push({
340
- eventStickyId: event.id,
341
- fieldValues: g.fieldValues,
342
- });
343
- }
344
- }
345
- // Resolve command reference in when
346
- let whenCommand = null;
347
- if (scenarioInput.when?.command) {
348
- const command = findElementOrExit(model.commands, scenarioInput.when.command, 'command');
349
- whenCommand = {
350
- commandStickyId: command.id,
351
- fieldValues: scenarioInput.when.commandFieldValues,
352
- };
353
- }
354
- // Resolve event references in when
355
- const whenEvents = [];
356
- if (scenarioInput.when?.events) {
357
- for (const e of scenarioInput.when.events) {
358
- const event = findElementOrExit(excludeLinkedCopies(model.events), e.event, 'event');
359
- whenEvents.push({
360
- eventStickyId: event.id,
361
- fieldValues: e.fieldValues,
362
- });
363
- }
364
- }
365
- // Resolve then clause
366
- const then = { type: scenarioInput.then.type };
367
- if (scenarioInput.then.type === 'error') {
368
- then.errorMessage = scenarioInput.then.errorMessage;
369
- then.errorType = scenarioInput.then.errorType;
370
- }
371
- else if (scenarioInput.then.type === 'events') {
372
- then.expectedEvents = [];
373
- if (scenarioInput.then.events) {
374
- for (const e of scenarioInput.then.events) {
375
- const event = findElementOrExit(excludeLinkedCopies(model.events), e.event, 'event');
376
- then.expectedEvents.push({
377
- eventStickyId: event.id,
378
- fieldValues: e.fieldValues,
379
- });
380
- }
381
- }
382
- }
383
- else if (scenarioInput.then.type === 'command') {
384
- if (!scenarioInput.then.command) {
385
- console.error('Error: command then type requires a command name');
386
- process.exit(1);
387
- }
388
- const command = findElementOrExit(model.commands, scenarioInput.then.command, 'command');
389
- then.expectedCommand = {
390
- commandStickyId: command.id,
391
- fieldValues: scenarioInput.then.commandFieldValues,
392
- };
393
- }
394
- else if (scenarioInput.then.type === 'readModelAssertion') {
395
- if (!scenarioInput.then.readModel) {
396
- console.error('Error: readModelAssertion requires a readModel name');
397
- process.exit(1);
398
- }
399
- const readModel = findElementOrExit(excludeLinkedCopies(model.readModels), scenarioInput.then.readModel, 'read model');
400
- const assertionGivenEvents = [];
401
- if (scenarioInput.then.givenEvents) {
402
- for (const g of scenarioInput.then.givenEvents) {
403
- const event = findElementOrExit(excludeLinkedCopies(model.events), g.event, 'event');
404
- assertionGivenEvents.push({
405
- eventStickyId: event.id,
406
- fieldValues: g.fieldValues,
407
- });
408
- }
409
- }
410
- then.readModelAssertion = {
411
- readModelStickyId: readModel.id,
412
- givenEvents: assertionGivenEvents,
413
- expectedFieldValues: scenarioInput.then.expected ?? {},
414
- };
415
- }
416
- // Calculate position below slice
417
- // Scenarios are tall (GWT rows with 100px stickies + labels + padding = ~400px+)
418
- // so we need adequate spacing between them
419
- const SCENARIO_GAP = 20; // Match slice gap
420
- const SCENARIO_ESTIMATED_HEIGHT = 450; // Approximate rendered height for positioning
421
- const existingScenarios = [...model.scenarios.values()].filter(s => s.sliceId === slice.id);
422
- const sliceBottom = slice.position.y + slice.size.height;
423
- let positionY;
424
- if (existingScenarios.length === 0) {
425
- positionY = sliceBottom + SCENARIO_GAP;
426
- }
427
- else {
428
- // Use estimated height since stored height (80px) doesn't reflect actual rendered size
429
- const lowestY = Math.max(...existingScenarios.map(s => s.position.y + SCENARIO_ESTIMATED_HEIGHT));
430
- positionY = lowestY + SCENARIO_GAP;
431
- }
432
- const position = {
433
- x: slice.position.x + 10,
434
- y: positionY,
435
- };
436
- // Generate scenario ID
437
- const scenarioId = crypto.randomUUID();
438
- // Append ScenarioCreated event
439
- appendEvent(filePath, {
440
- type: 'ScenarioCreated',
441
- data: {
442
- scenarioId,
443
- sliceId: slice.id,
444
- name: scenarioInput.name,
445
- position,
446
- width: 200,
447
- height: 80,
448
- timestamp: Date.now(),
449
- },
450
- });
451
- // Append description update if provided
452
- if (scenarioInput.description) {
453
- appendEvent(filePath, {
454
- type: 'ScenarioDescriptionUpdated',
455
- data: {
456
- scenarioId,
457
- description: scenarioInput.description,
458
- timestamp: Date.now(),
459
- },
460
- });
461
- }
462
- // Append given events update if provided
463
- if (givenEvents.length > 0) {
464
- appendEvent(filePath, {
465
- type: 'ScenarioGivenEventsUpdated',
466
- data: {
467
- scenarioId,
468
- givenEvents,
469
- timestamp: Date.now(),
470
- },
471
- });
472
- }
473
- // Append when command update if provided
474
- if (whenCommand) {
475
- appendEvent(filePath, {
476
- type: 'ScenarioWhenCommandUpdated',
477
- data: {
478
- scenarioId,
479
- whenCommand,
480
- timestamp: Date.now(),
481
- },
482
- });
483
- }
484
- // Append when events update if provided
485
- if (whenEvents.length > 0) {
486
- appendEvent(filePath, {
487
- type: 'ScenarioWhenEventsUpdated',
488
- data: {
489
- scenarioId,
490
- whenEvents,
491
- timestamp: Date.now(),
492
- },
493
- });
494
- }
495
- // Append then update
496
- appendEvent(filePath, {
497
- type: 'ScenarioThenUpdated',
498
- data: {
499
- scenarioId,
500
- then,
501
- timestamp: Date.now(),
502
- },
503
- });
504
- console.log(`Added scenario "${scenarioInput.name}" to slice "${slice.name}"`);
505
- }
@@ -1,4 +1,5 @@
1
1
  /**
2
2
  * Interactive init command to set up a project.
3
+ * Connects the current directory to an event model.
3
4
  */
4
5
  export declare function init(): Promise<void>;
@@ -1,14 +1,12 @@
1
1
  import * as readline from 'node:readline';
2
- import * as fs from 'node:fs';
3
- import * as path from 'node:path';
4
2
  import { isAuthenticated } from '../../lib/config.js';
5
- import { createCloudClient } from '../../lib/cloud-client.js';
3
+ import { listModels } from '../../api/index.js';
6
4
  import { saveProjectConfig, getProjectRoot, isInProject } from '../../lib/project-config.js';
7
5
  /**
8
6
  * Interactive init command to set up a project.
7
+ * Connects the current directory to an event model.
9
8
  */
10
9
  export async function init() {
11
- // Check if already initialized
12
10
  if (isInProject()) {
13
11
  console.log('This project is already initialized.');
14
12
  console.log('To reconfigure, delete .eventmodeler.json and run init again.');
@@ -16,6 +14,11 @@ export async function init() {
16
14
  }
17
15
  const projectRoot = getProjectRoot();
18
16
  console.log(`Initializing event model project in: ${projectRoot}\n`);
17
+ if (!isAuthenticated()) {
18
+ console.log('You need to be logged in first.');
19
+ console.log('Run "eventmodeler login" first, then try init again.');
20
+ return;
21
+ }
19
22
  const rl = readline.createInterface({
20
23
  input: process.stdin,
21
24
  output: process.stdout,
@@ -28,22 +31,46 @@ export async function init() {
28
31
  });
29
32
  };
30
33
  try {
31
- // Ask what type of backend
32
- console.log('How would you like to store your event model?\n');
33
- console.log(' 1. Cloud (recommended) - Store in eventmodeler.app cloud');
34
- console.log(' 2. Local file - Store in a .eventmodel file\n');
35
- const choice = await question('Enter choice (1 or 2): ');
36
- if (choice === '1') {
37
- await initCloud(question, projectRoot);
34
+ console.log('Fetching your event models...\n');
35
+ const models = await listModels();
36
+ if (models.length === 0) {
37
+ console.log('You don\'t have any event models yet.');
38
+ console.log('Create one at https://eventmodeler.app first, then run init again.');
39
+ rl.close();
40
+ return;
41
+ }
42
+ console.log('Your event models:\n');
43
+ models.forEach((model, index) => {
44
+ console.log(` ${index + 1}. ${model.name} (${model.modelId})`);
45
+ });
46
+ console.log(` ${models.length + 1}. Create a new model\n`);
47
+ const modelChoice = await question(`Select a model (1-${models.length + 1}): `);
48
+ const modelIndex = parseInt(modelChoice, 10) - 1;
49
+ let selectedModel;
50
+ if (modelIndex === models.length) {
51
+ console.log('\nTo create a new model, visit https://eventmodeler.app');
52
+ console.log('After creating it, run init again to link it to this project.');
53
+ rl.close();
54
+ return;
38
55
  }
39
- else if (choice === '2') {
40
- await initLocal(question, projectRoot);
56
+ else if (modelIndex >= 0 && modelIndex < models.length) {
57
+ selectedModel = models[modelIndex];
41
58
  }
42
59
  else {
43
60
  console.log('\nInvalid choice. Please run init again.');
44
61
  rl.close();
45
62
  return;
46
63
  }
64
+ const config = {
65
+ type: 'cloud',
66
+ modelId: selectedModel.modelId,
67
+ modelName: selectedModel.name,
68
+ };
69
+ const configPath = saveProjectConfig(config, projectRoot);
70
+ console.log(`\nProject initialized successfully!`);
71
+ console.log(`Config saved to: ${configPath}`);
72
+ console.log(`\nLinked to model: ${selectedModel.name}`);
73
+ console.log('\nYou can now use eventmodeler commands in this directory.');
47
74
  rl.close();
48
75
  }
49
76
  catch (err) {
@@ -51,83 +78,3 @@ export async function init() {
51
78
  throw err;
52
79
  }
53
80
  }
54
- async function initCloud(question, projectRoot) {
55
- // Check authentication
56
- if (!isAuthenticated()) {
57
- console.log('\nYou need to be logged in to use cloud storage.');
58
- console.log('Run "eventmodeler login" first, then try init again.');
59
- return;
60
- }
61
- console.log('\nFetching your event models...\n');
62
- const client = await createCloudClient();
63
- const models = await client.listModels();
64
- if (models.length === 0) {
65
- console.log('You don\'t have any event models yet.');
66
- console.log('Create one at https://eventmodeler.app first, then run init again.');
67
- return;
68
- }
69
- console.log('Your event models:\n');
70
- models.forEach((model, index) => {
71
- console.log(` ${index + 1}. ${model.name} (${model.modelId})`);
72
- });
73
- console.log(` ${models.length + 1}. Create a new model\n`);
74
- const modelChoice = await question(`Select a model (1-${models.length + 1}): `);
75
- const modelIndex = parseInt(modelChoice, 10) - 1;
76
- let selectedModel;
77
- if (modelIndex === models.length) {
78
- // Create new model - for now, redirect to web
79
- console.log('\nTo create a new model, visit https://eventmodeler.app');
80
- console.log('After creating it, run init again to link it to this project.');
81
- return;
82
- }
83
- else if (modelIndex >= 0 && modelIndex < models.length) {
84
- selectedModel = models[modelIndex];
85
- }
86
- else {
87
- console.log('\nInvalid choice. Please run init again.');
88
- return;
89
- }
90
- const config = {
91
- type: 'cloud',
92
- modelId: selectedModel.modelId,
93
- modelName: selectedModel.name,
94
- };
95
- const configPath = saveProjectConfig(config, projectRoot);
96
- console.log(`\nProject initialized successfully!`);
97
- console.log(`Config saved to: ${configPath}`);
98
- console.log(`\nLinked to cloud model: ${selectedModel.name}`);
99
- console.log('\nYou can now use eventmodeler commands in this directory.');
100
- }
101
- async function initLocal(question, projectRoot) {
102
- const defaultFile = './model.eventmodel';
103
- const filePath = await question(`Event model file path [${defaultFile}]: `);
104
- const finalPath = filePath || defaultFile;
105
- // Resolve relative to project root
106
- const absolutePath = path.isAbsolute(finalPath)
107
- ? finalPath
108
- : path.resolve(projectRoot, finalPath);
109
- // Check if file exists, create if not
110
- if (!fs.existsSync(absolutePath)) {
111
- const create = await question(`File doesn't exist. Create it? (y/n): `);
112
- if (create.toLowerCase() === 'y') {
113
- // Create empty event model file
114
- fs.writeFileSync(absolutePath, '');
115
- console.log(`\nCreated: ${absolutePath}`);
116
- }
117
- else {
118
- console.log('\nAborted. Please create the file and run init again.');
119
- return;
120
- }
121
- }
122
- // Store relative path in config
123
- const relativePath = path.relative(projectRoot, absolutePath);
124
- const config = {
125
- type: 'local',
126
- file: relativePath.startsWith('.') ? relativePath : `./${relativePath}`,
127
- };
128
- const configPath = saveProjectConfig(config, projectRoot);
129
- console.log(`\nProject initialized successfully!`);
130
- console.log(`Config saved to: ${configPath}`);
131
- console.log(`\nLinked to local file: ${config.file}`);
132
- console.log('\nYou can now use eventmodeler commands in this directory.');
133
- }