scjson 0.2.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.
- 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 +92 -226
- 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 -721
- package/tests/cli.test.js +0 -172
- package/types/scjson-browser.d.ts +0 -25
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Agent Name: js-cli
|
|
4
|
+
*
|
|
5
|
+
* Part of the scjson project.
|
|
6
|
+
* Developed by Softoboros Technology Inc.
|
|
7
|
+
* Licensed under the BSD 1-Clause License.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* @file Command line interface for converting SCXML and scjson files.
|
|
11
|
+
*/
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { Command } = require('commander');
|
|
15
|
+
const { xmlToJson, jsonToXml, } = require('./converters.js');
|
|
16
|
+
const program = new Command();
|
|
17
|
+
program
|
|
18
|
+
.name('scjson')
|
|
19
|
+
.description('SCXML <-> scjson converter and validator');
|
|
20
|
+
program
|
|
21
|
+
.command('validate')
|
|
22
|
+
.description('validate a scjson or SCXML file by round-tripping it')
|
|
23
|
+
.argument('<file>', 'file path')
|
|
24
|
+
.option('-r, --recursive', 'recurse into directories')
|
|
25
|
+
.action((file, options) => {
|
|
26
|
+
const src = path.resolve(file);
|
|
27
|
+
let success = true;
|
|
28
|
+
function validateFile(p) {
|
|
29
|
+
const data = fs.readFileSync(p, 'utf8');
|
|
30
|
+
try {
|
|
31
|
+
if (p.endsWith('.scxml')) {
|
|
32
|
+
const json = xmlToJson(data);
|
|
33
|
+
jsonToXml(json);
|
|
34
|
+
}
|
|
35
|
+
else if (p.endsWith('.scjson')) {
|
|
36
|
+
const xml = jsonToXml(data);
|
|
37
|
+
xmlToJson(xml);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
console.error(`Validation failed for ${p}: ${e.message}`);
|
|
45
|
+
success = false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (fs.statSync(src).isDirectory()) {
|
|
49
|
+
const pattern = options.recursive ? '**/*' : '*';
|
|
50
|
+
const files = require('glob').sync(pattern, { cwd: src, nodir: true });
|
|
51
|
+
files.forEach(f => validateFile(path.join(src, f)));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
validateFile(src);
|
|
55
|
+
}
|
|
56
|
+
if (!success) {
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* Convert all SCXML files in a directory to scjson.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} inputDir - Directory containing SCXML files.
|
|
64
|
+
* @param {string} outputDir - Destination directory for scjson output.
|
|
65
|
+
* @param {boolean} recursive - Recurse into subdirectories when true.
|
|
66
|
+
* @param {boolean} verify - Verify conversion without writing output.
|
|
67
|
+
* @param {boolean} keepEmpty - Keep null or empty elements when true.
|
|
68
|
+
* @returns {boolean} True if all files converted successfully.
|
|
69
|
+
*/
|
|
70
|
+
function convertDirectoryJson(inputDir, outputDir, recursive, verify, keepEmpty) {
|
|
71
|
+
const pattern = recursive ? '**/*.scxml' : '*.scxml';
|
|
72
|
+
const files = require('glob').sync(pattern, { cwd: inputDir, nodir: true });
|
|
73
|
+
let success = true;
|
|
74
|
+
files.forEach(f => {
|
|
75
|
+
const src = path.join(inputDir, f);
|
|
76
|
+
const dest = path.join(outputDir, f.replace(/\.scxml$/, '.scjson'));
|
|
77
|
+
if (!convertScxmlFile(src, dest, verify, keepEmpty)) {
|
|
78
|
+
success = false;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return success;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Convert all scjson files in a directory to SCXML.
|
|
85
|
+
*
|
|
86
|
+
* @param {string} inputDir - Directory containing scjson files.
|
|
87
|
+
* @param {string} outputDir - Destination directory for SCXML output.
|
|
88
|
+
* @param {boolean} recursive - Recurse into subdirectories when true.
|
|
89
|
+
* @param {boolean} verify - Verify conversion without writing output.
|
|
90
|
+
* @param {boolean} keepEmpty - Keep null or empty elements when true.
|
|
91
|
+
* @returns {boolean} True if all files converted successfully.
|
|
92
|
+
*/
|
|
93
|
+
function convertDirectoryXml(inputDir, outputDir, recursive, verify, keepEmpty) {
|
|
94
|
+
const pattern = recursive ? '**/*.scjson' : '*.scjson';
|
|
95
|
+
const files = require('glob').sync(pattern, { cwd: inputDir, nodir: true });
|
|
96
|
+
let success = true;
|
|
97
|
+
files.forEach(f => {
|
|
98
|
+
const src = path.join(inputDir, f);
|
|
99
|
+
const dest = path.join(outputDir, f.replace(/\.scjson$/, '.scxml'));
|
|
100
|
+
if (!convertScjsonFile(src, dest, verify, keepEmpty)) {
|
|
101
|
+
success = false;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
return success;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Convert a single SCXML file to scjson.
|
|
108
|
+
*
|
|
109
|
+
* @param {string} src - Path to the source SCXML file.
|
|
110
|
+
* @param {string} dest - Output path for the scjson file.
|
|
111
|
+
* @param {boolean} verify - Verify conversion without writing output.
|
|
112
|
+
* @param {boolean} keepEmpty - Keep null or empty elements when true.
|
|
113
|
+
* @returns {boolean} True if the conversion succeeded.
|
|
114
|
+
*/
|
|
115
|
+
function convertScxmlFile(src, dest, verify, keepEmpty) {
|
|
116
|
+
const xmlStr = fs.readFileSync(src, 'utf8');
|
|
117
|
+
try {
|
|
118
|
+
const { result: jsonStr, valid, errors } = xmlToJson(xmlStr, !keepEmpty);
|
|
119
|
+
if (!valid) {
|
|
120
|
+
console.warn(`Validation failed in xmlToJson for ${src}: ${JSON.stringify(errors, null, 2)}`);
|
|
121
|
+
}
|
|
122
|
+
if (verify) {
|
|
123
|
+
const { valid: xmlValid, errors: xmlErrors } = jsonToXml(jsonStr);
|
|
124
|
+
if (!xmlValid) {
|
|
125
|
+
console.warn(`Validation failed in jsonToXml for ${src}: ${JSON.stringify(xmlErrors, null, 2)}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
130
|
+
fs.writeFileSync(dest, jsonStr);
|
|
131
|
+
}
|
|
132
|
+
if (verify)
|
|
133
|
+
console.log(`Verified ${src}`);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
if (e.errors) {
|
|
138
|
+
console.error(`Failed to convert ${src}: ${e.message}\n${JSON.stringify(e.errors, null, 2)}`);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
console.error(`Failed to convert ${src}: ${e.message}`);
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Convert a single scjson file to SCXML.
|
|
148
|
+
*
|
|
149
|
+
* @param {string} src - Path to the source scjson file.
|
|
150
|
+
* @param {string} dest - Output path for the SCXML file.
|
|
151
|
+
* @param {boolean} verify - Verify conversion without writing output.
|
|
152
|
+
* @returns {boolean} True if the conversion succeeded.
|
|
153
|
+
*/
|
|
154
|
+
function convertScjsonFile(src, dest, verify) {
|
|
155
|
+
const jsonStr = fs.readFileSync(src, 'utf8');
|
|
156
|
+
try {
|
|
157
|
+
const { result: xmlStr, valid, errors } = jsonToXml(jsonStr);
|
|
158
|
+
if (!valid) {
|
|
159
|
+
console.warn(`Validation failed in jsonToXml for ${src}: ${JSON.stringify(errors, null, 2)}`);
|
|
160
|
+
}
|
|
161
|
+
if (verify) {
|
|
162
|
+
const { valid: jsonValid, errors: jsonErrors } = xmlToJson(xmlStr);
|
|
163
|
+
if (!jsonValid) {
|
|
164
|
+
console.warn(`Validation failed in xmlToJson for ${src}: ${JSON.stringify(jsonErrors, null, 2)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
169
|
+
fs.writeFileSync(dest, xmlStr);
|
|
170
|
+
}
|
|
171
|
+
if (verify)
|
|
172
|
+
console.log(`Verified ${src}`);
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
if (e.errors) {
|
|
177
|
+
console.error(`Failed to convert ${src}: ${e.message}\n${JSON.stringify(e.errors, null, 2)}`);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
console.error(`Failed to convert ${src}: ${e.message}`);
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
program
|
|
186
|
+
.command('json')
|
|
187
|
+
.argument('<path>', 'SCXML file or directory')
|
|
188
|
+
.option('-o, --output <path>', 'output file or directory')
|
|
189
|
+
.option('-r, --recursive', 'recurse into directories')
|
|
190
|
+
.option('-v, --verify', 'verify conversion without writing output')
|
|
191
|
+
.option('--keep-empty', 'keep null or empty items when producing JSON')
|
|
192
|
+
.action((p, opts) => {
|
|
193
|
+
const src = path.resolve(p);
|
|
194
|
+
const out = opts.output ? path.resolve(opts.output) : src;
|
|
195
|
+
if (fs.statSync(src).isDirectory()) {
|
|
196
|
+
const success = convertDirectoryJson(src, out, opts.recursive, opts.verify, opts.keepEmpty);
|
|
197
|
+
if (opts.verify && !success)
|
|
198
|
+
process.exitCode = 1;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const dest = opts.output && !opts.output.endsWith('.json') && !opts.output.endsWith('.scjson')
|
|
202
|
+
? path.join(out, path.basename(src).replace(/\.scxml$/, '.scjson'))
|
|
203
|
+
: (opts.output || src.replace(/\.scxml$/, '.scjson'));
|
|
204
|
+
const success = convertScxmlFile(src, dest, opts.verify, opts.keepEmpty);
|
|
205
|
+
if (opts.verify && !success)
|
|
206
|
+
process.exitCode = 1;
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
program
|
|
210
|
+
.command('xml')
|
|
211
|
+
.argument('<path>', 'scjson file or directory')
|
|
212
|
+
.option('-o, --output <path>', 'output file or directory')
|
|
213
|
+
.option('-r, --recursive', 'recurse into directories')
|
|
214
|
+
.option('-v, --verify', 'verify conversion without writing output')
|
|
215
|
+
.option('--keep-empty', 'keep null or empty items when producing JSON')
|
|
216
|
+
.action((p, opts) => {
|
|
217
|
+
const src = path.resolve(p);
|
|
218
|
+
const out = opts.output ? path.resolve(opts.output) : src;
|
|
219
|
+
if (fs.statSync(src).isDirectory()) {
|
|
220
|
+
const success = convertDirectoryXml(src, out, opts.recursive, opts.verify, opts.keepEmpty);
|
|
221
|
+
if (opts.verify && !success)
|
|
222
|
+
process.exitCode = 1;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
const dest = opts.output && !opts.output.endsWith('.xml') && !opts.output.endsWith('.scxml')
|
|
226
|
+
? path.join(out, path.basename(src).replace(/\.scjson$/, '.scxml'))
|
|
227
|
+
: (opts.output || src.replace(/\.scjson$/, '.scxml'));
|
|
228
|
+
const success = convertScjsonFile(src, dest, opts.verify, opts.keepEmpty);
|
|
229
|
+
if (opts.verify && !success)
|
|
230
|
+
process.exitCode = 1;
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
if (require.main === module) {
|
|
234
|
+
program.parse(process.argv);
|
|
235
|
+
}
|
|
236
|
+
module.exports = { program, xmlToJson, jsonToXml };
|