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
@@ -0,0 +1,57 @@
1
+ /**
2
+ * OSC Client and Server Example (ESM with Async/Await)
3
+ *
4
+ * This is the recommended pattern for modern Node.js applications.
5
+ * It demonstrates:
6
+ * - Using async/await for cleaner async code
7
+ * - Properly waiting for server to be ready
8
+ * - Sending multiple messages in parallel
9
+ * - Clean shutdown of resources
10
+ *
11
+ * To run this example:
12
+ * node examples/async-await.mjs
13
+ */
14
+
15
+ // Example: Using async/await with node-osc Client and Server (ESM)
16
+ import { once } from "node:events";
17
+ import { setImmediate } from "node:timers/promises";
18
+ import { Client, Server } from "node-osc";
19
+
20
+ // Create server on all interfaces, port 3333
21
+ const server = new Server(3333, "0.0.0.0");
22
+
23
+ // Wait for server to be ready using once() - cleaner than event listeners
24
+ await once(server, "listening");
25
+
26
+ console.log("OSC server listening on port 3333");
27
+
28
+ // Set up message handler
29
+ // Messages arrive as arrays: [address, ...arguments]
30
+ server.on("message", (msg) => {
31
+ const [address, ...args] = msg;
32
+ console.log("Received:", address, args);
33
+ });
34
+
35
+ // Create client pointing to localhost
36
+ const client = new Client("127.0.0.1", 3333);
37
+
38
+ // Send a simple message
39
+ await client.send("/hello", "world");
40
+ console.log("Sent /hello");
41
+
42
+ // Send multiple messages in parallel using Promise.all()
43
+ await Promise.all([
44
+ client.send("/counter", 1),
45
+ client.send("/counter", 2),
46
+ client.send("/counter", 3),
47
+ ]);
48
+ console.log("Sent counters");
49
+
50
+ // Allow socket I/O to be processed before shutting down
51
+ await setImmediate();
52
+
53
+ // Clean shutdown - always close resources
54
+ await client.close();
55
+ await server.close();
56
+
57
+ console.log("Client and server closed");
@@ -0,0 +1,92 @@
1
+ /**
2
+ * OSC Bundle Example
3
+ *
4
+ * This example demonstrates how to create and send OSC bundles.
5
+ * Bundles allow you to group multiple messages together, optionally
6
+ * with a timetag for synchronized processing.
7
+ *
8
+ * To run this example:
9
+ * node examples/bundle-example.mjs
10
+ */
11
+
12
+ import { once } from "node:events";
13
+ import { setImmediate } from "node:timers/promises";
14
+ import { Bundle, Client, Message, Server } from "node-osc";
15
+
16
+ // Start server
17
+ const server = new Server(3333, "0.0.0.0");
18
+ await once(server, "listening");
19
+ console.log("Server listening on port 3333\n");
20
+
21
+ // Handle bundles specifically
22
+ server.on("bundle", (bundle, rinfo) => {
23
+ console.log(`📦 Received bundle from ${rinfo.address}:${rinfo.port}`);
24
+ console.log(` Timetag: ${bundle.timetag}`);
25
+ console.log(` Elements: ${bundle.elements.length}`);
26
+
27
+ // Process each element in the bundle
28
+ bundle.elements.forEach((element, i) => {
29
+ if (element.oscType === 'message') {
30
+ const [address, ...args] = element;
31
+ console.log(` ${i + 1}. ${address}: ${args.join(', ')}`);
32
+ } else if (element.oscType === 'bundle') {
33
+ console.log(` ${i + 1}. [Nested Bundle]`);
34
+ }
35
+ });
36
+ console.log();
37
+ });
38
+
39
+ // Create client
40
+ const client = new Client("127.0.0.1", 3333);
41
+
42
+ // Example 1: Bundle without timetag (array notation)
43
+ console.log("Sending bundle without timetag...");
44
+ const bundle1 = new Bundle(
45
+ ["/synth/freq", 440],
46
+ ["/synth/amp", 0.5],
47
+ ["/synth/gate", 1]
48
+ );
49
+ await client.send(bundle1);
50
+ await setImmediate();
51
+
52
+ // Example 2: Bundle with timetag
53
+ console.log("Sending bundle with timetag...");
54
+ const bundle2 = new Bundle(
55
+ 10, // timetag
56
+ ["/oscillator/1/freq", 220],
57
+ ["/oscillator/2/freq", 330]
58
+ );
59
+ await client.send(bundle2);
60
+ await setImmediate();
61
+
62
+ // Example 3: Bundle with Message objects
63
+ console.log("Sending bundle with Message objects...");
64
+ const msg1 = new Message("/note", 60, 127);
65
+ const msg2 = new Message("/note", 64, 127);
66
+ const msg3 = new Message("/note", 67, 127);
67
+ const bundle3 = new Bundle(msg1, msg2, msg3);
68
+ await client.send(bundle3);
69
+ await setImmediate();
70
+
71
+ // Example 4: Nested bundles
72
+ console.log("Sending nested bundles...");
73
+ const innerBundle = new Bundle(["/inner/message", 123]);
74
+ const outerBundle = new Bundle(["/outer/message", 456]);
75
+ outerBundle.append(innerBundle);
76
+ await client.send(outerBundle);
77
+ await setImmediate();
78
+
79
+ // Example 5: Building a bundle incrementally
80
+ console.log("Sending incrementally built bundle...");
81
+ // Create bundle with initial element, then append more
82
+ const bundle5 = new Bundle(["/initial", 0]);
83
+ bundle5.append(["/control/1", 10]);
84
+ bundle5.append(["/control/2", 20]);
85
+ bundle5.append(["/control/3", 30]);
86
+ await client.send(bundle5);
87
+ await setImmediate();
88
+
89
+ // Clean shutdown
90
+ await client.close();
91
+ await server.close();
92
+ console.log("Done!");
@@ -1,23 +1,40 @@
1
+ /**
2
+ * OSC Client Example (CommonJS)
3
+ *
4
+ * This example demonstrates how to create an OSC client and send messages
5
+ * using the Message class with callbacks.
6
+ *
7
+ * To run this example:
8
+ * 1. Start the server: node examples/server.js
9
+ * 2. Run this client: node examples/client.js
10
+ */
11
+
1
12
  'use strict';
