iobroker.statechart 0.1.1 → 0.3.0

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.
Files changed (50) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +59 -438
  3. package/admin/build/tab.js +133 -130
  4. package/admin/statechart.png +0 -0
  5. package/admin/{style.css → tab.css} +0 -2
  6. package/admin/tab_m.html +1 -1
  7. package/build/lib/chart-analysis.js +367 -0
  8. package/build/lib/chart-analysis.js.map +7 -0
  9. package/build/lib/chart-codegen.js +405 -0
  10. package/build/lib/chart-codegen.js.map +7 -0
  11. package/build/lib/chart-helpers.js +102 -0
  12. package/build/lib/chart-helpers.js.map +7 -0
  13. package/build/lib/chart-model.js +477 -0
  14. package/build/lib/chart-model.js.map +7 -0
  15. package/build/lib/chart-runtime.js +1521 -0
  16. package/build/lib/chart-runtime.js.map +7 -0
  17. package/build/lib/chart-structure.js +172 -0
  18. package/build/lib/chart-structure.js.map +7 -0
  19. package/build/lib/chart-validation.js +372 -0
  20. package/build/lib/chart-validation.js.map +7 -0
  21. package/build/main.js +638 -0
  22. package/build/main.js.map +7 -0
  23. package/io-package.json +31 -34
  24. package/package.json +21 -17
  25. package/admin/i18n/de.json +0 -4
  26. package/admin/i18n/en.json +0 -4
  27. package/admin/i18n/es.json +0 -4
  28. package/admin/i18n/fr.json +0 -4
  29. package/admin/i18n/it.json +0 -4
  30. package/admin/i18n/nl.json +0 -4
  31. package/admin/i18n/pl.json +0 -4
  32. package/admin/i18n/pt.json +0 -4
  33. package/admin/i18n/ru.json +0 -4
  34. package/admin/i18n/uk.json +0 -4
  35. package/admin/i18n/zh-cn.json +0 -4
  36. package/admin/jsonConfig.json +0 -19
  37. package/admin/statechart.svg +0 -38
  38. package/lib/adapter-config.d.ts +0 -19
  39. package/lib/chart-codegen.js +0 -436
  40. package/lib/chart-codegen.test.js +0 -132
  41. package/lib/chart-helpers.js +0 -74
  42. package/lib/chart-helpers.test.js +0 -38
  43. package/lib/chart-model.js +0 -577
  44. package/lib/chart-model.test.js +0 -189
  45. package/lib/chart-runtime.js +0 -797
  46. package/lib/chart-runtime.test.js +0 -699
  47. package/lib/chart-structure.js +0 -106
  48. package/lib/chart-validation.js +0 -328
  49. package/lib/chart-validation.test.js +0 -149
  50. package/main.js +0 -279
