cborg 1.5.4 → 1.7.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/cjs/lib/bin.js CHANGED
@@ -14,15 +14,18 @@ var process__default = /*#__PURE__*/_interopDefaultLegacy(process);
14
14
  function usage(code) {
15
15
  console.error('Usage: cborg <command> <args>');
16
16
  console.error('Valid commands:');
17
- console.error('\thex2diag [hex input]');
18
- console.error('\thex2bin [hex input]');
19
- console.error('\thex2json [--pretty] [hex input]');
20
- console.error('\tbin2hex [binary input]');
21
17
  console.error('\tbin2diag [binary input]');
18
+ console.error('\tbin2hex [binary input]');
22
19
  console.error('\tbin2json [--pretty] [binary input]');
23
- console.error('\tjson2hex \'[json input]\'');
24
- console.error('\tjson2diag \'[json input]\'');
20
+ console.error('\tdiag2bin [diagnostic input]');
21
+ console.error('\tdiag2hex [diagnostic input]');
22
+ console.error('\tdiag2json [--pretty] [diagnostic input]');
23
+ console.error('\thex2bin [hex input]');
24
+ console.error('\thex2diag [hex input]');
25
+ console.error('\thex2json [--pretty] [hex input]');
25
26
  console.error('\tjson2bin \'[json input]\'');
27
+ console.error('\tjson2diag \'[json input]\'');
28
+ console.error('\tjson2hex \'[json input]\'');
26
29
  console.error('Input may either be supplied as an argument or piped via stdin');
27
30
  process__default["default"].exit(code || 0);
28
31
  }
@@ -54,22 +57,13 @@ async function run() {
54
57
  case 'help': {
55
58
  return usage(0);
56
59
  }
57
- case 'hex2json': {
58
- const {argv, pretty} = argvPretty();
59
- const bin = fromHex(argv.length < 4 ? (await fromStdin()).toString() : argv[3]);
60
- return console.log(JSON.stringify(decode.decode(bin), undefined, pretty ? 2 : undefined));
61
- }
62
- case 'hex2diag': {
63
- const bin = fromHex(process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3]);
60
+ case 'bin2diag': {
61
+ const bin = process__default["default"].argv.length < 4 ? await fromStdin() : new TextEncoder().encode(process__default["default"].argv[3]);
64
62
  for (const line of diagnostic.tokensToDiagnostic(bin)) {
65
63
  console.log(line);
66
64
  }
67
65
  return;
68
66
  }
69
- case 'hex2bin': {
70
- const bin = fromHex(process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3]);
71
- return process__default["default"].stdout.write(bin);
72
- }
73
67
  case 'bin2hex': {
74
68
  const bin = process__default["default"].argv.length < 4 ? await fromStdin() : new TextEncoder().encode(process__default["default"].argv[3]);
75
69
  return console.log(byteUtils.toHex(bin));
@@ -79,17 +73,39 @@ async function run() {
79
73
  const bin = argv.length < 4 ? await fromStdin() : new TextEncoder().encode(argv[3]);
80
74
  return console.log(JSON.stringify(decode.decode(bin), undefined, pretty ? 2 : undefined));
81
75
  }
82
- case 'bin2diag': {
83
- const bin = process__default["default"].argv.length < 4 ? await fromStdin() : new TextEncoder().encode(process__default["default"].argv[3]);
76
+ case 'diag2bin': {
77
+ const bin = diagnostic.fromDiag(process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3]);
78
+ return process__default["default"].stdout.write(bin);
79
+ }
80
+ case 'diag2hex': {
81
+ const bin = diagnostic.fromDiag(process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3]);
82
+ return console.log(byteUtils.toHex(bin));
83
+ }
84
+ case 'diag2json': {
85
+ const {argv, pretty} = argvPretty();
86
+ const bin = diagnostic.fromDiag(argv.length < 4 ? (await fromStdin()).toString() : argv[3]);
87
+ return console.log(JSON.stringify(decode.decode(bin), undefined, pretty ? 2 : undefined));
88
+ }
89
+ case 'hex2bin': {
90
+ const bin = fromHex(process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3]);
91
+ return process__default["default"].stdout.write(bin);
92
+ }
93
+ case 'hex2diag': {
94
+ const bin = fromHex(process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3]);
84
95
  for (const line of diagnostic.tokensToDiagnostic(bin)) {
85
96
  console.log(line);
86
97
  }