2
13
  const { Client, Message } = require('node-osc');
3
14
 
15
+ // Create a client connected to localhost on port 3333
4
16
  const client = new Client('127.0.0.1', 3333);
5
17
 
18
+ // Create a message using the Message class
6
19
  const message = new Message('/address');
7
- message.append('testing');
8
- message.append('testing');
9
- message.append(123);
20
+ message.append('testing'); // Append a string
21
+ message.append('testing'); // Append the same string again (for demonstration)
22
+ message.append(123); // Append an integer
10
23
 
24
+ // Send the message with a callback
11
25
  client.send(message, (err) => {
12
26
  if (err) {
13
27
  console.error(new Error(err));
14
28
  }
29
+ // Always close the client when done
15
30
  client.close();
16
31
  });
17
32
 
18
- // or
33
+ // Alternative ways to send messages:
34
+
35
+ // Method 1: Send address and arguments directly
19
36
  // client.send('/address', 'testing', 'testing', 123);
20
37
 
21
- // or
38
+ // Method 2: Create message with constructor arguments
22
39
  // const msg = new Message('/address', 1, 2, 3);
23
40
  // client.send(msg);
@@ -0,0 +1,152 @@
1
+ /**
2
+ * OSC Error Handling Example
3
+ *
4
+ * This example demonstrates proper error handling patterns with node-osc,
5
+ * including handling server errors, client errors, and ensuring cleanup.
6
+ *
7
+ * To run this example:
8
+ * node examples/error-handling.mjs
9
+ */
10
+
11
+ import { once } from "node:events";
12
+ import { Client, Server } from "node-osc";
13
+
14
+ console.log("=== OSC Error Handling Examples ===\n");
15
+
16
+ // Example 1: Server decode errors
17
+ console.log("1. Testing server decode error handling...");
18
+ const server = new Server(3333, "0.0.0.0");
19
+
20
+ // Set up error handler for server
21
+ server.on("error", (err, rinfo) => {
22
+ console.error(`❌ Server error from ${rinfo.address}:${rinfo.port}`);
23
+ console.error(` ${err.message}`);
24
+ });
25
+
26
+ await once(server, "listening");
27
+ console.log("✅ Server started successfully\n");
28
+
29
+ // Example 2: Try/catch with async/await
30
+ console.log("2. Testing client send with try/catch...");
31
+ const client = new Client("127.0.0.1", 3333);
32
+
33
+ try {
34
+ await client.send("/test", 123, "hello", true);
35
+ console.log("✅ Message sent successfully\n");
36
+ } catch (err) {
37
+ console.error(`❌ Failed to send message: ${err.message}\n`);
38
+ }
39
+
40
+ // Example 3: Error when sending on closed socket
41
+ console.log("3. Testing send on closed socket...");
42
+ await client.close();
43
+ console.log(" Client closed");
44
+
45
+ try {
46
+ await client.send("/test", 456);
47
+ console.log("✅ Message sent (this shouldn't happen)\n");
48
+ } catch (err) {
49
+ console.log(`✅ Caught expected error: ${err.message}`);
50
+ console.log(` Error code: ${err.code}\n`);
51
+ }
52
+
53
+ // Example 4: Try/finally for cleanup
54
+ console.log("4. Testing try/finally for guaranteed cleanup...");
55
+ const client2 = new Client("127.0.0.1", 3333);
56
+
57
+ try {
58
+ await client2.send("/cleanup/test", 789);
59
+ console.log("✅ Message sent");
60
+
61
+ // Simulate an error
62
+ throw new Error("Simulated error");
63
+ } catch (err) {
64
+ console.log(`⚠️ Caught error: ${err.message}`);
65
+ } finally {
66
+ // This always runs, even if there was an error
67
+ await client2.close();
68
+ console.log("✅ Client closed in finally block\n");
69
+ }
70
+
71
+ // Example 5: Callback-based error handling
72
+ console.log("5. Testing callback-based error handling...");
73
+ const client3 = new Client("127.0.0.1", 3333);
74
+
75
+ await new Promise((resolve) => {
76
+ client3.send("/callback/test", 999, (err) => {
77
+ if (err) {
78
+ console.error(`❌ Send error: ${err}`);
79
+ } else {
80
+ console.log("✅ Message sent via callback");
81
+ }
82
+
83
+ client3.close((err) => {
84
+ if (err) {
85
+ console.error(`❌ Close error: ${err}`);
86
+ } else {
87
+ console.log("✅ Client closed via callback\n");
88
+ }
89
+ resolve();
90
+ });
91
+ });
92
+ });
93
+
94
+ // Example 6: Multiple operations with proper error handling
95
+ console.log("6. Testing multiple operations with error handling...");
96
+ const client4 = new Client("127.0.0.1", 3333);
97
+
98
+ try {
99
+ // Send multiple messages
100
+ const results = await Promise.allSettled([
101
+ client4.send("/multi/1", 1),
102
+ client4.send("/multi/2", 2),
103
+ client4.send("/multi/3", 3),
104
+ ]);
105
+
106
+ // Check results
107
+ results.forEach((result, i) => {
108
+ if (result.status === "fulfilled") {
109
+ console.log(`✅ Message ${i + 1} sent successfully`);
110
+ } else {
111
+ console.error(`❌ Message ${i + 1} failed: ${result.reason}`);
112
+ }
113
+ });
114
+ } catch (err) {
115
+ console.error(`❌ Unexpected error: ${err.message}`);
116
+ } finally {
117
+ await client4.close();
118
+ console.log("✅ Client closed\n");
119
+ }
120
+
121
+ // Example 7: Server error event
122
+ console.log("7. Testing server message handling with error check...");
123
+ let messageReceived = false;
124
+
125
+ server.on("message", (msg) => {
126
+ messageReceived = true;
127
+ const [address, ...args] = msg;
128
+ console.log(`✅ Received: ${address} with args: ${args.join(", ")}`);
129
+ });
130
+
131
+ const client5 = new Client("127.0.0.1", 3333);
132
+ await client5.send("/final/test", "done");
133
+ await client5.close();
134
+
135
+ // Wait a bit for message to be received
136
+ await new Promise(resolve => setTimeout(resolve, 100));
137
+
138
+ if (!messageReceived) {
139
+ console.log("⚠️ Warning: Message was not received");
140
+ }
141
+
142
+ // Clean shutdown
143
+ await server.close();
144
+ console.log("\n✅ All tests complete, server closed");
145
+
146
+ console.log("\n=== Key Takeaways ===");
147
+ console.log("1. Always use try/catch with async/await");
148
+ console.log("2. Use try/finally to ensure cleanup");
149
+ console.log("3. Listen for 'error' events on servers");
150
+ console.log("4. Check for errors in callbacks");
151
+ console.log("5. Don't send on closed sockets");
152
+ console.log("6. Use Promise.allSettled for multiple operations");
package/examples/esm.mjs CHANGED
@@ -1,18 +1,39 @@
1
+ /**
2
+ * OSC Client and Server Example (ESM with Callbacks)
3
+ *
4
+ * This example demonstrates using node-osc with ES modules and callback-based API.
5
+ * It shows how to create both a client and server, send messages, and handle events.
6
+ *
7
+ * To run this example:
8
+ * node examples/esm.mjs
9
+ */
10
+
1
11
  import { Client, Server } from 'node-osc';
