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.
- package/.github/workflows/bump-version.yml +3 -3
- package/.github/workflows/create-release.yml +4 -4
- package/.github/workflows/nodejs.yml +4 -4
- package/README.md +3 -2
- package/agent.md +330 -0
- package/dist/lib/Client.js +1 -1
- package/dist/lib/Message.js +3 -1
- package/dist/lib/Server.js +7 -12
- package/dist/lib/internal/decode.js +3 -1
- package/dist/lib/osc.js +32 -3
- package/dist/test/lib/osc.js +32 -3
- package/dist/test/test-bundle.js +13 -12
- package/dist/test/test-client.js +68 -37
- package/dist/test/test-decode.js +35 -0
- package/dist/test/test-e2e.js +9 -9
- package/dist/test/test-encode-decode.js +455 -0
- package/dist/test/test-error-handling.js +14 -13
- package/dist/test/test-message.js +261 -64
- package/dist/test/test-osc-internal.js +151 -0
- package/dist/test/test-promises.js +90 -26
- package/dist/test/test-server.js +19 -16
- package/docs/README.md +81 -0
- package/examples/README.md +3 -1
- package/lib/Client.mjs +1 -1
- package/lib/Message.mjs +3 -1
- package/lib/Server.mjs +7 -12
- package/lib/internal/decode.mjs +3 -1
- package/lib/osc.mjs +32 -3
- package/package.json +2 -2
- package/rollup.config.mjs +1 -0
- package/test/test-bundle.mjs +14 -13
- package/test/test-client.mjs +69 -38
- package/test/test-decode.mjs +35 -0
- package/test/test-e2e.mjs +10 -10
- package/test/test-encode-decode.mjs +455 -0
- package/test/test-error-handling.mjs +15 -14
- package/test/test-message.mjs +262 -66
- package/test/test-osc-internal.mjs +151 -0
- package/test/test-promises.mjs +91 -27
- package/test/test-server.mjs +20 -17
- package/types/Message.d.mts.map +1 -1
- package/types/Server.d.mts.map +1 -1
- package/types/internal/decode.d.mts.map +1 -1
- package/types/osc.d.mts.map +1 -1
- package/dist/test/test-getPort.js +0 -20
- package/dist/test/util.js +0 -34
- package/test/test-getPort.mjs +0 -18
- package/test/util.mjs +0 -34
|
@@ -2,29 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
var node_events = require('node:events');
|
|
4
4
|
var tap = require('tap');
|
|
5
|
-
var util = require('./util.js');
|
|
6
5
|
var nodeOsc = require('node-osc');
|
|
7
6
|
|
|
8
|
-
tap.beforeEach(util.bootstrap);
|
|
9
|
-
|
|
10
7
|
tap.test('client: send with promise - array', async (t) => {
|
|
11
|
-
const
|
|
12
|
-
|
|
8
|
+
const server = new nodeOsc.Server(0, '127.0.0.1');
|
|
9
|
+
await node_events.once(server, 'listening');
|
|
10
|
+
const client = new nodeOsc.Client('127.0.0.1', server.port);
|
|
13
11
|
|
|
14
12
|
t.plan(1);
|
|
15
13
|
|
|
14
|
+
server.on('message', (msg) => {
|
|
15
|
+
server.close();
|
|
16
|
+
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
await client.send(['/test', 0, 1, 'testing', true]);
|
|
20
|
+
await client.close();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
tap.test('client: array is not mutated when sent with promise', async (t) => {
|
|
24
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
25
|
+
await node_events.once(oscServer, 'listening');
|
|
26
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
27
|
+
|
|
28
|
+
t.plan(2);
|
|
29
|
+
|
|
30
|
+
const originalArray = ['/test', 0, 1, 'testing', true];
|
|
31
|
+
const expectedArray = ['/test', 0, 1, 'testing', true];
|
|
32
|
+
|
|
16
33
|
oscServer.on('message', (msg) => {
|
|
17
34
|
oscServer.close();
|
|
18
35
|
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
|
|
19
36
|
});
|
|
20
37
|
|
|
21
|
-
await client.send(
|
|
38
|
+
await client.send(originalArray);
|
|
39
|
+
|
|
40
|
+
// Verify the original array was not mutated
|
|
41
|
+
t.same(originalArray, expectedArray, 'Original array should not be mutated');
|
|
42
|
+
|
|
22
43
|
await client.close();
|
|
23
44
|
});
|
|
24
45
|
|
|
25
46
|
tap.test('client: send with promise - string', async (t) => {
|
|
26
|
-
const oscServer = new nodeOsc.Server(
|
|
27
|
-
|
|
47
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
48
|
+
await node_events.once(oscServer, 'listening');
|
|
49
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
28
50
|
|
|
29
51
|
t.plan(1);
|
|
30
52
|
|
|
@@ -38,8 +60,9 @@ tap.test('client: send with promise - string', async (t) => {
|
|
|
38
60
|
});
|
|
39
61
|
|
|
40
62
|
tap.test('client: send with promise - message object', async (t) => {
|
|
41
|
-
const oscServer = new nodeOsc.Server(
|
|
42
|
-
|
|
63
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
64
|
+
await node_events.once(oscServer, 'listening');
|
|
65
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
43
66
|
|
|
44
67
|
t.plan(1);
|
|
45
68
|
|
|
@@ -56,8 +79,9 @@ tap.test('client: send with promise - message object', async (t) => {
|
|
|
56
79
|
});
|
|
57
80
|
|
|
58
81
|
tap.test('client: send with promise - multiple args', async (t) => {
|
|
59
|
-
const oscServer = new nodeOsc.Server(
|
|
60
|
-
|
|
82
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
83
|
+
await node_events.once(oscServer, 'listening');
|
|
84
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
61
85
|
|
|
62
86
|
t.plan(1);
|
|
63
87
|
|
|
@@ -71,12 +95,15 @@ tap.test('client: send with promise - multiple args', async (t) => {
|
|
|
71
95
|
});
|
|
72
96
|
|
|
73
97
|
tap.test('client: send promise rejection on closed socket', async (t) => {
|
|
74
|
-
const
|
|
98
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
99
|
+
await node_events.once(oscServer, 'listening');
|
|
100
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
75
101
|
|
|
76
102
|
t.plan(1);
|
|
77
103
|
|
|
78
104
|
await client.close();
|
|
79
|
-
|
|
105
|
+
await oscServer.close();
|
|
106
|
+
|
|
80
107
|
try {
|
|
81
108
|
await client.send('/boom');
|
|
82
109
|
t.fail('Should have thrown an error');
|
|
@@ -86,8 +113,9 @@ tap.test('client: send promise rejection on closed socket', async (t) => {
|
|
|
86
113
|
});
|
|
87
114
|
|
|
88
115
|
tap.test('client: async/await usage', async (t) => {
|
|
89
|
-
const oscServer = new nodeOsc.Server(
|
|
90
|
-
|
|
116
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
117
|
+
await node_events.once(oscServer, 'listening');
|
|
118
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
91
119
|
|
|
92
120
|
t.plan(1);
|
|
93
121
|
|
|
@@ -103,7 +131,7 @@ tap.test('client: async/await usage', async (t) => {
|
|
|
103
131
|
});
|
|
104
132
|
|
|
105
133
|
tap.test('server: close with promise', async (t) => {
|
|
106
|
-
const oscServer = new nodeOsc.Server(
|
|
134
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
107
135
|
|
|
108
136
|
t.plan(1);
|
|
109
137
|
|
|
@@ -114,7 +142,7 @@ tap.test('server: close with promise', async (t) => {
|
|
|
114
142
|
});
|
|
115
143
|
|
|
116
144
|
tap.test('server: no callback still emits listening event', async (t) => {
|
|
117
|
-
const oscServer = new nodeOsc.Server(
|
|
145
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
118
146
|
|
|
119
147
|
t.plan(1);
|
|
120
148
|
|
|
@@ -125,15 +153,16 @@ tap.test('server: no callback still emits listening event', async (t) => {
|
|
|
125
153
|
});
|
|
126
154
|
|
|
127
155
|
tap.test('client and server: full async/await workflow', async (t) => {
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
t.plan(2);
|
|
156
|
+
t.plan(3);
|
|
157
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
132
158
|
|
|
133
159
|
// Wait for server to be ready
|
|
134
160
|
await node_events.once(oscServer, 'listening');
|
|
135
161
|
t.pass('Server started');
|
|
136
162
|
|
|
163
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
164
|
+
t.pass('Client created');
|
|
165
|
+
|
|
137
166
|
// Set up message handler
|
|
138
167
|
const messageReceived = node_events.once(oscServer, 'message');
|
|
139
168
|
|
|
@@ -148,9 +177,9 @@ tap.test('client and server: full async/await workflow', async (t) => {
|
|
|
148
177
|
});
|
|
149
178
|
|
|
150
179
|
tap.test('client: multiple sends with promises', async (t) => {
|
|
151
|
-
const oscServer = new nodeOsc.Server(
|
|
152
|
-
|
|
153
|
-
|
|
180
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
181
|
+
await node_events.once(oscServer, 'listening');
|
|
182
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
154
183
|
t.plan(3);
|
|
155
184
|
|
|
156
185
|
const messages = [];
|
|
@@ -174,6 +203,8 @@ tap.test('client: multiple sends with promises', async (t) => {
|
|
|
174
203
|
});
|
|
175
204
|
|
|
176
205
|
tap.test('client: close promise rejection on error', async (t) => {
|
|
206
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
207
|
+
await node_events.once(oscServer, 'listening');
|
|
177
208
|
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
|
|
178
209
|
|
|
179
210
|
t.plan(1);
|
|
@@ -203,6 +234,7 @@ tap.test('client: close promise rejection on error', async (t) => {
|
|
|
203
234
|
};
|
|
204
235
|
|
|
205
236
|
try {
|
|
237
|
+
await oscServer.close();
|
|
206
238
|
await client.close();
|
|
207
239
|
t.fail('Should have thrown an error');
|
|
208
240
|
} catch (err) {
|
|
@@ -211,7 +243,7 @@ tap.test('client: close promise rejection on error', async (t) => {
|
|
|
211
243
|
});
|
|
212
244
|
|
|
213
245
|
tap.test('server: close promise rejection on error', async (t) => {
|
|
214
|
-
const oscServer = new nodeOsc.Server(
|
|
246
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
215
247
|
|
|
216
248
|
t.plan(1);
|
|
217
249
|
|
|
@@ -248,3 +280,35 @@ tap.test('server: close promise rejection on error', async (t) => {
|
|
|
248
280
|
t.equal(err.code, 'MOCK_ERROR', 'Should reject with mock error');
|
|
249
281
|
}
|
|
250
282
|
});
|
|
283
|
+
|
|
284
|
+
tap.test('client: send promise rejection on send error', async (t) => {
|
|
285
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
286
|
+
await node_events.once(oscServer, 'listening');
|
|
287
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
288
|
+
|
|
289
|
+
t.plan(1);
|
|
290
|
+
|
|
291
|
+
// Mock the socket's send method to simulate an error
|
|
292
|
+
const originalSend = client._sock.send;
|
|
293
|
+
client._sock.send = function(msg, offset, length, port, address, callback) {
|
|
294
|
+
// Simulate an error being passed to callback
|
|
295
|
+
const err = new Error('Mock send error');
|
|
296
|
+
err.code = 'MOCK_SEND_ERROR';
|
|
297
|
+
if (callback) {
|
|
298
|
+
setImmediate(() => callback(err));
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
t.teardown(async () => {
|
|
303
|
+
client._sock.send = originalSend;
|
|
304
|
+
await client.close();
|
|
305
|
+
await oscServer.close();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
await client.send('/test', 'data');
|
|
310
|
+
t.fail('Should have thrown an error');
|
|
311
|
+
} catch (err) {
|
|
312
|
+
t.equal(err.code, 'MOCK_SEND_ERROR', 'Should reject with mock send error');
|
|
313
|
+
}
|
|
314
|
+
});
|
package/dist/test/test-server.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
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
|
-
tap.
|
|
8
|
-
|
|
9
|
-
tap.test('server: create and close', (t) => {
|
|
7
|
+
tap.test('server: create and close', async (t) => {
|
|
10
8
|
t.plan(1);
|
|
11
|
-
const oscServer = new nodeOsc.Server(
|
|
9
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
10
|
+
await node_events.once(oscServer, 'listening');
|
|
12
11
|
oscServer.close((err) => {
|
|
13
12
|
t.error(err);
|
|
14
13
|
});
|
|
15
14
|
});
|
|
16
15
|
|
|
17
|
-
tap.test('server: listen to message', (t) => {
|
|
18
|
-
const oscServer = new nodeOsc.Server(
|
|
19
|
-
|
|
16
|
+
tap.test('server: listen to message', async (t) => {
|
|
17
|
+
const oscServer = new nodeOsc.Server(0);
|
|
18
|
+
await node_events.once(oscServer, 'listening');
|
|
19
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
20
20
|
|
|
21
21
|
t.plan(3);
|
|
22
22
|
|
|
@@ -38,9 +38,10 @@ tap.test('server: listen to message', (t) => {
|
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
tap.test('server: no defined host', (t) => {
|
|
42
|
-
const oscServer = new nodeOsc.Server(
|
|
43
|
-
|
|
41
|
+
tap.test('server: no defined host', async (t) => {
|
|
42
|
+
const oscServer = new nodeOsc.Server(0);
|
|
43
|
+
await node_events.once(oscServer, 'listening');
|
|
44
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
44
45
|
|
|
45
46
|
t.plan(3);
|
|
46
47
|
|
|
@@ -62,12 +63,13 @@ tap.test('server: no defined host', (t) => {
|
|
|
62
63
|
});
|
|
63
64
|
});
|
|
64
65
|
|
|
65
|
-
tap.test('server: callback as second arg', (t) => {
|
|
66
|
+
tap.test('server: callback as second arg', async (t) => {
|
|
66
67
|
t.plan(4);
|
|
67
|
-
const oscServer = new nodeOsc.Server(
|
|
68
|
+
const oscServer = new nodeOsc.Server(0, () => {
|
|
68
69
|
t.ok('callback called');
|
|
69
70
|
});
|
|
70
|
-
|
|
71
|
+
await node_events.once(oscServer, 'listening');
|
|
72
|
+
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
|
|
71
73
|
|
|
72
74
|
t.teardown(() => {
|
|
73
75
|
oscServer.close();
|
|
@@ -87,9 +89,10 @@ tap.test('server: callback as second arg', (t) => {
|
|
|
87
89
|
});
|
|
88
90
|
});
|
|
89
91
|
|
|
90
|
-
tap.test('server: bad message', (t) => {
|
|
92
|
+
tap.test('server: bad message', async (t) => {
|
|
91
93
|
t.plan(2);
|
|
92
|
-
const oscServer = new nodeOsc.Server(
|
|
94
|
+
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
|
|
95
|
+
await node_events.once(oscServer, 'listening');
|
|
93
96
|
t.throws(() => {
|
|
94
97
|
oscServer._sock.emit('message', 'whoops');
|
|
95
98
|
}, /can't decode incoming message:/);
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# node-osc Documentation
|
|
2
|
+
|
|
3
|
+
Welcome to the node-osc documentation! This directory contains comprehensive documentation for the node-osc library.
|
|
4
|
+
|
|
5
|
+
## Documentation Overview
|
|
6
|
+
|
|
7
|
+
### 📚 [API Reference](./API.md)
|
|
8
|
+
**Complete API documentation for all classes and functions**
|
|
9
|
+
|
|
10
|
+
Auto-generated from JSDoc comments in the source code. This is your reference for:
|
|
11
|
+
- All classes: `Server`, `Client`, `Message`, `Bundle`
|
|
12
|
+
- All methods and their parameters
|
|
13
|
+
- Low-level functions: `encode()` and `decode()`
|
|
14
|
+
- Return types and error conditions
|
|
15
|
+
- Code examples for each API
|
|
16
|
+
|
|
17
|
+
> **Note:** This file is automatically generated. To update it, edit the JSDoc comments in the source code and run `npm run docs`.
|
|
18
|
+
|
|
19
|
+
### 📘 [Usage Guide](./GUIDE.md)
|
|
20
|
+
**Best practices, patterns, and troubleshooting**
|
|
21
|
+
|
|
22
|
+
A comprehensive guide covering:
|
|
23
|
+
- Event handling patterns
|
|
24
|
+
- Error handling strategies
|
|
25
|
+
- OSC type system details
|
|
26
|
+
- Best practices for production use
|
|
27
|
+
- Troubleshooting common issues
|
|
28
|
+
- Advanced topics like custom transports and performance optimization
|
|
29
|
+
|
|
30
|
+
## Quick Navigation
|
|
31
|
+
|
|
32
|
+
**New to node-osc?**
|
|
33
|
+
1. Start with the [main README](../README.md) for a quick introduction and installation
|
|
34
|
+
2. Try the [examples](../examples/) to see working code
|
|
35
|
+
3. Read the [Usage Guide](./GUIDE.md) to learn best practices
|
|
36
|
+
4. Reference the [API documentation](./API.md) as needed
|
|
37
|
+
|
|
38
|
+
**Looking for something specific?**
|
|
39
|
+
- **How to send/receive messages** → [API Reference](./API.md) (Server and Client sections)
|
|
40
|
+
- **How to handle errors** → [Usage Guide](./GUIDE.md#error-handling)
|
|
41
|
+
- **Type system and data types** → [Usage Guide](./GUIDE.md#type-system)
|
|
42
|
+
- **Working with bundles** → [API Reference](./API.md#bundle)
|
|
43
|
+
- **Troubleshooting** → [Usage Guide](./GUIDE.md#troubleshooting)
|
|
44
|
+
- **Code examples** → [Examples directory](../examples/)
|
|
45
|
+
- **Advanced use cases** → [Usage Guide](./GUIDE.md#advanced-topics)
|
|
46
|
+
|
|
47
|
+
## Additional Resources
|
|
48
|
+
|
|
49
|
+
- **[Examples](../examples/)** - Working code examples for various use cases
|
|
50
|
+
- **[Main README](../README.md)** - Quick start and project overview
|
|
51
|
+
- **[OSC Specification](http://opensoundcontrol.org/spec-1_0)** - Official OSC protocol documentation
|
|
52
|
+
|
|
53
|
+
## Contributing to Documentation
|
|
54
|
+
|
|
55
|
+
### Updating API Documentation
|
|
56
|
+
|
|
57
|
+
The API documentation is automatically generated from JSDoc comments:
|
|
58
|
+
|
|
59
|
+
1. Edit JSDoc comments in the source files (`lib/**/*.mjs`)
|
|
60
|
+
2. Run `npm run docs` to regenerate `API.md`
|
|
61
|
+
3. Review the changes and commit
|
|
62
|
+
|
|
63
|
+
### Updating the Usage Guide
|
|
64
|
+
|
|
65
|
+
The Usage Guide (`GUIDE.md`) is manually maintained. When editing:
|
|
66
|
+
|
|
67
|
+
- Keep it focused on patterns, best practices, and how-to content
|
|
68
|
+
- Avoid duplicating API details (link to API.md instead)
|
|
69
|
+
- Include practical code examples
|
|
70
|
+
- Update the table of contents if adding new sections
|
|
71
|
+
|
|
72
|
+
## Documentation Structure Philosophy
|
|
73
|
+
|
|
74
|
+
Our documentation is organized to minimize duplication while maximizing usefulness:
|
|
75
|
+
|
|
76
|
+
- **README.md** (main) → Quick start, basic examples, installation
|
|
77
|
+
- **API.md** → Complete API reference with all technical details
|
|
78
|
+
- **GUIDE.md** → How to use the library effectively, patterns, and troubleshooting
|
|
79
|
+
- **examples/** → Working code you can run and learn from
|
|
80
|
+
|
|
81
|
+
This structure ensures you can find what you need without reading through repeated content.
|
package/examples/README.md
CHANGED
|
@@ -115,5 +115,7 @@ Demonstrates:
|
|
|
115
115
|
## Further Reading
|
|
116
116
|
|
|
117
117
|
- [Main README](../README.md) - Quick start guide
|
|
118
|
-
- [
|
|
118
|
+
- [Documentation Hub](../docs/) - Complete documentation with navigation guide
|
|
119
|
+
- [API Reference](../docs/API.md) - Complete API reference
|
|
120
|
+
- [Usage Guide](../docs/GUIDE.md) - Best practices and troubleshooting
|
|
119
121
|
- [OSC Specification](http://opensoundcontrol.org/spec-1_0) - Learn about the OSC protocol
|
package/lib/Client.mjs
CHANGED
package/lib/Message.mjs
CHANGED
|
@@ -109,7 +109,9 @@ class Message {
|
|
|
109
109
|
let argOut;
|
|
110
110
|
switch (typeof arg) {
|
|
111
111
|
case 'object':
|
|
112
|
-
if (arg
|
|
112
|
+
if (Buffer.isBuffer(arg)) {
|
|
113
|
+
this.args.push(arg);
|
|
114
|
+
} else if (arg instanceof Array) {
|
|
113
115
|
arg.forEach(a => this.append(a));
|
|
114
116
|
} else if (arg.type) {
|
|
115
117
|
if (typeTags[arg.type]) arg.type = typeTags[arg.type];
|
package/lib/Server.mjs
CHANGED
|
@@ -90,18 +90,13 @@ class Server extends EventEmitter {
|
|
|
90
90
|
});
|
|
91
91
|
this._sock.bind(port, host);
|
|
92
92
|
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
// For promise support, still emit the event but don't require a callback
|
|
101
|
-
this._sock.on('listening', () => {
|
|
102
|
-
this.emit('listening');
|
|
103
|
-
});
|
|
104
|
-
}
|
|
93
|
+
// Update port and emit listening event when socket is ready
|
|
94
|
+
this._sock.on('listening', () => {
|
|
95
|
+
// Update port with actual bound port (important when using port 0)
|
|
96
|
+
this.port = this._sock.address().port;
|
|
97
|
+
this.emit('listening');
|
|
98
|
+
if (cb) cb();
|
|
99
|
+
});
|
|
105
100
|
|
|
106
101
|
this._sock.on('message', (msg, rinfo) => {
|
|
107
102
|
try {
|
package/lib/internal/decode.mjs
CHANGED
|
@@ -3,7 +3,8 @@ import { decode } from '../osc.mjs';
|
|
|
3
3
|
function sanitizeMessage(decoded) {
|
|
4
4
|
const message = [];
|
|
5
5
|
message.push(decoded.address);
|
|
6
|
-
decoded.args
|
|
6
|
+
const args = decoded.args ?? [];
|
|
7
|
+
args.forEach(arg => {
|
|
7
8
|
message.push(arg.value);
|
|
8
9
|
});
|
|
9
10
|
return message;
|
|
@@ -13,6 +14,7 @@ function sanitizeBundle(decoded) {
|
|
|
13
14
|
decoded.elements = decoded.elements.map(element => {
|
|
14
15
|
if (element.oscType === 'bundle') return sanitizeBundle(element);
|
|
15
16
|
else if (element.oscType === 'message') return sanitizeMessage(element);
|
|
17
|
+
throw new Error('Malformed Packet');
|
|
16
18
|
});
|
|
17
19
|
return decoded;
|
|
18
20
|
}
|
package/lib/osc.mjs
CHANGED
|
@@ -7,8 +7,9 @@ import { Buffer } from 'node:buffer';
|
|
|
7
7
|
|
|
8
8
|
function padString(str) {
|
|
9
9
|
const nullTerminated = str + '\0';
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const byteLength = Buffer.byteLength(nullTerminated);
|
|
11
|
+
const padding = (4 - (byteLength % 4)) % 4;
|
|
12
|
+
return nullTerminated + '\0'.repeat(padding);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
function readString(buffer, offset) {
|
|
@@ -16,6 +17,9 @@ function readString(buffer, offset) {
|
|
|
16
17
|
while (end < buffer.length && buffer[end] !== 0) {
|
|
17
18
|
end++;
|
|
18
19
|
}
|
|
20
|
+
if (end >= buffer.length) {
|
|
21
|
+
throw new Error('Malformed Packet: Missing null terminator for string');
|
|
22
|
+
}
|
|
19
23
|
const str = buffer.subarray(offset, end).toString('utf8');
|
|
20
24
|
// Find next 4-byte boundary
|
|
21
25
|
const paddedLength = Math.ceil((end - offset + 1) / 4) * 4;
|
|
@@ -29,6 +33,9 @@ function writeInt32(value) {
|
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
function readInt32(buffer, offset) {
|
|
36
|
+
if (offset + 4 > buffer.length) {
|
|
37
|
+
throw new Error('Malformed Packet: Not enough bytes for int32');
|
|
38
|
+
}
|
|
32
39
|
const value = buffer.readInt32BE(offset);
|
|
33
40
|
return { value, offset: offset + 4 };
|
|
34
41
|
}
|
|
@@ -40,6 +47,9 @@ function writeFloat32(value) {
|
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
function readFloat32(buffer, offset) {
|
|
50
|
+
if (offset + 4 > buffer.length) {
|
|
51
|
+
throw new Error('Malformed Packet: Not enough bytes for float32');
|
|
52
|
+
}
|
|
43
53
|
const value = buffer.readFloatBE(offset);
|
|
44
54
|
return { value, offset: offset + 4 };
|
|
45
55
|
}
|
|
@@ -55,9 +65,18 @@ function writeBlob(value) {
|
|
|
55
65
|
function readBlob(buffer, offset) {
|
|
56
66
|
const lengthResult = readInt32(buffer, offset);
|
|
57
67
|
const length = lengthResult.value;
|
|
68
|
+
if (length < 0) {
|
|
69
|
+
throw new Error('Malformed Packet: Invalid blob length');
|
|
70
|
+
}
|
|
71
|
+
if (lengthResult.offset + length > buffer.length) {
|
|
72
|
+
throw new Error('Malformed Packet: Not enough bytes for blob');
|
|
73
|
+
}
|
|
58
74
|
const data = buffer.subarray(lengthResult.offset, lengthResult.offset + length);
|
|
59
75
|
const padding = 4 - (length % 4);
|
|
60
76
|
const nextOffset = lengthResult.offset + length + (padding === 4 ? 0 : padding);
|
|
77
|
+
if (nextOffset > buffer.length) {
|
|
78
|
+
throw new Error('Malformed Packet: Not enough bytes for blob padding');
|
|
79
|
+
}
|
|
61
80
|
return { value: data, offset: nextOffset };
|
|
62
81
|
}
|
|
63
82
|
|
|
@@ -65,7 +84,11 @@ function writeTimeTag(value) {
|
|
|
65
84
|
// For now, treat timetag as a double (8 bytes)
|
|
66
85
|
// OSC timetag is 64-bit: 32-bit seconds since 1900, 32-bit fractional
|
|
67
86
|
const buffer = Buffer.alloc(8);
|
|
68
|
-
if (
|
|
87
|
+
if (value === 0 || value === null || value === undefined) {
|
|
88
|
+
// Immediate execution
|
|
89
|
+
buffer.writeUInt32BE(0, 0);
|
|
90
|
+
buffer.writeUInt32BE(1, 4);
|
|
91
|
+
} else if (typeof value === 'number') {
|
|
69
92
|
// Convert to OSC timetag format
|
|
70
93
|
const seconds = Math.floor(value);
|
|
71
94
|
const fraction = Math.floor((value - seconds) * 0x100000000);
|
|
@@ -80,6 +103,9 @@ function writeTimeTag(value) {
|
|
|
80
103
|
}
|
|
81
104
|
|
|
82
105
|
function readTimeTag(buffer, offset) {
|
|
106
|
+
if (offset + 8 > buffer.length) {
|
|
107
|
+
throw new Error('Malformed Packet: Not enough bytes for timetag');
|
|
108
|
+
}
|
|
83
109
|
const seconds = buffer.readUInt32BE(offset);
|
|
84
110
|
const fraction = buffer.readUInt32BE(offset + 4);
|
|
85
111
|
|
|
@@ -375,6 +401,9 @@ function decodeBundleFromBuffer(buffer) {
|
|
|
375
401
|
const sizeResult = readInt32(buffer, offset);
|
|
376
402
|
const size = sizeResult.value;
|
|
377
403
|
offset = sizeResult.offset;
|
|
404
|
+
if (size <= 0 || offset + size > buffer.length) {
|
|
405
|
+
throw new Error('Malformed Packet');
|
|
406
|
+
}
|
|
378
407
|
|
|
379
408
|
// Read element data
|
|
380
409
|
const elementBuffer = buffer.subarray(offset, offset + size);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-osc",
|
|
3
3
|
"description": "pyOSC inspired library for sending and receiving OSC messages",
|
|
4
|
-
"version": "11.2.
|
|
4
|
+
"version": "11.2.2",
|
|
5
5
|
"exports": {
|
|
6
6
|
"types": "./types/index.d.mts",
|
|
7
7
|
"require": "./dist/lib/index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"build:types": "tsc",
|
|
29
29
|
"docs": "node scripts/generate-docs.mjs",
|
|
30
30
|
"prepublishOnly": "npm run build",
|
|
31
|
-
"lint": "eslint \"lib/**/*.mjs\" \"test/test-*.mjs\"
|
|
31
|
+
"lint": "eslint \"lib/**/*.mjs\" \"test/test-*.mjs\" \"examples/*.js\" \"examples/*.mjs\" \"scripts/*.mjs\" rollup.config.mjs",
|
|
32
32
|
"test": "npm run lint && npm run build && npm run test:esm && npm run test:cjs",
|
|
33
33
|
"test:esm": "tap test/test-*.mjs",
|
|
34
34
|
"test:cjs": "tap dist/test/test-*.js"
|
package/rollup.config.mjs
CHANGED
package/test/test-bundle.mjs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { once } from 'node:events';
|
|
2
|
+
import { test } from 'tap';
|
|
2
3
|
|
|
3
4
|
import { Client, Server, Bundle } from 'node-osc';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const server = new Server(t.context.port, '127.0.0.1');
|
|
10
|
-
const client = new Client('127.0.0.1', t.context.port);
|
|
6
|
+
test('bundle: verbose bundle', async (t) => {
|
|
7
|
+
const server = new Server(0, '127.0.0.1');
|
|
8
|
+
await once(server, 'listening');
|
|
9
|
+
const client = new Client('127.0.0.1', server.port);
|
|
11
10
|
|
|
12
11
|
t.plan(2);
|
|
13
12
|
|
|
@@ -34,9 +33,10 @@ test('bundle: verbose bundle', (t) => {
|
|
|
34
33
|
}));
|
|
35
34
|
});
|
|
36
35
|
|
|
37
|
-
test('bundle: array syntax', (t) => {
|
|
38
|
-
const server = new Server(
|
|
39
|
-
|
|
36
|
+
test('bundle: array syntax', async (t) => {
|
|
37
|
+
const server = new Server(0, '127.0.0.1');
|
|
38
|
+
await once(server, 'listening');
|
|
39
|
+
const client = new Client('127.0.0.1', server.port);
|
|
40
40
|
|
|
41
41
|
t.plan(2);
|
|
42
42
|
|
|
@@ -56,9 +56,10 @@ test('bundle: array syntax', (t) => {
|
|
|
56
56
|
));
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
test('bundle: nested bundle', (t) => {
|
|
60
|
-
const server = new Server(
|
|
61
|
-
|
|
59
|
+
test('bundle: nested bundle', async (t) => {
|
|
60
|
+
const server = new Server(0, '127.0.0.1');
|
|
61
|
+
await once(server, 'listening');
|
|
62
|
+
const client = new Client('127.0.0.1', server.port);
|
|
62
63
|
|
|
63
64
|
t.plan(4);
|
|
64
65
|
|