node-osc 11.1.0 → 11.2.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 (69) hide show
  1. package/.gitattributes +11 -0
  2. package/.github/workflows/bump-version.yml +2 -0
  3. package/.github/workflows/nodejs.yml +3 -0
  4. package/LICENSE +201 -165
  5. package/README.md +135 -42
  6. package/dist/lib/Bundle.js +66 -0
  7. package/dist/lib/Client.js +137 -22
  8. package/dist/lib/Message.js +87 -1
  9. package/dist/lib/Server.js +117 -6
  10. package/dist/lib/index.js +3 -0
  11. package/dist/lib/internal/decode.js +4 -4
  12. package/{lib/internal/osc.mjs → dist/lib/osc.js} +94 -23
  13. package/dist/test/lib/osc.js +395 -0
  14. package/dist/test/test-client.js +152 -0
  15. package/dist/test/test-e2e.js +9 -3
  16. package/dist/test/test-encode-decode.js +849 -0
  17. package/dist/test/test-error-handling.js +116 -0
  18. package/dist/test/test-osc-internal.js +399 -41
  19. package/dist/test/test-promises.js +250 -0
  20. package/dist/test/test-types.js +42 -0
  21. package/dist/test/util.js +15 -8
  22. package/docs/API.md +477 -0
  23. package/docs/GUIDE.md +605 -0
  24. package/examples/README.md +119 -0
  25. package/examples/async-await.mjs +57 -0
  26. package/examples/bundle-example.mjs +92 -0
  27. package/examples/client.js +22 -5
  28. package/examples/error-handling.mjs +152 -0
  29. package/examples/esm.mjs +21 -0
  30. package/examples/server.js +16 -0
  31. package/jsdoc.json +16 -0
  32. package/lib/Bundle.mjs +66 -0
  33. package/lib/Client.mjs +137 -22
  34. package/lib/Message.mjs +87 -1
  35. package/lib/Server.mjs +117 -6
  36. package/lib/index.mjs +1 -0
  37. package/lib/internal/decode.mjs +4 -4
  38. package/{dist/lib/internal/osc.js → lib/osc.mjs} +71 -7
  39. package/package.json +12 -10
  40. package/rollup.config.mjs +48 -37
  41. package/scripts/generate-docs.mjs +229 -0
  42. package/test/fixtures/types/test-cjs-types.ts +19 -0
  43. package/test/fixtures/types/test-esm-types.ts +35 -0
  44. package/test/fixtures/types/tsconfig-cjs.test.json +17 -0
  45. package/test/fixtures/types/tsconfig-esm.test.json +17 -0
  46. package/test/test-bundle.mjs +0 -1
  47. package/test/test-client.mjs +152 -0
  48. package/test/test-e2e.mjs +9 -3
  49. package/test/test-encode-decode.mjs +847 -0
  50. package/test/test-error-handling.mjs +115 -0
  51. package/test/test-osc-internal.mjs +400 -42
  52. package/test/test-promises.mjs +249 -0
  53. package/test/test-types.mjs +39 -0
  54. package/test/util.mjs +15 -8
  55. package/tsconfig.json +45 -0
  56. package/types/Bundle.d.mts +70 -0
  57. package/types/Bundle.d.mts.map +1 -0
  58. package/types/Client.d.mts +101 -0
  59. package/types/Client.d.mts.map +1 -0
  60. package/types/Message.d.mts +84 -0
  61. package/types/Message.d.mts.map +1 -0
  62. package/types/Server.d.mts +98 -0
  63. package/types/Server.d.mts.map +1 -0
  64. package/types/index.d.mts +6 -0
  65. package/types/index.d.mts.map +1 -0
  66. package/types/internal/decode.d.mts +4 -0
  67. package/types/internal/decode.d.mts.map +1 -0
  68. package/types/osc.d.mts +66 -0
  69. package/types/osc.d.mts.map +1 -0
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var tap = require('tap');
4
- var _osc = require('#osc');
4
+ var osc = require('./lib/osc.js');
5
5
 