2
12
 
13
+ // Create a client connected to localhost on port 3333
3
14
  const client = new Client('127.0.0.1', 3333);
15
+
16
+ // Create a server listening on port 3333, bound to all interfaces
4
17
  var server = new Server(3333, '0.0.0.0');
5
18
 
19
+ // Listen for when the server is ready
6
20
  server.on('listening', () => {
7
21
  console.log('OSC Server is Listening');
8
22
  });
9
23
 
24
+ // Listen for incoming messages
10
25
  server.on('message', (msg, rinfo) => {
26
+ // msg is an array: [address, ...arguments]
11
27
  console.log(`Message: ${msg}\nReceived from: ${rinfo.address}:${rinfo.port}`);
28
+
29
+ // Close the server after receiving a message
12
30
  server.close();
13
31
  });
14
32
 
33
+ // Send a message with callback-based API
15
34
  client.send('/hello', 'world', (err) => {
16
35
  if (err) console.error(err);
36
+
37
+ // Close the client after sending
17
38
  client.close();
18
39
  });
@@ -1,9 +1,25 @@
1
+ /**
2
+ * OSC Server Example (CommonJS)
3
+ *
4
+ * This example demonstrates how to create an OSC server that listens for
5
+ * incoming messages and displays them along with sender information.
6
+ *
7
+ * To run this example:
8
+ * 1. Start this server: node examples/server.js
9
+ * 2. Send messages from client: node examples/client.js
10
+ */
11
+
1
12
  'use strict';
