scjson 0.2.0 → 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.
- package/README.md +189 -3
- package/bin/scjson.js +1 -1
- package/dist/browser.cjs +12 -0
- package/dist/browser.d.cts +2 -0
- package/dist/browser.d.mts +6 -0
- package/dist/browser.mjs +12 -0
- package/dist/converters.d.ts +191 -0
- package/dist/converters.js +1133 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +236 -0
- package/{types → dist}/scjsonProps.d.ts +115 -173
- package/dist/scjsonProps.js +309 -0
- package/package.json +31 -56
- package/scjson.schema.json +160 -13
- package/browser.cjs +0 -243
- package/browser.mjs +0 -92
- package/index.js +0 -243
- package/scjsonProps.ts +0 -618
- package/tests/cli.test.js +0 -172
- package/types/scjson-browser.d.ts +0 -25
package/scjson.schema.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$defs": {
|
|
3
3
|
"Assign": {
|
|
4
|
+
"description": "update a datamodel location with an expression or value.",
|
|
4
5
|
"properties": {
|
|
5
6
|
"location": {
|
|
6
7
|
"title": "Location",
|
|
@@ -87,6 +88,7 @@
|
|
|
87
88
|
"type": "string"
|
|
88
89
|
},
|
|
89
90
|
"Cancel": {
|
|
91
|
+
"description": "cancel a pending `<send>` operation.",
|
|
90
92
|
"properties": {
|
|
91
93
|
"other_element": {
|
|
92
94
|
"items": {},
|
|
@@ -129,13 +131,22 @@
|
|
|
129
131
|
"type": "object"
|
|
130
132
|
},
|
|
131
133
|
"Content": {
|
|
134
|
+
"description": "inline payload used by `<send>` and `<invoke>`.",
|
|
132
135
|
"properties": {
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
"content": {
|
|
137
|
+
"anyOf": [
|
|
138
|
+
{
|
|
139
|
+
"items": {
|
|
140
|
+
"$ref": "#/$defs/Scxml"
|
|
141
|
+
},
|
|
142
|
+
"type": "array"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"type": "null"
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"default": null,
|
|
149
|
+
"title": "Content"
|
|
139
150
|
},
|
|
140
151
|
"expr": {
|
|
141
152
|
"anyOf": [
|
|
@@ -149,16 +160,19 @@
|
|
|
149
160
|
"default": null,
|
|
150
161
|
"title": "Expr"
|
|
151
162
|
},
|
|
152
|
-
"
|
|
153
|
-
"
|
|
154
|
-
|
|
155
|
-
|
|
163
|
+
"other_attributes": {
|
|
164
|
+
"additionalProperties": {
|
|
165
|
+
"type": "string"
|
|
166
|
+
},
|
|
167
|
+
"title": "Other Attributes",
|
|
168
|
+
"type": "object"
|
|
156
169
|
}
|
|
157
170
|
},
|
|
158
171
|
"title": "Content",
|
|
159
172
|
"type": "object"
|
|
160
173
|
},
|
|
161
174
|
"Data": {
|
|
175
|
+
"description": "represents a single datamodel variable.",
|
|
162
176
|
"properties": {
|
|
163
177
|
"id": {
|
|
164
178
|
"title": "Id",
|
|
@@ -208,6 +222,7 @@
|
|
|
208
222
|
"type": "object"
|
|
209
223
|
},
|
|
210
224
|
"Datamodel": {
|
|
225
|
+
"description": "container for one or more `<data>` elements.",
|
|
211
226
|
"properties": {
|
|
212
227
|
"data": {
|
|
213
228
|
"items": {
|
|
@@ -233,6 +248,7 @@
|
|
|
233
248
|
"type": "object"
|
|
234
249
|
},
|
|
235
250
|
"Donedata": {
|
|
251
|
+
"description": "payload returned when a `<final>` state is reached.",
|
|
236
252
|
"properties": {
|
|
237
253
|
"content": {
|
|
238
254
|
"anyOf": [
|
|
@@ -264,6 +280,7 @@
|
|
|
264
280
|
"type": "object"
|
|
265
281
|
},
|
|
266
282
|
"Else": {
|
|
283
|
+
"description": "fallback branch for `<if>` conditions.",
|
|
267
284
|
"properties": {
|
|
268
285
|
"other_attributes": {
|
|
269
286
|
"additionalProperties": {
|
|
@@ -277,6 +294,7 @@
|
|
|
277
294
|
"type": "object"
|
|
278
295
|
},
|
|
279
296
|
"Elseif": {
|
|
297
|
+
"description": "conditional branch following an `<if>`.",
|
|
280
298
|
"properties": {
|
|
281
299
|
"cond": {
|
|
282
300
|
"title": "Cond",
|
|
@@ -306,6 +324,7 @@
|
|
|
306
324
|
"type": "string"
|
|
307
325
|
},
|
|
308
326
|
"Final": {
|
|
327
|
+
"description": "marks a terminal state in the machine.",
|
|
309
328
|
"properties": {
|
|
310
329
|
"onentry": {
|
|
311
330
|
"items": {
|
|
@@ -357,6 +376,7 @@
|
|
|
357
376
|
"type": "object"
|
|
358
377
|
},
|
|
359
378
|
"Finalize": {
|
|
379
|
+
"description": "executed after an `<invoke>` completes.",
|
|
360
380
|
"properties": {
|
|
361
381
|
"other_element": {
|
|
362
382
|
"items": {},
|
|
@@ -431,6 +451,7 @@
|
|
|
431
451
|
"type": "object"
|
|
432
452
|
},
|
|
433
453
|
"Foreach": {
|
|
454
|
+
"description": "iterate over items within executable content.",
|
|
434
455
|
"properties": {
|
|
435
456
|
"other_element": {
|
|
436
457
|
"items": {},
|
|
@@ -529,6 +550,7 @@
|
|
|
529
550
|
"type": "object"
|
|
530
551
|
},
|
|
531
552
|
"History": {
|
|
553
|
+
"description": "pseudostate remembering previous active children.",
|
|
532
554
|
"properties": {
|
|
533
555
|
"other_element": {
|
|
534
556
|
"items": {},
|
|
@@ -576,6 +598,7 @@
|
|
|
576
598
|
"type": "object"
|
|
577
599
|
},
|
|
578
600
|
"HistoryTypeDatatype": {
|
|
601
|
+
"description": "type of `<history>` state: `shallow` or `deep`.",
|
|
579
602
|
"enum": [
|
|
580
603
|
"shallow",
|
|
581
604
|
"deep"
|
|
@@ -584,6 +607,7 @@
|
|
|
584
607
|
"type": "string"
|
|
585
608
|
},
|
|
586
609
|
"If": {
|
|
610
|
+
"description": "conditional execution block.",
|
|
587
611
|
"properties": {
|
|
588
612
|
"other_element": {
|
|
589
613
|
"items": {},
|
|
@@ -687,6 +711,7 @@
|
|
|
687
711
|
"type": "object"
|
|
688
712
|
},
|
|
689
713
|
"Initial": {
|
|
714
|
+
"description": "starting state within a compound state.",
|
|
690
715
|
"properties": {
|
|
691
716
|
"other_element": {
|
|
692
717
|
"items": {},
|
|
@@ -711,6 +736,7 @@
|
|
|
711
736
|
"type": "object"
|
|
712
737
|
},
|
|
713
738
|
"Invoke": {
|
|
739
|
+
"description": "run an external process or machine.",
|
|
714
740
|
"properties": {
|
|
715
741
|
"content": {
|
|
716
742
|
"items": {
|
|
@@ -831,6 +857,7 @@
|
|
|
831
857
|
"type": "object"
|
|
832
858
|
},
|
|
833
859
|
"Log": {
|
|
860
|
+
"description": "diagnostic output statement.",
|
|
834
861
|
"properties": {
|
|
835
862
|
"other_element": {
|
|
836
863
|
"items": {},
|
|
@@ -873,6 +900,7 @@
|
|
|
873
900
|
"type": "object"
|
|
874
901
|
},
|
|
875
902
|
"Onentry": {
|
|
903
|
+
"description": "actions performed when entering a state.",
|
|
876
904
|
"properties": {
|
|
877
905
|
"other_element": {
|
|
878
906
|
"items": {},
|
|
@@ -947,6 +975,7 @@
|
|
|
947
975
|
"type": "object"
|
|
948
976
|
},
|
|
949
977
|
"Onexit": {
|
|
978
|
+
"description": "actions performed when leaving a state.",
|
|
950
979
|
"properties": {
|
|
951
980
|
"other_element": {
|
|
952
981
|
"items": {},
|
|
@@ -1021,6 +1050,7 @@
|
|
|
1021
1050
|
"type": "object"
|
|
1022
1051
|
},
|
|
1023
1052
|
"Parallel": {
|
|
1053
|
+
"description": "coordinates concurrent regions.",
|
|
1024
1054
|
"properties": {
|
|
1025
1055
|
"onentry": {
|
|
1026
1056
|
"items": {
|
|
@@ -1107,6 +1137,7 @@
|
|
|
1107
1137
|
"type": "object"
|
|
1108
1138
|
},
|
|
1109
1139
|
"Param": {
|
|
1140
|
+
"description": "parameter passed to `<invoke>` or `<send>`.",
|
|
1110
1141
|
"properties": {
|
|
1111
1142
|
"other_element": {
|
|
1112
1143
|
"items": {},
|
|
@@ -1156,6 +1187,7 @@
|
|
|
1156
1187
|
"type": "object"
|
|
1157
1188
|
},
|
|
1158
1189
|
"Raise": {
|
|
1190
|
+
"description": "raise an internal event.",
|
|
1159
1191
|
"properties": {
|
|
1160
1192
|
"event": {
|
|
1161
1193
|
"title": "Event",
|
|
@@ -1176,6 +1208,7 @@
|
|
|
1176
1208
|
"type": "object"
|
|
1177
1209
|
},
|
|
1178
1210
|
"Script": {
|
|
1211
|
+
"description": "inline executable script.",
|
|
1179
1212
|
"properties": {
|
|
1180
1213
|
"src": {
|
|
1181
1214
|
"anyOf": [
|
|
@@ -1205,7 +1238,120 @@
|
|
|
1205
1238
|
"title": "Script",
|
|
1206
1239
|
"type": "object"
|
|
1207
1240
|
},
|
|
1241
|
+
"Scxml": {
|
|
1242
|
+
"description": "root element of an SCJSON document.",
|
|
1243
|
+
"properties": {
|
|
1244
|
+
"state": {
|
|
1245
|
+
"items": {
|
|
1246
|
+
"$ref": "#/$defs/State"
|
|
1247
|
+
},
|
|
1248
|
+
"title": "State",
|
|
1249
|
+
"type": "array"
|
|
1250
|
+
},
|
|
1251
|
+
"parallel": {
|
|
1252
|
+
"items": {
|
|
1253
|
+
"$ref": "#/$defs/Parallel"
|
|
1254
|
+
},
|
|
1255
|
+
"title": "Parallel",
|
|
1256
|
+
"type": "array"
|
|
1257
|
+
},
|
|
1258
|
+
"final": {
|
|
1259
|
+
"items": {
|
|
1260
|
+
"$ref": "#/$defs/Final"
|
|
1261
|
+
},
|
|
1262
|
+
"title": "Final",
|
|
1263
|
+
"type": "array"
|
|
1264
|
+
},
|
|
1265
|
+
"datamodel": {
|
|
1266
|
+
"items": {
|
|
1267
|
+
"$ref": "#/$defs/Datamodel"
|
|
1268
|
+
},
|
|
1269
|
+
"title": "Datamodel",
|
|
1270
|
+
"type": "array"
|
|
1271
|
+
},
|
|
1272
|
+
"script": {
|
|
1273
|
+
"items": {
|
|
1274
|
+
"$ref": "#/$defs/Script"
|
|
1275
|
+
},
|
|
1276
|
+
"title": "Script",
|
|
1277
|
+
"type": "array"
|
|
1278
|
+
},
|
|
1279
|
+
"other_element": {
|
|
1280
|
+
"items": {},
|
|
1281
|
+
"title": "Other Element",
|
|
1282
|
+
"type": "array"
|
|
1283
|
+
},
|
|
1284
|
+
"initial": {
|
|
1285
|
+
"items": {
|
|
1286
|
+
"type": "string"
|
|
1287
|
+
},
|
|
1288
|
+
"title": "Initial",
|
|
1289
|
+
"type": "array"
|
|
1290
|
+
},
|
|
1291
|
+
"name": {
|
|
1292
|
+
"anyOf": [
|
|
1293
|
+
{
|
|
1294
|
+
"type": "string"
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
"type": "null"
|
|
1298
|
+
}
|
|
1299
|
+
],
|
|
1300
|
+
"default": null,
|
|
1301
|
+
"title": "Name"
|
|
1302
|
+
},
|
|
1303
|
+
"version": {
|
|
1304
|
+
"anyOf": [
|
|
1305
|
+
{
|
|
1306
|
+
"type": "number"
|
|
1307
|
+
},
|
|
1308
|
+
{
|
|
1309
|
+
"type": "string"
|
|
1310
|
+
}
|
|
1311
|
+
],
|
|
1312
|
+
"default": "1.0",
|
|
1313
|
+
"title": "Version"
|
|
1314
|
+
},
|
|
1315
|
+
"datamodel_attribute": {
|
|
1316
|
+
"default": "null",
|
|
1317
|
+
"title": "Datamodel Attribute",
|
|
1318
|
+
"type": "string"
|
|
1319
|
+
},
|
|
1320
|
+
"binding": {
|
|
1321
|
+
"anyOf": [
|
|
1322
|
+
{
|
|
1323
|
+
"$ref": "#/$defs/BindingDatatype"
|
|
1324
|
+
},
|
|
1325
|
+
{
|
|
1326
|
+
"type": "null"
|
|
1327
|
+
}
|
|
1328
|
+
],
|
|
1329
|
+
"default": null
|
|
1330
|
+
},
|
|
1331
|
+
"exmode": {
|
|
1332
|
+
"anyOf": [
|
|
1333
|
+
{
|
|
1334
|
+
"$ref": "#/$defs/ExmodeDatatype"
|
|
1335
|
+
},
|
|
1336
|
+
{
|
|
1337
|
+
"type": "null"
|
|
1338
|
+
}
|
|
1339
|
+
],
|
|
1340
|
+
"default": null
|
|
1341
|
+
},
|
|
1342
|
+
"other_attributes": {
|
|
1343
|
+
"additionalProperties": {
|
|
1344
|
+
"type": "string"
|
|
1345
|
+
},
|
|
1346
|
+
"title": "Other Attributes",
|
|
1347
|
+
"type": "object"
|
|
1348
|
+
}
|
|
1349
|
+
},
|
|
1350
|
+
"title": "Scxml",
|
|
1351
|
+
"type": "object"
|
|
1352
|
+
},
|
|
1208
1353
|
"Send": {
|
|
1354
|
+
"description": "dispatch an external event.",
|
|
1209
1355
|
"properties": {
|
|
1210
1356
|
"content": {
|
|
1211
1357
|
"items": {
|
|
@@ -1356,6 +1502,7 @@
|
|
|
1356
1502
|
"type": "object"
|
|
1357
1503
|
},
|
|
1358
1504
|
"State": {
|
|
1505
|
+
"description": "basic state node.",
|
|
1359
1506
|
"properties": {
|
|
1360
1507
|
"onentry": {
|
|
1361
1508
|
"items": {
|
|
@@ -1463,6 +1610,7 @@
|
|
|
1463
1610
|
"type": "object"
|
|
1464
1611
|
},
|
|
1465
1612
|
"Transition": {
|
|
1613
|
+
"description": "edge between states triggered by events.",
|
|
1466
1614
|
"properties": {
|
|
1467
1615
|
"other_element": {
|
|
1468
1616
|
"items": {},
|
|
@@ -1588,6 +1736,7 @@
|
|
|
1588
1736
|
"type": "string"
|
|
1589
1737
|
}
|
|
1590
1738
|
},
|
|
1739
|
+
"$ref": "#/$defs/Scxml",
|
|
1591
1740
|
"properties": {
|
|
1592
1741
|
"state": {
|
|
1593
1742
|
"items": {
|
|
@@ -1694,7 +1843,5 @@
|
|
|
1694
1843
|
"title": "Other Attributes",
|
|
1695
1844
|
"type": "object"
|
|
1696
1845
|
}
|
|
1697
|
-
}
|
|
1698
|
-
"title": "Scxml",
|
|
1699
|
-
"type": "object"
|
|
1846
|
+
}
|
|
1700
1847
|
}
|
package/browser.cjs
DELETED
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Name: js-cli
|
|
3
|
-
*
|
|
4
|
-
* Part of the scjson project.
|
|
5
|
-
* Developed by Softoboros Technology Inc.
|
|
6
|
-
* Licensed under the BSD 1-Clause License.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const { Command } = require('commander');
|
|
12
|
-
const { XMLParser, XMLBuilder } = require('fast-xml-parser');
|
|
13
|
-
const Ajv = require('ajv');
|
|
14
|
-
|
|
15
|
-
const program = new Command();
|
|
16
|
-
const schema = require('./scjson.schema.json');
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Remove nulls and empty containers from values recursively.
|
|
20
|
-
*
|
|
21
|
-
* @param {*} value - Candidate value.
|
|
22
|
-
* @returns {*} Sanitised value.
|
|
23
|
-
*/
|
|
24
|
-
function removeEmpty(value) {
|
|
25
|
-
if (Array.isArray(value)) {
|
|
26
|
-
const arr = value.map(removeEmpty).filter(v => v !== undefined);
|
|
27
|
-
return arr.length > 0 ? arr : undefined;
|
|
28
|
-
}
|
|
29
|
-
if (value && typeof value === 'object') {
|
|
30
|
-
const obj = {};
|
|
31
|
-
for (const [k, v] of Object.entries(value)) {
|
|
32
|
-
const r = removeEmpty(v);
|
|
33
|
-
if (r !== undefined) obj[k] = r;
|
|
34
|
-
}
|
|
35
|
-
return Object.keys(obj).length > 0 ? obj : undefined;
|
|
36
|
-
}
|
|
37
|
-
if (value === null) {
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
if (value === '') {
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
return value;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const ajv = new Ajv({ useDefaults: true, strict: false });
|
|
47
|
-
const validate = ajv.compile(schema);
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Convert an SCXML string to scjson.
|
|
51
|
-
*
|
|
52
|
-
* @param {string} xmlStr - XML input.
|
|
53
|
-
* @param {boolean} [omitEmpty=true] - Remove empty values when true.
|
|
54
|
-
* @returns {string} JSON representation.
|
|
55
|
-
*
|
|
56
|
-
* Removes the XML namespace attribute and injects default values
|
|
57
|
-
* expected by the schema.
|
|
58
|
-
*/
|
|
59
|
-
function xmlToJson(xmlStr, omitEmpty = true) {
|
|
60
|
-
const parser = new XMLParser({ ignoreAttributes: false });
|
|
61
|
-
let obj = parser.parse(xmlStr);
|
|
62
|
-
if (obj.scxml) {
|
|
63
|
-
obj = obj.scxml;
|
|
64
|
-
}
|
|
65
|
-
if (omitEmpty) {
|
|
66
|
-
obj = removeEmpty(obj) || {};
|
|
67
|
-
}
|
|
68
|
-
if (obj['@_xmlns']) {
|
|
69
|
-
delete obj['@_xmlns'];
|
|
70
|
-
}
|
|
71
|
-
if (obj.version === undefined) {
|
|
72
|
-
obj.version = 1.0;
|
|
73
|
-
}
|
|
74
|
-
if (obj.datamodel_attribute === undefined) {
|
|
75
|
-
obj.datamodel_attribute = 'null';
|
|
76
|
-
}
|
|
77
|
-
if (!validate(obj)) {
|
|
78
|
-
throw new Error('Invalid scjson');
|
|
79
|
-
}
|
|
80
|
-
if (omitEmpty) {
|
|
81
|
-
obj = removeEmpty(obj) || {};
|
|
82
|
-
}
|
|
83
|
-
return JSON.stringify(obj, null, 2);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Convert a scjson string to SCXML.
|
|
88
|
-
*
|
|
89
|
-
* @param {string} jsonStr - JSON input.
|
|
90
|
-
* @returns {string} XML output.
|
|
91
|
-
*/
|
|
92
|
-
function jsonToXml(jsonStr) {
|
|
93
|
-
const builder = new XMLBuilder({ ignoreAttributes: false, format: true });
|
|
94
|
-
const obj = JSON.parse(jsonStr);
|
|
95
|
-
if (!validate(obj)) {
|
|
96
|
-
throw new Error('Invalid scjson');
|
|
97
|
-
}
|
|
98
|
-
return builder.build({ scxml: obj });
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
program
|
|
102
|
-
.name('scjson')
|
|
103
|
-
.description('SCXML <-> scjson converter and validator');
|
|
104
|
-
|
|
105
|
-
program
|
|
106
|
-
.command('validate')
|
|
107
|
-
.description('validate a scjson or SCXML file by round-tripping it')
|
|
108
|
-
.argument('<file>', 'file path')
|
|
109
|
-
.option('-r, --recursive', 'recurse into directories')
|
|
110
|
-
.action((file, options) => {
|
|
111
|
-
const src = path.resolve(file);
|
|
112
|
-
let success = true;
|
|
113
|
-
|
|
114
|
-
function validateFile(p) {
|
|
115
|
-
const data = fs.readFileSync(p, 'utf8');
|
|
116
|
-
try {
|
|
117
|
-
if (p.endsWith('.scxml')) {
|
|
118
|
-
const json = xmlToJson(data);
|
|
119
|
-
jsonToXml(json);
|
|
120
|
-
} else if (p.endsWith('.scjson')) {
|
|
121
|
-
const xml = jsonToXml(data);
|
|
122
|
-
xmlToJson(xml);
|
|
123
|
-
} else {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
} catch (e) {
|
|
127
|
-
console.error(`Validation failed for ${p}: ${e.message}`);
|
|
128
|
-
success = false;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (fs.statSync(src).isDirectory()) {
|
|
133
|
-
const pattern = options.recursive ? '**/*' : '*';
|
|
134
|
-
const files = require('glob').sync(pattern, { cwd: src, nodir: true });
|
|
135
|
-
files.forEach(f => validateFile(path.join(src, f)));
|
|
136
|
-
} else {
|
|
137
|
-
validateFile(src);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (!success) {
|
|
141
|
-
process.exitCode = 1;
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
function convertDirectoryJson(inputDir, outputDir, recursive, verify, keepEmpty) {
|
|
146
|
-
const pattern = recursive ? '**/*.scxml' : '*.scxml';
|
|
147
|
-
const files = require('glob').sync(pattern, { cwd: inputDir, nodir: true });
|
|
148
|
-
files.forEach(f => {
|
|
149
|
-
const src = path.join(inputDir, f);
|
|
150
|
-
const dest = path.join(outputDir, f.replace(/\.scxml$/, '.scjson'));
|
|
151
|
-
convertScxmlFile(src, dest, verify, keepEmpty);
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function convertDirectoryXml(inputDir, outputDir, recursive, verify, keepEmpty) {
|
|
156
|
-
const pattern = recursive ? '**/*.scjson' : '*.scjson';
|
|
157
|
-
const files = require('glob').sync(pattern, { cwd: inputDir, nodir: true });
|
|
158
|
-
files.forEach(f => {
|
|
159
|
-
const src = path.join(inputDir, f);
|
|
160
|
-
const dest = path.join(outputDir, f.replace(/\.scjson$/, '.scxml'));
|
|
161
|
-
convertScjsonFile(src, dest, verify, keepEmpty);
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function convertScxmlFile(src, dest, verify, keepEmpty) {
|
|
166
|
-
const xmlStr = fs.readFileSync(src, 'utf8');
|
|
167
|
-
try {
|
|
168
|
-
const jsonStr = xmlToJson(xmlStr, !keepEmpty);
|
|
169
|
-
if (verify) {
|
|
170
|
-
jsonToXml(jsonStr);
|
|
171
|
-
} else {
|
|
172
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
173
|
-
fs.writeFileSync(dest, jsonStr);
|
|
174
|
-
}
|
|
175
|
-
if (verify) console.log(`Verified ${src}`);
|
|
176
|
-
} catch (e) {
|
|
177
|
-
console.error(`Failed to convert ${src}: ${e.message}`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function convertScjsonFile(src, dest, verify) {
|
|
182
|
-
const jsonStr = fs.readFileSync(src, 'utf8');
|
|
183
|
-
try {
|
|
184
|
-
const xmlStr = jsonToXml(jsonStr);
|
|
185
|
-
if (verify) {
|
|
186
|
-
xmlToJson(xmlStr);
|
|
187
|
-
} else {
|
|
188
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
189
|
-
fs.writeFileSync(dest, xmlStr);
|
|
190
|
-
}
|
|
191
|
-
if (verify) console.log(`Verified ${src}`);
|
|
192
|
-
} catch (e) {
|
|
193
|
-
console.error(`Failed to convert ${src}: ${e.message}`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
program
|
|
198
|
-
.command('json')
|
|
199
|
-
.argument('<path>', 'SCXML file or directory')
|
|
200
|
-
.option('-o, --output <path>', 'output file or directory')
|
|
201
|
-
.option('-r, --recursive', 'recurse into directories')
|
|
202
|
-
.option('-v, --verify', 'verify conversion without writing output')
|
|
203
|
-
.option('--keep-empty', 'keep null or empty items when producing JSON')
|
|
204
|
-
.action((p, opts) => {
|
|
205
|
-
const src = path.resolve(p);
|
|
206
|
-
const out = opts.output ? path.resolve(opts.output) : src;
|
|
207
|
-
|
|
208
|
-
if (fs.statSync(src).isDirectory()) {
|
|
209
|
-
convertDirectoryJson(src, out, opts.recursive, opts.verify, opts.keepEmpty);
|
|
210
|
-
} else {
|
|
211
|
-
const dest = opts.output && !opts.output.endsWith('.json') && !opts.output.endsWith('.scjson')
|
|
212
|
-
? path.join(out, path.basename(src).replace(/\.scxml$/, '.scjson'))
|
|
213
|
-
: (opts.output || src.replace(/\.scxml$/, '.scjson'));
|
|
214
|
-
convertScxmlFile(src, dest, opts.verify, opts.keepEmpty);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
program
|
|
219
|
-
.command('xml')
|
|
220
|
-
.argument('<path>', 'scjson file or directory')
|
|
221
|
-
.option('-o, --output <path>', 'output file or directory')
|
|
222
|
-
.option('-r, --recursive', 'recurse into directories')
|
|
223
|
-
.option('-v, --verify', 'verify conversion without writing output')
|
|
224
|
-
.option('--keep-empty', 'keep null or empty items when producing JSON')
|
|
225
|
-
.action((p, opts) => {
|
|
226
|
-
const src = path.resolve(p);
|
|
227
|
-
const out = opts.output ? path.resolve(opts.output) : src;
|
|
228
|
-
|
|
229
|
-
if (fs.statSync(src).isDirectory()) {
|
|
230
|
-
convertDirectoryXml(src, out, opts.recursive, opts.verify, opts.keepEmpty);
|
|
231
|
-
} else {
|
|
232
|
-
const dest = opts.output && !opts.output.endsWith('.xml') && !opts.output.endsWith('.scxml')
|
|
233
|
-
? path.join(out, path.basename(src).replace(/\.scjson$/, '.scxml'))
|
|
234
|
-
: (opts.output || src.replace(/\.scjson$/, '.scxml'));
|
|
235
|
-
convertScjsonFile(src, dest, opts.verify, opts.keepEmpty);
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
if (require.main === module) {
|
|
240
|
-
program.parse(process.argv);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
module.exports = { program, xmlToJson, jsonToXml };
|
package/browser.mjs
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Name: js-browser
|
|
3
|
-
*
|
|
4
|
-
* Part of the scjson project.
|
|
5
|
-
* Developed by Softoboros Technology Inc.
|
|
6
|
-
* Licensed under the BSD 1-Clause License.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @file Browser friendly utilities for converting SCXML to scjson and back.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { XMLParser, XMLBuilder } from 'fast-xml-parser';
|
|
14
|
-
import Ajv from 'ajv';
|
|
15
|
-
import schema from './scjson.schema.json';
|
|
16
|
-
|
|
17
|
-
const ajv = new Ajv({ useDefaults: true, strict: false });
|
|
18
|
-
const validate = ajv.compile(schema);
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Remove nulls and empty containers from values recursively.
|
|
22
|
-
*
|
|
23
|
-
* @param {*} value - Candidate value.
|
|
24
|
-
* @returns {*} Sanitised value.
|
|
25
|
-
*/
|
|
26
|
-
export function removeEmpty(value) {
|
|
27
|
-
if (Array.isArray(value)) {
|
|
28
|
-
const arr = value.map(removeEmpty).filter(v => v !== undefined);
|
|
29
|
-
return arr.length > 0 ? arr : undefined;
|
|
30
|
-
}
|
|
31
|
-
if (value && typeof value === 'object') {
|
|
32
|
-
const obj = {};
|
|
33
|
-
for (const [k, v] of Object.entries(value)) {
|
|
34
|
-
const r = removeEmpty(v);
|
|
35
|
-
if (r !== undefined) obj[k] = r;
|
|
36
|
-
}
|
|
37
|
-
return Object.keys(obj).length > 0 ? obj : undefined;
|
|
38
|
-
}
|
|
39
|
-
if (value === null || value === '') {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
return value;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Convert an SCXML string to scjson.
|
|
47
|
-
*
|
|
48
|
-
* @param {string} xmlStr - XML input.
|
|
49
|
-
* @param {boolean} [omitEmpty=true] - Remove empty values when true.
|
|
50
|
-
* @returns {string} JSON representation.
|
|
51
|
-
*/
|
|
52
|
-
export function xmlToJson(xmlStr, omitEmpty = true) {
|
|
53
|
-
const parser = new XMLParser({ ignoreAttributes: false });
|
|
54
|
-
let obj = parser.parse(xmlStr);
|
|
55
|
-
if (obj.scxml) {
|
|
56
|
-
obj = obj.scxml;
|
|
57
|
-
}
|
|
58
|
-
if (omitEmpty) {
|
|
59
|
-
obj = removeEmpty(obj) || {};
|
|
60
|
-
}
|
|
61
|
-
if (obj['@_xmlns']) {
|
|
62
|
-
delete obj['@_xmlns'];
|
|
63
|
-
}
|
|
64
|
-
if (obj.version === undefined) {
|
|
65
|
-
obj.version = 1.0;
|
|
66
|
-
}
|
|
67
|
-
if (obj.datamodel_attribute === undefined) {
|
|
68
|
-
obj.datamodel_attribute = 'null';
|
|
69
|
-
}
|
|
70
|
-
if (!validate(obj)) {
|
|
71
|
-
throw new Error('Invalid scjson');
|
|
72
|
-
}
|
|
73
|
-
if (omitEmpty) {
|
|
74
|
-
obj = removeEmpty(obj) || {};
|
|
75
|
-
}
|
|
76
|
-
return JSON.stringify(obj, null, 2);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Convert a scjson string to SCXML.
|
|
81
|
-
*
|
|
82
|
-
* @param {string} jsonStr - JSON input.
|
|
83
|
-
* @returns {string} XML output.
|
|
84
|
-
*/
|
|
85
|
-
export function jsonToXml(jsonStr) {
|
|
86
|
-
const builder = new XMLBuilder({ ignoreAttributes: false, format: true });
|
|
87
|
-
const obj = JSON.parse(jsonStr);
|
|
88
|
-
if (!validate(obj)) {
|
|
89
|
-
throw new Error('Invalid scjson');
|
|
90
|
-
}
|
|
91
|
-
return builder.build({ scxml: obj });
|
|
92
|
-
}
|