6
6
  tap.test('osc: timetag encoding with non-number value', (t) => {
7
7
  // Test the else branch in writeTimeTag that writes zeros for non-number values
@@ -17,8 +17,8 @@ tap.test('osc: timetag encoding with non-number value', (t) => {
17
17
  ]
18
18
  };
19
19
 
20
- const buffer = _osc.toBuffer(bundle);
21
- const decoded = _osc.fromBuffer(buffer);
20
+ const buffer = osc.encode(bundle);
21
+ const decoded = osc.decode(buffer);
22
22
 
23
23
  t.equal(decoded.oscType, 'bundle', 'should decode as bundle');
24
24
  t.equal(decoded.timetag, 0, 'should decode timetag as 0 for immediate execution');
@@ -39,8 +39,8 @@ tap.test('osc: timetag with immediate execution values', (t) => {
39
39
  ]
40
40
  };
41
41
 
42
- const buffer = _osc.toBuffer(bundle);
43
- const decoded = _osc.fromBuffer(buffer);
42
+ const buffer = osc.encode(bundle);
43
+ const decoded = osc.decode(buffer);
44
44
 
45
45
  t.equal(decoded.timetag, 0, 'should handle immediate execution timetag');
46
46
  t.end();
@@ -60,7 +60,7 @@ tap.test('osc: argument encoding with unknown type', (t) => {
60
60
  };
61
61
 
62
62
  t.throws(() => {
63
- _osc.toBuffer(message);
63
+ osc.encode(message);
64
64
  }, /Unknown argument type: unknown/, 'should throw error for unknown argument type');
65
65
  t.end();
66
66
  });
@@ -78,8 +78,8 @@ tap.test('osc: argument encoding with boolean false', (t) => {
78
78
  ]
79
79
  };
80
80
 
81
- const buffer = _osc.toBuffer(message);
82
- const decoded = _osc.fromBuffer(buffer);
81
+ const buffer = osc.encode(message);
82
+ const decoded = osc.decode(buffer);
83
83
 
84
84
  t.equal(decoded.args[0].value, false, 'should encode and decode false boolean');
85
85
  t.end();
@@ -96,7 +96,7 @@ tap.test('osc: argument encoding with unsupported object', (t) => {
96
96
  };
97
97
 
98
98
  t.throws(() => {
99
- _osc.toBuffer(message);
99
+ osc.encode(message);
100
100
  }, /Don't know how to encode argument/, 'should throw error for unsupported object');
101
101
  t.end();
102
102
  });
@@ -110,7 +110,7 @@ tap.test('osc: argument encoding with undefined value', (t) => {
110
110
  };
111
111
 
112
112
  t.throws(() => {
113
- _osc.toBuffer(message);
113
+ osc.encode(message);
114
114
  }, /Don't know how to encode argument/, 'should throw error for undefined argument');
115
115
  t.end();
116
116
  });
@@ -123,7 +123,7 @@ tap.test('osc: argument decoding with unknown type tag', (t) => {
123
123
  const buffer = Buffer.from(addressPart + typeTagPart);
124
124
 
125
125
  t.throws(() => {
126
- _osc.fromBuffer(buffer);
126
+ osc.decode(buffer);
127
127
  }, /I don't understand the argument code X/, 'should throw error for unknown type tag');
128
128
  t.end();
129
129
  });
@@ -136,7 +136,7 @@ tap.test('osc: null argument encoding and decoding', (t) => {
136
136
  const typeTagPart = ',N\0\0'; // N is null type tag
137
137
  const buffer = Buffer.from(addressPart + typeTagPart);
138
138
 
139
- const decoded = _osc.fromBuffer(buffer);
139
+ const decoded = osc.decode(buffer);
140
140
  t.equal(decoded.args[0].value, null, 'should decode null argument');
141
141
  t.end();
142
142
  });
@@ -154,8 +154,8 @@ tap.test('osc: double type argument encoding', (t) => {
154
154
  ]
155
155
  };
156
156
 
157
- const buffer = _osc.toBuffer(message);
158
- const decoded = _osc.fromBuffer(buffer);
157
+ const buffer = osc.encode(message);
158
+ const decoded = osc.decode(buffer);
159
159
 
160
160
  t.ok(Math.abs(decoded.args[0].value - 3.14159) < 0.001, 'should encode double as float');
161
161
  t.end();
@@ -170,8 +170,8 @@ tap.test('osc: blob argument with Buffer', (t) => {
170
170
  args: [testBuffer] // Direct Buffer without type wrapper
171
171
  };
172
172
 
173
- const buffer = _osc.toBuffer(message);
174
- const decoded = _osc.fromBuffer(buffer);
173
+ const buffer = osc.encode(message);
174
+ const decoded = osc.decode(buffer);
175
175
 
176
176
  t.ok(Buffer.isBuffer(decoded.args[0].value), 'should decode as Buffer');
177
177
  t.equal(decoded.args[0].value.toString(), 'test data', 'should preserve blob content');
@@ -186,8 +186,8 @@ tap.test('osc: float number encoding', (t) => {
186
186
  args: [3.14159] // Non-integer number should be encoded as float
187
187
  };
188
188
 
189
- const buffer = _osc.toBuffer(message);
190
- const decoded = _osc.fromBuffer(buffer);
189
+ const buffer = osc.encode(message);
190
+ const decoded = osc.decode(buffer);
191
191
 
192
192
  t.ok(typeof decoded.args[0].value === 'number', 'should decode as number');
193
193
  t.ok(!Number.isInteger(decoded.args[0].value), 'should be float, not integer');
@@ -208,8 +208,8 @@ tap.test('osc: explicit integer type encoding', (t) => {
208
208
  ]
209
209
  };
210
210
 
211
- const buffer = _osc.toBuffer(message);
212
- const decoded = _osc.fromBuffer(buffer);
211
+ const buffer = osc.encode(message);
212
+ const decoded = osc.decode(buffer);
213
213
 
214
214
  t.equal(decoded.args[0].value, 42, 'should encode and decode integer');
215
215
  t.end();
@@ -228,8 +228,8 @@ tap.test('osc: explicit float type encoding', (t) => {
228
228
  ]
229
229
  };
230
230
 
231
- const buffer = _osc.toBuffer(message);
232
- const decoded = _osc.fromBuffer(buffer);
231
+ const buffer = osc.encode(message);
232
+ const decoded = osc.decode(buffer);
233
233
 
234
234
  t.ok(Math.abs(decoded.args[0].value - 3.14) < 0.001, 'should encode and decode float');
235
235
  t.end();
@@ -248,8 +248,8 @@ tap.test('osc: explicit string type encoding', (t) => {
248
248
  ]
249
249
  };
250
250
 
251
- const buffer = _osc.toBuffer(message);
252
- const decoded = _osc.fromBuffer(buffer);
251
+ const buffer = osc.encode(message);
252
+ const decoded = osc.decode(buffer);
253
253
 
254
254
  t.equal(decoded.args[0].value, 'hello', 'should encode and decode string');
255
255
  t.end();
@@ -269,8 +269,8 @@ tap.test('osc: explicit blob type encoding', (t) => {
269
269
  ]
270
270
  };
271
271
 
272
- const buffer = _osc.toBuffer(message);
273
- const decoded = _osc.fromBuffer(buffer);
272
+ const buffer = osc.encode(message);
273
+ const decoded = osc.decode(buffer);
274
274
 
275
275
  t.ok(Buffer.isBuffer(decoded.args[0].value), 'should decode as Buffer');
276
276
  t.equal(decoded.args[0].value.toString(), 'blob data', 'should preserve blob data');
@@ -290,8 +290,8 @@ tap.test('osc: explicit boolean true type encoding', (t) => {
290
290
  ]
291
291
  };
292
292
 
293
- const buffer = _osc.toBuffer(message);
294
- const decoded = _osc.fromBuffer(buffer);
293
+ const buffer = osc.encode(message);
294
+ const decoded = osc.decode(buffer);
295
295
 
296
296
  t.equal(decoded.args[0].value, true, 'should encode and decode boolean true');
297
297
  t.end();
@@ -311,8 +311,8 @@ tap.test('osc: MIDI type encoding with Buffer', (t) => {
311
311
  ]
312
312
  };
313
313
 
314
- const buffer = _osc.toBuffer(message);
315
- const decoded = _osc.fromBuffer(buffer);
314
+ const buffer = osc.encode(message);
315
+ const decoded = osc.decode(buffer);
316
316
 
317
317
  t.ok(Buffer.isBuffer(decoded.args[0].value), 'should decode as Buffer');
318
318
  t.equal(decoded.args[0].value.length, 4, 'should be 4 bytes');
@@ -341,8 +341,8 @@ tap.test('osc: MIDI type encoding with object', (t) => {
341
341
  ]
342
342
  };
343
343
 
344
- const buffer = _osc.toBuffer(message);
345
- const decoded = _osc.fromBuffer(buffer);
344
+ const buffer = osc.encode(message);
345
+ const decoded = osc.decode(buffer);
346
346
 
347
347
  t.ok(Buffer.isBuffer(decoded.args[0].value), 'should decode as Buffer');
348
348
  t.equal(decoded.args[0].value[0], 2, 'port should match');
@@ -366,7 +366,7 @@ tap.test('osc: MIDI type with invalid buffer length', (t) => {
366
366
  };
367
367
 
368
368
  t.throws(() => {
369
- _osc.toBuffer(message);
369
+ osc.encode(message);
370
370
  }, /MIDI message must be exactly 4 bytes/, 'should throw error for wrong buffer length');
371
371
  t.end();
372
372
  });
@@ -385,7 +385,7 @@ tap.test('osc: MIDI type with invalid value type', (t) => {
385
385
  };
386
386
 
387
387
  t.throws(() => {
388
- _osc.toBuffer(message);
388
+ osc.encode(message);
389
389
  }, /MIDI value must be a 4-byte Buffer or object/, 'should throw error for invalid value type');
390
390
  t.end();
391
391
  });
@@ -407,8 +407,8 @@ tap.test('osc: MIDI type with partial object', (t) => {
407
407
  ]
408
408
  };
409
409
 
410
- const buffer = _osc.toBuffer(message);
411
- const decoded = _osc.fromBuffer(buffer);
410
+ const buffer = osc.encode(message);
411
+ const decoded = osc.decode(buffer);
412
412
 
413
413
  t.equal(decoded.args[0].value[0], 0, 'port should default to 0');
414
414
  t.equal(decoded.args[0].value[1], 0x90, 'status should match');
@@ -436,7 +436,7 @@ tap.test('osc: MIDI type decoding with insufficient buffer data', (t) => {
436
436
  const malformedBuffer = Buffer.concat([address, typeTags, insufficientMidiData]);
437
437
 
438
438
  t.throws(() => {
439
- _osc.fromBuffer(malformedBuffer);
439
+ osc.decode(malformedBuffer);
440
440
  }, /Not enough bytes for MIDI message/, 'should throw error when MIDI data is truncated');
441
441
  t.end();
442
442
  });
@@ -458,8 +458,8 @@ tap.test('osc: MIDI type with falsy status and data1 values', (t) => {
458
458
  ]
459
459
  };
460
460
 
461
- const buffer = _osc.toBuffer(message);
462
- const decoded = _osc.fromBuffer(buffer);
461
+ const buffer = osc.encode(message);
462
+ const decoded = osc.decode(buffer);
463
463
 
464
464
  t.equal(decoded.args[0].value[0], 5, 'port should match');
465
465
  t.equal(decoded.args[0].value[1], 0, 'status should default to 0');
@@ -486,8 +486,8 @@ tap.test('osc: MIDI type with explicit zero status and data1 values', (t) => {
486
486
  ]
487
487
  };
488
488
 
489
- const buffer = _osc.toBuffer(message);
490
- const decoded = _osc.fromBuffer(buffer);
489
+ const buffer = osc.encode(message);
490
+ const decoded = osc.decode(buffer);
491
491
 
492
492
  t.equal(decoded.args[0].value[0], 3, 'port should match');
493
493
  t.equal(decoded.args[0].value[1], 0, 'status should be 0');
@@ -496,3 +496,361 @@ tap.test('osc: MIDI type with explicit zero status and data1 values', (t) => {
496
496
  t.end();
497
497
 
498
498
  });
499
+ tap.test('osc: timetag encoding with numeric value and fractions', (t) => {
500
+ // Test writeTimeTag with actual numeric timetag (lines 70-74)
501
+ const bundle = {
502
+ oscType: 'bundle',
503
+ timetag: 1234567890.5, // Numeric value with fractional part
504
+ elements: [
505
+ {
506
+ oscType: 'message',
507
+ address: '/test',
508
+ args: []
509
+ }
510
+ ]
511
+ };
512
+
513
+ const buffer = osc.encode(bundle);
514
+ const decoded = osc.decode(buffer);
515
+
516
+ t.equal(decoded.oscType, 'bundle', 'should decode as bundle');
517
+ t.ok(Math.abs(decoded.timetag - 1234567890.5) < 0.01, 'should preserve numeric timetag with fractions');
518
+ t.end();
519
+ });
520
+
521
+ tap.test('osc: timetag decoding with actual timestamp', (t) => {
522
+ // Test readTimeTag with non-zero, non-immediate values (lines 92-96)
523
+ // We encode a bundle with a real timestamp, then decode it
524
+ const bundle = {
525
+ oscType: 'bundle',
526
+ timetag: 1609459200.25, // A real timestamp: 2021-01-01 00:00:00.25
527
+ elements: [
528
+ {
529
+ oscType: 'message',
530
+ address: '/timestamp',
531
+ args: [{ type: 'i', value: 123 }]
532
+ }
533
+ ]
534
+ };
535
+
536
+ const buffer = osc.encode(bundle);
537
+ const decoded = osc.decode(buffer);
538
+
539
+ t.equal(decoded.oscType, 'bundle', 'should decode as bundle');
540
+ t.ok(decoded.timetag > 0, 'timetag should be positive');
541
+ t.ok(Math.abs(decoded.timetag - 1609459200.25) < 0.01, 'should preserve timestamp value');
542
+ t.end();
543
+ });
544
+
545
+ tap.test('osc: inferred integer encoding from raw number', (t) => {
546
+ // Test line 167: encoding raw integer without type wrapper
547
+ const message = {
548
+ oscType: 'message',
549
+ address: '/test',
550
+ args: [42] // Raw integer, not wrapped
551
+ };
552
+
553
+ const buffer = osc.encode(message);
554
+ const decoded = osc.decode(buffer);
555
+
556
+ t.equal(decoded.args[0].value, 42, 'should encode and decode raw integer');
557
+ t.end();
558
+ });
559
+
560
+ tap.test('osc: inferred float encoding from raw number', (t) => {
561
+ // Test line 169: encoding raw float without type wrapper
562
+ const message = {
563
+ oscType: 'message',
564
+ address: '/test',
565
+ args: [3.14159] // Raw float, not wrapped
566
+ };
567
+
568
+ const buffer = osc.encode(message);
569
+ const decoded = osc.decode(buffer);
570
+
571
+ t.ok(Math.abs(decoded.args[0].value - 3.14159) < 0.001, 'should encode and decode raw float');
572
+ t.end();
573
+ });
574
+
575
+ tap.test('osc: inferred string encoding from raw string', (t) => {
576
+ // Test line 172: encoding raw string without type wrapper
577
+ const message = {
578
+ oscType: 'message',
579
+ address: '/test',
580
+ args: ['hello world'] // Raw string, not wrapped
581
+ };
582
+
583
+ const buffer = osc.encode(message);
584
+ const decoded = osc.decode(buffer);
585
+
586
+ t.equal(decoded.args[0].value, 'hello world', 'should encode and decode raw string');
587
+ t.end();
588
+ });
589
+
590
+ tap.test('osc: inferred boolean true encoding from raw boolean', (t) => {
591
+ // Test line 174 (true branch): encoding raw boolean true
592
+ const message = {
593
+ oscType: 'message',
594
+ address: '/test',
595
+ args: [true] // Raw boolean true, not wrapped
596
+ };
597
+
598
+ const buffer = osc.encode(message);
599
+ const decoded = osc.decode(buffer);
600
+
601
+ t.equal(decoded.args[0].value, true, 'should encode and decode raw boolean true');
602
+ t.end();
603
+ });
604
+
605
+ tap.test('osc: inferred boolean false encoding from raw boolean', (t) => {
606
+ // Test line 174 (false branch): encoding raw boolean false
607
+ const message = {
608
+ oscType: 'message',
609
+ address: '/test',
610
+ args: [false] // Raw boolean false, not wrapped
611
+ };
612
+
613
+ const buffer = osc.encode(message);
614
+ const decoded = osc.decode(buffer);
615
+
616
+ t.equal(decoded.args[0].value, false, 'should encode and decode raw boolean false');
617
+ t.end();
618
+ });
619
+
620
+ tap.test('osc: bundle with only message elements (no nested bundles)', (t) => {
621
+ // Test line 252: encoding message elements in bundle (else branch)
622
+ const bundle = {
623
+ oscType: 'bundle',
624
+ timetag: 0,
625
+ elements: [
626
+ {
627
+ oscType: 'message',
628
+ address: '/msg1',
629
+ args: [{ type: 'i', value: 1 }]
630
+ },
631
+ {
632
+ oscType: 'message',
633
+ address: '/msg2',
634
+ args: [{ type: 'i', value: 2 }]
635
+ }
636
+ ]
637
+ };
638
+
639
+ const buffer = osc.encode(bundle);
640
+ const decoded = osc.decode(buffer);
641
+
642
+ t.equal(decoded.oscType, 'bundle', 'should decode as bundle');
643
+ t.equal(decoded.elements.length, 2, 'should have 2 elements');
644
+ t.equal(decoded.elements[0].oscType, 'message', 'first element should be message');
645
+ t.equal(decoded.elements[1].oscType, 'message', 'second element should be message');
646
+ t.end();
647
+ });
648
+
649
+ tap.test('osc: malformed packet with missing comma in type tags', (t) => {
650
+ // Test lines 292-293: decoding malformed packet without comma in type tags
651
+ const addressBuf = Buffer.from('/test\0\0\0', 'utf8');
652
+ const malformedTypeTagsBuf = Buffer.from('iXX\0', 'utf8'); // Missing leading comma
653
+ const buffer = Buffer.concat([addressBuf, malformedTypeTagsBuf]);
654
+
655
+ t.throws(() => {
656
+ osc.decode(buffer);
657
+ }, /Malformed Packet/, 'should throw on malformed type tags');
658
+ t.end();
659
+ });
660
+
661
+ tap.test('osc: bundle with nested bundle element', (t) => {
662
+ // Test line 252: encoding bundle elements in bundle (if branch)
663
+ const innerBundle = {
664
+ oscType: 'bundle',
665
+ timetag: 0,
666
+ elements: [
667
+ {
668
+ oscType: 'message',
669
+ address: '/inner',
670
+ args: [{ type: 'i', value: 99 }]
671
+ }
672
+ ]
673
+ };
674
+
675
+ const outerBundle = {
676
+ oscType: 'bundle',
677
+ timetag: 0,
678
+ elements: [
679
+ {
680
+ oscType: 'message',
681
+ address: '/outer',
682
+ args: [{ type: 's', value: 'test' }]
683
+ },
684
+ innerBundle
685
+ ]
686
+ };
687
+
688
+ const buffer = osc.encode(outerBundle);
689
+ const decoded = osc.decode(buffer);
690
+
691
+ t.equal(decoded.oscType, 'bundle', 'should decode as bundle');
692
+ t.equal(decoded.elements.length, 2, 'should have 2 elements');
693
+ t.equal(decoded.elements[0].oscType, 'message', 'first element should be message');
694
+ t.equal(decoded.elements[1].oscType, 'bundle', 'second element should be bundle');
695
+ t.equal(decoded.elements[1].elements[0].address, '/inner', 'nested bundle should have correct message');
696
+ t.end();
697
+ });
698
+
699
+ tap.test('osc: explicit integer type name', (t) => {
700
+ const message = {
701
+ oscType: 'message',
702
+ address: '/test',
703
+ args: [{ type: 'integer', value: 999 }]
704
+ };
705
+
706
+ const buffer = osc.encode(message);
707
+ const decoded = osc.decode(buffer);
708
+
709
+ t.equal(decoded.args[0].value, 999);
710
+ t.end();
711
+ });
712
+
713
+ tap.test('osc: explicit float type name', (t) => {
714
+ const message = {
715
+ oscType: 'message',
716
+ address: '/test',
717
+ args: [{ type: 'float', value: 1.414 }]
718
+ };
719
+
720
+ const buffer = osc.encode(message);
721
+ const decoded = osc.decode(buffer);
722
+
723
+ t.ok(Math.abs(decoded.args[0].value - 1.414) < 0.001);
724
+ t.end();
725
+ });
726
+
727
+ tap.test('osc: explicit string type name', (t) => {
728
+ const message = {
729
+ oscType: 'message',
730
+ address: '/test',
731
+ args: [{ type: 'string', value: 'test string' }]
732
+ };
733
+
734
+ const buffer = osc.encode(message);
735
+ const decoded = osc.decode(buffer);
736
+
737
+ t.equal(decoded.args[0].value, 'test string');
738
+ t.end();
739
+ });
740
+
741
+ tap.test('osc: explicit blob type name', (t) => {
742
+ const message = {
743
+ oscType: 'message',
744
+ address: '/test',
745
+ args: [{ type: 'blob', value: Buffer.from([0xAA, 0xBB]) }]
746
+ };
747
+
748
+ const buffer = osc.encode(message);
749
+ const decoded = osc.decode(buffer);
750
+
751
+ t.ok(Buffer.isBuffer(decoded.args[0].value));
752
+ t.same(decoded.args[0].value, Buffer.from([0xAA, 0xBB]));
753
+ t.end();
754
+ });
755
+
756
+ tap.test('osc: explicit double type name', (t) => {
757
+ const message = {
758
+ oscType: 'message',
759
+ address: '/test',
760
+ args: [{ type: 'double', value: 2.718281828 }]
761
+ };
762
+
763
+ const buffer = osc.encode(message);
764
+ const decoded = osc.decode(buffer);
765
+
766
+ t.ok(Math.abs(decoded.args[0].value - 2.718281828) < 0.001);
767
+ t.end();
768
+ });
769
+
770
+ tap.test('osc: blob padding when length is multiple of 4', (t) => {
771
+ // Test writeBlob line 52: padding === 4 branch (should use 0)
772
+ const message = {
773
+ oscType: 'message',
774
+ address: '/test',
775
+ args: [{ type: 'b', value: Buffer.from([1, 2, 3, 4]) }] // length 4
776
+ };
777
+
778
+ const buffer = osc.encode(message);
779
+ const decoded = osc.decode(buffer);
780
+
781
+ t.same(decoded.args[0].value, Buffer.from([1, 2, 3, 4]));
782
+ t.end();
783
+ });
784
+
785
+ tap.test('osc: blob padding when length is not multiple of 4', (t) => {
786
+ // Test writeBlob line 52: padding !== 4 branch (should use padding value)
787
+ const message = {
788
+ oscType: 'message',
789
+ address: '/test',
790
+ args: [{ type: 'b', value: Buffer.from([1, 2, 3]) }] // length 3
791
+ };
792
+
793
+ const buffer = osc.encode(message);
794
+ const decoded = osc.decode(buffer);
795
+
796
+ t.same(decoded.args[0].value, Buffer.from([1, 2, 3]));
797
+ t.end();
798
+ });
799
+
800
+ tap.test('osc: boolean type true value', (t) => {
801
+ // Test boolean case ternary: true branch
802
+ const message = {
803
+ oscType: 'message',
804
+ address: '/test',
805
+ args: [{ type: 'boolean', value: true }]
806
+ };
807
+
808
+ const buffer = osc.encode(message);
809
+ const decoded = osc.decode(buffer);
810
+
811
+ t.equal(decoded.args[0].value, true);
812
+ t.end();
813
+ });
814
+
815
+ tap.test('osc: boolean type false value', (t) => {
816
+ // Test boolean case ternary: false branch
817
+ const message = {
818
+ oscType: 'message',
819
+ address: '/test',
820
+ args: [{ type: 'boolean', value: false }]
821
+ };
822
+
823
+ const buffer = osc.encode(message);
824
+ const decoded = osc.decode(buffer);
825
+
826
+ t.equal(decoded.args[0].value, false);
827
+ t.end();
828
+ });
829
+
830
+ tap.test('osc: explicit T type', (t) => {
831
+ const message = {
832
+ oscType: 'message',
833
+ address: '/test',
834
+ args: [{ type: 'T', value: true }]
835
+ };
836
+
837
+ const buffer = osc.encode(message);
838
+ const decoded = osc.decode(buffer);
839
+
840
+ t.equal(decoded.args[0].value, true);
841
+ t.end();
842
+ });
843
+
844
+ tap.test('osc: explicit F type', (t) => {
845
+ const message = {
846
+ oscType: 'message',
847
+ address: '/test',
848
+ args: [{ type: 'F', value: false }]
849
+ };
850
+
851
+ const buffer = osc.encode(message);
852
+ const decoded = osc.decode(buffer);
853
+
854
+ t.equal(decoded.args[0].value, false);
855
+ t.end();
856
+ });