@astralyn/sash 0.1.1 → 0.1.2

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 (102) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +17 -5
  3. package/THIRD_PARTY_NOTICES.md +9 -0
  4. package/dist/api.js +14 -45
  5. package/dist/app-state.js +99 -0
  6. package/dist/autostart/command.js +24 -0
  7. package/dist/autostart/context.js +43 -0
  8. package/dist/autostart/files.js +60 -0
  9. package/dist/autostart/installation.js +48 -0
  10. package/dist/autostart/start.js +47 -0
  11. package/dist/autostart/windows-registry.js +88 -0
  12. package/dist/autostart/windows.js +59 -0
  13. package/dist/autostart-contract.js +31 -0
  14. package/dist/autostart-entry.js +10 -0
  15. package/dist/autostart.js +69 -0
  16. package/dist/cli.js +11 -11
  17. package/dist/commands/auto.js +15 -0
  18. package/dist/commands/lifecycle.js +5 -15
  19. package/dist/commands/logs.js +11 -4
  20. package/dist/commands/shared.js +2 -4
  21. package/dist/commands/status.js +5 -1
  22. package/dist/commands/update.js +7 -3
  23. package/dist/commands/web.js +14 -50
  24. package/dist/contracts.js +168 -348
  25. package/dist/core-config-validation.js +22 -38
  26. package/dist/core-update.js +126 -762
  27. package/dist/core.js +20 -46
  28. package/dist/daemon/app.js +129 -117
  29. package/dist/daemon/context.js +13 -7
  30. package/dist/daemon/entry.js +21 -38
  31. package/dist/daemon/errors.js +3 -0
  32. package/dist/daemon/handlers/autostart.js +18 -0
  33. package/dist/daemon/handlers/core.js +32 -9
  34. package/dist/daemon/handlers/daemon.js +17 -6
  35. package/dist/daemon/handlers/profiles.js +12 -2
  36. package/dist/daemon/handlers/settings.js +9 -36
  37. package/dist/daemon/router.js +26 -21
  38. package/dist/daemon/scheduler.js +3 -1
  39. package/dist/daemon/server.js +5 -2
  40. package/dist/daemon-client.js +13 -3
  41. package/dist/daemon-http.js +6 -0
  42. package/dist/daemon-lifecycle.js +19 -196
  43. package/dist/github.js +7 -2
  44. package/dist/http.js +7 -5
  45. package/dist/mihomo-config.js +2 -1
  46. package/dist/paths.js +5 -7
  47. package/dist/profile-model.js +87 -0
  48. package/dist/profile-service.js +250 -587
  49. package/dist/profiles.js +43 -171
  50. package/dist/runtime-lifecycle.js +112 -194
  51. package/dist/runtime-owner.js +37 -76
  52. package/dist/sash-client.js +40 -19
  53. package/dist/settings-service.js +29 -224
  54. package/dist/settings.js +61 -289
  55. package/dist/status.js +24 -16
  56. package/dist/supervisor.js +1 -14
  57. package/dist/sysproxy/common.js +5 -45
  58. package/dist/sysproxy/factory.js +24 -65
  59. package/dist/sysproxy/snapshot.js +24 -257
  60. package/dist/sysproxy.js +1 -4
  61. package/dist/system-proxy-manager.js +37 -35
  62. package/dist/ui/assets/{ConnectionsView-CfaOG2JV.js → ConnectionsView-BgXQM6Z4.js} +1 -1
  63. package/dist/ui/assets/{LogsView-BDiRATXN.js → LogsView-2f542P8z.js} +1 -1
  64. package/dist/ui/assets/{PaginationFooter-DTytr1iu.js → PaginationFooter-cj1tcZej.js} +1 -1
  65. package/dist/ui/assets/ProfileEditorDialog-C9M8uKZC.css +1 -0
  66. package/dist/ui/assets/ProfileEditorDialog-DD4y4GBC.js +14 -0
  67. package/dist/ui/assets/ProfilesView-BXU2DOA7.css +1 -0
  68. package/dist/ui/assets/ProfilesView-DNnzYIEY.js +7 -0
  69. package/dist/ui/assets/{RulesView-CjEyCN1A.js → RulesView-CtKvlckZ.js} +1 -1
  70. package/dist/ui/assets/SettingsView-BaeKFF_N.js +1 -0
  71. package/dist/ui/assets/SettingsView-CWjIe05v.css +1 -0
  72. package/dist/ui/assets/index-CBInDvdJ.js +19 -0
  73. package/dist/ui/assets/index-mOLy7fkB.css +1 -0
  74. package/dist/ui/assets/{theme-BwDBMsKO.js → theme-D40MF8cQ.js} +1 -1
  75. package/dist/ui/index.html +2 -2
  76. package/docs/architecture-proposal.md +109 -0
  77. package/docs/autostart.md +101 -0
  78. package/docs/backend.md +92 -224
  79. package/docs/frontend.md +60 -106
  80. package/docs/usage.md +92 -164
  81. package/package.json +3 -3
  82. package/dist/commands/upgrade.js +0 -43
  83. package/dist/core-install-transaction.js +0 -114
  84. package/dist/core-update-coordination.js +0 -105
  85. package/dist/core-update-service.js +0 -245
  86. package/dist/managed-state-transaction.js +0 -375
  87. package/dist/offline-mutation.js +0 -58
  88. package/dist/profile-migration.js +0 -101
  89. package/dist/runtime-recovery.js +0 -31
  90. package/dist/sysproxy/darwin.js +0 -287
  91. package/dist/sysproxy/gnome.js +0 -181
  92. package/dist/sysproxy/legacy.js +0 -58
  93. package/dist/ui/assets/CodeEditorModal-6m0TJWyF.css +0 -1
  94. package/dist/ui/assets/CodeEditorModal-D1zn-jRS.js +0 -14
  95. package/dist/ui/assets/ProfileEditorDialog-CyyP6OF1.js +0 -1
  96. package/dist/ui/assets/ProfilesView-ByqdFNHN.css +0 -1
  97. package/dist/ui/assets/ProfilesView-hFa7-O72.js +0 -2
  98. package/dist/ui/assets/SettingsFileDialog-CjBSyH4K.js +0 -1
  99. package/dist/ui/assets/SettingsView-D5QvJId6.css +0 -1
  100. package/dist/ui/assets/SettingsView-DsRntRfn.js +0 -2
  101. package/dist/ui/assets/index-CxQTf_P9.css +0 -1
  102. package/dist/ui/assets/index-_mb4OfYp.js +0 -19
