node-osc 11.2.0 → 11.2.2

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 (48) hide show
  1. package/.github/workflows/bump-version.yml +3 -3
  2. package/.github/workflows/create-release.yml +4 -4
  3. package/.github/workflows/nodejs.yml +4 -4
  4. package/README.md +3 -2
  5. package/agent.md +330 -0
  6. package/dist/lib/Client.js +1 -1
  7. package/dist/lib/Message.js +3 -1
  8. package/dist/lib/Server.js +7 -12
  9. package/dist/lib/internal/decode.js +3 -1
  10. package/dist/lib/osc.js +32 -3
  11. package/dist/test/lib/osc.js +32 -3
  12. package/dist/test/test-bundle.js +13 -12
  13. package/dist/test/test-client.js +68 -37
  14. package/dist/test/test-decode.js +35 -0
  15. package/dist/test/test-e2e.js +9 -9
  16. package/dist/test/test-encode-decode.js +455 -0
  17. package/dist/test/test-error-handling.js +14 -13
  18. package/dist/test/test-message.js +261 -64
  19. package/dist/test/test-osc-internal.js +151 -0
  20. package/dist/test/test-promises.js +90 -26
  21. package/dist/test/test-server.js +19 -16
  22. package/docs/README.md +81 -0
  23. package/examples/README.md +3 -1
  24. package/lib/Client.mjs +1 -1
  25. package/lib/Message.mjs +3 -1
  26. package/lib/Server.mjs +7 -12
  27. package/lib/internal/decode.mjs +3 -1
  28. package/lib/osc.mjs +32 -3
  29. package/package.json +2 -2
  30. package/rollup.config.mjs +1 -0
  31. package/test/test-bundle.mjs +14 -13
  32. package/test/test-client.mjs +69 -38
  33. package/test/test-decode.mjs +35 -0
  34. package/test/test-e2e.mjs +10 -10
  35. package/test/test-encode-decode.mjs +455 -0
  36. package/test/test-error-handling.mjs +15 -14
  37. package/test/test-message.mjs +262 -66
  38. package/test/test-osc-internal.mjs +151 -0
  39. package/test/test-promises.mjs +91 -27
  40. package/test/test-server.mjs +20 -17
  41. package/types/Message.d.mts.map +1 -1
  42. package/types/Server.d.mts.map +1 -1
  43. package/types/internal/decode.d.mts.map +1 -1
  44. package/types/osc.d.mts.map +1 -1
  45. package/dist/test/test-getPort.js +0 -20
  46. package/dist/test/util.js +0 -34
  47. package/test/test-getPort.mjs +0 -18
  48. package/test/util.mjs +0 -34
@@ -1,55 +1,69 @@
1
1
  'use strict';
2
2
 
3
+ var node_events = require('node:events');
3
4
  var tap = require('tap');
4
- var util = require('./util.js');
5
5
  var nodeOsc = require('node-osc');
6
6
 
7
7
  function round(num) {
8
8
  return Math.round(num * 100) / 100;
9
9
  }
10
10
 
