jmri-client 4.2.0-beta.2 → 5.1.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 (67) hide show
  1. package/README.md +3 -1
  2. package/dist/browser/jmri-client.js +88 -28
  3. package/dist/cjs/index.js +2442 -31
  4. package/dist/esm/index.js +2393 -17
  5. package/dist/types/client.d.ts +9 -1
  6. package/dist/types/index.d.ts +1 -1
  7. package/dist/types/managers/roster-manager.d.ts +9 -1
  8. package/dist/types/mocks/mock-data.d.ts +30 -6
  9. package/dist/types/mocks/mock-response-manager.d.ts +7 -2
  10. package/dist/types/types/jmri-messages.d.ts +22 -0
  11. package/docs/API.md +8 -0
  12. package/docs/BROWSER.md +4 -4
  13. package/docs/MIGRATION.md +30 -1
  14. package/docs/MOCK_MODE.md +15 -9
  15. package/package.json +17 -18
  16. package/dist/cjs/client.js +0 -366
  17. package/dist/cjs/core/connection-state-manager.js +0 -84
  18. package/dist/cjs/core/heartbeat-manager.js +0 -79
  19. package/dist/cjs/core/index.js +0 -25
  20. package/dist/cjs/core/message-queue.js +0 -59
  21. package/dist/cjs/core/reconnection-manager.js +0 -97
  22. package/dist/cjs/core/websocket-adapter.js +0 -135
  23. package/dist/cjs/core/websocket-client.js +0 -388
  24. package/dist/cjs/managers/index.js +0 -25
  25. package/dist/cjs/managers/light-manager.js +0 -111
  26. package/dist/cjs/managers/power-manager.js +0 -90
  27. package/dist/cjs/managers/roster-manager.js +0 -118
  28. package/dist/cjs/managers/system-connections-manager.js +0 -28
  29. package/dist/cjs/managers/throttle-manager.js +0 -233
  30. package/dist/cjs/managers/turnout-manager.js +0 -111
  31. package/dist/cjs/mocks/index.js +0 -12
  32. package/dist/cjs/mocks/mock-data.js +0 -237
  33. package/dist/cjs/mocks/mock-response-manager.js +0 -290
  34. package/dist/cjs/types/client-options.js +0 -66
  35. package/dist/cjs/types/events.js +0 -16
  36. package/dist/cjs/types/index.js +0 -23
  37. package/dist/cjs/types/jmri-messages.js +0 -95
  38. package/dist/cjs/types/throttle.js +0 -19
  39. package/dist/cjs/utils/exponential-backoff.js +0 -40
  40. package/dist/cjs/utils/index.js +0 -21
  41. package/dist/cjs/utils/message-id.js +0 -40
  42. package/dist/esm/client.js +0 -362
  43. package/dist/esm/core/connection-state-manager.js +0 -80
  44. package/dist/esm/core/heartbeat-manager.js +0 -75
  45. package/dist/esm/core/index.js +0 -9
  46. package/dist/esm/core/message-queue.js +0 -55
  47. package/dist/esm/core/reconnection-manager.js +0 -93
  48. package/dist/esm/core/websocket-adapter.js +0 -98
  49. package/dist/esm/core/websocket-client.js +0 -384
  50. package/dist/esm/managers/index.js +0 -9
  51. package/dist/esm/managers/light-manager.js +0 -107
  52. package/dist/esm/managers/power-manager.js +0 -86
  53. package/dist/esm/managers/roster-manager.js +0 -114
  54. package/dist/esm/managers/system-connections-manager.js +0 -24
  55. package/dist/esm/managers/throttle-manager.js +0 -229
  56. package/dist/esm/managers/turnout-manager.js +0 -107
  57. package/dist/esm/mocks/index.js +0 -6
  58. package/dist/esm/mocks/mock-data.js +0 -234
  59. package/dist/esm/mocks/mock-response-manager.js +0 -286
  60. package/dist/esm/types/client-options.js +0 -62
  61. package/dist/esm/types/events.js +0 -13
  62. package/dist/esm/types/index.js +0 -7
  63. package/dist/esm/types/jmri-messages.js +0 -89
  64. package/dist/esm/types/throttle.js +0 -15
  65. package/dist/esm/utils/exponential-backoff.js +0 -36
  66. package/dist/esm/utils/index.js +0 -5
  67. package/dist/esm/utils/message-id.js +0 -36
@@ -4,7 +4,7 @@
4
4
  import { EventEmitter } from 'eventemitter3';
5
5
  import { WebSocketClient } from './core/websocket-client.js';
