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
package/README.md CHANGED
@@ -28,7 +28,9 @@ WebSocket client for [JMRI](http://jmri.sourceforge.net/) with real-time updates
28
28
  npm install jmri-client
29
29
  ```
30
30
 
31
- **Requirements:** Node.js 18+
31
+ **Requirements:** Node.js 22+ · JMRI 5.0 or later
32
+
33
+ > `getSystemConnections()` and per-connection power/throttle prefix support require JMRI 5.15.7+. All other features work with any JMRI 5.x release.
32
34
 
33
35
  ## Quick Start
34
36
 
@@ -723,8 +723,8 @@ var mockData = {
723
723
  { "name": "F4", "label": "Dynamic Brake", "lockable": true, "icon": null, "selectedIcon": null },
724
724
  { "name": "F5", "label": null, "lockable": false, "icon": null, "selectedIcon": null }
725
725
  ],
726
- "attributes": [],
727
- "rosterGroups": []
726
+ "attributes": [{ "name": "RosterGroup:diesels", "value": "yes" }],
727
+ "rosterGroups": ["diesels"]
728
728
  },
729
729
  "id": 1
730
730
  },
@@ -754,8 +754,8 @@ var mockData = {
754
754
  { "name": "F3", "label": "Steam", "lockable": true, "icon": null, "selectedIcon": null },
755
755
  { "name": "F4", "label": null, "lockable": false, "icon": null, "selectedIcon": null }
756
756
  ],
757
- "attributes": [],
758
- "rosterGroups": []
757
+ "attributes": [{ "name": "RosterGroup:steam", "value": "yes" }],
758
+ "rosterGroups": ["steam"]
759
759
  },
760
760
  "id": 2
761
761
  },
@@ -786,13 +786,19 @@ var mockData = {
786
786
  { "name": "F4", "label": null, "lockable": false, "icon": null, "selectedIcon": null },
787
787
  { "name": "F5", "label": "Mars Light", "lockable": true, "icon": null, "selectedIcon": null }
788
788
  ],
789
- "attributes": [],
790
- "rosterGroups": []
789
+ "attributes": [{ "name": "RosterGroup:diesels", "value": "yes" }],
790
+ "rosterGroups": ["diesels"]
791
791
  },
792
792
  "id": 3
793
793
  }
794
794
  ]
795
795
  },
796
+ "rosterGroup": {
797
+ "list": [
798
+ { "type": "rosterGroup", "data": { "name": "diesels", "length": 2 } },
799
+ { "type": "rosterGroup", "data": { "name": "steam", "length": 1 } }
800
+ ]
801
+ },
796
802
  "throttle": {
797
803
  "acquire": {
798
804
  "success": {
@@ -891,6 +897,7 @@ var mockData = {
891
897
  // src/mocks/mock-response-manager.ts
892
898
  var MockResponseManager = class {
893
899
  constructor(options = {}) {
900
+ this.powerStateByPrefix = /* @__PURE__ */ new Map();
894
901
  this.throttles = /* @__PURE__ */ new Map();
895
902
  this.lights = /* @__PURE__ */ new Map([
896
903
  ["IL1", 4 /* OFF */],
@@ -919,6 +926,8 @@ var MockResponseManager = class {
919
926
  return this.getPowerResponse(message);
920
927
  case "roster":
921
928
  return this.getRosterResponse(message);
929
+ case "rosterGroup":
930
+ return this.getRosterGroupResponse();
922
931
  case "throttle":
923
932
  return this.getThrottleResponse(message);
924
933
  case "light":
@@ -940,34 +949,48 @@ var MockResponseManager = class {
940
949
  return JSON.parse(JSON.stringify(mockData.hello));
941
950
  }
942
951
  /**
943
- * Get power response
952
+ * Get power response, with optional per-prefix state tracking
944
953
  */
945
954
  getPowerResponse(message) {
946
- if (message.data?.state !== void 0) {
955
+ const prefix = message.data?.prefix;
956
+ if (message.method === "post" && message.data?.state !== void 0) {
957
+ if (prefix !== void 0) {
958
+ this.powerStateByPrefix.set(prefix, message.data.state);
959
+ return { type: "power", data: { state: message.data.state, prefix } };
960
+ }
947
961
  this.powerState = message.data.state;
948
- return {
949
- type: "power",
950
- data: { state: this.powerState }
951
- };
962
+ return { type: "power", data: { state: this.powerState } };
952
963
  }
953
- return {
954
- type: "power",
955
- data: { state: this.powerState }
956
- };
964
+ if (prefix !== void 0) {
965
+ const state = this.powerStateByPrefix.get(prefix) ?? 0 /* UNKNOWN */;
966
+ return { type: "power", data: { state, prefix } };
967
+ }
968
+ return { type: "power", data: { state: this.powerState } };
957
969
  }
958
970
  /**
959
- * Get roster response
971
+ * Get roster response, optionally filtered by group
960
972
  */
961
973
  getRosterResponse(message) {
962
974
  if (message.type === "roster" && message.method === "list") {
963
- return {
964
- type: "roster",
965
- data: JSON.parse(JSON.stringify(mockData.roster.list))
966
- };
975
+ const all = JSON.parse(JSON.stringify(mockData.roster.list));
976
+ const group = message.params?.group;
977
+ if (group) {
978
+ return {
979
+ type: "roster",
980
+ data: all.filter((e) => e.data.rosterGroups?.includes(group))
981
+ };
982
+ }
983
+ return { type: "roster", data: all };
967
984
  }
985
+ return { type: "roster", data: [] };
986
+ }
987
+ /**
988
+ * Get roster group response
989
+ */
990
+ getRosterGroupResponse() {
968
991
  return {
969
- type: "roster",
970
- data: []
992
+ type: "rosterGroup",
993
+ data: JSON.parse(JSON.stringify(mockData.rosterGroup.list))
971
994
  };
972
995
  }
973
996
  /**
@@ -986,13 +1009,11 @@ var MockResponseManager = class {
986
1009
  F1: false,
987
1010
  F2: false,
988
1011
  F3: false,
989
- F4: false
1012
+ F4: false,
1013
+ ...data.prefix !== void 0 && { prefix: data.prefix }
990
1014
  };
991
1015
  this.throttles.set(throttleId, throttleState);
992
- return {
993
- type: "throttle",
994
- data: { ...throttleState }
995
- };
1016
+ return { type: "throttle", data: { ...throttleState } };
996
1017
  }
997
1018
  if (data.release !== void 0 && data.throttle) {
998
1019
  this.throttles.delete(data.throttle);
@@ -1125,6 +1146,7 @@ var MockResponseManager = class {
1125
1146
  */
1126
1147
  reset() {
1127
1148
  this.powerState = 4 /* OFF */;
1149
+ this.powerStateByPrefix.clear();
1128
1150
  this.throttles.clear();
1129
1151
  this.lights = /* @__PURE__ */ new Map([
1130
1152
  ["IL1", 4 /* OFF */],
@@ -1608,6 +1630,32 @@ var RosterManager = class {
1608
1630
  }
1609
1631
  return results;
1610
1632
  }
1633
+ /**
1634
+ * Get all roster groups
1635
+ */
1636
+ async getRosterGroups() {
1637
+ const message = {
1638
+ type: "rosterGroup",
1639
+ method: "list"
1640
+ };
1641
+ const response = await this.client.request(message);
1642
+ if (!response.data) return [];
1643
+ return response.data.map((wrapper) => wrapper.data);
1644
+ }
1645
+ /**
1646
+ * Get roster entries belonging to a specific group
1647
+ */
1648
+ async getRosterEntriesByGroup(group) {
1649
+ const message = {
1650
+ type: "roster",
1651
+ method: "list",
1652
+ params: { group }
1653
+ };
1654
+ const response = await this.client.request(message);
1655
+ if (!response.data) return [];
1656
+ const entries = response.data;
1657
+ return entries.filter((e) => e.type === "rosterEntry");
1658
+ }
1611
1659
  /**
1612
1660
  * Get cached roster (no network request)
1613
1661
  */
@@ -2315,6 +2363,18 @@ var JmriClient = class extends import_index.default {
2315
2363
  async searchRoster(query) {
2316
2364
  return this.rosterManager.searchRoster(query);
2317
2365
  }
2366
+ /**
2367
+ * Get all roster groups
2368
+ */
2369
+ async getRosterGroups() {
2370
+ return this.rosterManager.getRosterGroups();
2371
+ }
2372
+ /**
2373
+ * Get roster entries belonging to a specific group
2374
+ */
2375
+ async getRosterEntriesByGroup(group) {
2376
+ return this.rosterManager.getRosterEntriesByGroup(group);
2377
+ }
2318
2378
  // ============================================================================
2319
2379
  // Turnout Control
2320
2380
  // ============================================================================