node-osc 11.1.1 → 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/dist/lib/{internal/osc.js → osc.js} +70 -5
  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/lib/{internal/osc.mjs → osc.mjs} +71 -4
  39. package/package.json +12 -10
  40. package/rollup.config.mjs +48 -41
  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
@@ -0,0 +1,250 @@
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: send with promise - string', 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(1);
30
+
31
+ oscServer.on('message', (msg) => {
32
+ oscServer.close();
33
+ t.same(msg, ['/test'], 'We should receive expected payload');
34
+ });
35
+
36
+ await client.send('/test');
37
+ await client.close();
38
+ });
39
+
40
+ tap.test('client: send with promise - message object', async (t) => {
41
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
42
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
43
+
44
+ t.plan(1);
45
+
46
+ oscServer.on('message', (msg) => {
47
+ oscServer.close();
48
+ t.same(msg, ['/test', 1, 2, 3, 'lol', false], 'we received the payload');
49
+ });
50
+
51
+ await client.send({
52
+ address: '/test',
53
+ args: [1, 2, 3, 'lol', false]
54
+ });
55
+ await client.close();
56
+ });
57
+
58
+ tap.test('client: send with promise - multiple args', async (t) => {
59
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
60
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
61
+
62
+ t.plan(1);
63
+
64
+ oscServer.on('message', (msg) => {
65
+ oscServer.close();
66
+ t.same(msg, ['/test', 1, 2, 'testing'], 'We should receive expected payload');
67
+ });
68
+
69
+ await client.send('/test', 1, 2, 'testing');
70
+ await client.close();
71
+ });
72
+
73
+ tap.test('client: send promise rejection on closed socket', async (t) => {
74
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
75
+
76
+ t.plan(1);
77
+
78
+ await client.close();
79
+
80
+ try {
81
+ await client.send('/boom');
82
+ t.fail('Should have thrown an error');
83
+ } catch (err) {
84
+ t.equal(err.code, 'ERR_SOCKET_DGRAM_NOT_RUNNING', 'Should reject with correct error code');
85
+ }
86
+ });
87
+
88
+ tap.test('client: async/await usage', async (t) => {
89
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
90
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
91
+
92
+ t.plan(1);
93
+
94
+ const messagePromise = node_events.once(oscServer, 'message');
95
+
96
+ await client.send('/async-test', 42, 'hello');
97
+ const [receivedMessage] = await messagePromise;
98
+
99
+ t.same(receivedMessage, ['/async-test', 42, 'hello'], 'Message received via async/await');
100
+
101
+ await client.close();
102
+ await oscServer.close();
103
+ });
104
+
105
+ tap.test('server: close with promise', async (t) => {
106
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
107
+
108
+ t.plan(1);
109
+
110
+ await node_events.once(oscServer, 'listening');
111
+
112
+ await oscServer.close();
113
+ t.pass('Server closed successfully with promise');
114
+ });
115
+
116
+ tap.test('server: no callback still emits listening event', async (t) => {
117
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
118
+
119
+ t.plan(1);
120
+
121
+ await node_events.once(oscServer, 'listening');
122
+ t.pass('listening event emitted');
123
+
124
+ await oscServer.close();
125
+ });
126
+
127
+ tap.test('client and server: full async/await workflow', async (t) => {
128
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
129
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
130
+
131
+ t.plan(2);
132
+
133
+ // Wait for server to be ready
134
+ await node_events.once(oscServer, 'listening');
135
+ t.pass('Server started');
136
+
137
+ // Set up message handler
138
+ const messageReceived = node_events.once(oscServer, 'message');
139
+
140
+ // Send message and wait for it to be received
141
+ await client.send('/workflow', 'test', 123);
142
+ const [msg] = await messageReceived;
143
+ t.same(msg, ['/workflow', 'test', 123], 'Message received correctly');
144
+
145
+ // Clean up
146
+ await client.close();
147
+ await oscServer.close();
148
+ });
149
+
150
+ tap.test('client: multiple sends with promises', async (t) => {
151
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
152
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
153
+
154
+ t.plan(3);
155
+
156
+ const messages = [];
157
+ oscServer.on('message', (msg) => {
158
+ messages.push(msg);
159
+ });
160
+
161
+ await client.send('/msg1', 1);
162
+ await client.send('/msg2', 2);
163
+ await client.send('/msg3', 3);
164
+
165
+ // Give a little time for all messages to be received
166
+ await new Promise((resolve) => setTimeout(resolve, 100));
167
+
168
+ t.equal(messages.length, 3, 'Received all three messages');
169
+ t.same(messages[0], ['/msg1', 1], 'First message correct');
170
+ t.same(messages[2], ['/msg3', 3], 'Last message correct');
171
+
172
+ await client.close();
173
+ await oscServer.close();
174
+ });
175
+
176
+ tap.test('client: close promise rejection on error', async (t) => {
177
+ const client = new nodeOsc.Client('127.0.0.1', t.context.port);
178
+
179
+ t.plan(1);
180
+
181
+ // Mock the socket's close method to simulate an error
182
+ const originalClose = client._sock.close.bind(client._sock);
183
+
184
+ // Set up teardown to ensure socket is properly closed
185
+ t.teardown(() => {
186
+ // Restore original close method first
187
+ client._sock.close = originalClose;
188
+ // Then close the socket
189
+ try {
190
+ client._sock.close(() => {});
191
+ } catch {
192
+ // Socket might already be closed, that's ok
193
+ }
194
+ });
195
+
196
+ client._sock.close = function(cb) {
197
+ // Simulate an error being passed to callback
198
+ if (cb) {
199
+ const err = new Error('Mock close error');
200
+ err.code = 'MOCK_ERROR';
201
+ setImmediate(() => cb(err));
202
+ }
203
+ };
204
+
205
+ try {
206
+ await client.close();
207
+ t.fail('Should have thrown an error');
208
+ } catch (err) {
209
+ t.equal(err.code, 'MOCK_ERROR', 'Should reject with mock error');
210
+ }
211
+ });
212
+
213
+ tap.test('server: close promise rejection on error', async (t) => {
214
+ const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
215
+
216
+ t.plan(1);
217
+
218
+ await node_events.once(oscServer, 'listening');
219
+
220
+ // Mock the socket's close method to simulate an error
221
+ const originalClose = oscServer._sock.close.bind(oscServer._sock);
222
+
223
+ // Set up teardown to ensure socket is properly closed
224
+ t.teardown(() => {
225
+ // Restore original close method first
226
+ oscServer._sock.close = originalClose;
227
+ // Then close the socket
228
+ try {
229
+ oscServer._sock.close(() => {});
230
+ } catch {
231
+ // Socket might already be closed, that's ok
232
+ }
233
+ });
234
+
235
+ oscServer._sock.close = function(cb) {
236
+ // Simulate an error being passed to callback
237
+ if (cb) {
238
+ const err = new Error('Mock close error');
239
+ err.code = 'MOCK_ERROR';
240
+ setImmediate(() => cb(err));
241
+ }
242
+ };
243
+
244
+ try {
245
+ await oscServer.close();
246
+ t.fail('Should have thrown an error');
247
+ } catch (err) {
248
+ t.equal(err.code, 'MOCK_ERROR', 'Should reject with mock error');
249
+ }
250
+ });
@@ -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;