node-osc 11.1.0 → 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.
- package/.gitattributes +11 -0
- package/.github/workflows/bump-version.yml +2 -0
- package/.github/workflows/nodejs.yml +3 -0
- package/LICENSE +201 -165
- package/README.md +135 -42
- package/dist/lib/Bundle.js +66 -0
- package/dist/lib/Client.js +137 -22
- package/dist/lib/Message.js +87 -1
- package/dist/lib/Server.js +117 -6
- package/dist/lib/index.js +3 -0
- package/dist/lib/internal/decode.js +4 -4
- package/{lib/internal/osc.mjs → dist/lib/osc.js} +94 -23
- package/dist/test/lib/osc.js +395 -0
- package/dist/test/test-client.js +152 -0
- package/dist/test/test-e2e.js +9 -3
- package/dist/test/test-encode-decode.js +849 -0
- package/dist/test/test-error-handling.js +116 -0
- package/dist/test/test-osc-internal.js +399 -41
- package/dist/test/test-promises.js +250 -0
- package/dist/test/test-types.js +42 -0
- package/dist/test/util.js +15 -8
- package/docs/API.md +477 -0
- package/docs/GUIDE.md +605 -0
- package/examples/README.md +119 -0
- package/examples/async-await.mjs +57 -0
- package/examples/bundle-example.mjs +92 -0
- package/examples/client.js +22 -5
- package/examples/error-handling.mjs +152 -0
- package/examples/esm.mjs +21 -0
- package/examples/server.js +16 -0
- package/jsdoc.json +16 -0
- package/lib/Bundle.mjs +66 -0
- package/lib/Client.mjs +137 -22
- package/lib/Message.mjs +87 -1
- package/lib/Server.mjs +117 -6
- package/lib/index.mjs +1 -0
- package/lib/internal/decode.mjs +4 -4
- package/{dist/lib/internal/osc.js → lib/osc.mjs} +71 -7
- package/package.json +12 -10
- package/rollup.config.mjs +48 -37
- package/scripts/generate-docs.mjs +229 -0
- package/test/fixtures/types/test-cjs-types.ts +19 -0
- package/test/fixtures/types/test-esm-types.ts +35 -0
- package/test/fixtures/types/tsconfig-cjs.test.json +17 -0
- package/test/fixtures/types/tsconfig-esm.test.json +17 -0
- package/test/test-bundle.mjs +0 -1
- package/test/test-client.mjs +152 -0
- package/test/test-e2e.mjs +9 -3
- package/test/test-encode-decode.mjs +847 -0
- package/test/test-error-handling.mjs +115 -0
- package/test/test-osc-internal.mjs +400 -42
- package/test/test-promises.mjs +249 -0
- package/test/test-types.mjs +39 -0
- package/test/util.mjs +15 -8
- package/tsconfig.json +45 -0
- package/types/Bundle.d.mts +70 -0
- package/types/Bundle.d.mts.map +1 -0
- package/types/Client.d.mts +101 -0
- package/types/Client.d.mts.map +1 -0
- package/types/Message.d.mts +84 -0
- package/types/Message.d.mts.map +1 -0
- package/types/Server.d.mts +98 -0
- package/types/Server.d.mts.map +1 -0
- package/types/index.d.mts +6 -0
- package/types/index.d.mts.map +1 -0
- package/types/internal/decode.d.mts +4 -0
- package/types/internal/decode.d.mts.map +1 -0
- package/types/osc.d.mts +66 -0
- package/types/osc.d.mts.map +1 -0
package/lib/Client.mjs
CHANGED
|
@@ -1,42 +1,92 @@
|
|
|
1
1
|
import { createSocket } from 'node:dgram';
|
|
2
|
-
import {
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
import { encode } from './osc.mjs';
|
|
3
4
|
import Message from './Message.mjs';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
/**
|
|
7
|
+
* OSC Client for sending messages and bundles over UDP.
|
|
8
|
+
*
|
|
9
|
+
* Extends EventEmitter and emits the following events:
|
|
10
|
+
* - 'error': Emitted when a socket error occurs
|
|
11
|
+
*
|
|
12
|
+
* @class
|
|
13
|
+
* @extends EventEmitter
|
|
14
|
+
* @example
|
|
15
|
+
* // Create a client
|
|
16
|
+
* const client = new Client('127.0.0.1', 3333);
|
|
17
|
+
*
|
|
18
|
+
* // Send a message with callback
|
|
19
|
+
* client.send('/oscAddress', 200, (err) => {
|
|
20
|
+
* if (err) console.error(err);
|
|
21
|
+
* client.close();
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Send a message with async/await
|
|
26
|
+
* const client = new Client('127.0.0.1', 3333);
|
|
27
|
+
* await client.send('/oscAddress', 200);
|
|
28
|
+
* await client.close();
|
|
29
|
+
*/
|
|
30
|
+
class Client extends EventEmitter {
|
|
31
|
+
/**
|
|
32
|
+
* Create an OSC Client.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} host - The hostname or IP address of the OSC server.
|
|
35
|
+
* @param {number} port - The port number of the OSC server.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const client = new Client('127.0.0.1', 3333);
|
|
39
|
+
*/
|
|
6
40
|
constructor(host, port) {
|
|
41
|
+
super();
|
|
7
42
|
this.host = host;
|
|
8
43
|
this.port = port;
|
|
9
44
|
this._sock = createSocket({
|
|
10
45
|
type: 'udp4',
|
|
11
46
|
reuseAddr: true
|
|
12
47
|
});
|
|
48
|
+
|
|
49
|
+
this._sock.on('error', (err) => {
|
|
50
|
+
this.emit('error', err);
|
|
51
|
+
});
|
|
13
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Close the client socket.
|
|
55
|
+
*
|
|
56
|
+
* This method can be used with either a callback or as a Promise.
|
|
57
|
+
*
|
|
58
|
+
* @param {Function} [cb] - Optional callback function called when socket is closed.
|
|
59
|
+
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* // With callback
|
|
63
|
+
* client.close((err) => {
|
|
64
|
+
* if (err) console.error(err);
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* // With async/await
|
|
69
|
+
* await client.close();
|
|
70
|
+
*/
|
|
14
71
|
close(cb) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
callback = () => {};
|
|
72
|
+
if (cb) {
|
|
73
|
+
this._sock.close(cb);
|
|
74
|
+
} else {
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
this._sock.close((err) => {
|
|
77
|
+
if (err) reject(err);
|
|
78
|
+
else resolve();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
25
81
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
message = {
|
|
29
|
-
address: message[0],
|
|
30
|
-
args: message.splice(1)
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
82
|
+
}
|
|
83
|
+
_performSend(message, args, callback) {
|
|
34
84
|
let mes;
|
|
35
85
|
let buf;
|
|
36
86
|
try {
|
|
37
87
|
switch (typeof message) {
|
|
38
88
|
case 'object':
|
|
39
|
-
buf =
|
|
89
|
+
buf = encode(message);
|
|
40
90
|
this._sock.send(buf, 0, buf.length, this.port, this.host, callback);
|
|
41
91
|
break;
|
|
42
92
|
case 'string':
|
|
@@ -44,7 +94,7 @@ class Client {
|
|
|
44
94
|
for (let i = 1; i < args.length; i++) {
|
|
45
95
|
mes.append(args[i]);
|
|
46
96
|
}
|
|
47
|
-
buf =
|
|
97
|
+
buf = encode(mes);
|
|
48
98
|
this._sock.send(buf, 0, buf.length, this.port, this.host, callback);
|
|
49
99
|
break;
|
|
50
100
|
default:
|
|
@@ -58,6 +108,71 @@ class Client {
|
|
|
58
108
|
callback(error);
|
|
59
109
|
}
|
|
60
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Send an OSC message or bundle to the server.
|
|
113
|
+
*
|
|
114
|
+
* This method can be used with either a callback or as a Promise.
|
|
115
|
+
* Messages can be sent in several formats:
|
|
116
|
+
* - As separate arguments: address followed by values
|
|
117
|
+
* - As a Message or Bundle object
|
|
118
|
+
* - As an array: [address, ...values]
|
|
119
|
+
*
|
|
120
|
+
* @param {...*} args - The message to send. Can be:
|
|
121
|
+
* - (address: string, ...values: any[], callback?: Function)
|
|
122
|
+
* - (message: Message|Bundle, callback?: Function)
|
|
123
|
+
* - (array: Array, callback?: Function)
|
|
124
|
+
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
|
|
125
|
+
*
|
|
126
|
+
* @throws {TypeError} If the message format is invalid.
|
|
127
|
+
* @throws {ReferenceError} If attempting to send on a closed socket.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* // Send with address and arguments
|
|
131
|
+
* client.send('/oscAddress', 200, 'hello', (err) => {
|
|
132
|
+
* if (err) console.error(err);
|
|
133
|
+
* });
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* // Send with async/await
|
|
137
|
+
* await client.send('/oscAddress', 200, 'hello');
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* // Send a Message object
|
|
141
|
+
* const msg = new Message('/test', 1, 2, 3);
|
|
142
|
+
* await client.send(msg);
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* // Send a Bundle object
|
|
146
|
+
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
|
|
147
|
+
* await client.send(bundle);
|
|
148
|
+
*/
|
|
149
|
+
send(...args) {
|
|
150
|
+
let message = args[0];
|
|
151
|
+
let callback;
|
|
152
|
+
|
|
153
|
+
// Convert array syntax to message object
|
|
154
|
+
if (message instanceof Array) {
|
|
155
|
+
message = {
|
|
156
|
+
address: message[0],
|
|
157
|
+
args: message.splice(1)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (typeof args[args.length - 1] === 'function') {
|
|
162
|
+
callback = args.pop();
|
|
163
|
+
this._performSend(message, args, callback);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
// No callback provided, return a Promise
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
callback = (err) => {
|
|
169
|
+
if (err) reject(err);
|
|
170
|
+
else resolve();
|
|
171
|
+
};
|
|
172
|
+
this._performSend(message, args, callback);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
61
176
|
}
|
|
62
177
|
|
|
63
178
|
export default Client;
|
package/lib/Message.mjs
CHANGED
|
@@ -2,23 +2,109 @@ const typeTags = {
|
|
|
2
2
|
s: 'string',
|
|
3
3
|
f: 'float',
|
|
4
4
|
i: 'integer',
|
|
5
|
-
b: 'blob'
|
|
5
|
+
b: 'blob',
|
|
6
|
+
m: 'midi'
|
|
6
7
|
};
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Represents a typed argument for an OSC message.
|
|
11
|
+
*
|
|
12
|
+
* @class
|
|
13
|
+
* @private
|
|
14
|
+
*/
|
|
8
15
|
class Argument {
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} type - The type of the argument (string, float, integer, blob, boolean).
|
|
18
|
+
* @param {*} value - The value of the argument.
|
|
19
|
+
*/
|
|
9
20
|
constructor(type, value) {
|
|
10
21
|
this.type = type;
|
|
11
22
|
this.value = value;
|
|
12
23
|
}
|
|
13
24
|
}
|
|
14
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Represents an OSC message with an address and arguments.
|
|
28
|
+
*
|
|
29
|
+
* OSC messages consist of an address pattern (string starting with '/')
|
|
30
|
+
* and zero or more arguments of various types.
|
|
31
|
+
*
|
|
32
|
+
* @class
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Create a message with constructor arguments
|
|
36
|
+
* const msg = new Message('/test', 1, 2, 'hello');
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // Create a message and append arguments
|
|
40
|
+
* const msg = new Message('/test');
|
|
41
|
+
* msg.append(1);
|
|
42
|
+
* msg.append('hello');
|
|
43
|
+
* msg.append(3.14);
|
|
44
|
+
*/
|
|
15
45
|
class Message {
|
|
46
|
+
/**
|
|
47
|
+
* Create an OSC Message.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} address - The OSC address pattern (e.g., '/oscillator/frequency').
|
|
50
|
+
* @param {...*} args - Optional arguments to include in the message.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* const msg = new Message('/test');
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* const msg = new Message('/test', 1, 2, 3);
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* const msg = new Message('/synth', 'note', 60, 0.5);
|
|
60
|
+
*/
|
|
16
61
|
constructor(address, ...args) {
|
|
17
62
|
this.oscType = 'message';
|
|
18
63
|
this.address = address;
|
|
19
64
|
this.args = args;
|
|
20
65
|
}
|
|
21
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Append an argument to the message.
|
|
69
|
+
*
|
|
70
|
+
* Automatically detects the type based on the JavaScript type:
|
|
71
|
+
* - Integers are encoded as OSC integers
|
|
72
|
+
* - Floats are encoded as OSC floats
|
|
73
|
+
* - Strings are encoded as OSC strings
|
|
74
|
+
* - Booleans are encoded as OSC booleans
|
|
75
|
+
* - Buffers are encoded as OSC blobs
|
|
76
|
+
* - Arrays are recursively appended
|
|
77
|
+
* - Objects with a 'type' property are used as-is
|
|
78
|
+
*
|
|
79
|
+
* @param {*} arg - The argument to append. Can be:
|
|
80
|
+
* - A primitive value (number, string, boolean)
|
|
81
|
+
* - A Buffer (encoded as blob)
|
|
82
|
+
* - An array of values (will be recursively appended)
|
|
83
|
+
* - An object with 'type' and 'value' properties for explicit type control
|
|
84
|
+
*
|
|
85
|
+
* @throws {Error} If the argument type cannot be encoded.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* const msg = new Message('/test');
|
|
89
|
+
* msg.append(42); // Integer
|
|
90
|
+
* msg.append(3.14); // Float
|
|
91
|
+
* msg.append('hello'); // String
|
|
92
|
+
* msg.append(true); // Boolean
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* // Append multiple values at once
|
|
96
|
+
* msg.append([1, 2, 3]);
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* // Explicitly specify type
|
|
100
|
+
* msg.append({ type: 'float', value: 42 });
|
|
101
|
+
* msg.append({ type: 'blob', value: Buffer.from('data') });
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* // MIDI messages (4 bytes: port, status, data1, data2)
|
|
105
|
+
* msg.append({ type: 'midi', value: { port: 0, status: 144, data1: 60, data2: 127 } });
|
|
106
|
+
* msg.append({ type: 'm', value: Buffer.from([0, 144, 60, 127]) });
|
|
107
|
+
*/
|
|
22
108
|
append(arg) {
|
|
23
109
|
let argOut;
|
|
24
110
|
switch (typeof arg) {
|
package/lib/Server.mjs
CHANGED
|
@@ -3,14 +3,84 @@ import { EventEmitter } from 'node:events';
|
|
|
3
3
|
|
|
4
4
|
import decode from '#decode';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* OSC Server for receiving messages and bundles over UDP.
|
|
8
|
+
*
|
|
9
|
+
* Emits the following events:
|
|
10
|
+
* - 'listening': Emitted when the server starts listening
|
|
11
|
+
* - 'message': Emitted when an OSC message is received (receives msg array and rinfo object)
|
|
12
|
+
* - 'bundle': Emitted when an OSC bundle is received (receives bundle object and rinfo object)
|
|
13
|
+
* - 'error': Emitted when a socket error or decoding error occurs (receives error and rinfo)
|
|
14
|
+
* - Address-specific events: Emitted for each message address (e.g., '/test')
|
|
15
|
+
*
|
|
16
|
+
* @class
|
|
17
|
+
* @extends EventEmitter
|
|
18
|
+
*
|
|
19
|
+
* @fires Server#listening
|
|
20
|
+
* @fires Server#message
|
|
21
|
+
* @fires Server#bundle
|
|
22
|
+
* @fires Server#error
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Create and listen for messages
|
|
26
|
+
* const server = new Server(3333, '0.0.0.0', () => {
|
|
27
|
+
* console.log('Server is listening');
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* server.on('message', (msg, rinfo) => {
|
|
31
|
+
* console.log('Message:', msg);
|
|
32
|
+
* console.log('From:', rinfo.address, rinfo.port);
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Using async/await with events.once
|
|
37
|
+
* import { once } from 'node:events';
|
|
38
|
+
*
|
|
39
|
+
* const server = new Server(3333, '0.0.0.0');
|
|
40
|
+
* await once(server, 'listening');
|
|
41
|
+
*
|
|
42
|
+
* server.on('message', (msg) => {
|
|
43
|
+
* console.log('Message:', msg);
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // Listen for specific OSC addresses
|
|
48
|
+
* server.on('/note', (msg) => {
|
|
49
|
+
* const [address, pitch, velocity] = msg;
|
|
50
|
+
* console.log(`Note: ${pitch}, Velocity: ${velocity}`);
|
|
51
|
+
* });
|
|
52
|
+
*/
|
|
6
53
|
class Server extends EventEmitter {
|
|
54
|
+
/**
|
|
55
|
+
* Create an OSC Server.
|
|
56
|
+
*
|
|
57
|
+
* @param {number} port - The port to listen on.
|
|
58
|
+
* @param {string} [host='127.0.0.1'] - The host address to bind to. Use '0.0.0.0' to listen on all interfaces.
|
|
59
|
+
* @param {Function} [cb] - Optional callback function called when server starts listening.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* // Basic server
|
|
63
|
+
* const server = new Server(3333);
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* // Server on all interfaces with callback
|
|
67
|
+
* const server = new Server(3333, '0.0.0.0', () => {
|
|
68
|
+
* console.log('Server started');
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* // Host parameter can be omitted, callback as second parameter
|
|
73
|
+
* const server = new Server(3333, () => {
|
|
74
|
+
* console.log('Server started on 127.0.0.1');
|
|
75
|
+
* });
|
|
76
|
+
*/
|
|
7
77
|
constructor(port, host='127.0.0.1', cb) {
|
|
8
78
|
super();
|
|
9
79
|
if (typeof host === 'function') {
|
|
10
80
|
cb = host;
|
|
11
81
|
host = '127.0.0.1';
|
|
12
82
|
}
|
|
13
|
-
|
|
83
|
+
|
|
14
84
|
let decoded;
|
|
15
85
|
this.port = port;
|
|
16
86
|
this.host = host;
|
|
@@ -19,10 +89,20 @@ class Server extends EventEmitter {
|
|
|
19
89
|
reuseAddr: true
|
|
20
90
|
});
|
|
21
91
|
this._sock.bind(port, host);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
92
|
+
|
|
93
|
+
// Support both callback and promise-based listening
|
|
94
|
+
if (cb) {
|
|
95
|
+
this._sock.on('listening', () => {
|
|
96
|
+
this.emit('listening');
|
|
97
|
+
cb();
|
|
98
|
+
});
|
|
99
|
+
} else {
|
|
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
|
+
}
|
|
105
|
+
|
|
26
106
|
this._sock.on('message', (msg, rinfo) => {
|
|
27
107
|
try {
|
|
28
108
|
decoded = decode(msg);
|
|
@@ -40,9 +120,40 @@ class Server extends EventEmitter {
|
|
|
40
120
|
this.emit(decoded[0], decoded, rinfo);
|
|
41
121
|
}
|
|
42
122
|
});
|
|
123
|
+
|
|
124
|
+
this._sock.on('error', (err) => {
|
|
125
|
+
this.emit('error', err);
|
|
126
|
+
});
|
|
43
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Close the server socket.
|
|
130
|
+
*
|
|
131
|
+
* This method can be used with either a callback or as a Promise.
|
|
132
|
+
*
|
|
133
|
+
* @param {Function} [cb] - Optional callback function called when socket is closed.
|
|
134
|
+
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* // With callback
|
|
138
|
+
* server.close((err) => {
|
|
139
|
+
* if (err) console.error(err);
|
|
140
|
+
* });
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* // With async/await
|
|
144
|
+
* await server.close();
|
|
145
|
+
*/
|
|
44
146
|
close(cb) {
|
|
45
|
-
|
|
147
|
+
if (cb) {
|
|
148
|
+
this._sock.close(cb);
|
|
149
|
+
} else {
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
this._sock.close((err) => {
|
|
152
|
+
if (err) reject(err);
|
|
153
|
+
else resolve();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
46
157
|
}
|
|
47
158
|
}
|
|
48
159
|
|
package/lib/index.mjs
CHANGED
package/lib/internal/decode.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decode } from '../osc.mjs';
|
|
2
2
|
|
|
3
3
|
function sanitizeMessage(decoded) {
|
|
4
4
|
const message = [];
|
|
@@ -17,8 +17,8 @@ function sanitizeBundle(decoded) {
|
|
|
17
17
|
return decoded;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function
|
|
21
|
-
const decoded =
|
|
20
|
+
function decodeAndSanitize(data, customDecode = decode) {
|
|
21
|
+
const decoded = customDecode(data);
|
|
22
22
|
if (decoded.oscType === 'bundle') {
|
|
23
23
|
return sanitizeBundle(decoded);
|
|
24
24
|
}
|
|
@@ -30,4 +30,4 @@ function decode(data, customFromBuffer = fromBuffer) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export default
|
|
33
|
+
export default decodeAndSanitize;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
// OSC 1.0 Protocol Implementation
|
|
4
2
|
// Based on http://opensoundcontrol.org/spec-1_0
|
|
5
3
|
|
|
6
4
|
// Helper functions for OSC encoding/decoding
|
|
7
5
|
|
|
6
|
+
import { Buffer } from 'node:buffer';
|
|
7
|
+
|
|
8
8
|
function padString(str) {
|
|
9
9
|
const nullTerminated = str + '\0';
|
|
10
10
|
const padding = 4 - (nullTerminated.length % 4);
|
|
@@ -149,6 +149,9 @@ function encodeArgument(arg) {
|
|
|
149
149
|
// For doubles, use float for now (OSC 1.0 doesn't have double)
|
|
150
150
|
return { tag: 'f', data: writeFloat32(arg.value) };
|
|
151
151
|
case 'T':
|
|
152
|
+
return { tag: 'T', data: Buffer.alloc(0) };
|
|
153
|
+
case 'F':
|
|
154
|
+
return { tag: 'F', data: Buffer.alloc(0) };
|
|
152
155
|
case 'boolean':
|
|
153
156
|
return arg.value ? { tag: 'T', data: Buffer.alloc(0) } : { tag: 'F', data: Buffer.alloc(0) };
|
|
154
157
|
case 'm':
|
|
@@ -202,7 +205,37 @@ function decodeArgument(tag, buffer, offset) {
|
|
|
202
205
|
}
|
|
203
206
|
}
|
|
204
207
|
|
|
205
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Encode an OSC message or bundle to a Buffer.
|
|
210
|
+
*
|
|
211
|
+
* This low-level function converts OSC messages and bundles into binary format
|
|
212
|
+
* for transmission or storage. Useful for sending OSC over custom transports
|
|
213
|
+
* (WebSocket, TCP, HTTP), storing to files, or implementing custom OSC routers.
|
|
214
|
+
*
|
|
215
|
+
* @param {Object} message - OSC message or bundle object with oscType property
|
|
216
|
+
* @returns {Buffer} The encoded OSC data ready for transmission
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* // Encode a message
|
|
220
|
+
* import { Message, encode } from 'node-osc';
|
|
221
|
+
*
|
|
222
|
+
* const message = new Message('/oscillator/frequency', 440);
|
|
223
|
+
* const buffer = encode(message);
|
|
224
|
+
* console.log('Encoded bytes:', buffer.length);
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* // Encode a bundle
|
|
228
|
+
* import { Bundle, encode } from 'node-osc';
|
|
229
|
+
*
|
|
230
|
+
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
|
|
231
|
+
* const buffer = encode(bundle);
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* // Send over WebSocket
|
|
235
|
+
* const buffer = encode(message);
|
|
236
|
+
* websocket.send(buffer);
|
|
237
|
+
*/
|
|
238
|
+
function encode(message) {
|
|
206
239
|
if (message.oscType === 'bundle') {
|
|
207
240
|
return encodeBundleToBuffer(message);
|
|
208
241
|
} else {
|
|
@@ -253,7 +286,39 @@ function encodeBundleToBuffer(bundle) {
|
|
|
253
286
|
return Buffer.concat([bundleStringBuffer, timetagBuffer, ...elementBuffers]);
|
|
254
287
|
}
|
|
255
288
|
|
|
256
|
-
|
|
289
|
+
/**
|
|
290
|
+
* Decode a Buffer containing OSC data into a message or bundle object.
|
|
291
|
+
*
|
|
292
|
+
* This low-level function parses binary OSC data back into JavaScript objects.
|
|
293
|
+
* Useful for receiving OSC over custom transports, reading from files,
|
|
294
|
+
* or implementing custom OSC routers.
|
|
295
|
+
*
|
|
296
|
+
* @param {Buffer} buffer - The Buffer containing OSC data
|
|
297
|
+
* @returns {Object} The decoded OSC message or bundle. Messages have
|
|
298
|
+
* {oscType: 'message', address: string, args: Array}, bundles have
|
|
299
|
+
* {oscType: 'bundle', timetag: number, elements: Array}
|
|
300
|
+
* @throws {Error} If the buffer contains malformed OSC data
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* // Decode received data
|
|
304
|
+
* import { decode } from 'node-osc';
|
|
305
|
+
*
|
|
306
|
+
* const decoded = decode(buffer);
|
|
307
|
+
* if (decoded.oscType === 'message') {
|
|
308
|
+
* console.log('Address:', decoded.address);
|
|
309
|
+
* console.log('Arguments:', decoded.args);
|
|
310
|
+
* }
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* // Round-trip encode/decode
|
|
314
|
+
* import { Message, encode, decode } from 'node-osc';
|
|
315
|
+
*
|
|
316
|
+
* const original = new Message('/test', 42, 'hello');
|
|
317
|
+
* const buffer = encode(original);
|
|
318
|
+
* const decoded = decode(buffer);
|
|
319
|
+
* console.log(decoded.address); // '/test'
|
|
320
|
+
*/
|
|
321
|
+
function decode(buffer) {
|
|
257
322
|
// Check if it's a bundle or message
|
|
258
323
|
if (buffer.length >= 8 && buffer.subarray(0, 8).toString() === '#bundle\0') {
|
|
259
324
|
return decodeBundleFromBuffer(buffer);
|
|
@@ -313,7 +378,7 @@ function decodeBundleFromBuffer(buffer) {
|
|
|
313
378
|
|
|
314
379
|
// Read element data
|
|
315
380
|
const elementBuffer = buffer.subarray(offset, offset + size);
|
|
316
|
-
const element =
|
|
381
|
+
const element = decode(elementBuffer);
|
|
317
382
|
elements.push(element);
|
|
318
383
|
offset += size;
|
|
319
384
|
}
|
|
@@ -325,5 +390,4 @@ function decodeBundleFromBuffer(buffer) {
|
|
|
325
390
|
};
|
|
326
391
|
}
|
|
327
392
|
|
|
328
|
-
|
|
329
|
-
exports.toBuffer = toBuffer;
|
|
393
|
+
export { encode, decode };
|
package/package.json
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-osc",
|
|
3
3
|
"description": "pyOSC inspired library for sending and receiving OSC messages",
|
|
4
|
-
"version": "11.
|
|
4
|
+
"version": "11.2.0",
|
|
5
5
|
"exports": {
|
|
6
|
+
"types": "./types/index.d.mts",
|
|
6
7
|
"require": "./dist/lib/index.js",
|
|
8
|
+
"import": "./lib/index.mjs",
|
|
7
9
|
"default": "./lib/index.mjs"
|
|
8
10
|
},
|
|
9
11
|
"imports": {
|
|
10
12
|
"#decode": {
|
|
11
13
|
"require": "./dist/lib/internal/decode.js",
|
|
12
14
|
"default": "./lib/internal/decode.mjs"
|
|
13
|
-
},
|
|
14
|
-
"#osc": {
|
|
15
|
-
"require": "./dist/lib/internal/osc.js",
|
|
16
|
-
"default": "./lib/internal/osc.mjs"
|
|
17
15
|
}
|
|
18
16
|
},
|
|
19
17
|
"author": {
|
|
@@ -23,12 +21,14 @@
|
|
|
23
21
|
"engines": {
|
|
24
22
|
"node": "^20.9.0 || ^22.11.0 || >=24.0.0"
|
|
25
23
|
},
|
|
26
|
-
"license": "
|
|
24
|
+
"license": "Apache-2.0",
|
|
27
25
|
"scripts": {
|
|
28
|
-
"clean": "rm -rf dist/",
|
|
29
|
-
"build": "npm run clean && rollup --config rollup.config.mjs",
|
|
26
|
+
"clean": "rm -rf dist/ types/",
|
|
27
|
+
"build": "npm run clean && rollup --config rollup.config.mjs && npm run build:types",
|
|
28
|
+
"build:types": "tsc",
|
|
29
|
+
"docs": "node scripts/generate-docs.mjs",
|
|
30
30
|
"prepublishOnly": "npm run build",
|
|
31
|
-
"lint": "eslint \"lib/**/*.mjs\" test
|
|
31
|
+
"lint": "eslint \"lib/**/*.mjs\" \"test/test-*.mjs\" test/util.mjs examples/*.js examples/*.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"
|
|
@@ -50,7 +50,9 @@
|
|
|
50
50
|
"@eslint/js": "^9.32.0",
|
|
51
51
|
"eslint": "^9.32.0",
|
|
52
52
|
"globals": "^16.3.0",
|
|
53
|
+
"jsdoc": "^4.0.5",
|
|
53
54
|
"rollup": "^4.46.2",
|
|
54
|
-
"tap": "^21.1.0"
|
|
55
|
+
"tap": "^21.1.0",
|
|
56
|
+
"typescript": "^5.9.3"
|
|
55
57
|
}
|
|
56
58
|
}
|