buttplug 3.2.1 → 4.0.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 (179) hide show
  1. package/.eslintrc.js +25 -25
  2. package/.jscsrc +2 -2
  3. package/.jshintrc +5 -5
  4. package/.prettierrc.json +3 -3
  5. package/.yarnrc.yml +5 -1
  6. package/CHANGELOG.md +577 -571
  7. package/LICENSE +27 -27
  8. package/README.md +97 -97
  9. package/dist/main/src/client/ButtplugBrowserWebsocketClientConnector.js +6 -9
  10. package/dist/main/src/client/ButtplugBrowserWebsocketClientConnector.js.map +1 -1
  11. package/dist/main/src/client/{Client.d.ts → ButtplugClient.d.ts} +3 -4
  12. package/dist/main/src/client/ButtplugClient.js +227 -0
  13. package/dist/main/src/client/ButtplugClient.js.map +1 -0
  14. package/dist/main/src/client/ButtplugClientConnectorException.js +17 -7
  15. package/dist/main/src/client/ButtplugClientConnectorException.js.map +1 -1
  16. package/dist/main/src/client/ButtplugClientDevice.d.ts +13 -28
  17. package/dist/main/src/client/ButtplugClientDevice.js +105 -247
  18. package/dist/main/src/client/ButtplugClientDevice.js.map +1 -1
  19. package/dist/main/src/client/ButtplugClientDeviceCommand.d.ts +42 -0
  20. package/dist/main/src/client/ButtplugClientDeviceCommand.js +105 -0
  21. package/dist/main/src/client/ButtplugClientDeviceCommand.js.map +1 -0
  22. package/dist/main/src/client/ButtplugClientDeviceFeature.d.ts +18 -0
  23. package/dist/main/src/client/ButtplugClientDeviceFeature.js +166 -0
  24. package/dist/main/src/client/ButtplugClientDeviceFeature.js.map +1 -0
  25. package/dist/main/src/client/ButtplugNodeWebsocketClientConnector.d.ts +1 -8
  26. package/dist/main/src/client/ButtplugNodeWebsocketClientConnector.js +1 -4
  27. package/dist/main/src/client/ButtplugNodeWebsocketClientConnector.js.map +1 -1
  28. package/dist/main/src/core/Exceptions.js +27 -9
  29. package/dist/main/src/core/Exceptions.js.map +1 -1
  30. package/dist/main/src/core/Logging.js +12 -6
  31. package/dist/main/src/core/Logging.js.map +1 -1
  32. package/dist/main/src/core/Messages.d.ts +118 -228
  33. package/dist/main/src/core/Messages.js +51 -404
  34. package/dist/main/src/core/Messages.js.map +1 -1
  35. package/dist/main/src/index.d.ts +2 -2
  36. package/dist/main/src/index.js +4 -2
  37. package/dist/main/src/index.js.map +1 -1
  38. package/dist/main/src/utils/ButtplugBrowserWebsocketConnector.js +40 -52
  39. package/dist/main/src/utils/ButtplugBrowserWebsocketConnector.js.map +1 -1
  40. package/dist/main/src/utils/ButtplugMessageSorter.js +27 -15
  41. package/dist/main/src/utils/ButtplugMessageSorter.js.map +1 -1
  42. package/dist/main/src/utils/Utils.js +1 -2
  43. package/dist/main/src/utils/Utils.js.map +1 -1
  44. package/dist/web/buttplug.js +1 -38
  45. package/dist/web/buttplug.mjs +595 -1984
  46. package/dist/web/client/ButtplugBrowserWebsocketClientConnector.d.ts +0 -7
  47. package/dist/web/client/{Client.d.ts → ButtplugClient.d.ts} +3 -11
  48. package/dist/web/client/ButtplugClientConnectorException.d.ts +0 -7
  49. package/dist/web/client/ButtplugClientDevice.d.ts +14 -29
  50. package/dist/web/client/ButtplugClientDeviceCommand.d.ts +42 -0
  51. package/dist/web/client/ButtplugClientDeviceFeature.d.ts +18 -0
  52. package/dist/web/client/ButtplugNodeWebsocketClientConnector.d.ts +1 -15
  53. package/dist/web/client/IButtplugClientConnector.d.ts +0 -7
  54. package/dist/web/core/Exceptions.d.ts +1 -1
  55. package/dist/web/core/Logging.d.ts +0 -7
  56. package/dist/web/core/Messages.d.ts +118 -227
  57. package/dist/web/index.d.ts +2 -2
  58. package/dist/web/utils/ButtplugBrowserWebsocketConnector.d.ts +0 -7
  59. package/examples/node/SYNC_MANIFEST.md +105 -0
  60. package/examples/node/application-example.ts +213 -0
  61. package/examples/node/async-example.ts +124 -0
  62. package/examples/node/connection-example.ts +76 -0
  63. package/examples/node/device-control-example.ts +131 -0
  64. package/examples/node/device-enumeration-example.ts +86 -0
  65. package/examples/node/device-info-example.ts +131 -0
  66. package/examples/node/errors-example.ts +166 -0
  67. package/examples/node/package-lock.json +281 -0
  68. package/examples/node/package.json +25 -0
  69. package/examples/node/remote-connector-example.ts +84 -0
  70. package/examples/node/tsconfig.json +14 -0
  71. package/examples/web/application-example.js +197 -0
  72. package/examples/web/async-example.js +90 -0
  73. package/examples/web/device-control-example.js +87 -0
  74. package/examples/web/device-enumeration-example.js +49 -0
  75. package/examples/web/device-info-example.js +100 -0
  76. package/examples/web/errors-example.js +110 -0
  77. package/examples/web/index.html +55 -0
  78. package/examples/web/logging.js +42 -0
  79. package/examples/web/ping-timeout-example.js +59 -0
  80. package/examples/web/remote-connector-example.js +68 -0
  81. package/node-test.js +24 -0
  82. package/node-test.ts +23 -5
  83. package/package.json +85 -87
  84. package/src/client/ButtplugBrowserWebsocketClientConnector.ts +25 -25
  85. package/src/client/ButtplugClient.ts +242 -0
  86. package/src/client/ButtplugClientConnectorException.ts +16 -16
  87. package/src/client/ButtplugClientDevice.ts +178 -401
  88. package/src/client/ButtplugClientDeviceCommand.ts +112 -0
  89. package/src/client/ButtplugClientDeviceFeature.ts +138 -0
  90. package/src/client/ButtplugNodeWebsocketClientConnector.ts +17 -17
  91. package/src/client/IButtplugClientConnector.ts +18 -18
  92. package/src/core/Exceptions.ts +107 -101
  93. package/src/core/Logging.ts +197 -197
  94. package/src/core/Messages.ts +209 -480
  95. package/src/core/index.d.ts +4 -4
  96. package/src/index.ts +21 -19
  97. package/src/utils/ButtplugBrowserWebsocketConnector.ts +89 -92
  98. package/src/utils/ButtplugMessageSorter.ts +66 -65
  99. package/src/utils/Utils.ts +3 -3
  100. package/tsconfig.json +22 -22
  101. package/tsfmt.json +14 -14
  102. package/tslint.json +27 -27
  103. package/typedocconfig.js +6 -6
  104. package/vite.config.ts +26 -26
  105. package/dist/main/src/client/Client.js +0 -242
  106. package/dist/main/src/client/Client.js.map +0 -1
  107. package/dist/main/src/core/MessageUtils.d.ts +0 -10
  108. package/dist/main/src/core/MessageUtils.js +0 -65
  109. package/dist/main/src/core/MessageUtils.js.map +0 -1
  110. package/dist/web/core/MessageUtils.d.ts +0 -10
  111. package/doc/.nojekyll +0 -1
  112. package/doc/assets/highlight.css +0 -22
  113. package/doc/assets/main.js +0 -58
  114. package/doc/assets/search.js +0 -1
  115. package/doc/assets/style.css +0 -1280
  116. package/doc/classes/ButtplugBrowserWebsocketClientConnector.html +0 -234
  117. package/doc/classes/ButtplugClient.html +0 -331
  118. package/doc/classes/ButtplugClientConnectorException.html +0 -216
  119. package/doc/classes/ButtplugClientDevice.html +0 -489
  120. package/doc/classes/ButtplugDeviceError.html +0 -218
  121. package/doc/classes/ButtplugDeviceMessage.html +0 -165
  122. package/doc/classes/ButtplugError.html +0 -220
  123. package/doc/classes/ButtplugInitError.html +0 -218
  124. package/doc/classes/ButtplugLogger.html +0 -288
  125. package/doc/classes/ButtplugMessage.html +0 -147
  126. package/doc/classes/ButtplugMessageError.html +0 -218
  127. package/doc/classes/ButtplugMessageSorter.html +0 -128
  128. package/doc/classes/ButtplugNodeWebsocketClientConnector.html +0 -239
  129. package/doc/classes/ButtplugPingError.html +0 -218
  130. package/doc/classes/ButtplugSystemMessage.html +0 -150
  131. package/doc/classes/ButtplugUnknownError.html +0 -218
  132. package/doc/classes/DeviceAdded.html +0 -186
  133. package/doc/classes/DeviceInfo.html +0 -114
  134. package/doc/classes/DeviceList.html +0 -160
  135. package/doc/classes/DeviceRemoved.html +0 -158
  136. package/doc/classes/Error.html +0 -179
  137. package/doc/classes/GenericDeviceMessageAttributes.html +0 -107
  138. package/doc/classes/GenericMessageSubcommand.html +0 -90
  139. package/doc/classes/LinearCmd.html +0 -187
  140. package/doc/classes/LogMessage.html +0 -134
  141. package/doc/classes/MessageAttributes.html +0 -160
  142. package/doc/classes/Ok.html +0 -151
  143. package/doc/classes/Ping.html +0 -151
  144. package/doc/classes/RawDeviceMessageAttributes.html +0 -86
  145. package/doc/classes/RawReadCmd.html +0 -188
  146. package/doc/classes/RawReading.html +0 -179
  147. package/doc/classes/RawSubscribeCmd.html +0 -170
  148. package/doc/classes/RawUnsubscribeCmd.html +0 -170
  149. package/doc/classes/RawWriteCmd.html +0 -188
  150. package/doc/classes/RequestDeviceList.html +0 -151
  151. package/doc/classes/RequestServerInfo.html +0 -169
  152. package/doc/classes/RotateCmd.html +0 -187
  153. package/doc/classes/RotateSubcommand.html +0 -108
  154. package/doc/classes/ScalarCmd.html +0 -170
  155. package/doc/classes/ScalarSubcommand.html +0 -108
  156. package/doc/classes/ScanningFinished.html +0 -146
  157. package/doc/classes/SensorDeviceMessageAttributes.html +0 -107
  158. package/doc/classes/SensorReadCmd.html +0 -179
  159. package/doc/classes/SensorReading.html +0 -188
  160. package/doc/classes/ServerInfo.html +0 -178
  161. package/doc/classes/StartScanning.html +0 -151
  162. package/doc/classes/StopAllDevices.html +0 -151
  163. package/doc/classes/StopDeviceCmd.html +0 -161
  164. package/doc/classes/StopScanning.html +0 -151
  165. package/doc/classes/VectorSubcommand.html +0 -108
  166. package/doc/enums/ActuatorType.html +0 -104
  167. package/doc/enums/ButtplugLogLevel.html +0 -97
  168. package/doc/enums/ErrorClass.html +0 -90
  169. package/doc/enums/SensorType.html +0 -90
  170. package/doc/functions/FromJSON.html +0 -113
  171. package/doc/index.html +0 -184
  172. package/doc/interfaces/IButtplugClientConnector.html +0 -137
  173. package/doc/modules.html +0 -176
  174. package/doc/variables/DEFAULT_MESSAGE_ID.html +0 -104
  175. package/doc/variables/MAX_ID.html +0 -104
  176. package/doc/variables/MESSAGE_SPEC_VERSION.html +0 -104
  177. package/doc/variables/SYSTEM_MESSAGE_ID.html +0 -104
  178. package/src/client/Client.ts +0 -276
  179. package/src/core/MessageUtils.ts +0 -48