6
6
  import { PartialClientOptions } from './types/client-options.js';
7
- import { PowerState, RosterEntryWrapper, TurnoutState, TurnoutData, LightState, LightData, SystemConnectionData } from './types/jmri-messages.js';
7
+ import { PowerState, RosterEntryWrapper, RosterGroup, TurnoutState, TurnoutData, LightState, LightData, SystemConnectionData } from './types/jmri-messages.js';
8
8
  import { ConnectionState } from './types/events.js';
9
9
  import { ThrottleAcquireOptions, ThrottleFunctionKey, ThrottleState } from './types/throttle.js';
10
10
  /**
@@ -104,6 +104,14 @@ export declare class JmriClient extends EventEmitter {
104
104
  * Search roster by partial name match
105
105
  */
106
106
  searchRoster(query: string): Promise<RosterEntryWrapper[]>;
107
+ /**
108
+ * Get all roster groups
109
+ */
110
+ getRosterGroups(): Promise<RosterGroup[]>;
111
+ /**
112
+ * Get roster entries belonging to a specific group
113
+ */
114
+ getRosterEntriesByGroup(group: string): Promise<RosterEntryWrapper[]>;
107
115
  /**
108
116
  * Get the current state of a turnout
109
117
  */
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export { JmriClient } from './client.js';
6
6
  export { WebSocketClient } from './core/websocket-client.js';
7
- export { JmriClientOptions, PartialClientOptions, ReconnectionOptions, HeartbeatOptions, MockOptions, PowerState, TurnoutState, LightState, RosterEntry, TurnoutData, LightData, JmriMessage, PowerMessage, TurnoutMessage, LightMessage, ThrottleMessage, RosterMessage, ConnectionState, EventPayloads, ThrottleAcquireOptions, ThrottleFunctionKey, ThrottleState } from './types/index.js';
7
+ export { JmriClientOptions, PartialClientOptions, ReconnectionOptions, HeartbeatOptions, MockOptions, PowerState, TurnoutState, LightState, RosterEntry, RosterGroup, TurnoutData, LightData, JmriMessage, PowerMessage, TurnoutMessage, LightMessage, ThrottleMessage, RosterMessage, ConnectionState, EventPayloads, ThrottleAcquireOptions, ThrottleFunctionKey, ThrottleState } from './types/index.js';
8
8
  export { isThrottleFunctionKey, isValidSpeed } from './types/throttle.js';
9
9
  export { powerStateToString, turnoutStateToString, lightStateToString } from './types/jmri-messages.js';
10
10
  export { MockResponseManager, mockResponseManager, mockData } from './mocks/index.js';
@@ -2,7 +2,7 @@
2
2
  * Roster management
3
3
  */
4
4
  import { WebSocketClient } from '../core/websocket-client.js';
5
- import { RosterEntryWrapper } from '../types/jmri-messages.js';
5
+ import { RosterEntryWrapper, RosterGroup } from '../types/jmri-messages.js';
6
6
  /**
7
7
  * Manages locomotive roster
8
8
  */
@@ -26,6 +26,14 @@ export declare class RosterManager {
26
26
  * Search roster by partial name match
27
27
  */
28
28
  searchRoster(query: string): Promise<RosterEntryWrapper[]>;
29
+ /**
30
+ * Get all roster groups
31
+ */
32
+ getRosterGroups(): Promise<RosterGroup[]>;
33
+ /**
34
+ * Get roster entries belonging to a specific group
35
+ */
36
+ getRosterEntriesByGroup(group: string): Promise<RosterEntryWrapper[]>;
29
37
  /**
30
38
  * Get cached roster (no network request)
31
39
  */
@@ -96,8 +96,11 @@ export declare const mockData: {
96
96
  readonly icon: null;
97
97
  readonly selectedIcon: null;
98
98
  }];
99
- readonly attributes: readonly [];
100
- readonly rosterGroups: readonly [];
99
+ readonly attributes: readonly [{
100
+ readonly name: "RosterGroup:diesels";
101
+ readonly value: "yes";
102
+ }];
103
+ readonly rosterGroups: readonly ["diesels"];
101
104
  };
102
105
  readonly id: 1;
103
106
  }, {
@@ -150,8 +153,11 @@ export declare const mockData: {
150
153
  readonly icon: null;
151
154
  readonly selectedIcon: null;
152
155
  }];
153
- readonly attributes: readonly [];
154
- readonly rosterGroups: readonly [];
156
+ readonly attributes: readonly [{
157
+ readonly name: "RosterGroup:steam";
158
+ readonly value: "yes";
159
+ }];
160
+ readonly rosterGroups: readonly ["steam"];
155
161
  };
