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
package/docs/GUIDE.md ADDED
@@ -0,0 +1,605 @@
1
+ # node-osc Guide
2
+
3
+ This guide provides best practices, patterns, and detailed information for using node-osc effectively.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Events](#events)
8
+ - [Error Handling](#error-handling)
9
+ - [Type System](#type-system)
10
+ - [Best Practices](#best-practices)
11
+ - [Troubleshooting](#troubleshooting)
12
+
13
+ ## Events
14
+
15
+ The `Server` class extends `EventEmitter` and emits several events for different scenarios.
16
+
17
+ ### Server Events
18
+
19
+ #### `listening`
20
+
21
+ Emitted when the server starts listening for messages.
22
+
23
+ ```javascript
24
+ server.on('listening', () => {
25
+ console.log('Server is ready to receive messages');
26
+ });
27
+ ```
28
+
29
+ #### `message`
30
+
31
+ Emitted when an OSC message is received.
32
+
33
+ **Parameters:**
34
+ - `msg` (Array): The message as an array where the first element is the address and subsequent elements are arguments
35
+ - `rinfo` (Object): Remote address information
36
+ - `address` (string): The sender's IP address
37
+ - `port` (number): The sender's port number
38
+
39
+ ```javascript
40
+ server.on('message', (msg, rinfo) => {
41
+ const [address, ...args] = msg;
42
+ console.log(`Received ${address} from ${rinfo.address}:${rinfo.port}`);
43
+ console.log('Arguments:', args);
44
+ });
45
+ ```
46
+
47
+ #### `bundle`
48
+
49
+ Emitted when an OSC bundle is received.
50
+
51
+ **Parameters:**
52
+ - `bundle` (Object): The bundle object
53
+ - `timetag` (number): The bundle's timetag
54
+ - `elements` (Array): Array of messages or nested bundles
55
+ - `rinfo` (Object): Remote address information
56
+
57
+ ```javascript
58
+ server.on('bundle', (bundle, rinfo) => {
59
+ console.log(`Received bundle with timetag ${bundle.timetag}`);
60
+ bundle.elements.forEach((element) => {
61
+ console.log('Element:', element);
62
+ });
63
+ });
64
+ ```
65
+
66
+ #### Address-Specific Events
67
+
68
+ The server also emits events for each message address received. This allows you to listen for specific OSC addresses without filtering in your code.
69
+
70
+ ```javascript
71
+ // Listen specifically for messages to /note
72
+ server.on('/note', (msg, rinfo) => {
73
+ const [address, pitch, velocity] = msg;
74
+ console.log(`Note: ${pitch}, Velocity: ${velocity}`);
75
+ });
76
+
77
+ // Listen for /oscillator/frequency
78
+ server.on('/oscillator/frequency', (msg) => {
79
+ const [address, freq] = msg;
80
+ console.log(`Frequency set to ${freq} Hz`);
81
+ });
82
+ ```
83
+
84
+ #### `error`
85
+
86
+ Emitted when there's an error decoding an incoming message or a socket error.
87
+
88
+ **Parameters:**
89
+ - `error` (Error): The error object
90
+ - `rinfo` (Object): Remote address information (for decode errors)
91
+
92
+ ```javascript
93
+ server.on('error', (error, rinfo) => {
94
+ if (rinfo) {
95
+ console.error(`Error from ${rinfo.address}:${rinfo.port}: ${error.message}`);
96
+ } else {
97
+ console.error('Socket error:', error.message);
98
+ }
99
+ });
100
+ ```
101
+
102
+ ### Client Events
103
+
104
+ #### `error`
105
+
106
+ Emitted when a socket error occurs.
107
+
108
+ ```javascript
109
+ client.on('error', (error) => {
110
+ console.error('Client error:', error.message);
111
+ });
112
+ ```
113
+
114
+ ## Error Handling
115
+
116
+ Proper error handling is essential for robust OSC applications.
117
+
118
+ ### Client Errors
119
+
120
+ #### Sending on Closed Socket
121
+
122
+ If you try to send a message after closing the client, a `ReferenceError` will be thrown:
123
+
124
+ ```javascript
125
+ const client = new Client('127.0.0.1', 3333);
126
+ await client.close();
127
+
128
+ try {
129
+ await client.send('/test', 123);
130
+ } catch (err) {
131
+ console.error(err.message); // "Cannot send message on closed socket."
132
+ console.error(err.code); // "ERR_SOCKET_DGRAM_NOT_RUNNING"
133
+ }
134
+ ```
135
+
136
+ **Prevention:** Always ensure the client is open before sending:
137
+
138
+ ```javascript
139
+ const client = new Client('127.0.0.1', 3333);
140
+ try {
141
+ await client.send('/test', 123);
142
+ } finally {
143
+ await client.close(); // Close after sending
144
+ }
145
+ ```
146
+
147
+ #### Invalid Message Format
148
+
149
+ Passing an invalid message format will throw a `TypeError`:
150
+
151
+ ```javascript
152
+ try {
153
+ await client.send(12345); // Not a valid message format
154
+ } catch (err) {
155
+ console.error(err.message); // "That Message Just Doesn't Seem Right"
156
+ }
157
+ ```
158
+
159
+ ### Server Errors
160
+
161
+ #### Decoding Errors
162
+
163
+ When the server receives malformed OSC data, it emits an `'error'` event rather than throwing:
164
+
165
+ ```javascript
166
+ server.on('error', (err, rinfo) => {
167
+ console.error(`Decode error from ${rinfo.address}: ${err.message}`);
168
+ });
169
+ ```
170
+
171
+ ### Error Handling Patterns
172
+
173
+ #### With Callbacks
174
+
175
+ ```javascript
176
+ client.send('/test', 123, (err) => {
177
+ if (err) {
178
+ console.error('Send failed:', err);
179
+ return;
180
+ }
181
+ console.log('Message sent successfully');
182
+ });
183
+ ```
184
+
185
+ #### With Async/Await
186
+
187
+ ```javascript
188
+ try {
189
+ await client.send('/test', 123);
190
+ console.log('Message sent successfully');
191
+ } catch (err) {
192
+ console.error('Send failed:', err);
193
+ }
194
+ ```
195
+
196
+ #### Always Close Resources
197
+
198
+ Use try/finally to ensure resources are cleaned up even if errors occur:
199
+
200
+ ```javascript
201
+ const client = new Client('127.0.0.1', 3333);
202
+ try {
203
+ await client.send('/test', 123);
204
+ await client.send('/test', 456);
205
+ } catch (err) {
206
+ console.error('Error sending:', err);
207
+ } finally {
208
+ await client.close(); // Always executes
209
+ }
210
+ ```
211
+
212
+ ## Type System
213
+
214
+ OSC supports several data types. node-osc automatically detects types for common JavaScript values:
215
+
216
+ | JavaScript Type | OSC Type | Description |
217
+ |----------------|----------|-------------|
218
+ | Integer number | `integer` | Whole numbers (e.g., 42, -10, 0) |
219
+ | Float number | `float` | Decimal numbers (e.g., 3.14, -0.5) |
220
+ | String | `string` | Text values (e.g., "hello") |
221
+ | Boolean | `boolean` | true or false |
222
+ | Buffer | `blob` | Binary data |
223
+ | MIDI object/Buffer | `midi` | MIDI messages (4 bytes: port, status, data1, data2) |
224
+
225
+ ### Automatic Type Detection
226
+
227
+ ```javascript
228
+ const msg = new Message('/test');
229
+ msg.append(42); // → integer
230
+ msg.append(3.14); // → float
231
+ msg.append('hello'); // → string
232
+ msg.append(true); // → boolean
233
+ ```
234
+
235
+ ### Explicit Type Control
236
+
237
+ For advanced use cases, you can explicitly specify types:
238
+
239
+ ```javascript
240
+ const msg = new Message('/test');
241
+
242
+ // Force a whole number to be sent as float
243
+ msg.append({ type: 'float', value: 42 });
244
+
245
+ // Use shorthand type notation
246
+ msg.append({ type: 'f', value: 42 }); // 'f' = float
247
+ msg.append({ type: 'i', value: 3.14 }); // 'i' = integer (truncates)
248
+ msg.append({ type: 's', value: 'text' }); // 's' = string
249
+ msg.append({ type: 'b', value: Buffer.from('data') }); // 'b' = blob
250
+ msg.append({ type: 'm', value: { port: 0, status: 144, data1: 60, data2: 127 } }); // 'm' = MIDI
251
+ ```
252
+
253
+ ### Supported Type Tags
254
+
255
+ - `'i'` or `'integer'` - 32-bit integer
256
+ - `'f'` or `'float'` - 32-bit float
257
+ - `'s'` or `'string'` - OSC string
258
+ - `'b'` or `'blob'` - Binary blob
259
+ - `'m'` or `'midi'` - MIDI message (4 bytes)
260
+ - `'boolean'` - Boolean value (true/false)
261
+ - `'T'` - True
262
+ - `'F'` - False
263
+
264
+ ## Best Practices
265
+
266
+ ### 1. Use Async/Await for Cleaner Code
267
+
268
+ Prefer async/await over callbacks for more readable code:
269
+
270
+ ```javascript
271
+ // ✅ Good - Clean and readable
272
+ async function sendMessages() {
273
+ const client = new Client('127.0.0.1', 3333);
274
+ try {
275
+ await client.send('/test', 1);
276
+ await client.send('/test', 2);
277
+ await client.send('/test', 3);
278
+ } finally {
279
+ await client.close();
280
+ }
281
+ }
282
+
283
+ // ❌ Less ideal - Callback pyramid
284
+ function sendMessages() {
285
+ const client = new Client('127.0.0.1', 3333);
286
+ client.send('/test', 1, (err) => {
287
+ if (err) return console.error(err);
288
+ client.send('/test', 2, (err) => {
289
+ if (err) return console.error(err);
290
+ client.send('/test', 3, (err) => {
291
+ if (err) return console.error(err);
292
+ client.close();
293
+ });
294
+ });
295
+ });
296
+ }
297
+ ```
298
+
299
+ ### 2. Always Close Resources
300
+
301
+ Always close clients and servers when done to prevent resource leaks:
302
+
303
+ ```javascript
304
+ const client = new Client('127.0.0.1', 3333);
305
+ try {
306
+ await client.send('/test', 123);
307
+ } finally {
308
+ await client.close(); // Always close
309
+ }
310
+ ```
311
+
312
+ ### 3. Use Address-Specific Event Listeners
313
+
314
+ For better code organization, use address-specific event listeners:
315
+
316
+ ```javascript
317
+ // ✅ Good - Clear and organized
318
+ server.on('/note', (msg) => {
319
+ handleNote(msg);
320
+ });
321
+
322
+ server.on('/control', (msg) => {
323
+ handleControl(msg);
324
+ });
325
+
326
+ // ❌ Less ideal - Manual routing
327
+ server.on('message', (msg) => {
328
+ const [address] = msg;
329
+ if (address === '/note') handleNote(msg);
330
+ else if (address === '/control') handleControl(msg);
331
+ });
332
+ ```
333
+
334
+ ### 4. Handle Errors Gracefully
335
+
336
+ Always implement error handling for both clients and servers:
337
+
338
+ ```javascript
339
+ // Client
340
+ try {
341
+ await client.send('/test', 123);
342
+ } catch (err) {
343
+ console.error('Failed to send:', err.message);
344
+ }
345
+
346
+ // Server
347
+ server.on('error', (err, rinfo) => {
348
+ console.error(`Server error from ${rinfo?.address}:`, err.message);
349
+ });
350
+ ```
351
+
352
+ ### 5. Use Bundles for Related Messages
353
+
354
+ When sending multiple related messages, use bundles for atomic operations:
355
+
356
+ ```javascript
357
+ // ✅ Good - Atomic operation
358
+ const bundle = new Bundle(
359
+ ['/synth/freq', 440],
360
+ ['/synth/amp', 0.5],
361
+ ['/synth/gate', 1]
362
+ );
363
+ await client.send(bundle);
364
+
365
+ // ❌ Less ideal - Separate messages (not atomic)
366
+ await client.send('/synth/freq', 440);
367
+ await client.send('/synth/amp', 0.5);
368
+ await client.send('/synth/gate', 1);
369
+ ```
370
+
371
+ ### 6. Listen on All Interfaces for Network Access
372
+
373
+ If you need to receive messages from other machines:
374
+
375
+ ```javascript
376
+ // Listen on all interfaces (accessible from network)
377
+ const server = new Server(3333, '0.0.0.0');
378
+
379
+ // Only localhost (default, more secure)
380
+ const server = new Server(3333, '127.0.0.1');
381
+ ```
382
+
383
+ ### 7. Use Descriptive OSC Addresses
384
+
385
+ Follow OSC naming conventions with hierarchical addresses:
386
+
387
+ ```javascript
388
+ // ✅ Good - Hierarchical and descriptive
389
+ await client.send('/synth/oscillator/1/frequency', 440);
390
+ await client.send('/mixer/channel/3/volume', 0.8);
391
+
392
+ // ❌ Less ideal - Flat and unclear
393
+ await client.send('/freq1', 440);
394
+ await client.send('/vol3', 0.8);
395
+ ```
396
+
397
+ ### 8. Validate Input Data
398
+
399
+ Validate data before sending to avoid runtime errors:
400
+
401
+ ```javascript
402
+ function sendNote(pitch, velocity) {
403
+ if (typeof pitch !== 'number' || pitch < 0 || pitch > 127) {
404
+ throw new Error('Invalid pitch: must be 0-127');
405
+ }
406
+ if (typeof velocity !== 'number' || velocity < 0 || velocity > 127) {
407
+ throw new Error('Invalid velocity: must be 0-127');
408
+ }
409
+ return client.send('/note', pitch, velocity);
410
+ }
411
+ ```
412
+
413
+ ### 9. Wait for Server Ready
414
+
415
+ Always wait for the server to be listening before sending messages:
416
+
417
+ ```javascript
418
+ import { once } from 'node:events';
419
+
420
+ const server = new Server(3333, '0.0.0.0');
421
+
422
+ // Wait for server to be ready
423
+ await once(server, 'listening');
424
+
425
+ // Now safe to send messages
426
+ console.log('Server ready!');
427
+ ```
428
+
429
+ ### 10. Use Parallel Sends When Appropriate
430
+
431
+ When sending multiple independent messages, use `Promise.all` for better performance:
432
+
433
+ ```javascript
434
+ // Send multiple messages in parallel
435
+ await Promise.all([
436
+ client.send('/track/1/volume', 0.8),
437
+ client.send('/track/2/volume', 0.6),
438
+ client.send('/track/3/volume', 0.9)
439
+ ]);
440
+ ```
441
+
442
+ ## Troubleshooting
443
+
444
+ ### Messages Not Being Received
445
+
446
+ **Possible causes and solutions:**
447
+
448
+ 1. **Firewall blocking UDP traffic**
449
+ - Check your firewall settings
450
+ - Ensure the UDP port is open
451
+ - Try with localhost first (`127.0.0.1`)
452
+
453
+ 2. **Wrong host binding**
454
+ - Server: Use `'0.0.0.0'` to listen on all interfaces
455
+ - Server: Use `'127.0.0.1'` for localhost only
456
+ - Client: Match the server's IP address
457
+
458
+ 3. **Port mismatch**
459
+ - Ensure client and server use the same port number
460
+ - Check if another process is using the port
461
+
462
+ 4. **Network connectivity**
463
+ - Test with localhost first (`127.0.0.1`)
464
+ - Verify network connectivity between machines
465
+ - Check if devices are on the same network
466
+
467
+ ### "Cannot send message on closed socket"
468
+
469
+ This error occurs when trying to send after closing the client:
470
+
471
+ ```javascript
472
+ // ❌ Wrong - Sending after close
473
+ await client.close();
474
+ await client.send('/test', 123); // Error!
475
+
476
+ // ✅ Correct - Send before close
477
+ await client.send('/test', 123);
478
+ await client.close();
479
+ ```
480
+
481
+ ### Server Not Listening
482
+
483
+ Ensure you wait for the server to start before sending messages:
484
+
485
+ ```javascript
486
+ const server = new Server(3333, '0.0.0.0');
487
+
488
+ // Wait for server to be ready
489
+ await new Promise(resolve => server.on('listening', resolve));
490
+
491
+ // Now safe to send messages
492
+ console.log('Server ready!');
493
+ ```
494
+
495
+ ### Messages Lost or Dropped
496
+
497
+ UDP is unreliable by design and messages can be lost:
498
+
499
+ **Solutions:**
500
+ 1. Use TCP-based OSC if reliability is critical (requires custom implementation)
501
+ 2. Implement acknowledgment messages
502
+ 3. Add retry logic for critical messages
503
+ 4. Use bundles to ensure related messages arrive together
504
+
505
+ ### High CPU Usage
506
+
507
+ If you're seeing high CPU usage:
508
+
509
+ 1. **Check for infinite loops** in event handlers
510
+ 2. **Rate limit** message sending if sending many messages
511
+ 3. **Use bundles** instead of many individual messages
512
+ 4. **Close unused connections** to free resources
513
+
514
+ ### Memory Leaks
515
+
516
+ To prevent memory leaks:
517
+
518
+ 1. **Always close** clients and servers when done
519
+ 2. **Remove event listeners** when no longer needed
520
+ 3. **Avoid** creating new clients/servers in loops
521
+ 4. **Reuse** client/server instances when possible
522
+
523
+ ```javascript
524
+ // ✅ Good - Proper cleanup
525
+ const server = new Server(3333);
526
+ const handler = (msg) => console.log(msg);
527
+ server.on('message', handler);
528
+
529
+ // Later, clean up
530
+ server.removeListener('message', handler);
531
+ await server.close();
532
+ ```
533
+
534
+ ## Advanced Topics
535
+
536
+ ### Custom Transports
537
+
538
+ The `encode` and `decode` functions allow you to use OSC over custom transports:
539
+
540
+ ```javascript
541
+ import { encode, decode, Message } from 'node-osc';
542
+ import WebSocket from 'ws';
543
+
544
+ // WebSocket server
545
+ const wss = new WebSocket.Server({ port: 8080 });
546
+ wss.on('connection', (ws) => {
547
+ ws.on('message', (data) => {
548
+ const oscMsg = decode(data);
549
+ console.log('Received:', oscMsg);
550
+ });
551
+ });
552
+
553
+ // WebSocket client
554
+ const ws = new WebSocket('ws://localhost:8080');
555
+ const message = new Message('/test', 123);
556
+ ws.send(encode(message));
557
+ ```
558
+
559
+ ### Timetags in Bundles
560
+
561
+ OSC bundles support timetags for scheduling:
562
+
563
+ ```javascript
564
+ // Immediate execution (timetag = 0)
565
+ const bundle = new Bundle(['/test', 1], ['/test', 2]);
566
+
567
+ // Scheduled execution (timetag in OSC time)
568
+ const futureTime = Date.now() / 1000 + 5; // 5 seconds from now
569
+ const scheduledBundle = new Bundle(futureTime, ['/test', 1]);
570
+ ```
571
+
572
+ **Note:** The server receives the timetag but does not automatically schedule execution. You must implement scheduling logic if needed.
573
+
574
+ ### Performance Optimization
575
+
576
+ For high-throughput applications:
577
+
578
+ 1. **Reuse client instances** instead of creating new ones
579
+ 2. **Use bundles** to send multiple messages together
580
+ 3. **Batch messages** and send periodically rather than immediately
581
+ 4. **Use binary blobs** for large data instead of many arguments
582
+ 5. **Profile your code** to identify bottlenecks
583
+
584
+ ```javascript
585
+ // ✅ Good - Reuse client
586
+ const client = new Client('127.0.0.1', 3333);
587
+ for (let i = 0; i < 1000; i++) {
588
+ await client.send('/test', i);
589
+ }
590
+ await client.close();
591
+
592
+ // ❌ Bad - Creating many clients
593
+ for (let i = 0; i < 1000; i++) {
594
+ const client = new Client('127.0.0.1', 3333);
595
+ await client.send('/test', i);
596
+ await client.close();
597
+ }
598
+ ```
599
+
600
+ ## Further Reading
601
+
602
+ - [API Documentation](./API.md) - Complete API reference
603
+ - [OSC Specification](http://opensoundcontrol.org/spec-1_0) - Official OSC 1.0 specification
604
+ - [Examples](../examples/) - Working code examples
605
+ - [Main README](../README.md) - Quick start guide
@@ -0,0 +1,119 @@
1
+ # node-osc Examples
2
+
3
+ This directory contains working examples demonstrating various ways to use the node-osc library.
4
+
5
+ ## Running the Examples
6
+
7
+ All examples can be run directly with Node.js. Some examples require a server and client running simultaneously.
8
+
9
+ ### Server/Client Examples
10
+
11
+ Run the server in one terminal:
12
+ ```bash
13
+ node examples/server.js
14
+ ```
15
+
16
+ Run the client in another terminal:
17
+ ```bash
18
+ node examples/client.js
19
+ ```
20
+
21
+ ### Standalone Examples
22
+
23
+ These examples run both client and server in the same process:
24
+
25
+ ```bash
26
+ # Callback-based example
27
+ node examples/esm.mjs
28
+
29
+ # Async/await example (recommended)
30
+ node examples/async-await.mjs
31
+
32
+ # Bundle example
33
+ node examples/bundle-example.mjs
34
+
35
+ # Error handling example
36
+ node examples/error-handling.mjs
37
+ ```
38
+
39
+ ## Example Files
40
+
41
+ ### [server.js](./server.js)
42
+ **CommonJS Server Example**
43
+
44
+ Demonstrates:
45
+ - Creating an OSC server with CommonJS
46
+ - Listening for messages
47
+ - Displaying remote client information
48
+ - Closing the server after receiving a message
49
+
50
+ ### [client.js](./client.js)
51
+ **CommonJS Client Example**
52
+
53
+ Demonstrates:
54
+ - Creating an OSC client with CommonJS
55
+ - Building messages with the Message class
56
+ - Sending messages with callbacks
57
+
58
+ ### [esm.mjs](./esm.mjs)
59
+ **ESM with Callbacks Example**
60
+
61
+ Demonstrates:
62
+ - Using node-osc with ES modules
63
+ - Callback-based API
64
+ - Server event listeners
65
+ - Client sending with callbacks
66
+
67
+ ### [async-await.mjs](./async-await.mjs)
68
+ **ESM with Async/Await Example** (Recommended Pattern)
69
+
70
+ Demonstrates:
71
+ - Modern async/await patterns
72
+ - Using `node:events.once()` to wait for server ready
73
+ - Sending multiple messages in parallel with `Promise.all()`
74
+ - Clean shutdown of both client and server
75
+ - Complete end-to-end workflow
76
+
77
+ Expected output:
78
+ ```
79
+ OSC server listening on port 3333
80
+ Sent /hello
81
+ Sent counters
82
+ Received: /hello [ 'world' ]
83
+ Received: /counter [ 1 ]
84
+ Received: /counter [ 2 ]
85
+ Received: /counter [ 3 ]
86
+ Client and server closed
87
+ ```
88
+
89
+ ### [bundle-example.mjs](./bundle-example.mjs)
90
+ **OSC Bundles Example**
91
+
92
+ Demonstrates:
93
+ - Creating OSC bundles with multiple messages
94
+ - Sending bundles atomically
95
+ - Receiving and processing bundles
96
+ - Using timetags
97
+
98
+ ### [error-handling.mjs](./error-handling.mjs)
99
+ **Error Handling Example**
100
+
101
+ Demonstrates:
102
+ - Proper error handling with try/catch
103
+ - Server error events
104
+ - Resource cleanup with finally blocks
105
+ - Handling closed socket errors
106
+
107
+ ## Tips
108
+
109
+ 1. **Always close resources**: Use try/finally or ensure you call `.close()` on clients and servers
110
+ 2. **Wait for server ready**: Use the 'listening' event before sending messages
111
+ 3. **Use async/await**: The async/await pattern is cleaner than callbacks for most use cases
112
+ 4. **Handle errors**: Always implement error handling in production code
113
+ 5. **Test locally first**: Start with `127.0.0.1` before trying network communication
114
+
115
+ ## Further Reading
116
+
117
+ - [Main README](../README.md) - Quick start guide
118
+ - [API Documentation](../docs/API.md) - Complete API reference
119
+ - [OSC Specification](http://opensoundcontrol.org/spec-1_0) - Learn about the OSC protocol