2
13
  var { Server } = require('node-osc');
3
14
 
15
+ // Create a server listening on port 3333, bound to all interfaces
4
16
  var oscServer = new Server(3333, '0.0.0.0');
5
17
 
18
+ // Listen for incoming OSC messages
6
19
  oscServer.on('message', function (msg, rinfo) {
20
+ // msg is an array: [address, ...arguments]
7
21
  console.log(`Message: ${msg}\nReceived from: ${rinfo.address}:${rinfo.port}`);
22
+
23
+ // Close the server after receiving one message (for demo purposes)
8
24
  oscServer.close();
9
25
  });
package/jsdoc.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "source": {
3
+ "include": [
4
+ "lib/Server.mjs",
5
+ "lib/Client.mjs",
6
+ "lib/Bundle.mjs",
7
+ "lib/Message.mjs",
8
+ "lib/osc.mjs"
9
+ ],
10
+ "includePattern": ".+\\.mjs$"
11
+ },
12
+ "opts": {
13
+ "encoding": "utf8",
14
+ "recurse": false
15
+ }
16
+ }
package/lib/Bundle.mjs CHANGED
@@ -1,11 +1,60 @@
1
1
  import Message from './Message.mjs';
2
2
 
3
+ /**
4
+ * Convert array notation to Message object.
5
+ * @private
6
+ * @param {Array|Message|Bundle} element - The element to sanitize.
7
+ * @returns {Message|Bundle} The sanitized element.
8
+ */
3
9
  function sanitize(element) {
4
10
  if (element instanceof Array) element = new Message(element[0], ...element.slice(1));
5
11
  return element;
6
12
  }
