node-osc 11.2.1 → 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 (40) hide show
  1. package/.github/workflows/nodejs.yml +1 -1
  2. package/README.md +3 -2
  3. package/agent.md +330 -0
  4. package/dist/lib/Server.js +7 -12
  5. package/dist/lib/internal/decode.js +3 -1
  6. package/dist/lib/osc.js +29 -1
  7. package/dist/test/lib/osc.js +29 -1
  8. package/dist/test/test-bundle.js +13 -12
  9. package/dist/test/test-client.js +49 -40
  10. package/dist/test/test-decode.js +35 -0
  11. package/dist/test/test-e2e.js +9 -9
  12. package/dist/test/test-encode-decode.js +96 -0
  13. package/dist/test/test-error-handling.js +14 -13
  14. package/dist/test/test-message.js +169 -119
  15. package/dist/test/test-osc-internal.js +151 -0
  16. package/dist/test/test-promises.js +71 -29
  17. package/dist/test/test-server.js +19 -16
  18. package/docs/README.md +81 -0
  19. package/examples/README.md +3 -1
  20. package/lib/Server.mjs +7 -12
  21. package/lib/internal/decode.mjs +3 -1
  22. package/lib/osc.mjs +29 -1
  23. package/package.json +2 -2
  24. package/test/test-bundle.mjs +14 -13
  25. package/test/test-client.mjs +50 -41
  26. package/test/test-decode.mjs +35 -0
  27. package/test/test-e2e.mjs +10 -10
  28. package/test/test-encode-decode.mjs +96 -0
  29. package/test/test-error-handling.mjs +15 -14
  30. package/test/test-message.mjs +171 -122
  31. package/test/test-osc-internal.mjs +151 -0
  32. package/test/test-promises.mjs +72 -30
  33. package/test/test-server.mjs +20 -17
  34. package/types/Server.d.mts.map +1 -1
  35. package/types/internal/decode.d.mts.map +1 -1
  36. package/types/osc.d.mts.map +1 -1
  37. package/dist/test/test-getPort.js +0 -20
  38. package/dist/test/util.js +0 -34
  39. package/test/test-getPort.mjs +0 -18
  40. 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,42 +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();
173
- });
199
+ client.send(m);
174
200
  });
175
201
 
