tesla-fleet-api 0.4.0 → 0.6.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 (215) hide show
  1. package/README.md +269 -1
  2. package/dist/_virtual/_rolldown/runtime.cjs +10 -0
  3. package/dist/bluetooth/adapters/shared.cjs +38 -0
  4. package/dist/bluetooth/adapters/shared.mjs +36 -0
  5. package/dist/bluetooth/async.cjs +121 -0
  6. package/dist/bluetooth/async.d.cts +10 -0
  7. package/dist/bluetooth/async.d.mts +10 -0
  8. package/dist/bluetooth/async.mjs +116 -0
  9. package/dist/bluetooth/broadcast.cjs +153 -0
  10. package/dist/bluetooth/broadcast.d.cts +59 -0
  11. package/dist/bluetooth/broadcast.d.mts +59 -0
  12. package/dist/bluetooth/broadcast.mjs +151 -0
  13. package/dist/bluetooth/constants.cjs +96 -0
  14. package/dist/bluetooth/constants.d.cts +68 -0
  15. package/dist/bluetooth/constants.d.mts +68 -0
  16. package/dist/bluetooth/constants.mjs +70 -0
  17. package/dist/bluetooth/framing.cjs +125 -0
  18. package/dist/bluetooth/framing.d.cts +45 -0
  19. package/dist/bluetooth/framing.d.mts +45 -0
  20. package/dist/bluetooth/framing.mjs +122 -0
  21. package/dist/bluetooth/homey.cjs +165 -0
  22. package/dist/bluetooth/homey.d.cts +84 -0
  23. package/dist/bluetooth/homey.d.mts +84 -0
  24. package/dist/bluetooth/homey.mjs +159 -0
  25. package/dist/bluetooth/names.cjs +24 -0
  26. package/dist/bluetooth/names.d.cts +10 -0
  27. package/dist/bluetooth/names.d.mts +10 -0
  28. package/dist/bluetooth/names.mjs +20 -0
  29. package/dist/bluetooth/noble.cjs +178 -0
  30. package/dist/bluetooth/noble.d.cts +58 -0
  31. package/dist/bluetooth/noble.d.mts +58 -0
  32. package/dist/bluetooth/noble.mjs +177 -0
  33. package/dist/bluetooth/streamglue.cjs +157 -0
  34. package/dist/bluetooth/streamglue.d.cts +37 -0
  35. package/dist/bluetooth/streamglue.d.mts +37 -0
  36. package/dist/bluetooth/streamglue.mjs +154 -0
  37. package/dist/bluetooth/teslabluetooth.cjs +67 -0
  38. package/dist/bluetooth/teslabluetooth.d.cts +41 -0
  39. package/dist/bluetooth/teslabluetooth.d.mts +41 -0
  40. package/dist/bluetooth/teslabluetooth.mjs +66 -0
  41. package/dist/bluetooth/transport.cjs +10 -0
  42. package/dist/bluetooth/transport.d.cts +97 -0
  43. package/dist/bluetooth/transport.d.mts +97 -0
  44. package/dist/bluetooth/transport.mjs +10 -0
  45. package/dist/bluetooth/vehiclebluetooth.cjs +1272 -0
  46. package/dist/bluetooth/vehiclebluetooth.d.cts +495 -0
  47. package/dist/bluetooth/vehiclebluetooth.d.mts +495 -0
  48. package/dist/bluetooth/vehiclebluetooth.mjs +1270 -0
  49. package/dist/bluetooth/verify.cjs +93 -0
  50. package/dist/bluetooth/verify.d.cts +35 -0
  51. package/dist/bluetooth/verify.d.mts +35 -0
  52. package/dist/bluetooth/verify.mjs +91 -0
  53. package/dist/bluetooth.cjs +52 -0
  54. package/dist/bluetooth.d.cts +10 -0
  55. package/dist/bluetooth.d.mts +10 -0
  56. package/dist/bluetooth.mjs +9 -0
  57. package/dist/commands.cjs +1080 -85
  58. package/dist/commands.d.cts +585 -54
  59. package/dist/commands.d.mts +583 -51
  60. package/dist/commands.mjs +1084 -93
  61. package/dist/const.cjs +22 -0
  62. package/dist/const.d.cts +21 -0
  63. package/dist/const.d.mts +21 -0
  64. package/dist/const.mjs +20 -1
  65. package/dist/energy.cjs +24 -19
  66. package/dist/energy.d.cts +21 -15
  67. package/dist/energy.d.mts +21 -15
  68. package/dist/energy.mjs +24 -19
  69. package/dist/energyisland.cjs +38 -0
  70. package/dist/energyisland.mjs +37 -0
  71. package/dist/energyparsers.cjs +118 -0
  72. package/dist/energyparsers.d.cts +39 -0
  73. package/dist/energyparsers.d.mts +39 -0
  74. package/dist/energyparsers.mjs +116 -0
  75. package/dist/energyspecific.cjs +375 -17
  76. package/dist/energyspecific.d.cts +199 -19
  77. package/dist/energyspecific.d.mts +197 -16
  78. package/dist/energyspecific.mjs +370 -18
  79. package/dist/exceptions.cjs +3 -2
  80. package/dist/exceptions.d.cts +3 -2
  81. package/dist/exceptions.d.mts +3 -2
  82. package/dist/exceptions.mjs +3 -2
  83. package/dist/funnel.cjs +297 -0
  84. package/dist/funnel.d.cts +118 -0
  85. package/dist/funnel.d.mts +118 -0
  86. package/dist/funnel.mjs +292 -0
  87. package/dist/index.cjs +61 -0
  88. package/dist/index.d.cts +22 -8
  89. package/dist/index.d.mts +22 -8
  90. package/dist/index.mjs +19 -6
  91. package/dist/log.cjs +75 -0
  92. package/dist/log.d.cts +4 -0
  93. package/dist/log.d.mts +4 -0
  94. package/dist/log.mjs +71 -0
  95. package/dist/oauth.cjs +143 -0
  96. package/dist/oauth.d.cts +69 -0
  97. package/dist/oauth.d.mts +69 -0
  98. package/dist/oauth.mjs +143 -0
  99. package/dist/powerwall/client.cjs +818 -0
  100. package/dist/powerwall/client.d.cts +297 -0
  101. package/dist/powerwall/client.d.mts +297 -0
  102. package/dist/powerwall/client.mjs +807 -0
  103. package/dist/powerwall/components.cjs +188 -0
  104. package/dist/powerwall/components.d.cts +45 -0
  105. package/dist/powerwall/components.d.mts +45 -0
  106. package/dist/powerwall/components.mjs +182 -0
  107. package/dist/powerwall/energysite.cjs +216 -0
  108. package/dist/powerwall/energysite.d.cts +104 -0
  109. package/dist/powerwall/energysite.d.mts +104 -0
  110. package/dist/powerwall/energysite.mjs +214 -0
  111. package/dist/powerwall/exceptions.cjs +59 -0
  112. package/dist/powerwall/exceptions.d.cts +38 -0
  113. package/dist/powerwall/exceptions.d.mts +38 -0
  114. package/dist/powerwall/exceptions.mjs +51 -0
  115. package/dist/powerwall/gateway.cjs +102 -0
  116. package/dist/powerwall/gateway.d.cts +57 -0
  117. package/dist/powerwall/gateway.d.mts +57 -0
  118. package/dist/powerwall/gateway.mjs +101 -0
  119. package/dist/powerwall/merge.cjs +163 -0
  120. package/dist/powerwall/merge.d.cts +63 -0
  121. package/dist/powerwall/merge.d.mts +63 -0
  122. package/dist/powerwall/merge.mjs +159 -0
  123. package/dist/powerwall/models.d.cts +95 -0
  124. package/dist/powerwall/models.d.mts +95 -0
  125. package/dist/powerwall/queries.cjs +17 -0
  126. package/dist/powerwall/queries.mjs +10 -0
  127. package/dist/powerwall/transport.cjs +232 -0
  128. package/dist/powerwall/transport.mjs +227 -0
  129. package/dist/powerwall.cjs +47 -0
  130. package/dist/powerwall.d.cts +9 -0
  131. package/dist/powerwall.d.mts +9 -0
  132. package/dist/powerwall.mjs +8 -0
  133. package/dist/router/base.cjs +203 -0
  134. package/dist/router/base.d.cts +112 -0
  135. package/dist/router/base.d.mts +112 -0
  136. package/dist/router/base.mjs +200 -0
  137. package/dist/router/energysite.cjs +65 -0
  138. package/dist/router/energysite.d.cts +47 -0
  139. package/dist/router/energysite.d.mts +47 -0
  140. package/dist/router/energysite.mjs +60 -0
  141. package/dist/router.cjs +13 -0
  142. package/dist/router.d.cts +3 -1
  143. package/dist/router.d.mts +3 -1
  144. package/dist/router.mjs +3 -1
  145. package/dist/signing/crypto.cjs +28 -0
  146. package/dist/signing/crypto.mjs +26 -1
  147. package/dist/signing/energy.cjs +123 -0
  148. package/dist/signing/energy.d.cts +45 -0
  149. package/dist/signing/energy.d.mts +45 -0
  150. package/dist/signing/energy.mjs +120 -0
  151. package/dist/signing/jws.cjs +229 -0
  152. package/dist/signing/jws.d.cts +45 -0
  153. package/dist/signing/jws.d.mts +45 -0
  154. package/dist/signing/jws.mjs +211 -0
  155. package/dist/signing/keys.cjs +191 -0
  156. package/dist/signing/keys.d.cts +21 -0
  157. package/dist/signing/keys.d.mts +21 -0
  158. package/dist/signing/keys.mjs +182 -0
  159. package/dist/signing/session.cjs +104 -30
  160. package/dist/signing/session.d.cts +60 -23
  161. package/dist/signing/session.d.mts +60 -23
  162. package/dist/signing/session.mjs +105 -31
  163. package/dist/tariff.cjs +27 -0
  164. package/dist/tariff.d.cts +11 -0
  165. package/dist/tariff.d.mts +11 -0
  166. package/dist/tariff.mjs +27 -1
  167. package/dist/teslafleetapi.cjs +256 -55
  168. package/dist/teslafleetapi.d.cts +110 -14
  169. package/dist/teslafleetapi.d.mts +110 -14
  170. package/dist/teslafleetapi.mjs +258 -57
  171. package/dist/teslemetry.cjs +152 -5
  172. package/dist/teslemetry.d.cts +95 -5
  173. package/dist/teslemetry.d.mts +93 -3
  174. package/dist/teslemetry.mjs +151 -6
  175. package/dist/teslemetryenergy.cjs +287 -0
  176. package/dist/teslemetryenergy.d.cts +154 -0
  177. package/dist/teslemetryenergy.d.mts +154 -0
  178. package/dist/teslemetryenergy.mjs +282 -0
  179. package/dist/teslemetryvehicle.cjs +407 -0
  180. package/dist/teslemetryvehicle.d.cts +238 -0
  181. package/dist/teslemetryvehicle.d.mts +238 -0
  182. package/dist/teslemetryvehicle.mjs +405 -0
  183. package/dist/tessie.cjs +42 -5
  184. package/dist/tessie.d.cts +51 -5
  185. package/dist/tessie.d.mts +49 -3
  186. package/dist/tessie.mjs +42 -5
  187. package/dist/tessievehicle.cjs +514 -0
  188. package/dist/tessievehicle.d.cts +333 -0
  189. package/dist/tessievehicle.d.mts +333 -0
  190. package/dist/tessievehicle.mjs +513 -0
  191. package/dist/types/live_status.d.cts +16 -16
  192. package/dist/types/live_status.d.mts +16 -16
  193. package/dist/types/responses.d.cts +5 -1
  194. package/dist/types/responses.d.mts +5 -1
  195. package/dist/types/vehicle.d.cts +7 -1
  196. package/dist/types/vehicle.d.mts +7 -1
  197. package/dist/types.d.cts +3 -3
  198. package/dist/types.d.mts +3 -3
  199. package/dist/util.cjs +41 -0
  200. package/dist/util.d.cts +18 -0
  201. package/dist/util.d.mts +18 -0
  202. package/dist/util.mjs +40 -0
  203. package/dist/vehicle.cjs +79 -36
  204. package/dist/vehicle.d.cts +42 -26
  205. package/dist/vehicle.d.mts +42 -26
  206. package/dist/vehicle.mjs +80 -37
  207. package/dist/vehiclesigned.cjs +20 -5
  208. package/dist/vehiclesigned.d.cts +14 -5
  209. package/dist/vehiclesigned.d.mts +14 -5
  210. package/dist/vehiclesigned.mjs +20 -5
  211. package/dist/vehiclespecific.cjs +126 -15
  212. package/dist/vehiclespecific.d.cts +82 -18
  213. package/dist/vehiclespecific.d.mts +82 -18
  214. package/dist/vehiclespecific.mjs +126 -15
  215. package/package.json +56 -3