7
13
 
14
+ /**
15
+ * Represents an OSC bundle containing multiple messages or nested bundles.
16
+ *
17
+ * OSC bundles allow multiple messages to be sent together, optionally with
18
+ * a timetag indicating when the bundle should be processed.
19
+ *
20
+ * @class
21
+ *
22
+ * @example
23
+ * // Create a bundle without a timetag
24
+ * const bundle = new Bundle(['/one', 1], ['/two', 2]);
25
+ *
26
+ * @example
27
+ * // Create a bundle with a timetag
28
+ * const bundle = new Bundle(10, ['/one', 1], ['/two', 2]);
29
+ *
30
+ * @example
31
+ * // Nest bundles
32
+ * const bundle1 = new Bundle(['/one', 1]);
33
+ * const bundle2 = new Bundle(['/two', 2]);
34
+ * bundle1.append(bundle2);
35
+ */
8
36
  class Bundle {
37
+ /**
38
+ * Create an OSC Bundle.
39
+ *
40
+ * @param {number|Message|Bundle|Array} [timetagOrElement=0] - Timetag, or if not a number, the first element and timetag will default to 0.
41
+ * @param {...(Message|Bundle|Array)} elements - Messages or bundles to include.
42
+ * Arrays will be automatically converted to Message objects.
43
+ *
44
+ * @example
45
+ * // Bundle without timetag
46
+ * const bundle = new Bundle(['/test', 1], ['/test2', 2]);
47
+ *
48
+ * @example
49
+ * // Bundle with timetag of 10
50
+ * const bundle = new Bundle(10, ['/test', 1]);
51
+ *
52
+ * @example
53
+ * // Bundle with Message objects
54
+ * const msg1 = new Message('/one', 1);
55
+ * const msg2 = new Message('/two', 2);
56
+ * const bundle = new Bundle(msg1, msg2);
57
+ */
9
58
  constructor(timetag, ...elements) {
10
59
  if (!(typeof timetag === 'number')) {
11
60
  elements.unshift(timetag);
@@ -16,6 +65,23 @@ class Bundle {
16
65
  this.elements = elements.map(sanitize);
17
66
  }
18
67
 
68
+ /**
69
+ * Append a message or bundle to this bundle.
70
+ *
71
+ * @param {Message|Bundle|Array} element - The message or bundle to append.
72
+ * Arrays will be automatically converted to Message objects.
73
+ *
74
+ * @example
75
+ * const bundle = new Bundle();
76
+ * bundle.append(['/test', 1]);
77
+ * bundle.append(new Message('/test2', 2));
78
+ *
79
+ * @example
80
+ * // Append a nested bundle
81
+ * const bundle1 = new Bundle(['/one', 1]);
82
+ * const bundle2 = new Bundle(['/two', 2]);
83
+ * bundle1.append(bundle2);
84
+ */
19
85
  append(element) {
20
86
  this.elements.push(sanitize(element));
21
87
  }