@@ -1,577 +0,0 @@
1
- "use strict";
2
-
3
- /*
4
- * The chart model shared by the editor (admin tab) and the runtime (adapter).
5
- *
6
- * A chart is stored as object "statechart.<instance>.charts.<chartId>" (type "channel"),
7
- * the model itself lives in `native.chart` of that object.
8
- */
9
-
10
- /** Current version of the chart model, increase on incompatible changes */
11
- const CHART_MODEL_VERSION = 1;
12
-
13
- /**
14
- * @typedef {"input" | "output" | "local"} SymbolScope
15
- */
16
-
17
- /**
18
- * A variable which can be used in conditions and actions.
19
- * Inputs and outputs are mapped to ioBroker states, locals only live inside the chart.
20
- *
21
- * @typedef {object} ChartSymbol
22
- * @property {string} name Name used in conditions and actions, must be a valid JavaScript identifier
23
- * @property {SymbolScope} scope Input (read from state), output (written to state) or local
24
- * @property {string} [stateId] ioBroker state ID for inputs and outputs
25
- * @property {any} [initial] Initial value for locals
26
- * @property {InputTrigger} [trigger] Inputs only: wake up the chart on every update (default) or only on changes
27
- * @property {AckFilter} [ack] Inputs only: which updates are used, depending on their ack flag, default "any"
28
- * @property {number} [debounceMs] Inputs only: wake up the chart only after the input had no update for this time
29
- */
30
-
31
- /**
32
- * @typedef {"update" | "change"} InputTrigger
33
- * @typedef {"any" | "ack" | "command"} AckFilter "ack": only confirmed values, "command": only commands (ack=false)
34
- */
35
-
36
- /**
37
- * @typedef {object} ChartState
38
- * @property {string} id Unique and stable ID inside the chart
39
- * @property {string} name Display name, also used for the "activeState" state
40
- * @property {string | null} [parent] ID of the parent state, null/undefined for top level states
41
- * @property {string} [initial] ID of the initial child state (only for states with children)
42
- * @property {string} [entry] Action executed when the state is entered
43
- * @property {string} [during] Action executed on every wakeup while the state is active and no transition is taken
44
- * @property {string} [exit] Action executed when the state is left
45
- * @property {string} [comment] Free text for the documentation, no influence on the execution
46
- * @property {number} x Position in the editor, relative to the parent state
47
- * @property {number} y Position in the editor, relative to the parent state
48
- * @property {number} [width] Size in the editor
49
- * @property {number} [height] Size in the editor
50
- */
51
-
52
- /**
53
- * @typedef {object} ChartTransition
54
- * @property {string} id Unique and stable ID inside the chart
55
- * @property {string} source ID of the source state
56
- * @property {string} target ID of the target state
57
- * @property {string} [label] Transition label in Stateflow syntax: event[condition]{action}
58
- * @property {number} [priority] Evaluation order of transitions with the same source, lower is first
59
- * @property {boolean} [reenterParent] Only for transitions from a state to one of its own children: leave and
60
- * enter the source state again. Default is an inner transition, which keeps the source state active.
61
- * @property {string} [comment] Free text for the documentation, no influence on the execution
62
- * @property {string} [sourceHandle] Side of the source state where the arrow starts (editor only)
63
- * @property {string} [targetHandle] Side of the target state where the arrow ends (editor only)
64
- */
65
-
66
- /**
67
- * A block with JavaScript code, executed once when the chart starts.
68
- * The functions declared in it can be used in all conditions and actions of the chart.
69
- *
70
- * @typedef {object} ChartFunction
71
- * @property {string} id Unique and stable ID inside the chart
72
- * @property {string} code JavaScript, usually one or more function declarations
73
- * @property {string} [comment] Free text for the documentation, no influence on the execution
74
- * @property {number} x Position in the editor
75
- * @property {number} y Position in the editor
76
- * @property {number} [width] Size in the editor
77
- * @property {number} [height] Size in the editor
78
- */
79
-
80
- /**
81
- * A note on the canvas of the editor, like an annotation in Stateflow. No influence on the execution.
82
- *
83
- * @typedef {object} ChartNote
84
- * @property {string} id Unique and stable ID inside the chart
85
- * @property {string} text
86
- * @property {string | null} [parent] ID of the state the note is placed in, it moves together with it
87
- * @property {number} x Position in the editor, relative to the parent state
88
- * @property {number} y Position in the editor, relative to the parent state
89
- * @property {number} [width] Size in the editor
90
- * @property {number} [height] Size in the editor
91
- */
92
-
93
- /**
94
- * @typedef {object} Chart
95
- * @property {number} version Model version, see CHART_MODEL_VERSION
96
- * @property {string} name Display name
97
- * @property {boolean} enabled If the chart is executed by the adapter
98
- * @property {boolean} [restoreState] Continue with the state from before a restart of the adapter, default true
99
- * @property {string} [initial] ID of the initial top level state (default transition)
100
- * @property {ChartSymbol[]} symbols
101
- * @property {ChartState[]} states
102
- * @property {ChartTransition[]} transitions
103
- * @property {ChartFunction[]} [functions] Missing in charts created before function blocks existed
104
- * @property {ChartNote[]} [notes] Missing in charts created before notes existed
105
- * @property {string} [comment] Free text for the documentation, no influence on the execution
106
- */
107
-
108
- /**
109
- * @typedef {object} TransitionLabel
110
- * @property {string} [event] Name of the triggering event (an input symbol), undefined = any wakeup
111
- * @property {number} [afterMs] Delay of a temporal transition after(n, unit) in milliseconds
112
- * @property {string} [condition] Guard expression
113
- * @property {string} [action] Transition action
114
- */
115
-
116
- const TIME_UNITS = {
117
- msec: 1,
118
- sec: 1000,
119
- min: 60_000,
120
- };
121
-
122
- /**
123
- * Creates a new, empty chart. It is paused, the user starts it when it is ready.
124
- *
125
- * @param {string} name Display name
126
- * @returns {Chart}
127
- */
128
- function createChart(name) {
129
- return {
130
- version: CHART_MODEL_VERSION,
131
- name,
132
- enabled: false,
133
- symbols: [],
134
- states: [],
135
- transitions: [],
136
- functions: [],
137
- notes: [],
138
- };
139
- }
140
-
141
- /**
142
- * Converts a display name to an ID which can be used in ioBroker object IDs
143
- *
144
- * @param {string} name
145
- * @returns {string}
146
- */
147
- function nameToId(name) {
148
- return (
149
- name
150
- .trim()
151
- .toLowerCase()
152
- .replace(/ä/g, "ae")
153
- .replace(/ö/g, "oe")
154
- .replace(/ü/g, "ue")
155
- .replace(/ß/g, "ss")
156
- .replace(/[^a-z0-9_-]+/g, "_")
157
- .replace(/^_+|_+$/g, "") || "chart"
158
- );
159
- }
160
-
161
- /**
162
- * @typedef {object} TextRange
163
- * @property {number} from Index of the first character
164
- * @property {number} to Index after the last character
165
- */
166
-
167
- /**
168
- * @typedef {object} TransitionLabelRanges Positions of the parts in the label text
169
- * @property {TextRange} [event]
170
- * @property {TextRange} [after] The whole after(...) expression
171
- * @property {TextRange} [condition] Content of [...] without the brackets
172
- * @property {TextRange} [action] Content of {...} without the brackets
173
- */
174
-
175
- /**
176
- * Fills the %s placeholders of a message
177
- *
178
- * @param {string} key Message with %s placeholders
179
- * @param {string[]} args
180
- * @returns {string}
181
- */
182
- function formatMessage(key, args) {
183
- let i = 0;
184
- return key.replace(/%s/g, () => args[i++] ?? "");
185
- }
186
-
187
- /**
188
- * Error in the structure of a transition label, knows where in the label the problem is
189
- */
190
- class LabelSyntaxError extends Error {
191
- /**
192
- * @param {string} key Message with %s placeholders, used to translate the message
193
- * @param {string[]} args
194
- * @param {number} from
195
- * @param {number} to
196
- */
197
- constructor(key, args, from, to) {
198
- super(formatMessage(key, args));
199
- this.key = key;
200
- this.args = args;
201
- this.from = from;
202
- this.to = to;
203
- }
204
- }
205
-
206
- /**
207
- * Reads a part enclosed in brackets, nested brackets of the same type are allowed
208
- *
209
- * @param {string} text
210
- * @param {number} start Index of the opening bracket
211
- * @param {string} open
212
- * @param {string} close
213
- * @returns {{ content: string; from: number; end: number }} from is the index of the (trimmed) content,
214
- * end is the index after the closing bracket
215
- */
216
- function readEnclosed(text, start, open, close) {
217
- let depth = 0;
218
- for (let i = start; i < text.length; i++) {
219
- if (text[i] === open) {
220
- depth++;
221
- } else if (text[i] === close) {
222
- depth--;
223
- if (depth === 0) {
224
- const raw = text.substring(start + 1, i);
225
- const content = raw.trim();
226
- return { content, from: start + 1 + raw.indexOf(content), end: i + 1 };
227
- }
228
- }
229
- }
230
- throw new LabelSyntaxError('Missing "%s"', [close], start, text.length);
231
- }
232
-
233
- /**
234
- * Parses a transition label and returns where its parts are, see parseTransitionLabel
235
- *
236
- * @param {string | undefined} label
237
- * @returns {{ label: TransitionLabel; ranges: TransitionLabelRanges }}
238
- * @throws {LabelSyntaxError}
239
- */
240
- function parseTransitionLabelWithRanges(label) {
241
- /** @type {TransitionLabel} */
242
- const result = {};
243
- /** @type {TransitionLabelRanges} */
244
- const ranges = {};
245
- const text = label || "";
246
- let pos = 0;
247
-
248
- const skipSpace = () => {
249
- while (pos < text.length && /\s/.test(text[pos])) {
250
- pos++;
251
- }
252
- };
253
-
254
- skipSpace();
255
- const eventMatch = /[A-Za-z_$][\w$]*/y;
256
- eventMatch.lastIndex = pos;
257
- const event = eventMatch.exec(text);
258
- if (event) {
259
- const from = pos;
260
- pos += event[0].length;
261
- skipSpace();
262
- if (event[0] === "after" && text[pos] === "(") {
263
- const { content, end } = readEnclosed(text, pos, "(", ")");
264
- const afterMatch = /^(\d+(?:\.\d+)?)\s*,\s*(msec|sec|min)$/.exec(content);
265
- if (!afterMatch) {
266
- throw new LabelSyntaxError(
267
- 'Invalid temporal expression "%s", expected e.g. after(5, sec)',
268
- [`after(${content})`],
269
- from,
270
- end,
271
- );
272
- }
273
- result.afterMs = parseFloat(afterMatch[1]) * TIME_UNITS[afterMatch[2]];
274
- ranges.after = { from, to: end };
275
- pos = end;
276
- } else {
277
- result.event = event[0];
278
- ranges.event = { from, to: from + event[0].length };
279
- }
280
- }
281
-
282
- skipSpace();
283
- if (text[pos] === "[") {
284
- const { content, from, end } = readEnclosed(text, pos, "[", "]");
285
- result.condition = content;
286
- ranges.condition = { from, to: from + content.length };
287
- pos = end;
288
- }
289
-
290
- skipSpace();
291
- if (text[pos] === "{") {
292
- const { content, from, end } = readEnclosed(text, pos, "{", "}");
293
- result.action = content;
294
- ranges.action = { from, to: from + content.length };
295
- pos = end;
296
- }
297
-
298
- skipSpace();
299
- if (pos < text.length) {
300
- throw new LabelSyntaxError(
301
- 'Unexpected "%s", expected event[condition]{action}',
302
- [text.substring(pos).trim()],
303
- pos,
304
- text.length,
305
- );
306
- }
307
- return { label: result, ranges };
308
- }
309
-
310
- /**
311
- * Parses a transition label in Stateflow syntax: event[condition]{action}
312
- * All parts are optional. Instead of an event, after(n, sec|msec|min) creates a temporal transition.
313
- *
314
- * @param {string | undefined} label
315
- * @returns {TransitionLabel}
316
- * @throws {LabelSyntaxError}
317
- */
318
- function parseTransitionLabel(label) {
319
- return parseTransitionLabelWithRanges(label).label;
320
- }
321
-
322
- /** Identifies files with exported charts */
323
- const EXPORT_FORMAT = "iobroker.statechart";
324
-
325
- /** Files exported before the adapter was renamed from "state-machine" to "statechart" */
326
- const LEGACY_EXPORT_FORMATS = ["iobroker.state-machine"];
327
-
328
- /**
329
- * The content of an export file
330
- *
331
- * @param {Chart[]} charts
332
- * @returns {{ format: string; version: number; exported: string; charts: Chart[] }}
333
- */
334
- function createExport(charts) {
335
- return { format: EXPORT_FORMAT, version: CHART_MODEL_VERSION, exported: new Date().toISOString(), charts };
336
- }
337
-
338
- const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
339
- const SCOPES = ["input", "output", "local"];
340
-
341
- /**
342
- * @param {any} value
343
- * @returns {value is Record<string, any>}
344
- */
345
- function isObject(value) {
346
- return typeof value === "object" && value !== null && !Array.isArray(value);
347
- }
348
-
349
- /**
350
- * Checks a chart of an export file and fills in defaults for missing optional values
351
- *
352
- * @param {any} data
353
- * @param {string} where Prefix of error messages, e.g. "Chart 1"
354
- * @returns {Chart}
355
- * @throws {Error} if the chart is invalid
356
- */
357
- function normalizeChart(data, where) {
358
- /** @param {string} message */
359
- const fail = message => {
360
- throw new Error(`${where}: ${message}`);
361
- };
362
- if (!isObject(data)) {
363
- fail("not an object");
364
- }
365
- if (typeof data.version === "number" && data.version > CHART_MODEL_VERSION) {
366
- fail(`created with a newer version of the adapter (chart version ${data.version})`);
367
- }
368
- for (const list of ["states", "transitions", "symbols"]) {
369
- if (!Array.isArray(data[list])) {
370
- fail(`"${list}" is missing or no list`);
371
- }
372
- }
373
- for (const list of ["functions", "notes"]) {
374
- if (data[list] !== undefined && !Array.isArray(data[list])) {
375
- fail(`"${list}" is no list`);
376
- }
377
- }
378
- /** @type {any[]} */
379
- const functions = data.functions || [];
380
- /** @type {any[]} */
381
- const notes = data.notes || [];
382
-
383
- // states, function blocks and notes are all nodes in the editor, so their IDs must be unique together
384
- const nodeIds = new Set();
385
- /**
386
- * @param {any} item
387
- * @param {string} kind
388
- * @param {number} index
389
- * @returns {string} the ID
390
- */
391
- const checkId = (item, kind, index) => {
392
- if (!isObject(item) || typeof item.id !== "string" || !item.id) {
393
- fail(`${kind} ${index + 1} has no ID`);
394
- }
395
- if (nodeIds.has(item.id)) {
396
- fail(`${kind} ${item.id}: the ID is used more than once`);
397
- }
398
- nodeIds.add(item.id);
399
- return item.id;
400
- };
401
- /**
402
- * @param {any} value
403
- * @param {string} message
404
- */
405
- const optionalString = (value, message) => {
406
- if (value !== undefined && value !== null && typeof value !== "string") {
407
- fail(message);
408
- }
409
- };
410
-
411
- data.states.forEach((/** @type {any} */ state, /** @type {number} */ i) => checkId(state, "state", i));
412
- functions.forEach((/** @type {any} */ fn, /** @type {number} */ i) => checkId(fn, "function block", i));
413
- notes.forEach((/** @type {any} */ note, /** @type {number} */ i) => checkId(note, "note", i));
414
- const stateIds = new Set(data.states.map((/** @type {any} */ s) => s.id));
415
- optionalString(data.comment, '"comment" is no text');
416
-
417
- /** @type {ChartState[]} */
418
- const states = data.states.map((/** @type {any} */ state) => {
419
- const at = `state ${state.id}`;
420
- if (state.parent && !stateIds.has(state.parent)) {
421
- fail(`${at}: unknown parent state "${state.parent}"`);
422
- }
423
- for (const field of ["entry", "during", "exit", "initial", "comment"]) {
424
- optionalString(state[field], `${at}: "${field}" is no text`);
425
- }
426
- return {
427
- ...state,
428
- name: typeof state.name === "string" && state.name ? state.name : state.id,
429
- parent: state.parent || null,
430
- x: Number.isFinite(state.x) ? state.x : 0,
431
- y: Number.isFinite(state.y) ? state.y : 0,
432
- };
433
- });
434
-
435
- // a parent must not be its own (indirect) child
436
- const parentOf = new Map(states.map(s => [s.id, s.parent]));
437
- for (const state of states) {
438
- let parent = state.parent;
439
- for (let i = 0; parent; i++) {
440
- if (parent === state.id || i > states.length) {
441
- fail(`state ${state.id}: the parent states form a cycle`);
442
- }
443
- parent = parentOf.get(parent) ?? null;
444
- }
445
- }
446
-
447
- const transitionIds = new Set();
448
- /** @type {ChartTransition[]} */
449
- const transitions = data.transitions.map((/** @type {any} */ transition, /** @type {number} */ i) => {
450
- if (!isObject(transition) || typeof transition.id !== "string" || !transition.id) {
451
- fail(`transition ${i + 1} has no ID`);
452
- }
453
- const at = `transition ${transition.id}`;
454
- if (transitionIds.has(transition.id)) {
455
- fail(`${at}: the ID is used more than once`);
456
- }
457
- transitionIds.add(transition.id);
458
- for (const end of ["source", "target"]) {
459
- if (!stateIds.has(transition[end])) {
460
- fail(`${at}: unknown ${end} state "${transition[end]}"`);
461
- }
462
- }
463
- optionalString(transition.label, `${at}: "label" is no text`);
464
- optionalString(transition.comment, `${at}: "comment" is no text`);
465
- if (transition.priority !== undefined && !Number.isFinite(transition.priority)) {
466
- fail(`${at}: "priority" is no number`);
467
- }
468
- return { ...transition };
469
- });
470
-
471
- const symbolNames = new Set();
472
- /** @type {ChartSymbol[]} */
473
- const symbols = data.symbols.map((/** @type {any} */ symbol, /** @type {number} */ i) => {
474
- if (!isObject(symbol) || typeof symbol.name !== "string" || !IDENTIFIER.test(symbol.name)) {
475
- fail(`variable ${i + 1}: invalid name`);
476
- }
477
- if (symbolNames.has(symbol.name)) {
478
- fail(`variable ${symbol.name}: the name is used more than once`);
479
- }
480
- symbolNames.add(symbol.name);
481
- if (!SCOPES.includes(symbol.scope)) {
482
- fail(`variable ${symbol.name}: invalid kind "${symbol.scope}"`);
483
- }
484
- optionalString(symbol.stateId, `variable ${symbol.name}: "stateId" is no text`);
485
- if (symbol.trigger !== undefined && !["update", "change"].includes(symbol.trigger)) {
486
- fail(`variable ${symbol.name}: invalid trigger "${symbol.trigger}"`);
487
- }
488
- if (symbol.ack !== undefined && !["any", "ack", "command"].includes(symbol.ack)) {
489
- fail(`variable ${symbol.name}: invalid ack filter "${symbol.ack}"`);
490
- }
491
- if (symbol.debounceMs !== undefined && !(Number.isFinite(symbol.debounceMs) && symbol.debounceMs >= 0)) {
492
- fail(`variable ${symbol.name}: "debounceMs" is no number of 0 or more`);
493
- }
494
- return { ...symbol };
495
- });
496
-
497
- /** @type {ChartFunction[]} */
498
- const normalizedFunctions = functions.map(fn => {
499
- if (typeof fn.code !== "string") {
500
- fail(`function block ${fn.id}: "code" is no text`);
501
- }
502
- optionalString(fn.comment, `function block ${fn.id}: "comment" is no text`);
503
- return { ...fn, x: Number.isFinite(fn.x) ? fn.x : 0, y: Number.isFinite(fn.y) ? fn.y : 0 };
504
- });
505
-
506
- /** @type {ChartNote[]} */
507
- const normalizedNotes = notes.map(note => {
508
- optionalString(note.text, `note ${note.id}: "text" is no text`);
509
- if (note.parent && !stateIds.has(note.parent)) {
510
- fail(`note ${note.id}: unknown parent state "${note.parent}"`);
511
- }
512
- return {
513
- ...note,
514
- text: note.text || "",
515
- parent: note.parent || null,
516
- x: Number.isFinite(note.x) ? note.x : 0,
517
- y: Number.isFinite(note.y) ? note.y : 0,
518
- };
519
- });
520
-
521
- return {
522
- ...data,
523
- version: CHART_MODEL_VERSION,
524
- name: typeof data.name === "string" && data.name.trim() ? data.name.trim() : where,
525
- initial: typeof data.initial === "string" && stateIds.has(data.initial) ? data.initial : undefined,
526
- enabled: data.enabled !== false,
527
- symbols,
528
- states,
529
- transitions,
530
- functions: normalizedFunctions,
531
- notes: normalizedNotes,
532
- };
533
- }
534
-
535
- /**
536
- * Reads the charts of an export file. A single chart (without the export wrapper) is accepted as well.
537
- *
538
- * @param {string} text Content of the file
539
- * @returns {Chart[]}
540
- * @throws {Error} if the file is invalid
541
- */
542
- function parseExport(text) {
543
- let data;
544
- try {
545
- data = JSON.parse(text);
546
- } catch (err) {
547
- throw new Error(`No valid JSON: ${err.message}`);
548
- }
549
- if (isObject(data) && (data.format === EXPORT_FORMAT || LEGACY_EXPORT_FORMATS.includes(data.format))) {
550
- if (typeof data.version === "number" && data.version > CHART_MODEL_VERSION) {
551
- throw new Error(`The file was created with a newer version of the adapter (version ${data.version})`);
552
- }
553
- if (!Array.isArray(data.charts) || !data.charts.length) {
554
- throw new Error("The file contains no charts");
555
- }
556
- return data.charts.map((/** @type {any} */ chart, /** @type {number} */ i) =>
557
- normalizeChart(chart, `Chart ${i + 1}`),
558
- );
559
- }
560
- if (isObject(data) && Array.isArray(data.states)) {
561
- return [normalizeChart(data, "Chart")];
562
- }
563
- throw new Error("The file contains no charts of the statechart adapter");
564
- }
565
-
566
- module.exports = {
567
- CHART_MODEL_VERSION,
568
- EXPORT_FORMAT,
569
- LabelSyntaxError,
570
- createChart,
571
- createExport,
572
- formatMessage,
573
- nameToId,
574
- parseExport,
575
- parseTransitionLabel,
576
- parseTransitionLabelWithRanges,
577
- };