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