@@ -0,0 +1,118 @@
1
+ const require_const = require("./const.cjs");
2
+ const require_exceptions = require("./exceptions.cjs");
3
+ // src/energyparsers.ts
4
+ function isRecord(value) {
5
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6
+ }
7
+ /** The first present key's value. Tests presence, so a legal falsy value is never read as missing. */
8
+ function field(payload, ...keys) {
9
+ for (const key of keys) if (Object.hasOwn(payload, key)) return payload[key];
10
+ return null;
11
+ }
12
+ /**
13
+ * Type a raw value against an enum. A recognized number or member name
14
+ * (case-insensitive) becomes the member's number. A present but unrecognized
15
+ * value stays as received, because only an absent field means `null`.
16
+ */
17
+ function normalizeEnum(members, value) {
18
+ if (typeof value === "string") {
19
+ const name = value.trim().toUpperCase();
20
+ return Object.hasOwn(members, name) ? members[name] : value;
21
+ }
22
+ return value;
23
+ }
24
+ function parseClient(payload) {
25
+ const roles = field(payload, "roles");
26
+ return {
27
+ public_key: field(payload, "public_key", "publicKey"),
28
+ state: normalizeEnum(require_const.AuthorizedClientState, field(payload, "state", "authorized_client_state")),
29
+ roles: Array.isArray(roles) ? roles.map((role) => normalizeEnum(require_const.AuthorizationRole, role)) : null,
30
+ verification: normalizeEnum(require_const.AuthorizedVerificationType, field(payload, "verification")),
31
+ raw: payload
32
+ };
33
+ }
34
+ function authorizedClientsList(payload) {
35
+ if (payload === null || payload === void 0) throw new require_exceptions.InvalidResponse("authorized_clients response body was null");
36
+ if (Array.isArray(payload)) return payload;
37
+ if (!isRecord(payload)) throw new require_exceptions.InvalidResponse(String(payload));
38
+ const value = field(isRecord(payload.response) ? payload.response : payload, "authorized_clients", "clients");
39
+ if (!Array.isArray(value)) throw new require_exceptions.InvalidResponse(payload);
40
+ return value;
41
+ }
42
+ /**
43
+ * Parse a raw `list_authorized_clients` reply: a bare list,
44
+ * `{response: {authorized_clients: [...]}}` or `{response: {clients: [...]}}`.
45
+ * @throws InvalidResponse for a null body or any other shape.
46
+ */
47
+ function parse_authorized_clients(payload) {
48
+ return {
49
+ clients: authorizedClientsList(payload).filter(isRecord).map(parseClient),
50
+ raw: payload
51
+ };
52
+ }
53
+ const OCTET = "(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])";
54
+ const DOTTED_QUAD = new RegExp(`^${OCTET}\\.${OCTET}\\.${OCTET}\\.${OCTET}$`);
55
+ /**
56
+ * Decode a `networking_status` ipv4 field. The live API serves a strict dotted
57
+ * quad; the big-endian uint32 form is accepted too. `0.0.0.0` and
58
+ * `255.255.255.255` are never a usable host, in either form. Anything else
59
+ * decodes to `null`, so one bad field cannot fail the lookup.
60
+ */
61
+ function decodeIpv4(value) {
62
+ if (typeof value === "number") {
63
+ if (!Number.isInteger(value) || value <= 0 || value >= 4294967295) return null;
64
+ return [
65
+ value >>> 24,
66
+ value >>> 16 & 255,
67
+ value >>> 8 & 255,
68
+ value & 255
69
+ ].join(".");
70
+ }
71
+ if (typeof value === "string" && DOTTED_QUAD.test(value) && value !== "0.0.0.0" && value !== "255.255.255.255") return value;
72
+ return null;
73
+ }
74
+ /**
75
+ * Whether `address` is a dotted-quad IPv4 address in a private range (RFC 1918) or
76
+ * the link-local range: the only places a gateway's LAN address can be. Anything
77
+ * else, including a hostname or a `host:port`, is not.
78
+ */
79
+ function is_lan_address(address) {
80
+ if (!DOTTED_QUAD.test(address)) return false;
81
+ const [first, second] = address.split(".").map(Number);
82
+ return first === 10 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 168 || first === 169 && second === 254;
83
+ }
84
+ function interfaceAddress(networkInterface) {
85
+ if (!isRecord(networkInterface)) return null;
86
+ const ipv4 = networkInterface.ipv4_config;
87
+ return isRecord(ipv4) ? decodeIpv4(ipv4.address) : null;
88
+ }
89
+ function networkingStatusBody(payload) {
90
+ if (payload === null || payload === void 0) throw new require_exceptions.InvalidResponse("networking_status response body was null");
91
+ if (!isRecord(payload)) throw new require_exceptions.InvalidResponse(String(payload));
92
+ if (!Object.hasOwn(payload, "response")) return payload;
93
+ if (!isRecord(payload.response)) throw new require_exceptions.InvalidResponse(payload);
94
+ return payload.response;
95
+ }
96
+ /**
97
+ * Pick the gateway's LAN IPv4 address from a `networking_status` reply, with or
98
+ * without the `response` envelope. Considers `eth` and `wifi` only, never `gsm`:
99
+ * an interface with `active_route` wins, then the first with any decodable address.
100
+ * @returns The address, or `null` when a well-formed reply has no usable interface.
101
+ * @throws InvalidResponse for a null body or a reply of another shape.
102
+ */
103
+ function parse_gateway_address(body) {
104
+ const status = networkingStatusBody(body);
105
+ const interfaces = [status.eth, status.wifi];
106
+ for (const networkInterface of interfaces) if (isRecord(networkInterface) && networkInterface.active_route) {
107
+ const address = interfaceAddress(networkInterface);
108
+ if (address !== null) return address;
109
+ }
110
+ for (const networkInterface of interfaces) {
111
+ const address = interfaceAddress(networkInterface);
112
+ if (address !== null) return address;
113
+ }
114
+ return null;
115
+ }
116
+ exports.is_lan_address = is_lan_address;
117
+ exports.parse_authorized_clients = parse_authorized_clients;
118
+ exports.parse_gateway_address = parse_gateway_address;
@@ -0,0 +1,39 @@
1
+ type Json = Record<string, unknown>;
2
+ /** A value typed against an enum: its number, or as received when unrecognized, or `null` when absent. */
3
+ export type EnumValue = number | string | null;
4
+ /**
5
+ * One entry from a `list_authorized_clients` reply. Tesla has not published this
6
+ * schema, so anything else on an entry is available through `raw`.
7
+ */
8
+ export type AuthorizedClient = {
9
+ public_key: string | null;
10
+ state: EnumValue;
11
+ roles: EnumValue[] | null;
12
+ verification: EnumValue;
13
+ raw: Json;
14
+ };
15
+ /**
16
+ * A parsed `list_authorized_clients` reply. A null body or an unknown shape
17
+ * raises `InvalidResponse` rather than reading as "no clients": the endpoint
18
+ * intermittently answers HTTP 200 with a null body. A genuinely empty list
19
+ * parses to `clients: []`.
20
+ */
21
+ export type AuthorizedClients = {
22
+ clients: AuthorizedClient[];
23
+ raw: unknown;
24
+ };
25
+ /**
26
+ * Parse a raw `list_authorized_clients` reply: a bare list,
27
+ * `{response: {authorized_clients: [...]}}` or `{response: {clients: [...]}}`.
28
+ * @throws InvalidResponse for a null body or any other shape.
29
+ */
30
+ export declare function parse_authorized_clients(payload: unknown): AuthorizedClients;
31
+ /**
32
+ * Pick the gateway's LAN IPv4 address from a `networking_status` reply, with or
33
+ * without the `response` envelope. Considers `eth` and `wifi` only, never `gsm`:
34
+ * an interface with `active_route` wins, then the first with any decodable address.
35
+ * @returns The address, or `null` when a well-formed reply has no usable interface.
36
+ * @throws InvalidResponse for a null body or a reply of another shape.
37
+ */
38
+ export declare function parse_gateway_address(body: unknown): string | null;
39
+ export {};
@@ -0,0 +1,39 @@
1
+ type Json = Record<string, unknown>;
2
+ /** A value typed against an enum: its number, or as received when unrecognized, or `null` when absent. */
3
+ export type EnumValue = number | string | null;
4
+ /**
5
+ * One entry from a `list_authorized_clients` reply. Tesla has not published this
6
+ * schema, so anything else on an entry is available through `raw`.
7
+ */
8
+ export type AuthorizedClient = {
9
+ public_key: string | null;
10
+ state: EnumValue;
11
+ roles: EnumValue[] | null;
12
+ verification: EnumValue;
13
+ raw: Json;
14
+ };
15
+ /**
16
+ * A parsed `list_authorized_clients` reply. A null body or an unknown shape
17
+ * raises `InvalidResponse` rather than reading as "no clients": the endpoint
18
+ * intermittently answers HTTP 200 with a null body. A genuinely empty list
19
+ * parses to `clients: []`.
20
+ */
21
+ export type AuthorizedClients = {
22
+ clients: AuthorizedClient[];
23
+ raw: unknown;
24
+ };
25
+ /**
26
+ * Parse a raw `list_authorized_clients` reply: a bare list,
27
+ * `{response: {authorized_clients: [...]}}` or `{response: {clients: [...]}}`.
28
+ * @throws InvalidResponse for a null body or any other shape.
29
+ */
30
+ export declare function parse_authorized_clients(payload: unknown): AuthorizedClients;
31
+ /**
32
+ * Pick the gateway's LAN IPv4 address from a `networking_status` reply, with or
33
+ * without the `response` envelope. Considers `eth` and `wifi` only, never `gsm`:
34
+ * an interface with `active_route` wins, then the first with any decodable address.
35
+ * @returns The address, or `null` when a well-formed reply has no usable interface.
36
+ * @throws InvalidResponse for a null body or a reply of another shape.
37
+ */
38
+ export declare function parse_gateway_address(body: unknown): string | null;
39
+ export {};
@@ -0,0 +1,116 @@
1
+ import { AuthorizationRole, AuthorizedClientState, AuthorizedVerificationType } from "./const.mjs";
2
+ import { InvalidResponse } from "./exceptions.mjs";
3
+ // src/energyparsers.ts
4
+ function isRecord(value) {
5
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6
+ }
7
+ /** The first present key's value. Tests presence, so a legal falsy value is never read as missing. */
8
+ function field(payload, ...keys) {
9
+ for (const key of keys) if (Object.hasOwn(payload, key)) return payload[key];
10
+ return null;
11
+ }
12
+ /**
13
+ * Type a raw value against an enum. A recognized number or member name
14
+ * (case-insensitive) becomes the member's number. A present but unrecognized
15
+ * value stays as received, because only an absent field means `null`.
16
+ */
17
+ function normalizeEnum(members, value) {
18
+ if (typeof value === "string") {
19
+ const name = value.trim().toUpperCase();
20
+ return Object.hasOwn(members, name) ? members[name] : value;
21
+ }
22
+ return value;
23
+ }
24
+ function parseClient(payload) {
25
+ const roles = field(payload, "roles");
26
+ return {
27
+ public_key: field(payload, "public_key", "publicKey"),
28
+ state: normalizeEnum(AuthorizedClientState, field(payload, "state", "authorized_client_state")),
29
+ roles: Array.isArray(roles) ? roles.map((role) => normalizeEnum(AuthorizationRole, role)) : null,
30
+ verification: normalizeEnum(AuthorizedVerificationType, field(payload, "verification")),
31
+ raw: payload
32
+ };
33
+ }
34
+ function authorizedClientsList(payload) {
35
+ if (payload === null || payload === void 0) throw new InvalidResponse("authorized_clients response body was null");
36
+ if (Array.isArray(payload)) return payload;
37
+ if (!isRecord(payload)) throw new InvalidResponse(String(payload));
38
+ const value = field(isRecord(payload.response) ? payload.response : payload, "authorized_clients", "clients");
39
+ if (!Array.isArray(value)) throw new InvalidResponse(payload);
40
+ return value;
41
+ }
42
+ /**
43
+ * Parse a raw `list_authorized_clients` reply: a bare list,
44
+ * `{response: {authorized_clients: [...]}}` or `{response: {clients: [...]}}`.
45
+ * @throws InvalidResponse for a null body or any other shape.
46
+ */
47
+ function parse_authorized_clients(payload) {
48
+ return {
49
+ clients: authorizedClientsList(payload).filter(isRecord).map(parseClient),
50
+ raw: payload
51
+ };
52
+ }
53
+ const OCTET = "(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])";
54
+ const DOTTED_QUAD = new RegExp(`^${OCTET}\\.${OCTET}\\.${OCTET}\\.${OCTET}$`);
55
+ /**
56
+ * Decode a `networking_status` ipv4 field. The live API serves a strict dotted
57
+ * quad; the big-endian uint32 form is accepted too. `0.0.0.0` and
58
+ * `255.255.255.255` are never a usable host, in either form. Anything else
59
+ * decodes to `null`, so one bad field cannot fail the lookup.
60
+ */
61
+ function decodeIpv4(value) {
62
+ if (typeof value === "number") {
63
+ if (!Number.isInteger(value) || value <= 0 || value >= 4294967295) return null;
64
+ return [
65
+ value >>> 24,
66
+ value >>> 16 & 255,
67
+ value >>> 8 & 255,
68
+ value & 255
69
+ ].join(".");
70
+ }
71
+ if (typeof value === "string" && DOTTED_QUAD.test(value) && value !== "0.0.0.0" && value !== "255.255.255.255") return value;
72
+ return null;
73
+ }
74
+ /**
75
+ * Whether `address` is a dotted-quad IPv4 address in a private range (RFC 1918) or
76
+ * the link-local range: the only places a gateway's LAN address can be. Anything
77
+ * else, including a hostname or a `host:port`, is not.
78
+ */
79
+ function is_lan_address(address) {
80
+ if (!DOTTED_QUAD.test(address)) return false;
81
+ const [first, second] = address.split(".").map(Number);
82
+ return first === 10 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 168 || first === 169 && second === 254;
83
+ }
84
+ function interfaceAddress(networkInterface) {
85
+ if (!isRecord(networkInterface)) return null;
86
+ const ipv4 = networkInterface.ipv4_config;
87
+ return isRecord(ipv4) ? decodeIpv4(ipv4.address) : null;
88
+ }
89
+ function networkingStatusBody(payload) {
90
+ if (payload === null || payload === void 0) throw new InvalidResponse("networking_status response body was null");
91
+ if (!isRecord(payload)) throw new InvalidResponse(String(payload));
92
+ if (!Object.hasOwn(payload, "response")) return payload;
93
+ if (!isRecord(payload.response)) throw new InvalidResponse(payload);
94
+ return payload.response;
95
+ }
96
+ /**
97
+ * Pick the gateway's LAN IPv4 address from a `networking_status` reply, with or
98
+ * without the `response` envelope. Considers `eth` and `wifi` only, never `gsm`:
99
+ * an interface with `active_route` wins, then the first with any decodable address.
100
+ * @returns The address, or `null` when a well-formed reply has no usable interface.
101
+ * @throws InvalidResponse for a null body or a reply of another shape.
102
+ */
103
+ function parse_gateway_address(body) {
104
+ const status = networkingStatusBody(body);
105
+ const interfaces = [status.eth, status.wifi];
106
+ for (const networkInterface of interfaces) if (isRecord(networkInterface) && networkInterface.active_route) {
107
+ const address = interfaceAddress(networkInterface);
108
+ if (address !== null) return address;
109
+ }
110
+ for (const networkInterface of interfaces) {
111
+ const address = interfaceAddress(networkInterface);
112
+ if (address !== null) return address;
113
+ }
114
+ return null;
115
+ }
116
+ export { is_lan_address, parse_authorized_clients, parse_gateway_address };