11
- tap.beforeEach(util.bootstrap);
11
+ tap.test('message: basic usage', async (t) => {
12
+ const server = new nodeOsc.Server(0, '127.0.0.1');
13
+ await node_events.once(server, 'listening');
14
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
15
+
16
+ t.plan(1);
17
+ t.teardown(async () => {
18
+ await server.close();
19
+ await client.close();
20
+ });
12
21
 
13
- tap.test('message: basic usage', (t) => {
14
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
15
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
16
22
  const m = new nodeOsc.Message('/address');
17
23
  m.append('testing');
18
24
  m.append(123);
19
25
  m.append([456, 789]);
20
26
 
21
- oscServer.on('message', (msg) => {
27
+ server.on('message', (msg) => {
22
28
  const expected = ['/address', 'testing', 123, 456, 789];
23
29
  t.same(msg, expected, `We reveived the payload: ${msg}`);
24
- oscServer.close();
25
- t.end();
26
30
  });
27
31
 
28
- client.send(m, () => {
29
- client.close();
30
- });
32
+ client.send(m);
31
33
  });
32
34
 
33
- tap.test('message: multiple args', (t) => {
34
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
35
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
35
+ tap.test('message: multiple args', async (t) => {
36
+ const server = new nodeOsc.Server(0, '127.0.0.1');
37
+ await node_events.once(server, 'listening');
38
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
39
+
40
+ t.plan(1);
41
+ t.teardown(async () => {
42
+ await server.close();
43
+ await client.close();
44
+ });
45
+
36
46
  const m = new nodeOsc.Message('/address', 'testing', 123, true);
37
47
 
38
- oscServer.on('message', (msg) => {
48
+ server.on('message', (msg) => {
39
49
  const expected = ['/address', 'testing', 123, true];
40
50
  t.same(msg, expected, `We reveived the payload: ${msg}`);
41
- oscServer.close();
42
- t.end();
43
51
  });
44
52
 
45
- client.send(m, () => {
46
- client.close();
47
- });
53
+ client.send(m);
48
54
  });
49
55
 
50
- tap.test('message: object', (t) => {
51
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
52
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
56
+ tap.test('message: object', async (t) => {
57
+ const server = new nodeOsc.Server(0, '127.0.0.1');
58
+ await node_events.once(server, 'listening');
59
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
60
+
61
+ t.plan(1);
62
+ t.teardown(async () => {
63
+ await server.close();
64
+ await client.close();
65
+ });
66
+
53
67
  const m = new nodeOsc.Message('/address');
54
68
  m.append({
55
69
  type: 'string',
@@ -60,43 +74,51 @@ tap.test('message: object', (t) => {
60
74
  value: 100
61
75
  });
62
76
 
63
- oscServer.on('message', (msg) => {
77
+ server.on('message', (msg) => {
64
78
  const expected = ['/address', 'test', 100];
65
79
  t.same(msg, expected, `We reveived the payload: ${msg}`);
66
- oscServer.close();
67
- t.end();
68
80
  });
69
81
 
70
- client.send(m, () => {
71
- client.close();
72
- });
82
+ client.send(m);
73
83
  });
74
84
 
75
- tap.test('message: float', (t) => {
76
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
77
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
85
+ tap.test('message: float', async (t) => {
86
+ const server = new nodeOsc.Server(0, '127.0.0.1');
87
+ await node_events.once(server, 'listening');
88
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
89
+
90
+ t.plan(2);
91
+ t.teardown(async () => {
92
+ await server.close();
93
+ await client.close();
94
+ });
95
+
78
96
  const m = new nodeOsc.Message('/address');
79
97
  m.append(3.14);
80
98
 
81
- oscServer.on('message', (msg) => {
99
+ server.on('message', (msg) => {
82
100
  const expected = [
83
101
  '/address',
84
102
  3.14
85
103
  ];
86
104
  t.equal(msg[0], expected[0], `We reveived the payload: ${msg}`);
87
105
  t.equal(round(msg[1]), expected[1], 'pie please');
88
- oscServer.close();
89
- t.end();
90
106
  });
91
107
 
92
- client.send(m, () => {
93
- client.close();
94
- });
108
+ client.send(m);
95
109
  });
96
110
 
97
- tap.test('message: alias messages', (t) => {
98
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
99
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
111
+ tap.test('message: alias messages', async (t) => {
112
+ const server = new nodeOsc.Server(0, '127.0.0.1');
113
+ await node_events.once(server, 'listening');
114
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
115
+
116
+ t.plan(5);
117
+ t.teardown(async () => {
118
+ await server.close();
119
+ await client.close();
120
+ });
121
+
100
122
  const m = new nodeOsc.Message('/address');
101
123
  m.append({
102
124
  type: 'i',
@@ -107,7 +129,7 @@ tap.test('message: alias messages', (t) => {
107
129
  value: 3.14
108
130
  });
109
131
 
110
- oscServer.on('message', (msg) => {
132
+ server.on('message', (msg) => {
111
133
  const expected = [
112
134
  '/address',
113
135
  123,
@@ -118,39 +140,47 @@ tap.test('message: alias messages', (t) => {
118
140
  t.ok(Number.isInteger(msg[1]), 'the first value is an int');
119
141
  t.equal(round(msg[2]), expected[2], 'pie please');
120
142
  t.ok(msg[2] % 1 !== 0, 'the second value is a float');
121
- oscServer.close();
122
- t.end();
123
143
  });
124
144
 
125
- client.send(m, () => {
126
- client.close();
127
- });
145
+ client.send(m);
128
146
  });
129
147
 
130
- tap.test('message: boolean', (t) => {
131
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
132
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
148
+ tap.test('message: boolean', async (t) => {
149
+ const server = new nodeOsc.Server(0, '127.0.0.1');
150
+ await node_events.once(server, 'listening');
151
+
152
+ t.plan(1);
153
+ t.teardown(async () => {
154
+ await server.close();
155
+ await client.close();
156
+ });
157
+
158
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
133
159
  const m = new nodeOsc.Message('/address');
134
160
  m.append(true);
135
161
 
136
- oscServer.on('message', (msg) => {
162
+ server.on('message', (msg) => {
137
163
  const expected = [
138
164
  '/address',
139
165
  true
140
166
  ];
141
167
  t.same(msg, expected, `We reveived the payload: ${msg}`);
142
- oscServer.close();
143
- t.end();
144
168
  });
145
169
 
146
- client.send(m, () => {
147
- client.close();
148
- });
170
+ client.send(m);
149
171
  });
150
172
 
151
- tap.test('message: blob', (t) => {
152
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
153
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
173
+ tap.test('message: blob', async (t) => {
174
+ const server = new nodeOsc.Server(0, '127.0.0.1');
175
+ await node_events.once(server, 'listening');
176
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
177
+
178
+ t.plan(1);
179
+ t.teardown(async () => {
180
+ await server.close();
181
+ await client.close();
182
+ });
183
+
154
184
  const m = new nodeOsc.Message('/address');
155
185
  const buf = Buffer.from('test');
156
186
  m.append({
@@ -158,19 +188,42 @@ tap.test('message: blob', (t) => {
158
188
  value: buf
159
189
  });
160
190
 
161
- oscServer.on('message', (msg) => {
191
+ server.on('message', (msg) => {
162
192
  const expected = [
163
193
  '/address',
164
194
  buf
165
195
  ];
166
196
  t.same(msg, expected, `We reveived the payload: ${msg}`);
167
- oscServer.close();
168
- t.end();
169
197
  });
170
198
 
171
- client.send(m, () => {
172
- client.close();
199
+ client.send(m);
200
+ });
201
+
202
+ tap.test('message: Buffer as blob', async (t) => {
203
+ const server = new nodeOsc.Server(0, '127.0.0.1');
204
+ await node_events.once(server, 'listening');
205
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
206
+
207
+ t.plan(1);
208
+ t.teardown(async () => {
209
+ await server.close();
210
+ await client.close();
211
+ });
212
+
213
+ const m = new nodeOsc.Message('/address');
214
+ const buf = Buffer.from('test buffer data');
215
+ // Directly append Buffer without wrapping in object
216
+ m.append(buf);
217
+
218
+ server.on('message', (msg) => {
219
+ const expected = [
220
+ '/address',
221
+ buf
222
+ ];
223
+ t.same(msg, expected, `We received the buffer payload: ${msg}`);
173
224
  });
225
+
226
+ client.send(m);
174
227
  });
175
228
 
176
229
  // test('message: timetag', (t) => {
@@ -192,6 +245,150 @@ tap.test('message: blob', (t) => {
192
245
  // });
193
246
  // });
194
247
 
248
+ tap.test('message: Buffer with multiple arguments', async (t) => {
249
+ const server = new nodeOsc.Server(0, '127.0.0.1');
250
+ await node_events.once(server, 'listening');
251
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
252
+
253
+ t.plan(6);
254
+ t.teardown(async () => {
255
+ await server.close();
256
+ await client.close();
257
+ });
258
+
259
+ const m = new nodeOsc.Message('/address');
260
+ const buf1 = Buffer.from('first');
261
+ const buf2 = Buffer.from('second');
262
+
263
+ m.append('string');
264
+ m.append(42);
265
+ m.append(buf1);
266
+ m.append(3.14);
267
+ m.append(buf2);
268
+
269
+ server.on('message', (msg) => {
270
+ t.equal(msg[0], '/address', 'Address matches');
271
+ t.equal(msg[1], 'string', 'String matches');
272
+ t.equal(msg[2], 42, 'Integer matches');
273
+ t.same(msg[3], buf1, 'First buffer matches');
274
+ t.equal(round(msg[4]), 3.14, 'Float matches');
275
+ t.same(msg[5], buf2, 'Second buffer matches');
276
+ });
277
+
278
+ client.send(m);
279
+ });
280
+
281
+ tap.test('message: Buffer in constructor', async (t) => {
282
+ const server = new nodeOsc.Server(0, '127.0.0.1');
283
+ await node_events.once(server, 'listening');
284
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
285
+
286
+ t.plan(1);
287
+ t.teardown(async () => {
288
+ await server.close();
289
+ await client.close();
290
+ });
291
+
292
+ const buf = Buffer.from('constructor buffer');
293
+ const m = new nodeOsc.Message('/address', 'test', buf, 123);
294
+
295
+ server.on('message', (msg) => {
296
+ const expected = [
297
+ '/address',
298
+ 'test',
299
+ buf,
300
+ 123
301
+ ];
302
+ t.same(msg, expected, `We received the constructor buffer payload: ${msg}`);
303
+ });
304
+
305
+ client.send(m);
306
+ });
307
+
308
+ tap.test('message: Buffer in array', async (t) => {
309
+ const server = new nodeOsc.Server(0, '127.0.0.1');
310
+ await node_events.once(server, 'listening');
311
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
312
+
313
+ t.plan(1);
314
+ t.teardown(async () => {
315
+ await server.close();
316
+ await client.close();
317
+ });
318
+
319
+ const m = new nodeOsc.Message('/address');
320
+ const buf1 = Buffer.from('array1');
321
+ const buf2 = Buffer.from('array2');
322
+
323
+ m.append([buf1, 'string', buf2, 456]);
324
+
325
+ server.on('message', (msg) => {
326
+ const expected = [
327
+ '/address',
328
+ buf1,
329
+ 'string',
330
+ buf2,
331
+ 456
332
+ ];
333
+ t.same(msg, expected, `We received the array with buffers: ${msg}`);
334
+ });
335
+
336
+ client.send(m);
337
+ });
338
+
339
+ tap.test('message: empty Buffer', async (t) => {
340
+ const server = new nodeOsc.Server(0, '127.0.0.1');
341
+ await node_events.once(server, 'listening');
342
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
343
+
344
+ t.plan(1);
345
+ t.teardown(async () => {
346
+ await server.close();
347
+ await client.close();
348
+ });
349
+
350
+ const m = new nodeOsc.Message('/address');
351
+ const buf = Buffer.from('');
352
+
353
+ m.append(buf);
354
+
355
+ server.on('message', (msg) => {
356
+ const expected = [
357
+ '/address',
358
+ buf
359
+ ];
360
+ t.same(msg, expected, `We received the empty buffer: ${msg}`);
361
+ });
362
+
363
+ client.send(m);
364
+ });
365
+
366
+ tap.test('message: large Buffer', async (t) => {
367
+ const server = new nodeOsc.Server(0, '127.0.0.1');
368
+ await node_events.once(server, 'listening');
369
+ const client = new nodeOsc.Client('127.0.0.1', server.port);
370
+
371
+ t.plan(4);
372
+ t.teardown(async () => {
373
+ await server.close();
374
+ await client.close();
375
+ });
376
+
377
+ const m = new nodeOsc.Message('/address');
378
+ const buf = Buffer.alloc(1024, 'x');
379
+
380
+ m.append(buf);
381
+
382
+ server.on('message', (msg) => {
383
+ t.equal(msg[0], '/address', 'Address matches');
384
+ t.ok(Buffer.isBuffer(msg[1]), 'Second element is a Buffer');
385
+ t.equal(msg[1].length, 1024, 'Buffer size matches');
386
+ t.same(msg[1], buf, 'Buffer content matches');
387
+ });
388
+
389
+ client.send(m);
390
+ });
391
+
195
392
  tap.test('message: error', (t) => {
196
393
  const m = new nodeOsc.Message('/address');
197
394
  t.plan(2);
@@ -25,6 +25,46 @@ tap.test('osc: timetag encoding with non-number value', (t) => {
25
25
  t.end();
26
26
  });
27
27
 
28
+ tap.test('osc: timetag encoding with zero value', (t) => {
29
+ const bundle = {
30
+ oscType: 'bundle',
31
+ timetag: 0,
32
+ elements: [
33
+ {
34
+ oscType: 'message',
35
+ address: '/test',
36
+ args: []
37
+ }
38
+ ]
39
+ };
40
+
41
+ const buffer = osc.encode(bundle);
42
+ const decoded = osc.decode(buffer);
43
+
44
+ t.equal(decoded.timetag, 0, 'should encode 0 as immediate execution');
45
+ t.end();
46
+ });
47
+
48
+ tap.test('osc: timetag encoding with numeric epoch value', (t) => {
49
+ const bundle = {
50
+ oscType: 'bundle',
51
+ timetag: 42,
52
+ elements: [
53
+ {
54
+ oscType: 'message',
55
+ address: '/test',
56
+ args: []
57
+ }
58
+ ]
59
+ };
60
+
61
+ const buffer = osc.encode(bundle);
62
+ const decoded = osc.decode(buffer);
63
+
64
+ t.equal(decoded.timetag, 42, 'should preserve numeric timetag values');
65
+ t.end();
66
+ });
67
+
28
68
  tap.test('osc: timetag with immediate execution values', (t) => {
29
69
  // Test readTimeTag with seconds === 0 && fraction === 1
30
70
  const bundle = {
@@ -767,6 +807,117 @@ tap.test('osc: explicit double type name', (t) => {
767
807
  t.end();
768
808
  });
769
809
 
810
+ tap.test('osc: malformed packet with missing string terminator', (t) => {
811
+ const buffer = Buffer.from('/test', 'utf8');
812
+
813
+ t.throws(() => {
814
+ osc.decode(buffer);
815
+ }, /Malformed Packet: Missing null terminator for string/, 'should throw on unterminated string');
816
+ t.end();
817
+ });
818
+
819
+ tap.test('osc: malformed packet with truncated int32 argument', (t) => {
820
+ const address = Buffer.from('/i\0\0', 'ascii');
821
+ const typeTags = Buffer.from(',i\0\0', 'ascii');
822
+ const truncated = Buffer.from([0x00, 0x01]);
823
+ const buffer = Buffer.concat([address, typeTags, truncated]);
824
+
825
+ t.throws(() => {
826
+ osc.decode(buffer);
827
+ }, /Malformed Packet: Not enough bytes for int32/, 'should throw on truncated int32');
828
+ t.end();
829
+ });
830
+
831
+ tap.test('osc: malformed packet with truncated float32 argument', (t) => {
832
+ const address = Buffer.from('/f\0\0', 'ascii');
833
+ const typeTags = Buffer.from(',f\0\0', 'ascii');
834
+ const truncated = Buffer.from([0x3f, 0x80, 0x00]);
835
+ const buffer = Buffer.concat([address, typeTags, truncated]);
836
+
837
+ t.throws(() => {
838
+ osc.decode(buffer);
839
+ }, /Malformed Packet: Not enough bytes for float32/, 'should throw on truncated float32');
840
+ t.end();
841
+ });
842
+
843
+ tap.test('osc: malformed packet with invalid blob length', (t) => {
844
+ const address = Buffer.from('/b\0\0', 'ascii');
845
+ const typeTags = Buffer.from(',b\0\0', 'ascii');
846
+ const length = Buffer.alloc(4);
847
+ length.writeInt32BE(-1, 0);
848
+ const buffer = Buffer.concat([address, typeTags, length]);
849
+
850
+ t.throws(() => {
851
+ osc.decode(buffer);
852
+ }, /Malformed Packet: Invalid blob length/, 'should throw on negative blob length');
853
+ t.end();
854
+ });
855
+
856
+ tap.test('osc: malformed packet with truncated blob data', (t) => {
857
+ const address = Buffer.from('/b\0\0', 'ascii');
858
+ const typeTags = Buffer.from(',b\0\0', 'ascii');
859
+ const length = Buffer.alloc(4);
860
+ length.writeInt32BE(4, 0);
861
+ const data = Buffer.from([0x01, 0x02]);
862
+ const buffer = Buffer.concat([address, typeTags, length, data]);
863
+
864
+ t.throws(() => {
865
+ osc.decode(buffer);
866
+ }, /Malformed Packet: Not enough bytes for blob/, 'should throw on truncated blob data');
867
+ t.end();
868
+ });
869
+
870
+ tap.test('osc: malformed packet with missing blob padding', (t) => {
871
+ const address = Buffer.from('/b\0\0', 'ascii');
872
+ const typeTags = Buffer.from(',b\0\0', 'ascii');
873
+ const length = Buffer.alloc(4);
874
+ length.writeInt32BE(3, 0);
875
+ const data = Buffer.from([0x01, 0x02, 0x03]);
876
+ const buffer = Buffer.concat([address, typeTags, length, data]);
877
+
878
+ t.throws(() => {
879
+ osc.decode(buffer);
880
+ }, /Malformed Packet: Not enough bytes for blob padding/, 'should throw on missing blob padding');
881
+ t.end();
882
+ });
883
+
884
+ tap.test('osc: malformed bundle with truncated timetag', (t) => {
885
+ const bundleHeader = Buffer.from('#bundle\0', 'ascii');
886
+ const timetag = Buffer.alloc(4);
887
+ const buffer = Buffer.concat([bundleHeader, timetag]);
888
+
889
+ t.throws(() => {
890
+ osc.decode(buffer);
891
+ }, /Malformed Packet: Not enough bytes for timetag/, 'should throw on truncated timetag');
892
+ t.end();
893
+ });
894
+
895
+ tap.test('osc: malformed bundle with invalid element size', (t) => {
896
+ const bundleHeader = Buffer.from('#bundle\0', 'ascii');
897
+ const timetag = Buffer.alloc(8);
898
+ const size = Buffer.alloc(4);
899
+ size.writeInt32BE(0, 0);
900
+ const buffer = Buffer.concat([bundleHeader, timetag, size]);
901
+
902
+ t.throws(() => {
903
+ osc.decode(buffer);
904
+ }, /Malformed Packet/, 'should throw on invalid bundle element size');
905
+ t.end();
906
+ });
907
+
908
+ tap.test('osc: malformed bundle with oversized element size', (t) => {
909
+ const bundleHeader = Buffer.from('#bundle\0', 'ascii');
910
+ const timetag = Buffer.alloc(8);
911
+ const size = Buffer.alloc(4);
912
+ size.writeInt32BE(12, 0);
913
+ const buffer = Buffer.concat([bundleHeader, timetag, size, Buffer.from([0x01, 0x02])]);
914
+
915
+ t.throws(() => {
916
+ osc.decode(buffer);
917
+ }, /Malformed Packet/, 'should throw on oversized bundle element size');
918
+ t.end();
919
+ });
920
+
770
921
  tap.test('osc: blob padding when length is multiple of 4', (t) => {
771
922
  // Test writeBlob line 52: padding === 4 branch (should use 0)
772
923
  const message = {