@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.
- package/CHANGELOG.md +25 -0
- package/README.md +17 -5
- package/THIRD_PARTY_NOTICES.md +9 -0
- package/dist/api.js +14 -45
- package/dist/app-state.js +99 -0
- package/dist/autostart/command.js +24 -0
- package/dist/autostart/context.js +43 -0
- package/dist/autostart/files.js +60 -0
- package/dist/autostart/installation.js +48 -0
- package/dist/autostart/start.js +47 -0
- package/dist/autostart/windows-registry.js +88 -0
- package/dist/autostart/windows.js +59 -0
- package/dist/autostart-contract.js +31 -0
- package/dist/autostart-entry.js +10 -0
- package/dist/autostart.js +69 -0
- package/dist/cli.js +11 -11
- package/dist/commands/auto.js +15 -0
- package/dist/commands/lifecycle.js +5 -15
- package/dist/commands/logs.js +11 -4
- package/dist/commands/shared.js +2 -4
- package/dist/commands/status.js +5 -1
- package/dist/commands/update.js +7 -3
- package/dist/commands/web.js +14 -50
- package/dist/contracts.js +168 -348
- package/dist/core-config-validation.js +22 -38
- package/dist/core-update.js +126 -762
- package/dist/core.js +20 -46
- package/dist/daemon/app.js +129 -117
- package/dist/daemon/context.js +13 -7
- package/dist/daemon/entry.js +21 -38
- package/dist/daemon/errors.js +3 -0
- package/dist/daemon/handlers/autostart.js +18 -0
- package/dist/daemon/handlers/core.js +32 -9
- package/dist/daemon/handlers/daemon.js +17 -6
- package/dist/daemon/handlers/profiles.js +12 -2
- package/dist/daemon/handlers/settings.js +9 -36
- package/dist/daemon/router.js +26 -21
- package/dist/daemon/scheduler.js +3 -1
- package/dist/daemon/server.js +5 -2
- package/dist/daemon-client.js +13 -3
- package/dist/daemon-http.js +6 -0
- package/dist/daemon-lifecycle.js +19 -196
- package/dist/github.js +7 -2
- package/dist/http.js +7 -5
- package/dist/mihomo-config.js +2 -1
- package/dist/paths.js +5 -7
- package/dist/profile-model.js +87 -0
- package/dist/profile-service.js +250 -587
- package/dist/profiles.js +43 -171
- package/dist/runtime-lifecycle.js +112 -194
- package/dist/runtime-owner.js +37 -76
- package/dist/sash-client.js +40 -19
- package/dist/settings-service.js +29 -224
- package/dist/settings.js +61 -289
- package/dist/status.js +24 -16
- package/dist/supervisor.js +1 -14
- package/dist/sysproxy/common.js +5 -45
- package/dist/sysproxy/factory.js +24 -65
- package/dist/sysproxy/snapshot.js +24 -257
- package/dist/sysproxy.js +1 -4
- package/dist/system-proxy-manager.js +37 -35
- package/dist/ui/assets/{ConnectionsView-CfaOG2JV.js → ConnectionsView-BgXQM6Z4.js} +1 -1
- package/dist/ui/assets/{LogsView-BDiRATXN.js → LogsView-2f542P8z.js} +1 -1
- package/dist/ui/assets/{PaginationFooter-DTytr1iu.js → PaginationFooter-cj1tcZej.js} +1 -1
- package/dist/ui/assets/ProfileEditorDialog-C9M8uKZC.css +1 -0
- package/dist/ui/assets/ProfileEditorDialog-DD4y4GBC.js +14 -0
- package/dist/ui/assets/ProfilesView-BXU2DOA7.css +1 -0
- package/dist/ui/assets/ProfilesView-DNnzYIEY.js +7 -0
- package/dist/ui/assets/{RulesView-CjEyCN1A.js → RulesView-CtKvlckZ.js} +1 -1
- package/dist/ui/assets/SettingsView-BaeKFF_N.js +1 -0
- package/dist/ui/assets/SettingsView-CWjIe05v.css +1 -0
- package/dist/ui/assets/index-CBInDvdJ.js +19 -0
- package/dist/ui/assets/index-mOLy7fkB.css +1 -0
- package/dist/ui/assets/{theme-BwDBMsKO.js → theme-D40MF8cQ.js} +1 -1
- package/dist/ui/index.html +2 -2
- package/docs/architecture-proposal.md +109 -0
- package/docs/autostart.md +101 -0
- package/docs/backend.md +92 -224
- package/docs/frontend.md +60 -106
- package/docs/usage.md +92 -164
- package/package.json +3 -3
- package/dist/commands/upgrade.js +0 -43
- package/dist/core-install-transaction.js +0 -114
- package/dist/core-update-coordination.js +0 -105
- package/dist/core-update-service.js +0 -245
- package/dist/managed-state-transaction.js +0 -375
- package/dist/offline-mutation.js +0 -58
- package/dist/profile-migration.js +0 -101
- package/dist/runtime-recovery.js +0 -31
- package/dist/sysproxy/darwin.js +0 -287
- package/dist/sysproxy/gnome.js +0 -181
- package/dist/sysproxy/legacy.js +0 -58
- package/dist/ui/assets/CodeEditorModal-6m0TJWyF.css +0 -1
- package/dist/ui/assets/CodeEditorModal-D1zn-jRS.js +0 -14
- package/dist/ui/assets/ProfileEditorDialog-CyyP6OF1.js +0 -1
- package/dist/ui/assets/ProfilesView-ByqdFNHN.css +0 -1
- package/dist/ui/assets/ProfilesView-hFa7-O72.js +0 -2
- package/dist/ui/assets/SettingsFileDialog-CjBSyH4K.js +0 -1
- package/dist/ui/assets/SettingsView-D5QvJId6.css +0 -1
- package/dist/ui/assets/SettingsView-DsRntRfn.js +0 -2
- package/dist/ui/assets/index-CxQTf_P9.css +0 -1
- 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
|
-
|
|
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
|
-
|
|
8
|
+
throw new TypeError(`${name} must be a plain object`);
|
|
21
9
|
return value;
|
|
22
10
|
}
|
|
23
|
-
function
|
|
24
|
-
if (!
|
|
25
|
-
|
|
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
|
|
34
|
-
if (typeof value !== "
|
|
35
|
-
|
|
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
|
|
58
|
-
if (typeof value !== "number" || !Number.
|
|
59
|
-
|
|
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
|
|
26
|
+
function timestamp(value, name) {
|
|
64
27
|
if (!isCanonicalIsoTimestamp(value))
|
|
65
|
-
|
|
28
|
+
throw new TypeError(`${name} must be a canonical timestamp`);
|
|
66
29
|
return value;
|
|
67
30
|
}
|
|
68
|
-
function optionalString(source, key
|
|
69
|
-
return Object.hasOwn(source, key) ?
|
|
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
|
|
75
|
-
|
|
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
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
159
|
-
const source = objectValue(value, contract, "response");
|
|
46
|
+
const source = object(value, "health");
|
|
160
47
|
return {
|
|
161
|
-
token:
|
|
162
|
-
pid:
|
|
163
|
-
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
|
|
168
|
-
const source = objectValue(value, contract, "response");
|
|
54
|
+
const source = object(value, "bootstrap");
|
|
169
55
|
return {
|
|
170
|
-
token:
|
|
171
|
-
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
|
|
176
|
-
const source = objectValue(value, contract, "response");
|
|
61
|
+
const source = object(value, "session");
|
|
177
62
|
return {
|
|
178
|
-
token:
|
|
179
|
-
daemonToken:
|
|
63
|
+
token: string(source.token, "token"),
|
|
64
|
+
daemonToken: string(source.daemonToken, "daemonToken"),
|
|
180
65
|
};
|
|
181
66
|
}
|
|
182
67
|
export function parseCoreStartResult(value) {
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
|
|
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
|
|
208
|
-
|
|
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
|
|
215
|
-
const
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
|
229
|
-
const
|
|
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 (!
|
|
246
|
-
|
|
88
|
+
if (!["mixedPort", "allowLan", "systemProxy"].includes(key))
|
|
89
|
+
throw new TypeError(`Unknown settings field: ${key}`);
|
|
247
90
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
|
292
|
-
const source = objectValue(value, contract, "response");
|
|
104
|
+
const source = object(value, "settings write");
|
|
293
105
|
return {
|
|
294
|
-
restartRequired:
|
|
295
|
-
settings: parsePublicSettings(
|
|
106
|
+
restartRequired: boolean(source.restartRequired, "restartRequired"),
|
|
107
|
+
settings: parsePublicSettings(source.settings),
|
|
296
108
|
};
|
|
297
109
|
}
|
|
298
|
-
|
|
299
|
-
const
|
|
300
|
-
const
|
|
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
|
-
|
|
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
|
|
306
|
-
const
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
|
319
|
-
const source = objectValue(value, contract, "response");
|
|
134
|
+
const source = object(value, "profile action");
|
|
320
135
|
return {
|
|
321
|
-
profile: parseProfileMeta(
|
|
322
|
-
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
|
-
|
|
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
|
|
356
|
-
const source = objectValue(value, contract, "response");
|
|
145
|
+
const source = object(value, "profile content");
|
|
357
146
|
return {
|
|
358
|
-
name:
|
|
359
|
-
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
|
|
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:
|
|
368
|
-
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
|
-
|
|
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
|
|
380
|
-
const
|
|
381
|
-
|
|
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
|
-
|
|
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
|
|
388
|
-
const
|
|
389
|
-
const
|
|
390
|
-
const
|
|
391
|
-
const
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
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
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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:
|
|
410
|
-
|
|
411
|
-
|
|
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:
|
|
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:
|
|
419
|
-
applied:
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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(
|
|
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
|
}
|