package/dist/contracts.js CHANGED
@@ -1,428 +1,248 @@
1
1
  import { isCanonicalIsoTimestamp, isPlainObject } from "./json-shape.js";
2
+ import { parseProfileMeta } from "./profile-model.js";
3
+ export { parseProfilesIndex } from "./profile-model.js";
2
4
  export const WEB_SOCKET_AUTH_PROTOCOL = "sash";
3
5
  export const WEB_SOCKET_TOKEN_PROTOCOL_PREFIX = "sash-token.";
4
- export function apiErrorBody(code, message) {
5
- return { error: { code, message } };
6
- }
7
- export function parseApiErrorBody(value) {
8
- if (!isPlainObject(value) || !isPlainObject(value.error))
9
- return undefined;
10
- const { code, message } = value.error;
11
- if (typeof code !== "string" || typeof message !== "string")
12
- return undefined;
13
- return { code, message };
14
- }
15
- function invalid(contract, path, expected) {
16
- throw new TypeError(`${contract} response is invalid: ${path} must be ${expected}`);
17
- }
18
- function objectValue(value, contract, path) {
6
+ function object(value, name) {
19
7
  if (!isPlainObject(value))
20
- invalid(contract, path, "a plain object");
8
+ throw new TypeError(`${name} must be a plain object`);
21
9
  return value;
22
10
  }
23
- function required(source, key, contract, path) {
24
- if (!Object.hasOwn(source, key))
25
- invalid(contract, path, "present");
26
- return source[key];
27
- }
28
- function booleanValue(value, contract, path) {
29
- if (typeof value !== "boolean")
30
- invalid(contract, path, "a boolean");
11
+ function string(value, name, allowEmpty = false) {
12
+ if (typeof value !== "string" || (!allowEmpty && !value.trim()))
13
+ throw new TypeError(`${name} must be a string${allowEmpty ? "" : " with content"}`);
31
14
  return value;
32
15
  }
33
- function stringValue(value, contract, path, nonEmpty = false) {
34
- if (typeof value !== "string" || (nonEmpty && !value.trim())) {
35
- invalid(contract, path, nonEmpty ? "a non-empty string" : "a string");
36
- }
37
- return value;
38
- }
39
- function finiteNumber(value, contract, path) {
40
- if (typeof value !== "number" || !Number.isFinite(value)) {
41
- invalid(contract, path, "a finite number");
42
- }
43
- return value;
44
- }
45
- function positiveSafeInteger(value, contract, path) {
46
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) {
47
- invalid(contract, path, "a positive safe integer");
48
- }
49
- return value;
50
- }
51
- function nonNegativeSafeInteger(value, contract, path) {
52
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
53
- invalid(contract, path, "a non-negative safe integer");
54
- }
16
+ function boolean(value, name) {
17
+ if (typeof value !== "boolean")
18
+ throw new TypeError(`${name} must be a boolean`);
55
19
  return value;
56
20
  }
57
- function portValue(value, contract, path) {
58
- if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 65_535) {
59
- invalid(contract, path, "an integer port from 1 to 65535");
60
- }
21
+ function integer(value, name, min = 0, max = Number.MAX_SAFE_INTEGER) {
22
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < min || value > max)
23
+ throw new TypeError(`${name} must be an integer from ${min} to ${max}`);
61
24
  return value;
62
25
  }
