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