87
98
  return;
88
99
  }
89
- case 'json2hex': {
100
+ case 'hex2json': {
101
+ const {argv, pretty} = argvPretty();
102
+ const bin = fromHex(argv.length < 4 ? (await fromStdin()).toString() : argv[3]);
103
+ return console.log(JSON.stringify(decode.decode(bin), undefined, pretty ? 2 : undefined));
104
+ }
105
+ case 'json2bin': {
90
106
  const inp = process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3];
91
107
  const obj = JSON.parse(inp);
92
- return console.log(byteUtils.toHex(encode.encode(obj)));
108
+ return process__default["default"].stdout.write(encode.encode(obj));
93
109
  }
94
110
  case 'json2diag': {
95
111
  const inp = process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3];
@@ -99,10 +115,10 @@ async function run() {
99
115
  }
100
116
  return;
101
117
  }
102
- case 'json2bin': {
118
+ case 'json2hex': {
103
119
  const inp = process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3];
104
120
  const obj = JSON.parse(inp);
105
- return process__default["default"].stdout.write(encode.encode(obj));
121
+ return console.log(byteUtils.toHex(encode.encode(obj)));
106
122
  }
107
123
  default: {
108
124
  if (process__default["default"].argv.findIndex(a => a.endsWith('mocha')) === -1) {
@@ -4,7 +4,10 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var decode = require('./decode.js');
6
6
  var byteUtils = require('./byte-utils.js');
7
+ var _0uint = require('./0uint.js');
7
8
 
9
+ const utf8Encoder = new TextEncoder();
10
+ const utf8Decoder = new TextDecoder();
8
11
  function* tokensToDiagnostic(inp, width = 100) {
9
12
  const tokeniser = new decode.Tokeniser(inp);
10
13
  let pos = 0;
@@ -17,6 +20,7 @@ function* tokensToDiagnostic(inp, width = 100) {
17
20
  let margin = ''.padStart(indent.length * 2, ' ');
18
21
  let vLength = token.encodedLength - 1;
19
22
  let v = String(token.value);
23
+ let outp = `${ margin }${ slc(0, 1) }`;
20
24
  const str = token.type.name === 'bytes' || token.type.name === 'string';
21
25
  if (token.type.name === 'string') {
22
26
  v = v.length;
@@ -25,7 +29,29 @@ function* tokensToDiagnostic(inp, width = 100) {
25
29
  v = token.value.length;
26
30
  vLength -= v;
27
31
  }
28
- let outp = `${ margin }${ slc(0, 1) } ${ slc(1, vLength) }`;
32
+ let multilen;
33
+ switch (token.type.name) {
34
+ case 'string':
35
+ case 'bytes':
36
+ case 'map':
37
+ case 'array':
38
+ multilen = token.type.name === 'string' ? utf8Encoder.encode(token.value).length : token.value.length;
39
+ if (multilen >= _0uint.uintBoundaries[0]) {
40
+ if (multilen < _0uint.uintBoundaries[1]) {
41
+ outp += ` ${ slc(1, 1) }`;
42
+ } else if (multilen < _0uint.uintBoundaries[2]) {
43
+ outp += ` ${ slc(1, 2) }`;
44
+ } else if (multilen < _0uint.uintBoundaries[3]) {
45
+ outp += ` ${ slc(1, 4) }`;
46
+ } else if (multilen < _0uint.uintBoundaries[4]) {
47
+ outp += ` ${ slc(1, 8) }`;
48
+ }
49
+ }
50
+ break;
51
+ default:
52
+ outp += ` ${ slc(1, vLength) }`;
53
+ break;
54
+ }
29
55
  outp = outp.padEnd(width / 2, ' ');
30
56
  outp += `# ${ margin }${ token.type.name }`;
31
57
  if (token.type.name !== v) {
@@ -34,13 +60,13 @@ function* tokensToDiagnostic(inp, width = 100) {
34
60
  yield outp;
35
61
  if (str) {
36
62
  margin += ' ';
37
- const repr = token.type.name === 'bytes' ? token.value : new TextEncoder().encode(token.value);
63
+ const repr = token.type.name === 'bytes' ? token.value : utf8Encoder.encode(token.value);
38
64
  const wh = (width / 2 - margin.length - 1) / 2;
39
65
  let snip = 0;
40
66
  while (repr.length - snip > 0) {
41
67
  const piece = repr.slice(snip, snip + wh);
42
68
  snip += piece.length;
43
- const st = token.type.name === 'string' ? new TextDecoder().decode(piece) : piece.reduce((p, c) => {
69
+ const st = token.type.name === 'string' ? utf8Decoder.decode(piece) : piece.reduce((p, c) => {
44
70
  if (c < 32 || c === 127) {
45
71
  return `${ p }\\x${ c.toString(16).padStart(2, '0') }`;
46
72
  }
@@ -49,6 +75,9 @@ function* tokensToDiagnostic(inp, width = 100) {
49
75
  yield `${ margin }${ byteUtils.toHex(piece) }`.padEnd(width / 2, ' ') + `# ${ margin }"${ st }"`;
50
76
  }
51
77
  }
78
+ if (indent.length) {
79
+ indent[indent.length - 1]--;
80
+ }
52
81
  if (!token.type.terminal) {
53
82
  switch (token.type.name) {
54
83
  case 'map':
@@ -63,16 +92,23 @@ function* tokensToDiagnostic(inp, width = 100) {
63
92
  default:
64
93
  throw new Error(`Unknown token type '${ token.type.name }'`);
65
94
  }
66
- } else {
67
- if (indent.length) {
68
- indent[indent.length - 1]--;
69
- if (indent[indent.length - 1] === 0) {
70
- indent.pop();
71
- }
72
- }
95
+ }
96
+ while (indent.length && indent[indent.length - 1] <= 0) {
97
+ indent.pop();
73
98
  }
74
99
  pos += token.encodedLength;
75
100
  }
76
101
  }
102
+ function fromDiag(input) {
103
+ if (typeof input !== 'string') {
104
+ throw new TypeError('Expected string input');
105
+ }
106
+ input = input.replace(/#.*?$/mg, '').replace(/[\s\r\n]+/mg, '');
107
+ if (/[^a-f0-9]/i.test(input)) {
108
+ throw new TypeError('Input string was not CBOR diagnostic format');
109
+ }
110
+ return byteUtils.fromHex(input);
111
+ }
77
112
 
113
+ exports.fromDiag = fromDiag;
78
114
  exports.tokensToDiagnostic = tokensToDiagnostic;
@@ -4,6 +4,7 @@ var chai = require('chai');
4
4
  var child_process = require('child_process');
5
5
  var process = require('process');
6
6
  var path = require('path');
7
+ var os = require('os');
7
8
  var url = require('url');
8
9
  require('../lib/bin.js');
9
10
 
@@ -14,6 +15,52 @@ var process__default = /*#__PURE__*/_interopDefaultLegacy(process);
14
15
  var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
15
16
 
16
17
  const {assert} = chai__default["default"];
18
+ const fixture1JsonString = '{"a":1,"b":[2,3],"smile":"\uD83D\uDE00"}';
19
+ const fixture1JsonPrettyString = `{
20
+ "a": 1,
21
+ "b": [
22
+ 2,
23
+ 3
24
+ ],
25
+ "smile": "😀"
26
+ }
27
+ `;
28
+ const fixture1HexString = 'a3616101616282020365736d696c6564f09f9880';
29
+ const fixture1Bin = fromHex(fixture1HexString);
30
+ const fixture1BinString = new TextDecoder().decode(fixture1Bin);
31
+ const fixture1DiagnosticString = `a3 # map(3)
32
+ 61 # string(1)
33
+ 61 # "a"
34
+ 01 # uint(1)
35
+ 61 # string(1)
36
+ 62 # "b"
37
+ 82 # array(2)
38
+ 02 # uint(2)
39
+ 03 # uint(3)
40
+ 65 # string(5)
41
+ 736d696c65 # "smile"
42
+ 64 # string(2)
43
+ f09f9880 # "😀"
44
+ `;
45
+ const fixture2HexString = 'a4616101616282020363627566440102036165736d696c6564f09f9880';
46
+ const fixture2DiagnosticString = `a4 # map(4)
47
+ 61 # string(1)
48
+ 61 # "a"
49
+ 01 # uint(1)
50
+ 61 # string(1)
51
+ 62 # "b"
52
+ 82 # array(2)
53
+ 02 # uint(2)
54
+ 03 # uint(3)
55
+ 63 # string(3)
56
+ 627566 # "buf"
57
+ 44 # bytes(4)
58
+ 01020361 # "\\x01\\x02\\x03a"
59
+ 65 # string(5)
60
+ 736d696c65 # "smile"
61
+ 64 # string(2)
62
+ f09f9880 # "😀"
63
+ `;
17
64
  const binPath = path__default["default"].join(path__default["default"].dirname(url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('node-test/node-test-bin.js', document.baseURI).href)))), '../lib/bin.js');
18
65
  function fromHex(hex) {
19
66
  return new Uint8Array(hex.split('').map((c, i, d) => i % 2 === 0 ? `0x${ c }${ d[i + 1] }` : '').filter(Boolean).map(e => parseInt(e, 16)));
@@ -48,15 +95,18 @@ describe('Bin', () => {
48
95
  assert.strictEqual(e.stdout, '');
49
96
  assert.strictEqual(e.stderr, `Usage: cborg <command> <args>
50
97
  Valid commands:
51
- \thex2diag [hex input]
52
- \thex2bin [hex input]
53
- \thex2json [--pretty] [hex input]
54
- \tbin2hex [binary input]
55
98
  \tbin2diag [binary input]
99
+ \tbin2hex [binary input]
56
100
  \tbin2json [--pretty] [binary input]
57
- \tjson2hex '[json input]'
58
- \tjson2diag '[json input]'
101
+ \tdiag2bin [diagnostic input]
102
+ \tdiag2hex [diagnostic input]
103
+ \tdiag2json [--pretty] [diagnostic input]
104
+ \thex2bin [hex input]
105
+ \thex2diag [hex input]
106
+ \thex2json [--pretty] [hex input]
59
107
  \tjson2bin '[json input]'
108
+ \tjson2diag '[json input]'
109
+ \tjson2hex '[json input]'
60
110
  Input may either be supplied as an argument or piped via stdin
61
111
  `);
62
112
  }
@@ -70,15 +120,18 @@ Input may either be supplied as an argument or piped via stdin
70
120
  assert.strictEqual(e.stderr, `Unknown command: 'blip'
71
121
  Usage: cborg <command> <args>
72
122
  Valid commands:
73
- \thex2diag [hex input]
74
- \thex2bin [hex input]
75
- \thex2json [--pretty] [hex input]
76
- \tbin2hex [binary input]
77
123
  \tbin2diag [binary input]
124
+ \tbin2hex [binary input]
78
125
  \tbin2json [--pretty] [binary input]
79
- \tjson2hex '[json input]'
80
- \tjson2diag '[json input]'
126
+ \tdiag2bin [diagnostic input]
127
+ \tdiag2hex [diagnostic input]
128
+ \tdiag2json [--pretty] [diagnostic input]
129
+ \thex2bin [hex input]
130
+ \thex2diag [hex input]
131
+ \thex2json [--pretty] [hex input]
81
132
  \tjson2bin '[json input]'
133
+ \tjson2diag '[json input]'
134
+ \tjson2hex '[json input]'
82
135
  Input may either be supplied as an argument or piped via stdin
83
136
  `);
84
137
  }
@@ -88,135 +141,194 @@ Input may either be supplied as an argument or piped via stdin
88
141
  assert.strictEqual(stdout, '');
89
142
  assert.strictEqual(stderr, `Usage: cborg <command> <args>
90
143
  Valid commands:
91
- \thex2diag [hex input]
92
- \thex2bin [hex input]
93
- \thex2json [--pretty] [hex input]
94
- \tbin2hex [binary input]
95
144
  \tbin2diag [binary input]
145
+ \tbin2hex [binary input]
96
146
  \tbin2json [--pretty] [binary input]
97
- \tjson2hex '[json input]'
98
- \tjson2diag '[json input]'
147
+ \tdiag2bin [diagnostic input]
148
+ \tdiag2hex [diagnostic input]
149
+ \tdiag2json [--pretty] [diagnostic input]
150
+ \thex2bin [hex input]
151
+ \thex2diag [hex input]
152
+ \thex2json [--pretty] [hex input]
99
153
  \tjson2bin '[json input]'
154
+ \tjson2diag '[json input]'
155
+ \tjson2hex '[json input]'
100
156
  Input may either be supplied as an argument or piped via stdin
101
157
  `);
102
158
  });
159
+ it('bin2diag (stdin)', async () => {
160
+ const {stdout, stderr} = await execBin('bin2diag', fixture1Bin);
161
+ assert.strictEqual(stderr, '');
162
+ assert.strictEqual(stdout, fixture1DiagnosticString);
163
+ });
164
+ it('bin2hex (stdin)', async () => {
165
+ const {stdout, stderr} = await execBin('bin2hex', fixture1Bin);
166
+ assert.strictEqual(stderr, '');
167
+ assert.strictEqual(stdout, `${ fixture1HexString }\n`);
168
+ });
169
+ it('bin2json (stdin)', async () => {
170
+ const {stdout, stderr} = await execBin('bin2json', fixture1Bin);
171
+ assert.strictEqual(stderr, '');
172
+ assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
173
+ });
174
+ it('bin2json pretty (stdin)', async () => {
175
+ const {stdout, stderr} = await execBin('bin2json --pretty', fixture1Bin);
176
+ assert.strictEqual(stderr, '');
177
+ assert.strictEqual(stdout, fixture1JsonPrettyString);
178
+ });
103
179
  for (const stdin of [
104
180
  true,
105
181
  false
106
182
  ]) {
107
- it(`hex2json${ stdin ? ' (stdin)' : '' }`, async () => {
108
- const {stdout, stderr} = stdin ? await execBin('hex2json a3616101616282020365736d696c6564f09f9880') : await execBin('hex2json', 'a3616101616282020365736d696c6564f09f9880');
109
- assert.strictEqual(stderr, '');
110
- assert.strictEqual(stdout, '{"a":1,"b":[2,3],"smile":"\uD83D\uDE00"}\n');
111
- });
112
- it(`hex2json pretty${ stdin ? ' (stdin)' : '' }`, async () => {
113
- const {stdout, stderr} = stdin ? await execBin('hex2json --pretty a3616101616282020365736d696c6564f09f9880') : await execBin('hex2json --pretty', 'a3616101616282020365736d696c6564f09f9880');
183
+ if (os.platform() !== 'win32' || stdin) {
184
+ it(`diag2bin${ stdin ? ' (stdin)' : '' }`, async () => {
185
+ const {stdout, stderr} = !stdin ? await execBin(`diag2bin '${ fixture1DiagnosticString }'`) : await execBin('diag2bin', fixture1DiagnosticString);
186
+ assert.strictEqual(stderr, '');
187
+ assert.strictEqual(stdout, fixture1BinString);
188
+ });
189
+ it(`diag2hex${ stdin ? ' (stdin)' : '' }`, async () => {
190
+ const {stdout, stderr} = !stdin ? await execBin(`diag2hex '${ fixture1DiagnosticString }'`) : await execBin('diag2hex', fixture1DiagnosticString);
191
+ assert.strictEqual(stderr, '');
192
+ assert.strictEqual(stdout, `${ fixture1HexString }\n`);
193
+ });
194
+ it(`diag2json${ stdin ? ' (stdin)' : '' }`, async () => {
195
+ const {stdout, stderr} = !stdin ? await execBin(`diag2json '${ fixture1DiagnosticString }'`) : await execBin('diag2json', fixture1DiagnosticString);
196
+ assert.strictEqual(stderr, '');
197
+ assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
198
+ });
199
+ it(`diag2json pretty${ stdin ? ' (stdin)' : '' }`, async () => {
200
+ const {stdout, stderr} = !stdin ? await execBin(`diag2json --pretty '${ fixture1DiagnosticString }'`) : await execBin('diag2json --pretty', fixture1DiagnosticString);
201
+ assert.strictEqual(stderr, '');
202
+ assert.strictEqual(stdout, fixture1JsonPrettyString);
203
+ });
204
+ }
205
+ it(`hex2bin${ stdin ? ' (stdin)' : '' }`, async () => {
206
+ const {stdout, stderr} = !stdin ? await execBin(`hex2bin ${ fixture1HexString }`) : await execBin('hex2bin', fixture1HexString);
114
207
  assert.strictEqual(stderr, '');
115
- assert.strictEqual(stdout, `{
116
- "a": 1,
117
- "b": [
118
- 2,
119
- 3
120
- ],
121
- "smile": "😀"
122
- }
123
- `);
208
+ assert.strictEqual(stdout, fixture1BinString);
124
209
  });
125
210
  it(`hex2diag${ stdin ? ' (stdin)' : '' }`, async () => {
126
- const {stdout, stderr} = stdin ? await execBin('hex2diag a4616101616282020363627566440102036165736d696c6564f09f9880') : await execBin('hex2diag', 'a4616101616282020363627566440102036165736d696c6564f09f9880');
211
+ const {stdout, stderr} = !stdin ? await execBin(`hex2diag ${ fixture2HexString }`) : await execBin('hex2diag', fixture2HexString);
127
212
  assert.strictEqual(stderr, '');
128
- assert.strictEqual(stdout, `a4 # map(4)
129
- 61 # string(1)
130
- 61 # "a"
131
- 01 # uint(1)
132
- 61 # string(1)
133
- 62 # "b"
134
- 82 # array(2)
135
- 02 # uint(2)
136
- 03 # uint(3)
137
- 63 # string(3)
138
- 627566 # "buf"
139
- 44 # bytes(4)
140
- 01020361 # "\\x01\\x02\\x03a"
141
- 65 # string(5)
142
- 736d696c65 # "smile"
143
- 64 f09f # string(2)
144
- f09f9880 # "😀"
145
- `);
213
+ assert.strictEqual(stdout, fixture2DiagnosticString);
146
214
  });
147
- it(`hex2bin${ stdin ? ' (stdin)' : '' }`, async () => {
148
- const {stdout, stderr} = stdin ? await execBin('hex2bin a3616101616282020365736d696c6564f09f9880') : await execBin('hex2bin', 'a3616101616282020365736d696c6564f09f9880');
215
+ it(`hex2json${ stdin ? ' (stdin)' : '' }`, async () => {
216
+ const {stdout, stderr} = !stdin ? await execBin(`hex2json ${ fixture1HexString }`) : await execBin('hex2json', fixture1HexString);
149
217
  assert.strictEqual(stderr, '');
150
- assert.strictEqual(stdout, new TextDecoder().decode(fromHex('a3616101616282020365736d696c6564f09f9880')));
218
+ assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
151
219
  });
152
- it(`json2hex${ stdin ? ' (stdin)' : '' }`, async () => {
153
- const {stdout, stderr} = stdin ? await execBin('json2hex "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"\uD83D\uDE00\\"}"') : await execBin('json2hex', '{"a":1,"b":[2,3],"smile":"\uD83D\uDE00"}');
220
+ it(`hex2json pretty${ stdin ? ' (stdin)' : '' }`, async () => {
221
+ const {stdout, stderr} = !stdin ? await execBin(`hex2json --pretty ${ fixture1HexString }`) : await execBin('hex2json --pretty', fixture1HexString);
154
222
  assert.strictEqual(stderr, '');
155
- assert.strictEqual(stdout, 'a3616101616282020365736d696c6564f09f9880\n');
223
+ assert.strictEqual(stdout, fixture1JsonPrettyString);
156
224
  });
157
225
  it(`json2bin${ stdin ? ' (stdin)' : '' }`, async () => {
158
- const {stdout, stderr} = stdin ? await execBin('json2bin "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"\uD83D\uDE00\\"}"') : await execBin('json2bin', '{"a":1,"b":[2,3],"smile":"\uD83D\uDE00"}');
226
+ const {stdout, stderr} = !stdin ? await execBin('json2bin "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"\uD83D\uDE00\\"}"') : await execBin('json2bin', fixture1JsonString);
159
227
  assert.strictEqual(stderr, '');
160
- assert.strictEqual(stdout, new TextDecoder().decode(fromHex('a3616101616282020365736d696c6564f09f9880')));
228
+ assert.strictEqual(stdout, fixture1BinString);
161
229
  });
162
230
  it(`json2diag${ stdin ? ' (stdin)' : '' }`, async () => {
163
- const {stdout, stderr} = stdin ? await execBin('json2diag "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"\uD83D\uDE00\\"}"') : await execBin('json2diag', '{"a":1,"b":[2,3],"smile":"\uD83D\uDE00"}');
231
+ const {stdout, stderr} = !stdin ? await execBin('json2diag "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"\uD83D\uDE00\\"}"') : await execBin('json2diag', fixture1JsonString);
164
232
  assert.strictEqual(stderr, '');
165
- assert.strictEqual(stdout, `a3 # map(3)
166
- 61 # string(1)
167
- 61 # "a"
168
- 01 # uint(1)
169
- 61 # string(1)
170
- 62 # "b"
171
- 82 # array(2)
172
- 02 # uint(2)
173
- 03 # uint(3)
174
- 65 # string(5)
175
- 736d696c65 # "smile"
176
- 64 f09f # string(2)
177
- f09f9880 # "😀"
178
- `);
233
+ assert.strictEqual(stdout, fixture1DiagnosticString);
234
+ });
235
+ it(`json2hex${ stdin ? ' (stdin)' : '' }`, async () => {
236
+ const {stdout, stderr} = !stdin ? await execBin(`json2hex "${ fixture1JsonString.replace(/"/g, '\\"') }"`) : await execBin('json2hex', fixture1JsonString);
237
+ assert.strictEqual(stderr, '');
238
+ assert.strictEqual(stdout, `${ fixture1HexString }\n`);
179
239
  });
180
240
  }
181
- it('bin2diag (stdin)', async () => {
182
- const {stdout, stderr} = await execBin('bin2diag', fromHex('a3616101616282020365736d696c6564f09f9880'));
241
+ it('diag indenting', async () => {
242
+ const {stdout, stderr} = await execBin('json2diag', '{"a":[],"b":{},"c":{"a":1,"b":{"a":{"a":{}}}},"d":{"a":{"a":{"a":1},"b":2,"c":[]}},"e":[[[[{"a":{}}]]]],"f":1}');
183
243
  assert.strictEqual(stderr, '');
184
- assert.strictEqual(stdout, `a3 # map(3)
244
+ assert.strictEqual(stdout, `a6 # map(6)
185
245
  61 # string(1)
186
246
  61 # "a"
187
- 01 # uint(1)
247
+ 80 # array(0)
188
248
  61 # string(1)
189
249
  62 # "b"
190
- 82 # array(2)
191
- 02 # uint(2)
192
- 03 # uint(3)
193
- 65 # string(5)
194
- 736d696c65 # "smile"
195
- 64 f09f # string(2)
196
- f09f9880 # "😀"
250
+ a0 # map(0)
251
+ 61 # string(1)
252
+ 63 # "c"
253
+ a2 # map(2)
254
+ 61 # string(1)
255
+ 61 # "a"
256
+ 01 # uint(1)
257
+ 61 # string(1)
258
+ 62 # "b"
259
+ a1 # map(1)
260
+ 61 # string(1)
261
+ 61 # "a"
262
+ a1 # map(1)
263
+ 61 # string(1)
264
+ 61 # "a"
265
+ a0 # map(0)
266
+ 61 # string(1)
267
+ 64 # "d"
268
+ a1 # map(1)
269
+ 61 # string(1)
270
+ 61 # "a"
271
+ a3 # map(3)
272
+ 61 # string(1)
273
+ 61 # "a"
274
+ a1 # map(1)
275
+ 61 # string(1)
276
+ 61 # "a"
277
+ 01 # uint(1)
278
+ 61 # string(1)
279
+ 62 # "b"
280
+ 02 # uint(2)
281
+ 61 # string(1)
282
+ 63 # "c"
283
+ 80 # array(0)
284
+ 61 # string(1)
285
+ 65 # "e"
286
+ 81 # array(1)
287
+ 81 # array(1)
288
+ 81 # array(1)
289
+ 81 # array(1)
290
+ a1 # map(1)
291
+ 61 # string(1)
292
+ 61 # "a"
293
+ a0 # map(0)
294
+ 61 # string(1)
295
+ 66 # "f"
296
+ 01 # uint(1)
197
297
  `);
198
298
  });
199
- it('bin2json (stdin)', async () => {
200
- const {stdout, stderr} = await execBin('bin2json', fromHex('a3616101616282020365736d696c6564f09f9880'));
201
- assert.strictEqual(stderr, '');
202
- assert.strictEqual(stdout, '{"a":1,"b":[2,3],"smile":"\uD83D\uDE00"}\n');
203
- });
204
- it('bin2json pretty (stdin)', async () => {
205
- const {stdout, stderr} = await execBin('bin2json --pretty', fromHex('a3616101616282020365736d696c6564f09f9880'));
206
- assert.strictEqual(stderr, '');
207
- assert.strictEqual(stdout, `{
208
- "a": 1,
209
- "b": [
210
- 2,
211
- 3
212
- ],
213
- "smile": "😀"
214
- }
299
+ describe('diag length bytes', () => {
300
+ it('compact', async () => {
301
+ const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaa"');
302
+ assert.strictEqual(stderr, '');
303
+ assert.strictEqual(stdout, `77 # string(23)
304
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
215
305
  `);
216
- });
217
- it('bin2hex (stdin)', async () => {
218
- const {stdout, stderr} = await execBin('bin2hex', fromHex('a3616101616282020365736d696c6564f09f9880'));
219
- assert.strictEqual(stderr, '');
220
- assert.strictEqual(stdout, 'a3616101616282020365736d696c6564f09f9880\n');
306
+ });
307
+ it('1-byte', async () => {
308
+ const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"');
309
+ assert.strictEqual(stderr, '');
310
+ assert.strictEqual(stdout, `78 23 # string(35)
311
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
312
+ 616161616161616161616161 # "aaaaaaaaaaaa"
313
+ `);
314
+ });
315
+ it('2-byte', async () => {
316
+ const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"');
317
+ assert.strictEqual(stderr, '');
318
+ assert.strictEqual(stdout, `79 0100 # string(256)
319
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
320
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
321
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
322
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
323
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
324
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
325
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
326
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
327
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
328
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
329
+ 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
330
+ 616161 # "aaa"
331
+ `);
332
+ });
221
333
  });
222
334
  });
@@ -198,6 +198,8 @@ describe('float', () => {
198
198
  ]), { allowIndefinite: false }), /indefinite/);
199
199
  });
200
200
  it('can switch off undefined support', () => {
201
+ assert.deepStrictEqual(decode.decode(byteUtils.fromHex('f7')), undefined);
202
+ assert.throws(() => decode.decode(byteUtils.fromHex('f7'), { allowUndefined: false }), /undefined/);
201
203
  assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f7')), [
202
204
  1,
203
205
  2,
@@ -205,6 +207,20 @@ describe('float', () => {
205
207
  ]);
206
208
  assert.throws(() => decode.decode(byteUtils.fromHex('830102f7'), { allowUndefined: false }), /undefined/);
207
209
  });
210
+ it('can coerce undefined to null', () => {
211
+ assert.deepStrictEqual(decode.decode(byteUtils.fromHex('f7'), { coerceUndefinedToNull: false }), undefined);
212
+ assert.deepStrictEqual(decode.decode(byteUtils.fromHex('f7'), { coerceUndefinedToNull: true }), null);
213
+ assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f7'), { coerceUndefinedToNull: false }), [
214
+ 1,
215
+ 2,
216
+ undefined
217
+ ]);
218
+ assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f7'), { coerceUndefinedToNull: true }), [
219
+ 1,
220
+ 2,
221
+ null
222
+ ]);
223
+ });
208
224
  it('can switch off Infinity support', () => {
209
225
  assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f97c00')), [
210
226
  1,