node-osc 11.1.1 → 11.2.1

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 (72) hide show
  1. package/.gitattributes +11 -0
  2. package/.github/workflows/bump-version.yml +5 -3
  3. package/.github/workflows/create-release.yml +4 -4
  4. package/.github/workflows/nodejs.yml +6 -3
  5. package/LICENSE +201 -165
  6. package/README.md +135 -42
  7. package/dist/lib/Bundle.js +66 -0
  8. package/dist/lib/Client.js +137 -22
  9. package/dist/lib/Message.js +90 -2
  10. package/dist/lib/Server.js +117 -6
  11. package/dist/lib/index.js +3 -0
  12. package/dist/lib/internal/decode.js +4 -4
  13. package/dist/lib/{internal/osc.js → osc.js} +73 -7
  14. package/dist/test/lib/osc.js +396 -0
  15. package/dist/test/test-client.js +174 -0
  16. package/dist/test/test-e2e.js +9 -3
  17. package/dist/test/test-encode-decode.js +1208 -0
  18. package/dist/test/test-error-handling.js +116 -0
  19. package/dist/test/test-message.js +147 -0
  20. package/dist/test/test-osc-internal.js +399 -41
  21. package/dist/test/test-promises.js +272 -0
  22. package/dist/test/test-types.js +42 -0
  23. package/dist/test/util.js +15 -8
  24. package/docs/API.md +477 -0
  25. package/docs/GUIDE.md +605 -0
  26. package/examples/README.md +119 -0
  27. package/examples/async-await.mjs +57 -0
  28. package/examples/bundle-example.mjs +92 -0
  29. package/examples/client.js +22 -5
  30. package/examples/error-handling.mjs +152 -0
  31. package/examples/esm.mjs +21 -0
  32. package/examples/server.js +16 -0
  33. package/jsdoc.json +16 -0
  34. package/lib/Bundle.mjs +66 -0
  35. package/lib/Client.mjs +137 -22
  36. package/lib/Message.mjs +90 -2
  37. package/lib/Server.mjs +117 -6
  38. package/lib/index.mjs +1 -0
  39. package/lib/internal/decode.mjs +4 -4
  40. package/lib/{internal/osc.mjs → osc.mjs} +74 -6
  41. package/package.json +12 -10
  42. package/rollup.config.mjs +49 -41
  43. package/scripts/generate-docs.mjs +229 -0
  44. package/test/fixtures/types/test-cjs-types.ts +19 -0
  45. package/test/fixtures/types/test-esm-types.ts +35 -0
  46. package/test/fixtures/types/tsconfig-cjs.test.json +17 -0
  47. package/test/fixtures/types/tsconfig-esm.test.json +17 -0
  48. package/test/test-bundle.mjs +0 -1
  49. package/test/test-client.mjs +174 -0
  50. package/test/test-e2e.mjs +9 -3
  51. package/test/test-encode-decode.mjs +1206 -0
  52. package/test/test-error-handling.mjs +115 -0
  53. package/test/test-message.mjs +147 -0
  54. package/test/test-osc-internal.mjs +400 -42
  55. package/test/test-promises.mjs +271 -0
  56. package/test/test-types.mjs +39 -0
  57. package/test/util.mjs +15 -8
  58. package/tsconfig.json +45 -0
  59. package/types/Bundle.d.mts +70 -0
  60. package/types/Bundle.d.mts.map +1 -0
  61. package/types/Client.d.mts +101 -0
  62. package/types/Client.d.mts.map +1 -0
  63. package/types/Message.d.mts +84 -0
  64. package/types/Message.d.mts.map +1 -0
  65. package/types/Server.d.mts +98 -0
  66. package/types/Server.d.mts.map +1 -0
  67. package/types/index.d.mts +6 -0
  68. package/types/index.d.mts.map +1 -0
  69. package/types/internal/decode.d.mts +4 -0
  70. package/types/internal/decode.d.mts.map +1 -0
  71. package/types/osc.d.mts +66 -0
  72. package/types/osc.d.mts.map +1 -0