63
- function timestampValue(value, contract, path) {
26
+ function timestamp(value, name) {
64
27
  if (!isCanonicalIsoTimestamp(value))
65
- invalid(contract, path, "a canonical ISO timestamp");
28
+ throw new TypeError(`${name} must be a canonical timestamp`);
66
29
  return value;
67
30
  }
68
- function optionalString(source, key, contract, path) {
69
- return Object.hasOwn(source, key) ? stringValue(source[key], contract, path) : undefined;
70
- }
71
- function knownFlag(source, key, contract, path) {
72
- return Object.hasOwn(source, key) ? booleanValue(source[key], contract, path) : false;
31
+ function optionalString(source, key) {
32
+ return Object.hasOwn(source, key) ? string(source[key], key, true) : undefined;
73
33
  }
74
- function parseSystemProxyState(value, contract, path) {
75
- const source = objectValue(value, contract, path);
76
- const server = optionalString(source, "server", contract, `${path}.server`);
77
- const details = optionalString(source, "details", contract, `${path}.details`);
78
- return {
79
- supported: booleanValue(required(source, "supported", contract, `${path}.supported`), contract, `${path}.supported`),
80
- enabled: booleanValue(required(source, "enabled", contract, `${path}.enabled`), contract, `${path}.enabled`),
81
- ...(server !== undefined ? { server } : {}),
82
- ...(details !== undefined ? { details } : {}),
83
- };
84
- }
85
- function parseCoreState(value, contract) {
86
- const source = objectValue(value, contract, "core");
87
- const pid = Object.hasOwn(source, "pid")
88
- ? positiveSafeInteger(source.pid, contract, "core.pid")
89
- : undefined;
90
- const startedAt = Object.hasOwn(source, "startedAt")
91
- ? timestampValue(source.startedAt, contract, "core.startedAt")
92
- : undefined;
93
- const healthy = Object.hasOwn(source, "healthy")
94
- ? booleanValue(source.healthy, contract, "core.healthy")
95
- : undefined;
96
- const version = optionalString(source, "version", contract, "core.version");
97
- const tunActive = Object.hasOwn(source, "tunActive")
98
- ? booleanValue(source.tunActive, contract, "core.tunActive")
99
- : undefined;
100
- return {
101
- running: booleanValue(required(source, "running", contract, "core.running"), contract, "core.running"),
102
- ...(pid !== undefined ? { pid } : {}),
103
- ...(startedAt !== undefined ? { startedAt } : {}),
104
- ...(healthy !== undefined ? { healthy } : {}),
105
- ...(version !== undefined ? { version } : {}),
106
- ...(tunActive !== undefined ? { tunActive } : {}),
107
- };
108
- }
109
- export function parsePublicSettings(value) {
110
- const contract = "sashd settings";
111
- const source = objectValue(value, contract, "settings");
112
- return {
113
- mixedPort: portValue(required(source, "mixedPort", contract, "settings.mixedPort"), contract, "settings.mixedPort"),
114
- controller: stringValue(required(source, "controller", contract, "settings.controller"), contract, "settings.controller", true),
115
- tun: booleanValue(required(source, "tun", contract, "settings.tun"), contract, "settings.tun"),
116
- allowLan: booleanValue(required(source, "allowLan", contract, "settings.allowLan"), contract, "settings.allowLan"),
117
- daemonPort: portValue(required(source, "daemonPort", contract, "settings.daemonPort"), contract, "settings.daemonPort"),
118
- systemProxy: booleanValue(required(source, "systemProxy", contract, "settings.systemProxy"), contract, "settings.systemProxy"),
119
- };
120
- }
121
- function parseProfileMeta(value, contract, path) {
122
- const source = objectValue(value, contract, path);
123
- const subInfoValue = Object.hasOwn(source, "subInfo")
124
- ? objectValue(source.subInfo, contract, `${path}.subInfo`)
125
- : undefined;
126
- const subInfo = subInfoValue
127
- ? {
128
- upload: finiteNumber(required(subInfoValue, "upload", contract, `${path}.subInfo.upload`), contract, `${path}.subInfo.upload`),
129
- download: finiteNumber(required(subInfoValue, "download", contract, `${path}.subInfo.download`), contract, `${path}.subInfo.download`),
130
- total: finiteNumber(required(subInfoValue, "total", contract, `${path}.subInfo.total`), contract, `${path}.subInfo.total`),
131
- ...(Object.hasOwn(subInfoValue, "expire")
132
- ? {
133
- expire: finiteNumber(subInfoValue.expire, contract, `${path}.subInfo.expire`),
134
- }
135
- : {}),
136
- }
137
- : undefined;
138
- const homePage = optionalString(source, "homePage", contract, `${path}.homePage`);
139
- const lastError = optionalString(source, "lastError", contract, `${path}.lastError`);
140
- return {
141
- id: stringValue(required(source, "id", contract, `${path}.id`), contract, `${path}.id`, true),
142
- name: stringValue(required(source, "name", contract, `${path}.name`), contract, `${path}.name`),
143
- url: stringValue(required(source, "url", contract, `${path}.url`), contract, `${path}.url`),
144
- intervalHours: nonNegativeSafeInteger(required(source, "intervalHours", contract, `${path}.intervalHours`), contract, `${path}.intervalHours`),
145
- createdAt: timestampValue(required(source, "createdAt", contract, `${path}.createdAt`), contract, `${path}.createdAt`),
146
- updatedAt: timestampValue(required(source, "updatedAt", contract, `${path}.updatedAt`), contract, `${path}.updatedAt`),
147
- ...(subInfo !== undefined ? { subInfo } : {}),
148
- ...(homePage !== undefined ? { homePage } : {}),
149
- ...(lastError !== undefined ? { lastError } : {}),
150
- };
34
+ export function apiErrorBody(code, message) {
35
+ return { error: { code, message } };
151
36
  }
152
- function optionalProxyCount(source, contract, path) {
153
- return Object.hasOwn(source, "proxyCount")
154
- ? { proxyCount: nonNegativeSafeInteger(source.proxyCount, contract, `${path}.proxyCount`) }
155
- : {};
37
+ export function parseApiErrorBody(value) {
38
+ if (!isPlainObject(value) ||
39
+ !isPlainObject(value.error) ||
40
+ typeof value.error.code !== "string" ||
41
+ typeof value.error.message !== "string")
42
+ return undefined;
43
+ return { code: value.error.code, message: value.error.message };
156
44
  }
157
45
  export function parseHealthInfo(value) {
158
- const contract = "sashd health";
159
- const source = objectValue(value, contract, "response");
46
+ const source = object(value, "health");
160
47
  return {
161
- token: stringValue(required(source, "token", contract, "token"), contract, "token", true),
162
- pid: positiveSafeInteger(required(source, "pid", contract, "pid"), contract, "pid"),
163
- startedAt: timestampValue(required(source, "startedAt", contract, "startedAt"), contract, "startedAt"),
48
+ token: string(source.token, "token"),
49
+ pid: integer(source.pid, "pid", 1),
50
+ startedAt: timestamp(source.startedAt, "startedAt"),
164
51
  };
165
52
  }
166
53
  export function parseWebBootstrapInfo(value) {
167
- const contract = "sashd web bootstrap";
168
- const source = objectValue(value, contract, "response");
54
+ const source = object(value, "bootstrap");
169
55
  return {
170
- token: stringValue(required(source, "token", contract, "token"), contract, "token", true),
171
- expiresAt: timestampValue(required(source, "expiresAt", contract, "expiresAt"), contract, "expiresAt"),
56
+ token: string(source.token, "token"),
57
+ expiresAt: timestamp(source.expiresAt, "expiresAt"),
172
58
  };
173
59
  }
174
60
  export function parseWebSessionInfo(value) {
175
- const contract = "sashd web session";
176
- const source = objectValue(value, contract, "response");
61
+ const source = object(value, "session");
177
62
  return {
178
- token: stringValue(required(source, "token", contract, "token"), contract, "token", true),
179
- daemonToken: stringValue(required(source, "daemonToken", contract, "daemonToken"), contract, "daemonToken", true),
63
+ token: string(source.token, "token"),
64
+ daemonToken: string(source.daemonToken, "daemonToken"),
180
65
  };
181
66
  }
182
67
  export function parseCoreStartResult(value) {
183
- const contract = "sashd core start";
184
- const source = objectValue(value, contract, "response");
185
- const version = optionalString(source, "version", contract, "version");
186
- const tunActive = Object.hasOwn(source, "tunActive")
187
- ? booleanValue(source.tunActive, contract, "tunActive")
188
- : undefined;
189
- return {
190
- pid: positiveSafeInteger(required(source, "pid", contract, "pid"), contract, "pid"),
191
- ...(version !== undefined ? { version } : {}),
192
- ...(tunActive !== undefined ? { tunActive } : {}),
193
- };
194
- }
195
- export function parseCoreReloadResult(value) {
196
- const contract = "sashd core reload";
197
- const source = objectValue(value, contract, "response");
198
- const sourceKind = stringValue(required(source, "source", contract, "source"), contract, "source");
199
- if (sourceKind !== "subscription" && sourceKind !== "default") {
200
- invalid(contract, "source", `"subscription" or "default"`);
201
- }
202
- return {
203
- proxyCount: nonNegativeSafeInteger(required(source, "proxyCount", contract, "proxyCount"), contract, "proxyCount"),
204
- source: sourceKind,
205
- };
68
+ const source = object(value, "Core start");
69
+ const version = optionalString(source, "version");
70
+ return { pid: integer(source.pid, "pid", 1), ...(version !== undefined ? { version } : {}) };
206
71
  }
207
- export function parseShutdownResult(value) {
208
- const contract = "sashd shutdown";
209
- const source = objectValue(value, contract, "response");
210
- return {
211
- coreWasRunning: booleanValue(required(source, "coreWasRunning", contract, "coreWasRunning"), contract, "coreWasRunning"),
212
- };
72
+ export function parseCoreUpdateResponse(value) {
73
+ return { version: string(object(value, "Core update").version, "version") };
213
74
  }
214
- export function parseSystemProxyStatusResponse(value) {
215
- const contract = "sashd system proxy";
216
- const source = objectValue(value, contract, "response");
217
- const state = parseSystemProxyState(source, contract, "response");
218
- const queryError = optionalString(source, "queryError", contract, "queryError");
75
+ export function parsePublicSettings(value) {
76
+ const source = object(value, "settings");
219
77
  return {
220
- desired: booleanValue(required(source, "desired", contract, "desired"), contract, "desired"),
221
- applied: booleanValue(required(source, "applied", contract, "applied"), contract, "applied"),
222
- ...state,
223
- appliedKnown: knownFlag(source, "appliedKnown", contract, "appliedKnown"),
224
- stateKnown: knownFlag(source, "stateKnown", contract, "stateKnown"),
225
- ...(queryError !== undefined ? { queryError } : {}),
78
+ mixedPort: integer(source.mixedPort, "mixedPort", 1, 65535),
79
+ controller: string(source.controller, "controller"),
80
+ allowLan: boolean(source.allowLan, "allowLan"),
81
+ daemonPort: integer(source.daemonPort, "daemonPort", 1, 65535),
82
+ systemProxy: boolean(source.systemProxy, "systemProxy"),
226
83
  };
227
84
  }
228
- export function parseSettingsPatch(value, direction = "response") {
229
- const subject = `sashd settings patch ${direction}`;
230
- const fail = (path, expected) => {
231
- throw new TypeError(`${subject} is invalid: ${path} must be ${expected}`);
232
- };
233
- if (!isPlainObject(value))
234
- fail("body", "a plain object");
235
- const source = value;
236
- const allowed = new Set([
237
- "mixedPort",
238
- "allowLan",
239
- "tun",
240
- "systemProxy",
241
- "daemonPort",
242
- "daemonSecret",
243
- ]);
85
+ export function parseSettingsPatch(value) {
86
+ const source = object(value, "settings patch");
244
87
  for (const key of Object.keys(source)) {
245
- if (!allowed.has(key))
246
- fail(key, "a known settings patch field");
88
+ if (!["mixedPort", "allowLan", "systemProxy"].includes(key))
89
+ throw new TypeError(`Unknown settings field: ${key}`);
247
90
  }
248
- const portField = (key) => {
249
- if (!Object.hasOwn(source, key))
250
- return undefined;
251
- const field = source[key];
252
- if (typeof field !== "number" || !Number.isInteger(field) || field < 1 || field > 65_535) {
253
- return fail(key, "an integer port from 1 to 65535");
254
- }
255
- return field;
256
- };
257
- const boolField = (key) => {
258
- if (!Object.hasOwn(source, key))
259
- return undefined;
260
- const field = source[key];
261
- if (typeof field !== "boolean")
262
- return fail(key, "a boolean");
263
- return field;
91
+ return {
92
+ ...(Object.hasOwn(source, "mixedPort")
93
+ ? { mixedPort: integer(source.mixedPort, "mixedPort", 1, 65535) }
94
+ : {}),
95
+ ...(Object.hasOwn(source, "allowLan")
96
+ ? { allowLan: boolean(source.allowLan, "allowLan") }
97
+ : {}),
98
+ ...(Object.hasOwn(source, "systemProxy")
99
+ ? { systemProxy: boolean(source.systemProxy, "systemProxy") }
100
+ : {}),
264
101
  };
265
- const patch = {};
266
- const mixedPort = portField("mixedPort");
267
- if (mixedPort !== undefined)
268
- patch.mixedPort = mixedPort;
269
- const daemonPort = portField("daemonPort");
270
- if (daemonPort !== undefined)
271
- patch.daemonPort = daemonPort;
272
- const allowLan = boolField("allowLan");
273
- if (allowLan !== undefined)
274
- patch.allowLan = allowLan;
275
- const tun = boolField("tun");
276
- if (tun !== undefined)
277
- patch.tun = tun;
278
- const systemProxy = boolField("systemProxy");
279
- if (systemProxy !== undefined)
280
- patch.systemProxy = systemProxy;
281
- if (Object.hasOwn(source, "daemonSecret")) {
282
- const secret = source.daemonSecret;
283
- if (typeof secret !== "string" || !secret.trim()) {
284
- fail("daemonSecret", "a non-empty string");
285
- }
286
- patch.daemonSecret = secret;
287
- }
288
- return patch;
289
102
  }
290
103
  export function parseSettingsWriteResult(value) {
291
- const contract = "sashd settings write";
292
- const source = objectValue(value, contract, "response");
104
+ const source = object(value, "settings write");
293
105
  return {
294
- restartRequired: booleanValue(required(source, "restartRequired", contract, "restartRequired"), contract, "restartRequired"),
295
- settings: parsePublicSettings(required(source, "settings", contract, "settings")),
106
+ restartRequired: boolean(source.restartRequired, "restartRequired"),
107
+ settings: parsePublicSettings(source.settings),
296
108
  };
297
109
  }
298
- export function parseSettingsFileContent(value) {
299
- const contract = "sashd settings file";
300
- const source = objectValue(value, contract, "response");
110
+ function parseSystemProxyState(value) {
111
+ const source = object(value, "system proxy");
112
+ const server = optionalString(source, "server");
113
+ const details = optionalString(source, "details");
301
114
  return {
302
- content: stringValue(required(source, "content", contract, "content"), contract, "content"),
115
+ supported: boolean(source.supported, "supported"),
116
+ enabled: boolean(source.enabled, "enabled"),
117
+ ...(server !== undefined ? { server } : {}),
118
+ ...(details !== undefined ? { details } : {}),
303
119
  };
304
120
  }
305
- export function parseProfilesIndex(value) {
306
- const contract = "sashd profiles";
307
- const source = objectValue(value, contract, "response");
308
- const activeValue = required(source, "activeId", contract, "activeId");
309
- const profilesValue = required(source, "profiles", contract, "profiles");
310
- if (!Array.isArray(profilesValue))
311
- invalid(contract, "profiles", "an array");
312
- return {
313
- activeId: activeValue === null ? null : stringValue(activeValue, contract, "activeId", true),
314
- profiles: profilesValue.map((entry, index) => parseProfileMeta(entry, contract, `profiles[${index}]`)),
121
+ export function parseSystemProxyStatusResponse(value) {
122
+ const source = object(value, "system proxy status");
123
+ const queryError = optionalString(source, "queryError");
124
+ return {
125
+ ...parseSystemProxyState(source),
126
+ desired: boolean(source.desired, "desired"),
127
+ applied: boolean(source.applied, "applied"),
128
+ appliedKnown: boolean(source.appliedKnown, "appliedKnown"),
129
+ stateKnown: boolean(source.stateKnown, "stateKnown"),
130
+ ...(queryError !== undefined ? { queryError } : {}),
315
131
  };
316
132
  }
317
133
  export function parseProfileActionResponse(value) {
318
- const contract = "sashd profile action";
319
- const source = objectValue(value, contract, "response");
134
+ const source = object(value, "profile action");
320
135
  return {
321
- profile: parseProfileMeta(required(source, "profile", contract, "profile"), contract, "profile"),
322
- activated: booleanValue(required(source, "activated", contract, "activated"), contract, "activated"),
323
- ...optionalProxyCount(source, contract, "response"),
136
+ profile: parseProfileMeta(source.profile),
137
+ activated: boolean(source.activated, "activated"),
324
138
  };
325
139
  }
326
140
  export function parseProfileUpdateResponse(value) {
327
- const contract = "sashd profile update";
328
- const source = objectValue(value, contract, "response");
329
- return {
330
- profile: parseProfileMeta(required(source, "profile", contract, "profile"), contract, "profile"),
331
- ...optionalProxyCount(source, contract, "response"),
332
- };
333
- }
334
- export function parseProfilesUpdateAllResponse(value) {
335
- const contract = "sashd profiles update-all";
336
- const source = objectValue(value, contract, "response");
337
- const failedValue = required(source, "failed", contract, "failed");
338
- if (!Array.isArray(failedValue))
339
- invalid(contract, "failed", "an array");
340
- return {
341
- updated: nonNegativeSafeInteger(required(source, "updated", contract, "updated"), contract, "updated"),
342
- failed: failedValue.map((entry, index) => {
343
- const path = `failed[${index}]`;
344
- const failure = objectValue(entry, contract, path);
345
- return {
346
- id: stringValue(required(failure, "id", contract, `${path}.id`), contract, `${path}.id`),
347
- name: stringValue(required(failure, "name", contract, `${path}.name`), contract, `${path}.name`),
348
- error: stringValue(required(failure, "error", contract, `${path}.error`), contract, `${path}.error`),
349
- };
350
- }),
351
- ...optionalProxyCount(source, contract, "response"),
352
- };
141
+ return { profile: parseProfileMeta(object(value, "profile").profile) };
353
142
  }
143
+ export { parseProfileUpdateResponse as parseProfileRenameResponse };
354
144
  export function parseProfileContentResponse(value) {
355
- const contract = "sashd profile content";
356
- const source = objectValue(value, contract, "response");
145
+ const source = object(value, "profile content");
357
146
  return {
358
- name: stringValue(required(source, "name", contract, "name"), contract, "name"),
359
- content: stringValue(required(source, "content", contract, "content"), contract, "content"),
147
+ name: string(source.name, "name"),
148
+ content: string(source.content, "content", true),
149
+ revision: integer(source.revision, "revision", 1),
360
150
  };
361
151
  }
362
152
  export function parseProfileActivateResponse(value) {
363
- const contract = "sashd profile activate";
364
- const source = objectValue(value, contract, "response");
365
- const activeValue = required(source, "activeId", contract, "activeId");
153
+ const source = object(value, "profile selection");
366
154
  return {
367
- activeId: activeValue === null ? null : stringValue(activeValue, contract, "activeId", true),
368
- proxyCount: nonNegativeSafeInteger(required(source, "proxyCount", contract, "proxyCount"), contract, "proxyCount"),
155
+ activeId: source.activeId === null ? null : string(source.activeId, "activeId"),
156
+ proxyCount: integer(source.proxyCount, "proxyCount"),
369
157
  };
370
158
  }
371
159
  export function parseProfileRemoveResponse(value) {
372
- const contract = "sashd profile remove";
373
- const source = objectValue(value, contract, "response");
374
- return {
375
- wasActive: booleanValue(required(source, "wasActive", contract, "wasActive"), contract, "wasActive"),
376
- ...optionalProxyCount(source, contract, "response"),
377
- };
160
+ return { wasActive: boolean(object(value, "profile removal").wasActive, "wasActive") };
378
161
  }
379
- export function parseProfileRenameResponse(value) {
380
- const contract = "sashd profile rename";
381
- const source = objectValue(value, contract, "response");
162
+ export function parseProfilesUpdateAllResponse(value) {
163
+ const source = object(value, "profiles update");
164
+ if (!Array.isArray(source.failed))
165
+ throw new TypeError("failed must be an array");
382
166
  return {
383
- profile: parseProfileMeta(required(source, "profile", contract, "profile"), contract, "profile"),
167
+ updated: integer(source.updated, "updated"),
168
+ failed: source.failed.map((item) => {
169
+ const failure = object(item, "profile error");
170
+ return {
171
+ id: string(failure.id, "id"),
172
+ name: string(failure.name, "name"),
173
+ error: string(failure.error, "error"),
174
+ };
175
+ }),
384
176
  };
385
177
  }
386
178
  export function parseDaemonStatus(value) {
387
- const contract = "sashd status";
388
- const source = objectValue(value, contract, "response");
389
- const daemonSource = objectValue(required(source, "daemon", contract, "daemon"), contract, "daemon");
390
- const revisionsSource = objectValue(required(source, "revisions", contract, "revisions"), contract, "revisions");
391
- const proxySource = objectValue(required(source, "systemProxy", contract, "systemProxy"), contract, "systemProxy");
392
- const actual = Object.hasOwn(proxySource, "actual")
393
- ? parseSystemProxyState(proxySource.actual, contract, "systemProxy.actual")
394
- : undefined;
395
- const queryError = optionalString(proxySource, "queryError", contract, "systemProxy.queryError");
396
- const activeValue = required(source, "activeProfile", contract, "activeProfile");
397
- const activeProfile = activeValue === null
179
+ const source = object(value, "status");
180
+ const daemon = object(source.daemon, "daemon");
181
+ const revisions = object(source.revisions, "revisions");
182
+ const core = object(source.core, "core");
183
+ const proxy = object(source.systemProxy, "systemProxy");
184
+ const configuration = object(source.configuration, "configuration");
185
+ const selected = source.activeProfile === null ? null : object(source.activeProfile, "activeProfile");
186
+ const applied = configuration.appliedProfile === null
398
187
  ? null
399
- : (() => {
400
- const active = objectValue(activeValue, contract, "activeProfile");
401
- return {
402
- id: stringValue(required(active, "id", contract, "activeProfile.id"), contract, "activeProfile.id", true),
403
- name: stringValue(required(active, "name", contract, "activeProfile.name"), contract, "activeProfile.name"),
404
- url: stringValue(required(active, "url", contract, "activeProfile.url"), contract, "activeProfile.url"),
405
- };
406
- })();
188
+ : object(configuration.appliedProfile, "appliedProfile");
189
+ const appliedSettings = configuration.appliedSettings === null
190
+ ? null
191
+ : object(configuration.appliedSettings, "appliedSettings");
192
+ const version = optionalString(core, "version");
193
+ const queryError = optionalString(proxy, "queryError");
407
194
  return {
408
195
  daemon: {
409
- pid: positiveSafeInteger(required(daemonSource, "pid", contract, "daemon.pid"), contract, "daemon.pid"),
410
- startedAt: timestampValue(required(daemonSource, "startedAt", contract, "daemon.startedAt"), contract, "daemon.startedAt"),
411
- port: portValue(required(daemonSource, "port", contract, "daemon.port"), contract, "daemon.port"),
196
+ pid: integer(daemon.pid, "daemon.pid", 1),
197
+ bootId: string(daemon.bootId, "daemon.bootId"),
198
+ startedAt: timestamp(daemon.startedAt, "daemon.startedAt"),
199
+ port: integer(daemon.port, "daemon.port", 1, 65535),
412
200
  },
413
201
  revisions: {
414
- profiles: nonNegativeSafeInteger(required(revisionsSource, "profiles", contract, "revisions.profiles"), contract, "revisions.profiles"),
202
+ profiles: integer(revisions.profiles, "revisions.profiles"),
203
+ runtime: integer(revisions.runtime, "revisions.runtime"),
204
+ },
205
+ core: {
206
+ running: boolean(core.running, "core.running"),
207
+ ...(Object.hasOwn(core, "pid") ? { pid: integer(core.pid, "core.pid", 1) } : {}),
208
+ ...(Object.hasOwn(core, "startedAt")
209
+ ? { startedAt: timestamp(core.startedAt, "core.startedAt") }
210
+ : {}),
211
+ ...(Object.hasOwn(core, "healthy") ? { healthy: boolean(core.healthy, "core.healthy") } : {}),
212
+ ...(version !== undefined ? { version } : {}),
213
+ },
214
+ configuration: {
215
+ pending: boolean(configuration.pending, "configuration.pending"),
216
+ appliedProfile: applied
217
+ ? {
218
+ id: string(applied.id, "appliedProfile.id"),
219
+ revision: integer(applied.revision, "appliedProfile.revision", 1),
220
+ name: string(applied.name, "appliedProfile.name"),
221
+ url: string(applied.url, "appliedProfile.url", true),
222
+ }
223
+ : null,
224
+ appliedSettings: appliedSettings
225
+ ? {
226
+ mixedPort: integer(appliedSettings.mixedPort, "appliedSettings.mixedPort", 1, 65535),
227
+ allowLan: boolean(appliedSettings.allowLan, "appliedSettings.allowLan"),
228
+ }
229
+ : null,
415
230
  },
416
- core: parseCoreState(required(source, "core", contract, "core"), contract),
417
231
  systemProxy: {
418
- desired: booleanValue(required(proxySource, "desired", contract, "systemProxy.desired"), contract, "systemProxy.desired"),
419
- applied: booleanValue(required(proxySource, "applied", contract, "systemProxy.applied"), contract, "systemProxy.applied"),
420
- ...(actual !== undefined ? { actual } : {}),
421
- appliedKnown: knownFlag(proxySource, "appliedKnown", contract, "systemProxy.appliedKnown"),
422
- stateKnown: knownFlag(proxySource, "stateKnown", contract, "systemProxy.stateKnown"),
232
+ desired: boolean(proxy.desired, "systemProxy.desired"),
233
+ applied: boolean(proxy.applied, "systemProxy.applied"),
234
+ appliedKnown: boolean(proxy.appliedKnown, "systemProxy.appliedKnown"),
235
+ stateKnown: boolean(proxy.stateKnown, "systemProxy.stateKnown"),
236
+ ...(Object.hasOwn(proxy, "actual") ? { actual: parseSystemProxyState(proxy.actual) } : {}),
423
237
  ...(queryError !== undefined ? { queryError } : {}),
424
238
  },
425
- settings: parsePublicSettings(required(source, "settings", contract, "settings")),
426
- activeProfile,
239
+ settings: parsePublicSettings(source.settings),
240
+ activeProfile: selected
241
+ ? {
242
+ id: string(selected.id, "activeProfile.id"),
243
+ name: string(selected.name, "activeProfile.name"),
244
+ url: string(selected.url, "activeProfile.url", true),
245
+ }
246
+ : null,
427
247
  };
428
248
  }