176
- tap.test('message: Buffer as blob', (t) => {
177
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
178
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
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
+
179
213
  const m = new nodeOsc.Message('/address');
180
214
  const buf = Buffer.from('test buffer data');
181
215
  // Directly append Buffer without wrapping in object
182
216
  m.append(buf);
183
217
 
184
- oscServer.on('message', (msg) => {
218
+ server.on('message', (msg) => {
185
219
  const expected = [
186
220
  '/address',
187
221
  buf
188
222
  ];
189
223
  t.same(msg, expected, `We received the buffer payload: ${msg}`);
190
- oscServer.close();
191
- t.end();
192
224
  });
193
225
 
194
- client.send(m, () => {
195
- client.close();
196
- });
226
+ client.send(m);
197
227
  });
198
228
 
199
229
  // test('message: timetag', (t) => {
@@ -215,9 +245,17 @@ tap.test('message: Buffer as blob', (t) => {
215
245
  // });
216
246
  // });
217
247
 
218
- tap.test('message: Buffer with multiple arguments', (t) => {
219
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
220
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
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
+
221
259
  const m = new nodeOsc.Message('/address');
222
260
  const buf1 = Buffer.from('first');
223
261
  const buf2 = Buffer.from('second');
@@ -228,29 +266,33 @@ tap.test('message: Buffer with multiple arguments', (t) => {
228
266
  m.append(3.14);
229
267
  m.append(buf2);
230
268
 
231
- oscServer.on('message', (msg) => {
269
+ server.on('message', (msg) => {
232
270
  t.equal(msg[0], '/address', 'Address matches');
233
271
  t.equal(msg[1], 'string', 'String matches');
234
272
  t.equal(msg[2], 42, 'Integer matches');
235
273
  t.same(msg[3], buf1, 'First buffer matches');
236
274
  t.equal(round(msg[4]), 3.14, 'Float matches');
237
275
  t.same(msg[5], buf2, 'Second buffer matches');
238
- oscServer.close();
239
- t.end();
240
276
  });
241
277
 
242
- client.send(m, () => {
243
- client.close();
244
- });
278
+ client.send(m);
245
279
  });
246
280
 
247
- tap.test('message: Buffer in constructor', (t) => {
248
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
249
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
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
+
250
292
  const buf = Buffer.from('constructor buffer');
251
293
  const m = new nodeOsc.Message('/address', 'test', buf, 123);
252
294
 
253
- oscServer.on('message', (msg) => {
295
+ server.on('message', (msg) => {
254
296
  const expected = [
255
297
  '/address',
256
298
  'test',
@@ -258,25 +300,29 @@ tap.test('message: Buffer in constructor', (t) => {
258
300
  123
259
301
  ];
260
302
  t.same(msg, expected, `We received the constructor buffer payload: ${msg}`);
261
- oscServer.close();
262
- t.end();
263
303
  });
264
304
 
265
- client.send(m, () => {
266
- client.close();
267
- });
305
+ client.send(m);
268
306
  });
269
307
 
270
- tap.test('message: Buffer in array', (t) => {
271
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
272
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
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
+
273
319
  const m = new nodeOsc.Message('/address');
274
320
  const buf1 = Buffer.from('array1');
275
321
  const buf2 = Buffer.from('array2');
276
322
 
277
323
  m.append([buf1, 'string', buf2, 456]);
278
324
 
279
- oscServer.on('message', (msg) => {
325
+ server.on('message', (msg) => {
280
326
  const expected = [
281
327
  '/address',
282
328
  buf1,
@@ -285,58 +331,62 @@ tap.test('message: Buffer in array', (t) => {
285
331
  456
286
332
  ];
287
333
  t.same(msg, expected, `We received the array with buffers: ${msg}`);
288
- oscServer.close();
289
- t.end();
290
334
  });
291
335
 
292
- client.send(m, () => {
293
- client.close();
294
- });
336
+ client.send(m);
295
337
  });
296
338
 
297
- tap.test('message: empty Buffer', (t) => {
298
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
299
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
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
+
300
350
  const m = new nodeOsc.Message('/address');
301
351
  const buf = Buffer.from('');
302
352
 
303
353
  m.append(buf);
304
354
 
305
- oscServer.on('message', (msg) => {
355
+ server.on('message', (msg) => {
306
356
  const expected = [
307
357
  '/address',
308
358
  buf
309
359
  ];
310
360
  t.same(msg, expected, `We received the empty buffer: ${msg}`);
311
- oscServer.close();
312
- t.end();
313
361
  });
314
362
 
315
- client.send(m, () => {
316
- client.close();
317
- });
363
+ client.send(m);
318
364
  });
319
365
 
320
- tap.test('message: large Buffer', (t) => {
321
- const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
322
- const client = new nodeOsc.Client('127.0.0.1', t.context.port);
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
+
323
377
  const m = new nodeOsc.Message('/address');
324
378
  const buf = Buffer.alloc(1024, 'x');
325
379
 
326
380
  m.append(buf);
327
381
 
328
- oscServer.on('message', (msg) => {
382
+ server.on('message', (msg) => {
329
383
  t.equal(msg[0], '/address', 'Address matches');
330
384
  t.ok(Buffer.isBuffer(msg[1]), 'Second element is a Buffer');
331
385
  t.equal(msg[1].length, 1024, 'Buffer size matches');
332
386
  t.same(msg[1], buf, 'Buffer content matches');
333
- oscServer.close();
334
- t.end();
335
387
  });
336
388
 
337
- client.send(m, () => {
338
- client.close();
339
- });
389
+ client.send(m);
340
390
  });
341
391
 
342
392
  tap.test('message: error', (t) => {
@@ -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 = {