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.
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/{lib/internal/osc.mjs → dist/lib/osc.js} +94 -23
  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/{dist/lib/internal/osc.js → lib/osc.mjs} +71 -7
  39. package/package.json +12 -10
  40. package/rollup.config.mjs +48 -37
  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
package/README.md CHANGED
@@ -3,19 +3,84 @@
3
3
  A no frills [Open Sound Control](http://opensoundcontrol.org) client and server.
4
4
  Heavily inspired by [pyOSC](https://trac.v2.nl/wiki/pyOSC).
5
5
 
6
+ ## Installation
7
+
6
8
  Install using npm
7
9
 
8
- ```
10
+ ```bash
9
11
  npm install node-osc
10
12
  ```
11
13
 
12
- ## Written using ESM supports CJS
14
+ ## Features
15
+
16
+ - 🚀 Simple and intuitive API
17
+ - 🔄 Both callback and async/await support
18
+ - 📦 Send and receive OSC messages and bundles
19
+ - 🌐 Works with both ESM and CommonJS
20
+ - 📘 TypeScript type definitions included (generated from JSDoc)
21
+ - 📝 Comprehensive documentation and examples
22
+ - ✅ Well tested and actively maintained
23
+
24
+ ## Quick Start
25
+
26
+ ### Sending Messages
27
+
28
+ ```js
29
+ import { Client } from 'node-osc';
30
+
31
+ const client = new Client('127.0.0.1', 3333);
32
+ await client.send('/oscAddress', 200);
33
+ await client.close();
34
+ ```
35
+
36
+ ### Receiving Messages
37
+
38
+ ```js
39
+ import { Server } from 'node-osc';
40
+
41
+ const server = new Server(3333, '0.0.0.0');
42
+
43
+ server.on('message', (msg) => {
44
+ console.log(`Message: ${msg}`);
45
+ });
46
+ ```
47
+
48
+ ## Documentation
49
+
50
+ - 📚 **[API Documentation](./docs/API.md)** - Complete API reference generated from source code
51
+ - 📘 **[Guide](./docs/GUIDE.md)** - Best practices, error handling, and troubleshooting
52
+ - 📖 **[Examples](./examples/)** - Working examples for various use cases
13
53
 
14
- Supports the latest versions of Node.js 20, 22, and 24 in both ESM + CJS
54
+ ## Compatibility
55
+
56
+ Written using ESM, supports CJS.
57
+
58
+ Supports the latest versions of Node.js 20, 22, and 24 in both ESM + CJS.
59
+
60
+ ## TypeScript
61
+
62
+ TypeScript type definitions are included! No need to install `@types/node-osc`.
63
+
64
+ The types are automatically generated from JSDoc comments during the build process and included with the package. A single `.d.mts` type definition format is provided that works for both ESM and CommonJS consumers.
65
+
66
+ **Note:** If you previously installed `@types/node-osc`, you should uninstall it to avoid conflicts:
67
+ ```bash
68
+ npm uninstall @types/node-osc
69
+ ```
15
70
 
16
- ## Example
71
+ ## More Examples
17
72
 
18
- ### Sending OSC messages:
73
+ ### Sending with async/await
74
+
75
+ ```js
76
+ import { Client } from 'node-osc';
77
+
78
+ const client = new Client('127.0.0.1', 3333);
79
+ await client.send('/oscAddress', 200);
80
+ await client.close();
81
+ ```
82
+
83
+ ### Sending with callbacks
19
84
 
20
85
  ```js
21
86
  import { Client } from 'node-osc';
@@ -25,89 +90,117 @@ client.send('/oscAddress', 200, () => {
25
90
  client.close();
26
91
  });
27
92
  ```
28
-
29
- ### Listening for OSC messages:
93
+
94
+ ### Listening for OSC messages
30
95
 
31
96
  ```js
32
97
  import { Server } from 'node-osc';
33
98
 
34
- var oscServer = new Server(3333, '0.0.0.0', () => {
99
+ const oscServer = new Server(3333, '0.0.0.0', () => {
35
100
  console.log('OSC Server is listening');
36
101
  });
37
102
 
38
103
  oscServer.on('message', function (msg) {
39
104
  console.log(`Message: ${msg}`);
40
- oscServer.close();
41
105
  });
42
106
  ```
43
107
 
44
- ### Sending OSC bundles:
108
+ ### Sending OSC bundles
45
109
 
46
110
  ```js
47
111
  import { Bundle, Client } from 'node-osc';
48
112
 
49
- // a bundle without an explicit time tag
50
113
  const bundle = new Bundle(['/one', 1], ['/two', 2], ['/three', 3]);
51
-
52
- // a bundle with a timetag of 10
53
- bundle.append(new Bundle(10, ['/four', 4]));
54
-
55
114
  const client = new Client('127.0.0.1', 3333);
56
- client.send(bundle));
115
+ await client.send(bundle);
116
+ await client.close();
57
117
  ```
58
118
 
59
- ### Listening for OSC bundles:
119
+ ### Listening for OSC bundles
60
120
 
61
121
  ```js
62
122
  import { Server } from 'node-osc';
63
123
 
64
- var oscServer = new Server(3333, '0.0.0.0', () => {
124
+ const oscServer = new Server(3333, '0.0.0.0', () => {
65
125
  console.log('OSC Server is listening');
66
126
  });
67
127
 
68
128
  oscServer.on('bundle', function (bundle) {
69
- bundle.elements.forEach((element, i) => {
70
- console.log(`Timestamp: ${bundle.timetag[i]}`);
129
+ bundle.elements.forEach((element) => {
130
+ console.log(`Timestamp: ${bundle.timetag}`);
71
131
  console.log(`Message: ${element}`);
72
132
  });
73
- oscServer.close();
74
133
  });
75
134
  ```
76
135
 
77
- ### CJS API
136
+ ### Low-Level Encoding and Decoding
78
137
 
79
- This just works due to conditional exports, isn't that cool!
138
+ For advanced use cases, you can directly encode and decode OSC messages:
139
+
140
+ ```js
141
+ import { Message, encode, decode } from 'node-osc';
142
+
143
+ // Encode a message to binary
144
+ const message = new Message('/oscillator/frequency', 440);
145
+ const buffer = encode(message);
146
+
147
+ // Decode binary data back to a message
148
+ const decoded = decode(buffer);
149
+ console.log('Address:', decoded.address);
150
+ console.log('Value:', decoded.args[0].value);
151
+ ```
152
+
153
+ This is useful for:
154
+ - Sending OSC over non-UDP transports (WebSocket, TCP, HTTP)
155
+ - Storing OSC messages to files or databases
156
+ - Testing and debugging OSC implementations
157
+ - Building custom OSC routers or processors
158
+
159
+ See the **[API Documentation](./docs/API.md)** for complete details.
160
+
161
+ ## CommonJS
162
+
163
+ Both callback and promise-based APIs work with CommonJS!
80
164
 
81
165
  ```js
82
166
  const { Client, Server } = require('node-osc');
83
167
 
84
- const client = new Client('127.0.0.1', 3333);
85
- var server = new Server(3333, '0.0.0.0');
168
+ async function main() {
169
+ const server = new Server(3333, '0.0.0.0');
170
+ const client = new Client('127.0.0.1', 3333);
86
171
 
87
- server.on('listening', () => {
88
- console.log('OSC Server is listening.');
89
- })
172
+ await new Promise((resolve) => {
173
+ server.on('listening', resolve);
174
+ });
90
175
 
91
- server.on('message', (msg) => {
92
- console.log(`Message: ${msg}`);
93
- server.close();
94
- });
176
+ server.on('message', (msg) => {
177
+ console.log(`Message: ${msg}`);
178
+ });
95
179
 
96
- client.send('/hello', 'world', (err) => {
97
- if (err) console.error(err);
98
- client.close();
99
- });
180
+ await client.send('/hello', 'world');
181
+ await client.close();
182
+ await server.close();
183
+ }
184
+
185
+ main();
100
186
  ```
101
187
 
102
- ## Typescript
188
+ ## Examples
103
189
 
104
- To install type definitions for node-osc:
105
-
106
- `npm install --save @types/node-osc` or `yarn add @types/node-osc`
190
+ See the [examples](./examples/) directory for more usage examples:
191
+ - [client.js](./examples/client.js) - CommonJS client example
192
+ - [server.js](./examples/server.js) - CommonJS server example
193
+ - [esm.mjs](./examples/esm.mjs) - ESM example with callbacks
194
+ - [async-await.mjs](./examples/async-await.mjs) - ESM example with async/await
195
+ - [bundle-example.mjs](./examples/bundle-example.mjs) - Working with bundles
196
+ - [error-handling.mjs](./examples/error-handling.mjs) - Error handling patterns
107
197
 
108
- The types should then be automatically included by the compiler.
198
+ ## Contributing
109
199
 
200
+ Contributions are welcome! Please feel free to submit a Pull Request.
110
201
 
111
202
  ## License
112
203
 
113
- LGPL. Please see the file lesser.txt for details.
204
+ Apache-2.0
205
+
206
+ **Note:** This project was relicensed from LGPL-3.0-or-later to Apache-2.0 in December 2025.
@@ -2,12 +2,61 @@
2
2
 
3
3
  var Message = require('./Message.js');
4
4
 
5
+ /**
6
+ * Convert array notation to Message object.
7
+ * @private
8
+ * @param {Array|Message|Bundle} element - The element to sanitize.
9
+ * @returns {Message|Bundle} The sanitized element.
10
+ */
5
11
  function sanitize(element) {
6
12
  if (element instanceof Array) element = new Message(element[0], ...element.slice(1));
7
13
  return element;
8
14
  }
9
15
 
16
+ /**
17
+ * Represents an OSC bundle containing multiple messages or nested bundles.
18
+ *
19
+ * OSC bundles allow multiple messages to be sent together, optionally with
20
+ * a timetag indicating when the bundle should be processed.
21
+ *
22
+ * @class
23
+ *
24
+ * @example
25
+ * // Create a bundle without a timetag
26
+ * const bundle = new Bundle(['/one', 1], ['/two', 2]);
27
+ *
28
+ * @example
29
+ * // Create a bundle with a timetag
30
+ * const bundle = new Bundle(10, ['/one', 1], ['/two', 2]);
31
+ *
32
+ * @example
33
+ * // Nest bundles
34
+ * const bundle1 = new Bundle(['/one', 1]);
35
+ * const bundle2 = new Bundle(['/two', 2]);
36
+ * bundle1.append(bundle2);
37
+ */
10
38
  class Bundle {
39
+ /**
40
+ * Create an OSC Bundle.
41
+ *
42
+ * @param {number|Message|Bundle|Array} [timetagOrElement=0] - Timetag, or if not a number, the first element and timetag will default to 0.
43
+ * @param {...(Message|Bundle|Array)} elements - Messages or bundles to include.
44
+ * Arrays will be automatically converted to Message objects.
45
+ *
46
+ * @example
47
+ * // Bundle without timetag
48
+ * const bundle = new Bundle(['/test', 1], ['/test2', 2]);
49
+ *
50
+ * @example
51
+ * // Bundle with timetag of 10
52
+ * const bundle = new Bundle(10, ['/test', 1]);
53
+ *
54
+ * @example
55
+ * // Bundle with Message objects
56
+ * const msg1 = new Message('/one', 1);
57
+ * const msg2 = new Message('/two', 2);
58
+ * const bundle = new Bundle(msg1, msg2);
59
+ */
11
60
  constructor(timetag, ...elements) {
12
61
  if (!(typeof timetag === 'number')) {
13
62
  elements.unshift(timetag);
@@ -18,6 +67,23 @@ class Bundle {
18
67
  this.elements = elements.map(sanitize);
19
68
  }
20
69
 
70
+ /**
71
+ * Append a message or bundle to this bundle.
72
+ *
73
+ * @param {Message|Bundle|Array} element - The message or bundle to append.
74
+ * Arrays will be automatically converted to Message objects.
75
+ *
76
+ * @example
77
+ * const bundle = new Bundle();
78
+ * bundle.append(['/test', 1]);
79
+ * bundle.append(new Message('/test2', 2));
80
+ *
81
+ * @example
82
+ * // Append a nested bundle
83
+ * const bundle1 = new Bundle(['/one', 1]);
84
+ * const bundle2 = new Bundle(['/two', 2]);
85
+ * bundle1.append(bundle2);
86
+ */
21
87
  append(element) {
22
88
  this.elements.push(sanitize(element));
23
89
  }
@@ -1,44 +1,94 @@
1
1
  'use strict';
2
2
 
3
3
  var node_dgram = require('node:dgram');
4
- var _osc = require('#osc');
4
+ var node_events = require('node:events');
5
+ var osc = require('./osc.js');
5
6
  var Message = require('./Message.js');
6
7
 
7
- class Client {
8
+ /**
9
+ * OSC Client for sending messages and bundles over UDP.
10
+ *
11
+ * Extends EventEmitter and emits the following events:
12
+ * - 'error': Emitted when a socket error occurs
13
+ *
14
+ * @class
15
+ * @extends EventEmitter
16
+ * @example
17
+ * // Create a client
18
+ * const client = new Client('127.0.0.1', 3333);
19
+ *
20
+ * // Send a message with callback
21
+ * client.send('/oscAddress', 200, (err) => {
22
+ * if (err) console.error(err);
23
+ * client.close();
24
+ * });
25
+ *
26
+ * @example
27
+ * // Send a message with async/await
28
+ * const client = new Client('127.0.0.1', 3333);
29
+ * await client.send('/oscAddress', 200);
30
+ * await client.close();
31
+ */
32
+ class Client extends node_events.EventEmitter {
33
+ /**
34
+ * Create an OSC Client.
35
+ *
36
+ * @param {string} host - The hostname or IP address of the OSC server.
37
+ * @param {number} port - The port number of the OSC server.
38
+ *
39
+ * @example
40
+ * const client = new Client('127.0.0.1', 3333);
41
+ */
8
42
  constructor(host, port) {
43
+ super();
9
44
  this.host = host;
10
45
  this.port = port;
11
46
  this._sock = node_dgram.createSocket({
12
47
  type: 'udp4',
13
48
  reuseAddr: true
14
49
  });
50
+
51
+ this._sock.on('error', (err) => {
52
+ this.emit('error', err);
53
+ });
15
54
  }
55
+ /**
56
+ * Close the client socket.
57
+ *
58
+ * This method can be used with either a callback or as a Promise.
59
+ *
60
+ * @param {Function} [cb] - Optional callback function called when socket is closed.
61
+ * @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
62
+ *
63
+ * @example
64
+ * // With callback
65
+ * client.close((err) => {
66
+ * if (err) console.error(err);
67
+ * });
68
+ *
69
+ * @example
70
+ * // With async/await
71
+ * await client.close();
72
+ */
16
73
  close(cb) {
17
- this._sock.close(cb);
18
- }
19
- send(...args) {
20
- let message = args[0];
21
- let callback;
22
- if (typeof args[args.length - 1] === 'function') {
23
- callback = args.pop();
24
- }
25
- else {
26
- callback = () => {};
74
+ if (cb) {
75
+ this._sock.close(cb);
76
+ } else {
77
+ return new Promise((resolve, reject) => {
78
+ this._sock.close((err) => {
79
+ if (err) reject(err);
80
+ else resolve();
81
+ });
82
+ });
27
83
  }
28
-
29
- if (message instanceof Array) {
30
- message = {
31
- address: message[0],
32
- args: message.splice(1)
33
- };
34
- }
35
-
84
+ }
85
+ _performSend(message, args, callback) {
36
86
  let mes;
37
87
  let buf;
38
88
  try {
39
89
  switch (typeof message) {
40
90
  case 'object':
41
- buf = _osc.toBuffer(message);
91
+ buf = osc.encode(message);
42
92
  this._sock.send(buf, 0, buf.length, this.port, this.host, callback);
43
93
  break;
44
94
  case 'string':
@@ -46,7 +96,7 @@ class Client {
46
96
  for (let i = 1; i < args.length; i++) {
47
97
  mes.append(args[i]);
48
98
  }
49
- buf = _osc.toBuffer(mes);
99
+ buf = osc.encode(mes);
50
100
  this._sock.send(buf, 0, buf.length, this.port, this.host, callback);
51
101
  break;
52
102
  default:
@@ -60,6 +110,71 @@ class Client {
60
110
  callback(error);
61
111
  }
62
112
  }
113
+ /**
114
+ * Send an OSC message or bundle to the server.
115
+ *
116
+ * This method can be used with either a callback or as a Promise.
117
+ * Messages can be sent in several formats:
118
+ * - As separate arguments: address followed by values
119
+ * - As a Message or Bundle object
120
+ * - As an array: [address, ...values]
121
+ *
122
+ * @param {...*} args - The message to send. Can be:
123
+ * - (address: string, ...values: any[], callback?: Function)
124
+ * - (message: Message|Bundle, callback?: Function)
125
+ * - (array: Array, callback?: Function)
126
+ * @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
127
+ *
128
+ * @throws {TypeError} If the message format is invalid.
129
+ * @throws {ReferenceError} If attempting to send on a closed socket.
130
+ *
131
+ * @example
132
+ * // Send with address and arguments
133
+ * client.send('/oscAddress', 200, 'hello', (err) => {
134
+ * if (err) console.error(err);
135
+ * });
136
+ *
137
+ * @example
138
+ * // Send with async/await
139
+ * await client.send('/oscAddress', 200, 'hello');
140
+ *
141
+ * @example
142
+ * // Send a Message object
143
+ * const msg = new Message('/test', 1, 2, 3);
144
+ * await client.send(msg);
145
+ *
146
+ * @example
147
+ * // Send a Bundle object
148
+ * const bundle = new Bundle(['/one', 1], ['/two', 2]);
149
+ * await client.send(bundle);
150
+ */
151
+ send(...args) {
152
+ let message = args[0];
153
+ let callback;
154
+
155
+ // Convert array syntax to message object
156
+ if (message instanceof Array) {
157
+ message = {
158
+ address: message[0],
159
+ args: message.splice(1)
160
+ };
161
+ }
162
+
163
+ if (typeof args[args.length - 1] === 'function') {
164
+ callback = args.pop();
165
+ this._performSend(message, args, callback);
166
+ }
167
+ else {
168
+ // No callback provided, return a Promise
169
+ return new Promise((resolve, reject) => {
170
+ callback = (err) => {
171
+ if (err) reject(err);
172
+ else resolve();
173
+ };
174
+ this._performSend(message, args, callback);
175
+ });
176
+ }
177
+ }
63
178
  }
64
179
 
65
180
  module.exports = Client;
@@ -4,23 +4,109 @@ const typeTags = {
4
4
  s: 'string',
5
5
  f: 'float',
6
6
  i: 'integer',
7
- b: 'blob'
7
+ b: 'blob',
8
+ m: 'midi'
8
9
  };
9
10
 
11
+ /**
12
+ * Represents a typed argument for an OSC message.
13
+ *
14
+ * @class
15
+ * @private
16
+ */
10
17
  class Argument {
18
+ /**
19
+ * @param {string} type - The type of the argument (string, float, integer, blob, boolean).
20
+ * @param {*} value - The value of the argument.
21
+ */
11
22
  constructor(type, value) {
12
23
  this.type = type;
13
24
  this.value = value;
14
25
  }
15
26
  }
16
27
 
28
+ /**
29
+ * Represents an OSC message with an address and arguments.
30
+ *
31
+ * OSC messages consist of an address pattern (string starting with '/')
32
+ * and zero or more arguments of various types.
33
+ *
34
+ * @class
35
+ *
36
+ * @example
37
+ * // Create a message with constructor arguments
38
+ * const msg = new Message('/test', 1, 2, 'hello');
39
+ *
40
+ * @example
41
+ * // Create a message and append arguments
42
+ * const msg = new Message('/test');
43
+ * msg.append(1);
44
+ * msg.append('hello');
45
+ * msg.append(3.14);
46
+ */
17
47
  class Message {
48
+ /**
49
+ * Create an OSC Message.
50
+ *
51
+ * @param {string} address - The OSC address pattern (e.g., '/oscillator/frequency').
52
+ * @param {...*} args - Optional arguments to include in the message.
53
+ *
54
+ * @example
55
+ * const msg = new Message('/test');
56
+ *
57
+ * @example
58
+ * const msg = new Message('/test', 1, 2, 3);
59
+ *
60
+ * @example
61
+ * const msg = new Message('/synth', 'note', 60, 0.5);
62
+ */
18
63
  constructor(address, ...args) {
19
64
  this.oscType = 'message';
20
65
  this.address = address;
21
66
  this.args = args;
22
67
  }
23
68
 
69
+ /**
70
+ * Append an argument to the message.
71
+ *
72
+ * Automatically detects the type based on the JavaScript type:
73
+ * - Integers are encoded as OSC integers
74
+ * - Floats are encoded as OSC floats
75
+ * - Strings are encoded as OSC strings
76
+ * - Booleans are encoded as OSC booleans
77
+ * - Buffers are encoded as OSC blobs
78
+ * - Arrays are recursively appended
79
+ * - Objects with a 'type' property are used as-is
80
+ *
81
+ * @param {*} arg - The argument to append. Can be:
82
+ * - A primitive value (number, string, boolean)
83
+ * - A Buffer (encoded as blob)
84
+ * - An array of values (will be recursively appended)
85
+ * - An object with 'type' and 'value' properties for explicit type control
86
+ *
87
+ * @throws {Error} If the argument type cannot be encoded.
88
+ *
89
+ * @example
90
+ * const msg = new Message('/test');
91
+ * msg.append(42); // Integer
92
+ * msg.append(3.14); // Float
93
+ * msg.append('hello'); // String
94
+ * msg.append(true); // Boolean
95
+ *
96
+ * @example
97
+ * // Append multiple values at once
98
+ * msg.append([1, 2, 3]);
99
+ *
100
+ * @example
101
+ * // Explicitly specify type
102
+ * msg.append({ type: 'float', value: 42 });
103
+ * msg.append({ type: 'blob', value: Buffer.from('data') });
104
+ *
105
+ * @example
106
+ * // MIDI messages (4 bytes: port, status, data1, data2)
107
+ * msg.append({ type: 'midi', value: { port: 0, status: 144, data1: 60, data2: 127 } });
108
+ * msg.append({ type: 'm', value: Buffer.from([0, 144, 60, 127]) });
109
+ */
24
110
  append(arg) {
25
111
  let argOut;
26
112
  switch (typeof arg) {