156
162
  readonly id: 2;
157
163
  }, {
@@ -210,12 +216,30 @@ export declare const mockData: {
210
216
  readonly icon: null;
211
217
  readonly selectedIcon: null;
212
218
  }];
213
- readonly attributes: readonly [];
214
- readonly rosterGroups: readonly [];
219
+ readonly attributes: readonly [{
220
+ readonly name: "RosterGroup:diesels";
221
+ readonly value: "yes";
222
+ }];
223
+ readonly rosterGroups: readonly ["diesels"];
215
224
  };
216
225
  readonly id: 3;
217
226
  }];
218
227
  };
228
+ readonly rosterGroup: {
229
+ readonly list: readonly [{
230
+ readonly type: "rosterGroup";
231
+ readonly data: {
232
+ readonly name: "diesels";
233
+ readonly length: 2;
234
+ };
235
+ }, {
236
+ readonly type: "rosterGroup";
237
+ readonly data: {
238
+ readonly name: "steam";
239
+ readonly length: 1;
240
+ };
241
+ }];
242
+ };
219
243
  readonly throttle: {
220
244
  readonly acquire: {
221
245
  readonly success: {
@@ -20,6 +20,7 @@ export interface MockResponseManagerOptions {
20
20
  export declare class MockResponseManager {
21
21
  private responseDelay;
22
22
  private powerState;
23
+ private powerStateByPrefix;
23
24
  private throttles;
24
25
  private lights;
25
26
  private turnouts;
@@ -33,13 +34,17 @@ export declare class MockResponseManager {
33
34
  */
34
35
  private getHelloResponse;
35
36
  /**
36
- * Get power response
37
+ * Get power response, with optional per-prefix state tracking
37
38
  */
38
39
  private getPowerResponse;
39
40
  /**
40
- * Get roster response
41
+ * Get roster response, optionally filtered by group
41
42
  */
42
43
  private getRosterResponse;
44
+ /**
45
+ * Get roster group response
46
+ */
47
+ private getRosterGroupResponse;
43
48
  /**
44
49
  * Get throttle response
45
50
  */
@@ -10,6 +10,7 @@ export interface JmriMessage {
10
10
  method?: 'get' | 'post' | 'put' | 'delete' | 'list';
11
11
  data?: any;
12
12
  id?: number;
13
+ params?: Record<string, string>;
13
14
  }
14
15
  /**
15
16
  * Power state values (from JMRI JSON protocol constants)
@@ -166,6 +167,27 @@ export interface RosterMessage extends JmriMessage {
166
167
  export interface RosterData {
167
168
  [key: string]: RosterEntry;
168
169
  }
170
+ export interface RosterGroup {
171
+ name: string;
172
+ length: number;
173
+ }
174
+ export interface RosterGroupWrapper {
175
+ type: 'rosterGroup';
176
+ data: RosterGroup;
177
+ }
178
+ export interface RosterGroupMessage extends JmriMessage {
179
+ type: 'rosterGroup';
180
+ method: 'list';
181
+ data?: RosterGroupWrapper[];
182
+ }
183
+ export interface RosterMessageWithParams extends JmriMessage {
184
+ type: 'roster';
185
+ method: 'list';
186
+ params?: {
187
+ group: string;
188
+ };
189
+ data?: RosterResponse;
190
+ }
169
191
  /**
170
192
  * Turnout state values (from JMRI JSON protocol constants)
171
193
  * UNKNOWN = 0 (state cannot be determined)
package/docs/API.md CHANGED
@@ -141,6 +141,13 @@ const entry = await client.getRosterEntryByAddress(3);
141
141
 
142
142
  // Search roster
143
143
  const results = await client.searchRoster('steam');
144
+
145
+ // Get all roster groups
146
+ const groups: RosterGroup[] = await client.getRosterGroups();
147
+ // [{ name: 'locos', length: 3 }, { name: 'trams', length: 2 }]
148
+
149
+ // Get roster entries for a specific group
150
+ const locos = await client.getRosterEntriesByGroup('locos');
144
151
  ```
145
152
 
146
153
  ## Light Control
@@ -267,6 +274,7 @@ import {
267
274
  LightState,
268
275
  LightData,
269
276
  RosterEntry,
277
+ RosterGroup,
270
278
  TurnoutState,
271
279
  TurnoutData,
272
280
  ThrottleState,
package/docs/BROWSER.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Browser Usage Guide
2
2
 
3
- jmri-client v3.2.0+ supports both Node.js and browser environments. This guide shows how to use jmri-client in web applications.
3
+ jmri-client supports both Node.js and browser environments. This guide shows how to use jmri-client in web applications.
4
4
 
5
5
  ## Installation
6
6
 
@@ -112,7 +112,7 @@ For quick prototypes without a build step, use the browser bundle:
112
112
 
113
113
  <script type="module">
114
114
  // Use the browser bundle from CDN
115
- import { JmriClient } from 'https://unpkg.com/jmri-client@3.3.0/dist/browser/jmri-client.js';
115
+ import { JmriClient } from 'https://unpkg.com/jmri-client@5.0.0/dist/browser/jmri-client.js';
116
116
 
117
117
  const client = new JmriClient({
118
118
  host: 'localhost',
@@ -144,8 +144,8 @@ For quick prototypes without a build step, use the browser bundle:
144
144
  ```
145
145
 
146
146
  Alternative CDNs:
147
- - **unpkg**: `https://unpkg.com/jmri-client@3.3.0/dist/browser/jmri-client.js`
148
- - **jsDelivr**: `https://cdn.jsdelivr.net/npm/jmri-client@3.3.0/dist/browser/jmri-client.js`
147
+ - **unpkg**: `https://unpkg.com/jmri-client@5.0.0/dist/browser/jmri-client.js`
148
+ - **jsDelivr**: `https://cdn.jsdelivr.net/npm/jmri-client@5.0.0/dist/browser/jmri-client.js`
149
149
 
150
150
  ## React Example
151
151
 
package/docs/MIGRATION.md CHANGED
@@ -1,4 +1,33 @@
1
- # Migration Guide: v2.x to v3.x
1
+ # Migration Guide
2
+
3
+ ## v4.x to v5.0
4
+
5
+ ### Breaking Changes
6
+
7
+ #### Node.js version requirement
8
+
9
+ **v4.x:** Node.js 18+
10
+ **v5.0:** Node.js 22+
11
+
12
+ Node 18 reached end-of-life in April 2025 and Node 20 in April 2026. v5.0 requires Node 22 (Active LTS, EOL April 2027) or later.
13
+
14
+ ```bash
15
+ node --version # Should be >= 22.0.0
16
+ ```
17
+
18
+ #### No API changes
19
+
20
+ There are no API-level breaking changes between v4.x and v5.0. All existing code targeting v4.x will work without modification.
21
+
22
+ ### What's new in v5.0
23
+
24
+ - Node.js 22+ required (drops EOL Node 18 and 20)
25
+ - Built with TypeScript 6
26
+ - esbuild-based build pipeline — faster, smaller outputs, no more dual `tsc` compilation
27
+
28
+ ---
29
+
30
+ ## v2.x to v3.x
2
31
 
3
32
  ## Overview
4
33
 
package/docs/MOCK_MODE.md CHANGED
@@ -65,13 +65,18 @@ Connection establishment response with JMRI version info:
65
65
  Track power ON/OFF states
66
66
 
67
67
  ### Roster
68
- Three sample locomotives:
69
- - **CSX754** - GP38-2 diesel locomotive
70
- - **UP3985** - Challenger 4-6-6-4 steam locomotive
71
- - **BNSF5240** - SD40-2 diesel locomotive
68
+ Three sample locomotives across two groups:
69
+ - **CSX754** - GP38-2 diesel locomotive (`diesels` group)
70
+ - **UP3985** - Challenger 4-6-6-4 steam locomotive (`steam` group)
71
+ - **BNSF5240** - SD40-2 diesel locomotive (`diesels` group)
72
72
 
73
73
  Each includes realistic function key mappings (F0-F4).
74
74
 
75
+ ### Roster Groups
76
+ Two sample groups:
77
+ - **diesels** - CSX754, BNSF5240 (length: 2)
78
+ - **steam** - UP3985 (length: 1)
79
+
75
80
  ### Lights
76
81
  Three sample lights:
77
82
  - **IL1** - Yard Light (OFF)
@@ -163,10 +168,10 @@ const response = await mockManager.getMockResponse({
163
168
 
164
169
  The mock system maintains state for realistic behavior:
165
170
 
166
- - **Power state** - Remembers if power is ON or OFF
171
+ - **Power state** - Remembers if power is ON or OFF; tracked independently per connection prefix when one is supplied
167
172
  - **Light states** - Tracks ON/OFF state per light
168
173
  - **Turnout states** - Tracks CLOSED/THROWN state per turnout
169
- - **Throttles** - Tracks acquired throttles and their states
174
+ - **Throttles** - Tracks acquired throttles and their states, including connection prefix
170
175
  - **Speed/Direction** - Maintains current speed and direction per throttle
171
176
  - **Functions** - Tracks function key states (F0-F28)
172
177
 
@@ -208,11 +213,12 @@ The mock data structure follows the JMRI JSON protocol specification.
208
213
  Mock mode implements the full JMRI client API. All methods work identically:
209
214
 
210
215
  - ✅ `connect()` / `disconnect()`
211
- - ✅ `getPower()` / `powerOn()` / `powerOff()`
212
- - ✅ `getRoster()`
216
+ - ✅ `getPower()` / `powerOn()` / `powerOff()` (with optional prefix)
217
+ - ✅ `getRoster()` / `getRosterEntryByName()` / `getRosterEntryByAddress()` / `searchRoster()`
218
+ - ✅ `getRosterGroups()` / `getRosterEntriesByGroup()`
213
219
  - ✅ `getLight()` / `setLight()` / `turnOnLight()` / `turnOffLight()` / `listLights()`
214
220
  - ✅ `getTurnout()` / `setTurnout()` / `throwTurnout()` / `closeTurnout()` / `listTurnouts()`
215
- - ✅ `acquireThrottle()` / `releaseThrottle()`
221
+ - ✅ `acquireThrottle()` / `releaseThrottle()` (with optional prefix)
216
222
  - ✅ `setThrottleSpeed()`
217
223
  - ✅ `setThrottleDirection()`
218
224
  - ✅ `setThrottleFunction()`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jmri-client",
3
- "version": "4.2.0-beta.2",
3
+ "version": "5.1.0",
4
4
  "description": "WebSocket client for JMRI with real-time updates and throttle control - works in both Node.js and browsers",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -8,19 +8,18 @@
8
8
  "types": "./dist/types/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
+ "types": "./dist/types/index.d.ts",
11
12
  "browser": "./dist/browser/jmri-client.js",
12
13
  "import": "./dist/esm/index.js",
13
- "require": "./dist/cjs/index.js",
14
- "types": "./dist/types/index.d.ts"
14
+ "require": "./dist/cjs/index.js"
15
15
  }
16
16
  },
17
17
  "scripts": {
18
18
  "prepublishOnly": "npm run build",
19
- "build": "npm run build:cjs && npm run build:esm && npm run build:types && npm run build:browser",
20
- "build:cjs": "tsc -p tsconfig.json",
21
- "build:esm": "tsc -p tsconfig.esm.json",
22
- "build:types": "tsc -p tsconfig.json --declaration --emitDeclarationOnly --outDir dist/types",
23
- "build:browser": "node build-browser.mjs",
19
+ "clean": "rm -rf dist",
20
+ "build": "npm run clean && node build.mjs && npm run build:types",
21
+ "build:types": "tsc -p tsconfig.json",
22
+ "typecheck": "tsc -p tsconfig.json --noEmit",
24
23
  "test": "jest --no-cache --coverage",
25
24
  "test:browser": "node ./tests/integration/serve-test.mjs",
26
25
  "test:reconnect": "node ./tests/integration/forced-reconnect.mjs",
@@ -59,21 +58,21 @@
59
58
  "url": "https://github.com/yamanote1138/jmri-client/issues"
60
59
  },
61
60
  "engines": {
62
- "node": ">=18"
61
+ "node": ">=22"
63
62
  },
64
63
  "devDependencies": {
65
- "@types/jest": "^29.0.3",
66
- "@types/node": "^18.19.0",
67
- "@types/ws": "^8.5.10",
68
- "esbuild": "^0.27.3",
69
- "jest": "^29.0.3",
70
- "playwright": "^1.58.2",
71
- "ts-jest": "^29.4.6",
72
- "typescript": "^5.3.3"
64
+ "@types/jest": "^30.0.0",
65
+ "@types/node": "^22.0.0",
66
+ "@types/ws": "^8.18.0",
67
+ "esbuild": "^0.28.0",
68
+ "jest": "^30.0.0",
69
+ "playwright": "^1.60.0",
70
+ "ts-jest": "^29.4.11",
71
+ "typescript": "^6.0.0"
73
72
  },
74
73
  "dependencies": {
75
74
  "eventemitter3": "^5.0.4",
76
- "ws": "^8.16.0"
75
+ "ws": "^8.21.0"
77
76
  },
78
77
  "files": [
79
78
  "dist",