@@ -0,0 +1,86 @@
1
+ // Buttplug TypeScript - Device Enumeration Example
2
+ //
3
+ // This example demonstrates how to scan for devices and handle
4
+ // device connection/disconnection events.
5
+ //
6
+ // Prerequisites:
7
+ // 1. Install Intiface Central: https://intiface.com/central
8
+ // 2. Start the server in Intiface Central
9
+ // 3. Run: npx ts-node --esm device-enumeration-example.ts
10
+
11
+ import {
12
+ ButtplugClient,
13
+ ButtplugNodeWebsocketClientConnector,
14
+ ButtplugClientDevice,
15
+ } from 'buttplug';
16
+ import * as readline from 'readline';
17
+
18
+ async function waitForEnter(prompt: string): Promise<void> {
19
+ const rl = readline.createInterface({
20
+ input: process.stdin,
21
+ output: process.stdout,
22
+ });
23
+ return new Promise((resolve) => {
24
+ rl.question(prompt, () => {
25
+ rl.close();
26
+ resolve();
27
+ });
28
+ });
29
+ }
30
+
31
+ async function main(): Promise<void> {
32
+ const client = new ButtplugClient('Device Enumeration Example');
33
+
34
+ // Set up event handlers BEFORE connecting.
35
+ // This ensures we don't miss any events.
36
+
37
+ client.addListener('deviceadded', (device: ButtplugClientDevice) => {
38
+ console.log(`Device connected: ${device.name}`);
39
+ });
40
+
41
+ client.addListener('deviceremoved', (device: ButtplugClientDevice) => {
42
+ console.log(`Device disconnected: ${device.name}`);
43
+ });
44
+
45
+ client.addListener('scanningfinished', () => {
46
+ console.log('Scanning finished.');
47
+ });
48
+
49
+ // Connect to the server
50
+ const connector = new ButtplugNodeWebsocketClientConnector(
51
+ 'ws://127.0.0.1:12345'
52
+ );
53
+
54
+ console.log('Connecting...');
55
+ await client.connect(connector);
56
+ console.log('Connected!');
57
+
58
+ // Start scanning for devices.
59
+ // Devices will be announced via the 'deviceadded' event.
60
+ console.log('\nStarting device scan...');
61
+ console.log('Turn on your devices now!');
62
+ await client.startScanning();
63
+
64
+ await waitForEnter('\nPress Enter to stop scanning...');
65
+
66
+ // Stop scanning. Some protocols scan continuously until told to stop.
67
+ await client.stopScanning();
68
+
69
+ // The client maintains a map of all known devices.
70
+ // This map persists even after scanning stops.
71
+ console.log('\nCurrently connected devices:');
72
+ if (client.devices.size === 0) {
73
+ console.log(' (no devices connected)');
74
+ } else {
75
+ for (const [index, device] of client.devices) {
76
+ console.log(` - ${device.name} (Index: ${index})`);
77
+ }
78
+ }
79
+
80
+ await waitForEnter('\nPress Enter to disconnect...');
81
+
82
+ await client.disconnect();
83
+ console.log('Disconnected.');
84
+ }
85
+
86
+ main().catch(console.error);
@@ -0,0 +1,131 @@
1
+ // Buttplug TypeScript - Device Info Example
2
+ //
3
+ // This example demonstrates how to introspect device features
4
+ // and capabilities in detail.
5
+ //
6
+ // Prerequisites:
7
+ // 1. Install Intiface Central: https://intiface.com/central
8
+ // 2. Start the server in Intiface Central
9
+ // 3. Run: npx ts-node --esm device-info-example.ts
10
+
11
+ import {
12
+ ButtplugClient,
13
+ ButtplugNodeWebsocketClientConnector,
14
+ ButtplugClientDevice,
15
+ OutputType,
16
+ InputType,
17
+ } from 'buttplug';
18
+ import * as readline from 'readline';
19
+
20
+ async function waitForEnter(prompt: string): Promise<void> {
21
+ const rl = readline.createInterface({
22
+ input: process.stdin,
23
+ output: process.stdout,
24
+ });
25
+ return new Promise((resolve) => {
26
+ rl.question(prompt, () => {
27
+ rl.close();
28
+ resolve();
29
+ });
30
+ });
31
+ }
32
+
33
+ function printDeviceInfo(device: ButtplugClientDevice): void {
34
+ console.log(`\n${'='.repeat(50)}`);
35
+ console.log(`Device: ${device.name}`);
36
+ if (device.displayName) {
37
+ console.log(`Display Name: ${device.displayName}`);
38
+ }
39
+ console.log(`Index: ${device.index}`);
40
+ if (device.messageTimingGap !== undefined) {
41
+ console.log(`Message Timing Gap: ${device.messageTimingGap}ms`);
42
+ }
43
+ console.log(`${'='.repeat(50)}`);
44
+
45
+ // Collect output capabilities
46
+ const outputTypes: string[] = [];
47
+ if (device.hasOutput(OutputType.Vibrate)) outputTypes.push('Vibrate');
48
+ if (device.hasOutput(OutputType.Rotate)) outputTypes.push('Rotate');
49
+ if (device.hasOutput(OutputType.Oscillate)) outputTypes.push('Oscillate');
50
+ if (device.hasOutput(OutputType.Position)) outputTypes.push('Position');
51
+ if (device.hasOutput(OutputType.Constrict)) outputTypes.push('Constrict');
52
+ if (device.hasOutput(OutputType.Inflate)) outputTypes.push('Inflate');
53
+ if (device.hasOutput(OutputType.Temperature)) outputTypes.push('Temperature');
54
+ if (device.hasOutput(OutputType.Led)) outputTypes.push('LED');
55
+
56
+ if (outputTypes.length > 0) {
57
+ console.log(`\nOutput Capabilities: ${outputTypes.join(', ')}`);
58
+ }
59
+
60
+ // Collect input capabilities
61
+ const inputTypes: string[] = [];
62
+ if (device.hasInput(InputType.Battery)) inputTypes.push('Battery');
63
+ if (device.hasInput(InputType.RSSI)) inputTypes.push('RSSI');
64
+ if (device.hasInput(InputType.Button)) inputTypes.push('Button');
65
+ if (device.hasInput(InputType.Pressure)) inputTypes.push('Pressure');
66
+
67
+ if (inputTypes.length > 0) {
68
+ console.log(`Input Capabilities: ${inputTypes.join(', ')}`);
69
+ }
70
+
71
+ // Detailed feature breakdown
72
+ console.log('\nDetailed Features:');
73
+ for (const [index, feature] of device.features) {
74
+ // Access the underlying feature definition
75
+ const def = (feature as any)._feature;
76
+ console.log(`\n Feature ${index}: ${def.FeatureDescriptor}`);
77
+
78
+ if (def.Output) {
79
+ console.log(' Outputs:');
80
+ for (const [type, config] of Object.entries(def.Output)) {
81
+ const cfg = config as { Value: number[] };
82
+ console.log(
83
+ ` - ${type}: steps ${cfg.Value[0]}-${cfg.Value[1]}`
84
+ );
85
+ }
86
+ }
87
+
88
+ if (def.Input) {
89
+ console.log(' Inputs:');
90
+ for (const [type, config] of Object.entries(def.Input)) {
91
+ const cfg = config as { Value: number[]; Command: string[] };
92
+ console.log(
93
+ ` - ${type}: commands [${cfg.Command.join(', ')}]`
94
+ );
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ async function main(): Promise<void> {
101
+ const client = new ButtplugClient('Device Info Example');
102
+
103
+ // Connect
104
+ const connector = new ButtplugNodeWebsocketClientConnector(
105
+ 'ws://127.0.0.1:12345'
106
+ );
107
+
108
+ console.log('Connecting...');
109
+ await client.connect(connector);
110
+ console.log('Connected! Scanning for devices...');
111
+
112
+ await client.startScanning();
113
+ await waitForEnter('Turn on your devices, then press Enter...');
114
+ await client.stopScanning();
115
+
116
+ // Display info for all connected devices
117
+ if (client.devices.size === 0) {
118
+ console.log('No devices found.');
119
+ } else {
120
+ console.log(`\nFound ${client.devices.size} device(s):`);
121
+ for (const [_, device] of client.devices) {
122
+ printDeviceInfo(device);
123
+ }
124
+ }
125
+
126
+ await waitForEnter('\nPress Enter to disconnect...');
127
+ await client.disconnect();
128
+ console.log('Disconnected.');
129
+ }
130
+
131
+ main().catch(console.error);
@@ -0,0 +1,166 @@
1
+ // Buttplug TypeScript - Error Handling Example
2
+ //
3
+ // This example demonstrates the different error types in Buttplug
4
+ // and how to handle them. This is a reference for error handling patterns.
5
+ //
6
+ // Prerequisites:
7
+ // 1. Install Intiface Central: https://intiface.com/central
8
+ // 2. Start the server in Intiface Central
9
+ // 3. Run: npx ts-node --esm errors-example.ts
10
+
11
+ import {
12
+ ButtplugClient,
13
+ ButtplugNodeWebsocketClientConnector,
14
+ ButtplugClientConnectorException,
15
+ ButtplugError,
16
+ ButtplugDeviceError,
17
+ ButtplugInitError,
18
+ ButtplugMessageError,
19
+ ButtplugPingError,
20
+ DeviceOutput,
21
+ } from 'buttplug';
22
+ import * as readline from 'readline';
23
+
24
+ async function waitForEnter(prompt: string): Promise<void> {
25
+ const rl = readline.createInterface({
26
+ input: process.stdin,
27
+ output: process.stdout,
28
+ });
29
+ return new Promise((resolve) => {
30
+ rl.question(prompt, () => {
31
+ rl.close();
32
+ resolve();
33
+ });
34
+ });
35
+ }
36
+
37
+ function delay(ms: number): Promise<void> {
38
+ return new Promise((resolve) => setTimeout(resolve, ms));
39
+ }
40
+
41
+ // All Buttplug errors inherit from ButtplugError.
42
+ // Here's the hierarchy:
43
+ //
44
+ // ButtplugError (base class)
45
+ // +-- ButtplugClientConnectorException - Connection/transport issues
46
+ // +-- ButtplugInitError - Client/server version mismatch
47
+ // +-- ButtplugDeviceError - Device communication errors
48
+ // +-- ButtplugMessageError - Invalid message format/content
49
+ // +-- ButtplugPingError - Server ping timeout
50
+
51
+ function handleButtplugError(e: unknown): void {
52
+ if (e instanceof ButtplugClientConnectorException) {
53
+ // The connector couldn't establish or maintain connection.
54
+ // Causes: server not running, wrong address, network issues,
55
+ // SSL/TLS problems, connection dropped.
56
+ console.log(`[Connector Error] ${e.message}`);
57
+ console.log('Check that the server is running and accessible.');
58
+ } else if (e instanceof ButtplugInitError) {
59
+ // Client and server couldn't agree on protocol version.
60
+ // Usually means you need to upgrade client or server.
61
+ console.log(`[Init/Handshake Error] ${e.message}`);
62
+ console.log('Client and server versions may be incompatible.');
63
+ } else if (e instanceof ButtplugDeviceError) {
64
+ // Something went wrong communicating with a device.
65
+ // Causes: device disconnected, invalid command for device,
66
+ // device rejected command, hardware error.
67
+ console.log(`[Device Error] ${e.message}`);
68
+ console.log(
69
+ "The device may have disconnected or doesn't support this command."
70
+ );
71
+ } else if (e instanceof ButtplugMessageError) {
72
+ // The message sent was invalid.
73
+ // Causes: malformed message, missing required fields,
74
+ // invalid parameter values.
75
+ console.log(`[Message Error] ${e.message}`);
76
+ console.log(
77
+ 'This usually indicates a bug in the client library or application.'
78
+ );
79
+ } else if (e instanceof ButtplugPingError) {
80
+ // Server didn't receive ping in time, connection terminated.
81
+ // The ping system ensures dead connections are detected.
82
+ console.log(`[Ping Error] ${e.message}`);
83
+ console.log('Connection was lost due to ping timeout.');
84
+ } else if (e instanceof ButtplugError) {
85
+ // Unknown or future error type
86
+ console.log(`[Buttplug Error] ${e.message}`);
87
+ } else if (e instanceof Error) {
88
+ // Non-Buttplug error
89
+ console.log(`[System Error] ${e.message}`);
90
+ } else {
91
+ console.log(`[Unknown Error] ${e}`);
92
+ }
93
+ }
94
+
95
+ async function main(): Promise<void> {
96
+ console.log('Error Handling Example');
97
+ console.log('======================\n');
98
+
99
+ // Example 1: Connection error (server not running)
100
+ console.log('1. Attempting to connect to non-existent server...');
101
+ const client1 = new ButtplugClient('Error Example');
102
+ try {
103
+ const badConnector = new ButtplugNodeWebsocketClientConnector(
104
+ 'ws://127.0.0.1:99999'
105
+ );
106
+ await client1.connect(badConnector);
107
+ } catch (e) {
108
+ handleButtplugError(e);
109
+ }
110
+
111
+ // Example 2: Demonstrating promise-based error handling
112
+ console.log('\n2. Demonstrating promise-based error handling...');
113
+ const client2 = new ButtplugClient('Promise Error Example');
114
+ const badConnector2 = new ButtplugNodeWebsocketClientConnector(
115
+ 'ws://127.0.0.1:99998'
116
+ );
117
+
118
+ // You can also catch errors using .catch() on promises
119
+ await client2
120
+ .connect(badConnector2)
121
+ .then(() => {
122
+ console.log('Connected (unexpected!)');
123
+ })
124
+ .catch((e) => {
125
+ console.log('Caught error using .catch():');
126
+ handleButtplugError(e);
127
+ });
128
+
129
+ // Example 3: Handling errors when sending commands after disconnect
130
+ console.log('\n3. Demonstrating error after disconnect...');
131
+ const client3 = new ButtplugClient('Disconnect Error Example');
132
+ try {
133
+ const connector = new ButtplugNodeWebsocketClientConnector(
134
+ 'ws://127.0.0.1:12345'
135
+ );
136
+ await client3.connect(connector);
137
+ console.log('Connected successfully.');
138
+
139
+ // Scan briefly to get a device
140
+ await client3.startScanning();
141
+ await delay(1000);
142
+ await client3.stopScanning();
143
+
144
+ if (client3.devices.size > 0) {
145
+ const device = client3.devices.values().next().value!;
146
+ console.log(`Found device: ${device.name}`);
147
+
148
+ // Disconnect
149
+ await client3.disconnect();
150
+ console.log('Disconnected.');
151
+
152
+ // Now try to send a command - this will throw
153
+ console.log('Attempting to send command after disconnect...');
154
+ await device.runOutput(DeviceOutput.Vibrate.percent(0.5));
155
+ } else {
156
+ console.log('No devices found to test with.');
157
+ await client3.disconnect();
158
+ }
159
+ } catch (e) {
160
+ handleButtplugError(e);
161
+ }
162
+
163
+ await waitForEnter('\nPress Enter to exit...');
164
+ }
165
+
166
+ main().catch(console.error);
@@ -0,0 +1,281 @@
1
+ {
2
+ "name": "buttplug-examples",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "buttplug-examples",
9
+ "version": "1.0.0",
10
+ "dependencies": {
11
+ "buttplug": "file:../js"
12
+ },
13
+ "devDependencies": {
14
+ "@types/node": "^22.0.0",
15
+ "ts-node": "^10.9.2",
16
+ "typescript": "^5.6.0"
17
+ }
18
+ },
19
+ "../js": {
20
+ "name": "buttplug",
21
+ "version": "4.0.0",
22
+ "license": "BSD-3-Clause",
23
+ "dependencies": {
24
+ "eventemitter3": "^5.0.1",
25
+ "ws": "^8.18.3"
26
+ },
27
+ "devDependencies": {
28
+ "@types/commander": "^2.12.5",
29
+ "@types/expect-puppeteer": "^5.0.6",
30
+ "@types/jest": "^30.0.0",
31
+ "@types/jest-environment-puppeteer": "^5.0.6",
32
+ "@types/node": "^24.10.1",
33
+ "@types/uuid-parse": "^1.0.2",
34
+ "@types/ws": "^8.18.1",
35
+ "@typescript-eslint/eslint-plugin": "^8.47.0",
36
+ "@typescript-eslint/parser": "^8.47.0",
37
+ "copyfiles": "^2.4.1",
38
+ "cross-env": "^10.1.0",
39
+ "eslint": "^9.39.1",
40
+ "eslint-plugin-node": "^11.1.0",
41
+ "gts": "^6.0.2",
42
+ "jest": "^30.2.0",
43
+ "mock-socket": "^9.3.1",
44
+ "pkg": "^5.8.1",
45
+ "tmp": "^0.2.5",
46
+ "trash": "^10.0.0",
47
+ "trash-cli": "^7.0.0",
48
+ "ts-jest": "^29.4.5",
49
+ "ts-node": "^10.9.2",
50
+ "tslib": "^2.8.1",
51
+ "typedoc": "^0.28.14",
52
+ "typescript": "^5.9.3",
53
+ "vite": "^7.2.4",
54
+ "vite-plugin-dts": "^4.5.4",
55
+ "yarn": "^1.22.22"
56
+ }
57
+ },
58
+ "node_modules/@cspotcode/source-map-support": {
59
+ "version": "0.8.1",
60
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
61
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
62
+ "dev": true,
63
+ "license": "MIT",
64
+ "dependencies": {
65
+ "@jridgewell/trace-mapping": "0.3.9"
66
+ },
67
+ "engines": {
68
+ "node": ">=12"
69
+ }
70
+ },
71
+ "node_modules/@jridgewell/resolve-uri": {
72
+ "version": "3.1.2",
73
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
74
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
75
+ "dev": true,
76
+ "license": "MIT",
77
+ "engines": {
78
+ "node": ">=6.0.0"
79
+ }
80
+ },
81
+ "node_modules/@jridgewell/sourcemap-codec": {
82
+ "version": "1.5.5",
83
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
84
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
85
+ "dev": true,
86
+ "license": "MIT"
87
+ },
88
+ "node_modules/@jridgewell/trace-mapping": {
89
+ "version": "0.3.9",
90
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
91
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
92
+ "dev": true,
93
+ "license": "MIT",
94
+ "dependencies": {
95
+ "@jridgewell/resolve-uri": "^3.0.3",
96
+ "@jridgewell/sourcemap-codec": "^1.4.10"
97
+ }
98
+ },
99
+ "node_modules/@tsconfig/node10": {
100
+ "version": "1.0.12",
101
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
102
+ "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
103
+ "dev": true,
104
+ "license": "MIT"
105
+ },
106
+ "node_modules/@tsconfig/node12": {
107
+ "version": "1.0.11",
108
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
109
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
110
+ "dev": true,
111
+ "license": "MIT"
112
+ },
113
+ "node_modules/@tsconfig/node14": {
114
+ "version": "1.0.3",
115
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
116
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
117
+ "dev": true,
118
+ "license": "MIT"
119
+ },
120
+ "node_modules/@tsconfig/node16": {
121
+ "version": "1.0.4",
122
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
123
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
124
+ "dev": true,
125
+ "license": "MIT"
126
+ },
127
+ "node_modules/@types/node": {
128
+ "version": "22.19.7",
129
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz",
130
+ "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==",
131
+ "dev": true,
132
+ "license": "MIT",
133
+ "dependencies": {
134
+ "undici-types": "~6.21.0"
135
+ }
136
+ },
137
+ "node_modules/acorn": {
138
+ "version": "8.15.0",
139
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
140
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
141
+ "dev": true,
142
+ "license": "MIT",
143
+ "bin": {
144
+ "acorn": "bin/acorn"
145
+ },
146
+ "engines": {
147
+ "node": ">=0.4.0"
148
+ }
149
+ },
150
+ "node_modules/acorn-walk": {
151
+ "version": "8.3.4",
152
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
153
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
154
+ "dev": true,
155
+ "license": "MIT",
156
+ "dependencies": {
157
+ "acorn": "^8.11.0"
158
+ },
159
+ "engines": {
160
+ "node": ">=0.4.0"
161
+ }
162
+ },
163
+ "node_modules/arg": {
164
+ "version": "4.1.3",
165
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
166
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
167
+ "dev": true,
168
+ "license": "MIT"
169
+ },
170
+ "node_modules/buttplug": {
171
+ "resolved": "../js",
172
+ "link": true
173
+ },
174
+ "node_modules/create-require": {
175
+ "version": "1.1.1",
176
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
177
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
178
+ "dev": true,
179
+ "license": "MIT"
180
+ },
181
+ "node_modules/diff": {
182
+ "version": "4.0.4",
183
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
184
+ "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
185
+ "dev": true,
186
+ "license": "BSD-3-Clause",
187
+ "engines": {
188
+ "node": ">=0.3.1"
189
+ }
190
+ },
191
+ "node_modules/make-error": {
192
+ "version": "1.3.6",
193
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
194
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
195
+ "dev": true,
196
+ "license": "ISC"
197
+ },
198
+ "node_modules/ts-node": {
199
+ "version": "10.9.2",
200
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
201
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
202
+ "dev": true,
203
+ "license": "MIT",
204
+ "dependencies": {
205
+ "@cspotcode/source-map-support": "^0.8.0",
206
+ "@tsconfig/node10": "^1.0.7",
207
+ "@tsconfig/node12": "^1.0.7",
208
+ "@tsconfig/node14": "^1.0.0",
209
+ "@tsconfig/node16": "^1.0.2",
210
+ "acorn": "^8.4.1",
211
+ "acorn-walk": "^8.1.1",
212
+ "arg": "^4.1.0",
213
+ "create-require": "^1.1.0",
214
+ "diff": "^4.0.1",
215
+ "make-error": "^1.1.1",
216
+ "v8-compile-cache-lib": "^3.0.1",
217
+ "yn": "3.1.1"
218
+ },
219
+ "bin": {
220
+ "ts-node": "dist/bin.js",
221
+ "ts-node-cwd": "dist/bin-cwd.js",
222
+ "ts-node-esm": "dist/bin-esm.js",
223
+ "ts-node-script": "dist/bin-script.js",
224
+ "ts-node-transpile-only": "dist/bin-transpile.js",
225
+ "ts-script": "dist/bin-script-deprecated.js"
226
+ },
227
+ "peerDependencies": {
228
+ "@swc/core": ">=1.2.50",
229
+ "@swc/wasm": ">=1.2.50",
230
+ "@types/node": "*",
231
+ "typescript": ">=2.7"
232
+ },
233
+ "peerDependenciesMeta": {
234
+ "@swc/core": {
235
+ "optional": true
236
+ },
237
+ "@swc/wasm": {
238
+ "optional": true
239
+ }
240
+ }
241
+ },
242
+ "node_modules/typescript": {
243
+ "version": "5.9.3",
244
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
245
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
246
+ "dev": true,
247
+ "license": "Apache-2.0",
248
+ "bin": {
249
+ "tsc": "bin/tsc",
250
+ "tsserver": "bin/tsserver"
251
+ },
252
+ "engines": {
253
+ "node": ">=14.17"
254
+ }
255
+ },
256
+ "node_modules/undici-types": {
257
+ "version": "6.21.0",
258
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
259
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
260
+ "dev": true,
261
+ "license": "MIT"
262
+ },
263
+ "node_modules/v8-compile-cache-lib": {
264
+ "version": "3.0.1",
265
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
266
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
267
+ "dev": true,
268
+ "license": "MIT"
269
+ },
270
+ "node_modules/yn": {
271
+ "version": "3.1.1",
272
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
273
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
274
+ "dev": true,
275
+ "license": "MIT",
276
+ "engines": {
277
+ "node": ">=6"
278
+ }
279
+ }
280
+ }
281
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "buttplug-examples",
3
+ "version": "1.0.0",
4
+ "description": "Example code for the Buttplug TypeScript/JavaScript library",
5
+ "private": true,
6
+ "type": "module",
7
+ "scripts": {
8
+ "connection": "npx ts-node --esm connection-example.ts",
9
+ "device-enumeration": "npx ts-node --esm device-enumeration-example.ts",
10
+ "device-control": "npx ts-node --esm device-control-example.ts",
11
+ "device-info": "npx ts-node --esm device-info-example.ts",
12
+ "async": "npx ts-node --esm async-example.ts",
13
+ "errors": "npx ts-node --esm errors-example.ts",
14
+ "remote-connector": "npx ts-node --esm remote-connector-example.ts",
15
+ "application": "npx ts-node --esm application-example.ts"
16
+ },
17
+ "dependencies": {
18
+ "buttplug": "file:../js"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^22.0.0",
22
+ "ts-node": "^10.9.2",
23
+ "typescript": "^5.6.0"
24
+ }
25
+ }