@@ -0,0 +1,272 @@
1
+ 'use strict';
2
+
3
+ var node_events = require('node:events');
4
+ var tap = require('tap');
5
+ var util = require('./util.js');
6
+ var nodeOsc = require('node-osc');
7
+
8
+ tap.beforeEach(util.bootstrap);
9
+
10
+ tap.test('client: send with promise - array', async (t) => {
11
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
12
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
13
+
14
+ t.plan(1);
15
+
16
+ oscServer.on('message', (msg) => {
17
+ oscServer.close();
18
+ t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
19
+ });
20
+
21
+ await client.send(['/test', 0, 1, 'testing', true]);
22
+ await client.close();
23
+ });
24
+
25
+ tap.test('client: array is not mutated when sent with promise', async (t) => {
26
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
27
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
28
+
29
+ t.plan(2);
30
+
31
+ const originalArray = ['/test', 0, 1, 'testing', true];
32
+ const expectedArray = ['/test', 0, 1, 'testing', true];
33
+
34
+ oscServer.on('message', (msg) => {
35
+ oscServer.close();
36
+ t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
37
+ });
38
+
39
+ await client.send(originalArray);
40
+
41
+ // Verify the original array was not mutated
42
+ t.same(originalArray, expectedArray, 'Original array should not be mutated');
43
+
44
+ await client.close();
45
+ });
46
+
47
+ tap.test('client: send with promise - string', async (t) => {
48
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
49
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
50
+
51
+ t.plan(1);
52
+
53
+ oscServer.on('message', (msg) => {
54
+ oscServer.close();
55
+ t.same(msg, ['/test'], 'We should receive expected payload');
56
+ });
57
+
58
+ await client.send('/test');
59
+ await client.close();
60
+ });
61
+
62
+ tap.test('client: send with promise - message object', async (t) => {
63
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
64
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
65
+
66
+ t.plan(1);
67
+
68
+ oscServer.on('message', (msg) => {
69
+ oscServer.close();
70
+ t.same(msg, ['/test', 1, 2, 3, 'lol', false], 'we received the payload');
71
+ });
72
+
73
+ await client.send({
74
+ address: '/test',
75
+ args: [1, 2, 3, 'lol', false]
76
+ });
77
+ await client.close();
78
+ });
79
+
80
+ tap.test('client: send with promise - multiple args', async (t) => {
81
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
82
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
83
+
84
+ t.plan(1);
85
+
86
+ oscServer.on('message', (msg) => {
87
+ oscServer.close();
88
+ t.same(msg, ['/test', 1, 2, 'testing'], 'We should receive expected payload');
89
+ });
90
+
91
+ await client.send('/test', 1, 2, 'testing');
92
+ await client.close();
93
+ });
94
+
95
+ tap.test('client: send promise rejection on closed socket', async (t) => {
96
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
97
+
98
+ t.plan(1);
99
+
100
+ await client.close();
101
+
102
+ try {
103
+ await client.send('/boom');
104
+ t.fail('Should have thrown an error');
105
+ } catch (err) {
106
+ t.equal(err.code, 'ERR_SOCKET_DGRAM_NOT_RUNNING', 'Should reject with correct error code');
107
+ }
108
+ });
109
+
110
+ tap.test('client: async/await usage', async (t) => {
111
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
112
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
113
+
114
+ t.plan(1);
115
+
116
+ const messagePromise = node_events.once(oscServer, 'message');
117
+
118
+ await client.send('/async-test', 42, 'hello');
119
+ const [receivedMessage] = await messagePromise;
120
+
121
+ t.same(receivedMessage, ['/async-test', 42, 'hello'], 'Message received via async/await');
122
+
123
+ await client.close();
124
+ await oscServer.close();
125
+ });
126
+
127
+ tap.test('server: close with promise', async (t) => {
128
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
129
+
130
+ t.plan(1);
131
+
132
+ await node_events.once(oscServer, 'listening');
133
+
134
+ await oscServer.close();
135
+ t.pass('Server closed successfully with promise');
136
+ });
137
+
138
+ tap.test('server: no callback still emits listening event', async (t) => {
139
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
140
+
141
+ t.plan(1);
142
+
143
+ await node_events.once(oscServer, 'listening');
144
+ t.pass('listening event emitted');
145
+
146
+ await oscServer.close();
147
+ });
148
+
149
+ tap.test('client and server: full async/await workflow', async (t) => {
150
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
151
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
152
+
153
+ t.plan(2);
154
+
155
+ // Wait for server to be ready
156
+ await node_events.once(oscServer, 'listening');
157
+ t.pass('Server started');
158
+
159
+ // Set up message handler
160
+ const messageReceived = node_events.once(oscServer, 'message');
161
+
162
+ // Send message and wait for it to be received
163
+ await client.send('/workflow', 'test', 123);
164
+ const [msg] = await messageReceived;
165
+ t.same(msg, ['/workflow', 'test', 123], 'Message received correctly');
166
+
167
+ // Clean up
168
+ await client.close();
169
+ await oscServer.close();
170
+ });
171
+
172
+ tap.test('client: multiple sends with promises', async (t) => {
173
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
174
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
175
+
176
+ t.plan(3);
177
+
178
+ const messages = [];
179
+ oscServer.on('message', (msg) => {
180
+ messages.push(msg);
181
+ });
182
+
183
+ await client.send('/msg1', 1);
184
+ await client.send('/msg2', 2);
185
+ await client.send('/msg3', 3);
186
+
187
+ // Give a little time for all messages to be received
188
+ await new Promise((resolve) => setTimeout(resolve, 100));
189
+
190
+ t.equal(messages.length, 3, 'Received all three messages');
191
+ t.same(messages[0], ['/msg1', 1], 'First message correct');
192
+ t.same(messages[2], ['/msg3', 3], 'Last message correct');
193
+
194
+ await client.close();
195
+ await oscServer.close();
196
+ });
197
+
198
+ tap.test('client: close promise rejection on error', async (t) => {
199
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
200
+
201
+ t.plan(1);
202
+
203
+ // Mock the socket's close method to simulate an error
204
+ const originalClose = client._sock.close.bind(client._sock);
205
+
206
+ // Set up teardown to ensure socket is properly closed
207
+ t.teardown(() => {
208
+ // Restore original close method first
209
+ client._sock.close = originalClose;
210
+ // Then close the socket
211
+ try {
212
+ client._sock.close(() => {});
213
+ } catch {
214
+ // Socket might already be closed, that's ok
215
+ }
216
+ });
217
+
218
+ client._sock.close = function(cb) {
219
+ // Simulate an error being passed to callback
220
+ if (cb) {
221
+ const err = new Error('Mock close error');
222
+ err.code = 'MOCK_ERROR';
223
+ setImmediate(() => cb(err));
224
+ }
225
+ };
226
+
227
+ try {
228
+ await client.close();
229
+ t.fail('Should have thrown an error');
230
+ } catch (err) {
231
+ t.equal(err.code, 'MOCK_ERROR', 'Should reject with mock error');
232
+ }
233
+ });
234
+
235
+ tap.test('server: close promise rejection on error', async (t) => {
236
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
237
+
238
+ t.plan(1);
239
+
240
+ await node_events.once(oscServer, 'listening');
241
+
242
+ // Mock the socket's close method to simulate an error
243
+ const originalClose = oscServer._sock.close.bind(oscServer._sock);
244
+
245
+ // Set up teardown to ensure socket is properly closed
246
+ t.teardown(() => {
247
+ // Restore original close method first
248
+ oscServer._sock.close = originalClose;
249
+ // Then close the socket
250
+ try {
251
+ oscServer._sock.close(() => {});
252
+ } catch {
253
+ // Socket might already be closed, that's ok
254
+ }
255
+ });
256
+
257
+ oscServer._sock.close = function(cb) {
258
+ // Simulate an error being passed to callback
259
+ if (cb) {
260
+ const err = new Error('Mock close error');
261
+ err.code = 'MOCK_ERROR';
262
+ setImmediate(() => cb(err));
263
+ }
264
+ };
265
+
266
+ try {
267
+ await oscServer.close();
268
+ t.fail('Should have thrown an error');
269
+ } catch (err) {
270
+ t.equal(err.code, 'MOCK_ERROR', 'Should reject with mock error');
271
+ }
272
+ });
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ var tap = require('tap');
4
+ var node_child_process = require('node:child_process');
5
+ var node_path = require('node:path');
6
+ var node_url = require('node:url');
7
+
8
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
9
+ const __dirname$1 = node_url.fileURLToPath(new URL('.', (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('test-types.js', document.baseURI).href))));
10
+
11
+ // Only run in ESM mode (not when transpiled to CJS in dist/)
12
+ // Normalize path separators for cross-platform compatibility
13
+ const normalizedPath = __dirname$1.replace(/\\/g, '/');
14
+ const isESM = !normalizedPath.includes('/dist/');
15
+
16
+ tap.test('types: TypeScript compilation', (t) => {
17
+ let tsconfigPath;
18
+ const testRoot = node_path.resolve(__dirname$1, isESM ? '.': '../../test');
19
+ if (isESM) {
20
+ tsconfigPath = node_path.join(testRoot, 'fixtures', 'types', 'tsconfig-esm.test.json');
21
+ }
22
+ else {
23
+ tsconfigPath = node_path.join(testRoot, 'fixtures', 'types', 'tsconfig-cjs.test.json');
24
+ }
25
+
26
+ try {
27
+ // Run TypeScript compiler
28
+ const cmd = 'npx tsc --project "' + tsconfigPath + '"';
29
+ node_child_process.execSync(cmd, {
30
+ encoding: 'utf-8',
31
+ stdio: 'pipe',
32
+ cwd: node_path.join(testRoot, 'fixtures', 'types')
33
+ });
34
+ t.pass('TypeScript types compile successfully');
35
+ } catch (error) {
36
+ t.fail('TypeScript compilation failed: ' + error.message);
37
+ if (error.stdout) console.log('STDOUT:', error.stdout);
38
+ if (error.stderr) console.log('STDERR:', error.stderr);
39
+ }
40
+
41
+ t.end();
42
+ });
package/dist/test/util.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var node_net = require('node:net');
4
+ var promises = require('node:timers/promises');
4
5
 
5
6
  async function bootstrap(t) {
6
7
  const port = await getPort();
@@ -9,18 +10,24 @@ async function bootstrap(t) {
9
10
  };
10
11
  }
11
12
 
12
- function getPort() {
13
- return new Promise((resolve, reject) => {
14
- const server = node_net.createServer();
15
- server.unref();
13
+ async function getPort() {
14
+ const server = node_net.createServer();
15
+ server.unref();
16
+
17
+ const port = await new Promise((resolve, reject) => {
16
18
  server.on('error', reject);
17
19
  server.listen(() => {
18
- const { port } = server.address();
19
- server.close(() => {
20
- resolve(port);
21
- });
20
+ resolve(server.address().port);
22
21
  });
23
22
  });
23
+
24
+ await server.close();
25
+
26
+ // Allow the event loop to process and ensure port is fully released
27
+ // This prevents EACCES errors when immediately rebinding to the same port
28
+ await promises.setImmediate();
29
+
30
+ return port;
24
31
  }
25
32
 
26
33
  exports.bootstrap = bootstrap;