@tutti-os/desktop-update-admission 0.0.266
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/LICENSE +201 -0
- package/README.md +112 -0
- package/dist/chunk-GONYAEKF.js +139 -0
- package/dist/chunk-GONYAEKF.js.map +1 -0
- package/dist/chunk-LV67FBCG.js +112 -0
- package/dist/chunk-LV67FBCG.js.map +1 -0
- package/dist/chunk-TRALK3GT.js +33 -0
- package/dist/chunk-TRALK3GT.js.map +1 -0
- package/dist/chunk-X4U7GODT.js +134 -0
- package/dist/chunk-X4U7GODT.js.map +1 -0
- package/dist/chunk-ZQTIYDGZ.js +603 -0
- package/dist/chunk-ZQTIYDGZ.js.map +1 -0
- package/dist/contracts/index.d.ts +97 -0
- package/dist/contracts/index.js +15 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/core/index.d.ts +19 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/development/index.d.ts +133 -0
- package/dist/development/index.js +289 -0
- package/dist/development/index.js.map +1 -0
- package/dist/development/mock-server-cli.d.ts +1 -0
- package/dist/development/mock-server-cli.js +67 -0
- package/dist/development/mock-server-cli.js.map +1 -0
- package/dist/electron-main/index.d.ts +38 -0
- package/dist/electron-main/index.js +498 -0
- package/dist/electron-main/index.js.map +1 -0
- package/dist/i18n/index.d.ts +64 -0
- package/dist/i18n/index.js +74 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/mandatory-updater/index.d.ts +23 -0
- package/dist/mandatory-updater/index.js +10 -0
- package/dist/mandatory-updater/index.js.map +1 -0
- package/dist/preload/index.d.ts +9 -0
- package/dist/preload/index.js +30 -0
- package/dist/preload/index.js.map +1 -0
- package/dist/react/index.d.ts +13 -0
- package/dist/react/index.js +116 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +98 -0
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
import {
|
|
2
|
+
validateMinimumVersionResponse
|
|
3
|
+
} from "./chunk-LV67FBCG.js";
|
|
4
|
+
|
|
5
|
+
// src/development/environment.ts
|
|
6
|
+
var desktopUpdateAdmissionDevelopmentEnvironment = {
|
|
7
|
+
currentVersion: "DESKTOP_UPDATE_ADMISSION_CURRENT_VERSION",
|
|
8
|
+
development: "DESKTOP_UPDATE_ADMISSION_DEV",
|
|
9
|
+
download: "DESKTOP_UPDATE_ADMISSION_DOWNLOAD",
|
|
10
|
+
foregroundIntervalMs: "DESKTOP_UPDATE_ADMISSION_FOREGROUND_INTERVAL_MS",
|
|
11
|
+
install: "DESKTOP_UPDATE_ADMISSION_INSTALL",
|
|
12
|
+
latestVersion: "DESKTOP_UPDATE_ADMISSION_LATEST_VERSION",
|
|
13
|
+
minimumVersion: "DESKTOP_UPDATE_ADMISSION_MINIMUM_VERSION",
|
|
14
|
+
mockServerUrl: "DESKTOP_UPDATE_ADMISSION_MOCK_SERVER_URL",
|
|
15
|
+
policy: "DESKTOP_UPDATE_ADMISSION_POLICY",
|
|
16
|
+
policySequence: "DESKTOP_UPDATE_ADMISSION_POLICY_SEQUENCE",
|
|
17
|
+
scenario: "DESKTOP_UPDATE_ADMISSION_SCENARIO",
|
|
18
|
+
transport: "DESKTOP_UPDATE_ADMISSION_TRANSPORT",
|
|
19
|
+
updater: "DESKTOP_UPDATE_ADMISSION_UPDATER"
|
|
20
|
+
};
|
|
21
|
+
function invalidDevelopmentScenario(message) {
|
|
22
|
+
throw new Error(`invalid desktop update development scenario: ${message}`);
|
|
23
|
+
}
|
|
24
|
+
function readRequiredDevelopmentEnvironment(env, name) {
|
|
25
|
+
const value = env[name]?.trim();
|
|
26
|
+
if (!value) {
|
|
27
|
+
return invalidDevelopmentScenario(`${name} is required`);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function readDevelopmentEnabled(env) {
|
|
32
|
+
const name = desktopUpdateAdmissionDevelopmentEnvironment.development;
|
|
33
|
+
const value = env[name]?.trim().toLowerCase();
|
|
34
|
+
if (!value || ["0", "false", "no", "off"].includes(value)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (["1", "true", "yes", "on"].includes(value)) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return invalidDevelopmentScenario(`${name} must be a boolean flag`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/development/version.ts
|
|
44
|
+
var strictSemVerPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/u;
|
|
45
|
+
var managedStablePattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u;
|
|
46
|
+
var managedRcPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-rc\.(0|[1-9]\d*)$/u;
|
|
47
|
+
function validateStrictDevelopmentSemVer(value, name) {
|
|
48
|
+
const normalized = value.trim();
|
|
49
|
+
const match = strictSemVerPattern.exec(normalized);
|
|
50
|
+
if (!match) {
|
|
51
|
+
return invalidDevelopmentScenario(`${name} must be valid SemVer`);
|
|
52
|
+
}
|
|
53
|
+
const prerelease = match[4];
|
|
54
|
+
if (prerelease) {
|
|
55
|
+
for (const identifier of prerelease.split(".")) {
|
|
56
|
+
if (/^\d+$/u.test(identifier) && identifier.length > 1 && identifier[0] === "0") {
|
|
57
|
+
return invalidDevelopmentScenario(`${name} must be valid SemVer`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return normalized;
|
|
62
|
+
}
|
|
63
|
+
function parseDevelopmentManagedVersion(value) {
|
|
64
|
+
const stable = managedStablePattern.exec(value);
|
|
65
|
+
if (stable) {
|
|
66
|
+
return {
|
|
67
|
+
channel: "stable",
|
|
68
|
+
core: [stable[1], stable[2], stable[3]],
|
|
69
|
+
rc: null
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const rc = managedRcPattern.exec(value);
|
|
73
|
+
if (!rc) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
channel: "rc",
|
|
78
|
+
core: [rc[1], rc[2], rc[3]],
|
|
79
|
+
rc: rc[4]
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function compareNumericIdentifier(left, right) {
|
|
83
|
+
if (left.length !== right.length) {
|
|
84
|
+
return left.length < right.length ? -1 : 1;
|
|
85
|
+
}
|
|
86
|
+
return left === right ? 0 : left < right ? -1 : 1;
|
|
87
|
+
}
|
|
88
|
+
function compareDevelopmentManagedVersions(left, right) {
|
|
89
|
+
for (const index of [0, 1, 2]) {
|
|
90
|
+
const compared = compareNumericIdentifier(
|
|
91
|
+
left.core[index],
|
|
92
|
+
right.core[index]
|
|
93
|
+
);
|
|
94
|
+
if (compared !== 0) {
|
|
95
|
+
return compared;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (left.rc === right.rc) {
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
if (left.rc === null) {
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
if (right.rc === null) {
|
|
105
|
+
return -1;
|
|
106
|
+
}
|
|
107
|
+
return compareNumericIdentifier(left.rc, right.rc);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/development/policyScenario.ts
|
|
111
|
+
function parseOutcome(value) {
|
|
112
|
+
switch (value) {
|
|
113
|
+
case "allowed":
|
|
114
|
+
case "upgradeRequired":
|
|
115
|
+
case "disabled":
|
|
116
|
+
case "unsupported":
|
|
117
|
+
case "unmanagedPrerelease":
|
|
118
|
+
case "error":
|
|
119
|
+
case "timeout":
|
|
120
|
+
return value;
|
|
121
|
+
default:
|
|
122
|
+
return invalidDevelopmentScenario(
|
|
123
|
+
`unknown policy outcome ${JSON.stringify(value)}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function configuredMinimum(value, name) {
|
|
128
|
+
const version = validateStrictDevelopmentSemVer(value, name);
|
|
129
|
+
if (!parseDevelopmentManagedVersion(version)) {
|
|
130
|
+
return invalidDevelopmentScenario(
|
|
131
|
+
`${name} must be a managed stable or RC version`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return { kind: "configured", version };
|
|
135
|
+
}
|
|
136
|
+
function createPolicyStep(token, fallbackMinimumVersion) {
|
|
137
|
+
const [rawOutcome, rawMinimumVersion, ...extra] = token.split("@");
|
|
138
|
+
if (!rawOutcome || extra.length > 0) {
|
|
139
|
+
return invalidDevelopmentScenario(
|
|
140
|
+
`invalid policy step ${JSON.stringify(token)}`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
const outcome = parseOutcome(rawOutcome.trim());
|
|
144
|
+
if (outcome === "allowed" || outcome === "upgradeRequired") {
|
|
145
|
+
return {
|
|
146
|
+
minimum: configuredMinimum(
|
|
147
|
+
rawMinimumVersion?.trim() || fallbackMinimumVersion || "",
|
|
148
|
+
desktopUpdateAdmissionDevelopmentEnvironment.minimumVersion
|
|
149
|
+
),
|
|
150
|
+
outcome,
|
|
151
|
+
policySource: "defaultMinimum"
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (rawMinimumVersion !== void 0) {
|
|
155
|
+
return invalidDevelopmentScenario(
|
|
156
|
+
`policy outcome ${outcome} must not include a minimum version`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (outcome === "error") {
|
|
160
|
+
return {
|
|
161
|
+
message: "Development minimum-version policy check failed",
|
|
162
|
+
outcome
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return { outcome };
|
|
166
|
+
}
|
|
167
|
+
function createPresetPolicySteps(name, minimumVersion) {
|
|
168
|
+
const requiredMinimum = () => configuredMinimum(
|
|
169
|
+
minimumVersion || "",
|
|
170
|
+
desktopUpdateAdmissionDevelopmentEnvironment.minimumVersion
|
|
171
|
+
);
|
|
172
|
+
switch (name) {
|
|
173
|
+
case "startup-force-success":
|
|
174
|
+
case "startup-updater-unavailable":
|
|
175
|
+
case "startup-target-below-minimum":
|
|
176
|
+
case "startup-download-error":
|
|
177
|
+
return [
|
|
178
|
+
{
|
|
179
|
+
minimum: requiredMinimum(),
|
|
180
|
+
outcome: "upgradeRequired",
|
|
181
|
+
policySource: "defaultMinimum"
|
|
182
|
+
}
|
|
183
|
+
];
|
|
184
|
+
case "startup-policy-timeout":
|
|
185
|
+
return [{ outcome: "timeout" }];
|
|
186
|
+
case "retry-policy-released":
|
|
187
|
+
return [
|
|
188
|
+
{
|
|
189
|
+
minimum: requiredMinimum(),
|
|
190
|
+
outcome: "upgradeRequired",
|
|
191
|
+
policySource: "defaultMinimum"
|
|
192
|
+
},
|
|
193
|
+
{ outcome: "disabled" }
|
|
194
|
+
];
|
|
195
|
+
case "foreground-upgrade-required":
|
|
196
|
+
return [
|
|
197
|
+
{
|
|
198
|
+
minimum: { kind: "requestCurrentVersion" },
|
|
199
|
+
outcome: "allowed",
|
|
200
|
+
policySource: "defaultMinimum"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
minimum: requiredMinimum(),
|
|
204
|
+
outcome: "upgradeRequired",
|
|
205
|
+
policySource: "defaultMinimum"
|
|
206
|
+
}
|
|
207
|
+
];
|
|
208
|
+
default:
|
|
209
|
+
return invalidDevelopmentScenario(
|
|
210
|
+
`unknown named scenario ${JSON.stringify(name)}`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function resolvePolicySteps(env) {
|
|
215
|
+
const names = desktopUpdateAdmissionDevelopmentEnvironment;
|
|
216
|
+
const fallbackMinimumVersion = env[names.minimumVersion]?.trim();
|
|
217
|
+
const preset = env[names.scenario]?.trim();
|
|
218
|
+
if (preset) {
|
|
219
|
+
for (const conflictingName of [names.policy, names.policySequence]) {
|
|
220
|
+
if (env[conflictingName]?.trim()) {
|
|
221
|
+
return invalidDevelopmentScenario(
|
|
222
|
+
`${names.scenario} and ${conflictingName} are mutually exclusive`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return createPresetPolicySteps(preset, fallbackMinimumVersion);
|
|
227
|
+
}
|
|
228
|
+
const sequence = env[names.policySequence]?.trim();
|
|
229
|
+
const single = env[names.policy]?.trim();
|
|
230
|
+
if (sequence && single) {
|
|
231
|
+
return invalidDevelopmentScenario(
|
|
232
|
+
`${names.policy} and ${names.policySequence} are mutually exclusive`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
const rawSteps = sequence ? sequence.split(",").map((value) => value.trim()) : single ? [single] : invalidDevelopmentScenario(
|
|
236
|
+
`${names.policy} or ${names.policySequence} is required`
|
|
237
|
+
);
|
|
238
|
+
if (rawSteps.some((value) => value.length === 0)) {
|
|
239
|
+
return invalidDevelopmentScenario(
|
|
240
|
+
`${names.policySequence} contains an empty step`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
return rawSteps.map(
|
|
244
|
+
(token) => createPolicyStep(token, fallbackMinimumVersion)
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
function configuredVersion(minimum, currentVersion) {
|
|
248
|
+
return minimum.kind === "configured" ? minimum.version : currentVersion;
|
|
249
|
+
}
|
|
250
|
+
function validateDevelopmentPolicyScenarioForCurrentVersion(scenario, currentVersion) {
|
|
251
|
+
const normalizedCurrent = validateStrictDevelopmentSemVer(
|
|
252
|
+
currentVersion,
|
|
253
|
+
desktopUpdateAdmissionDevelopmentEnvironment.currentVersion
|
|
254
|
+
);
|
|
255
|
+
const current = parseDevelopmentManagedVersion(normalizedCurrent);
|
|
256
|
+
for (const step of scenario.policySteps) {
|
|
257
|
+
if (step.outcome === "unmanagedPrerelease") {
|
|
258
|
+
if (current) {
|
|
259
|
+
invalidDevelopmentScenario(
|
|
260
|
+
"unmanagedPrerelease requires an unmanaged currentVersion"
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (step.outcome !== "allowed" && step.outcome !== "upgradeRequired") {
|
|
266
|
+
if ((step.outcome === "disabled" || step.outcome === "unsupported") && !current) {
|
|
267
|
+
invalidDevelopmentScenario(
|
|
268
|
+
`${step.outcome} requires a managed currentVersion`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (!current) {
|
|
274
|
+
invalidDevelopmentScenario(
|
|
275
|
+
`${step.outcome} requires a managed currentVersion`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
const minimumVersion = configuredVersion(step.minimum, normalizedCurrent);
|
|
279
|
+
const minimum = parseDevelopmentManagedVersion(minimumVersion);
|
|
280
|
+
if (!minimum || minimum.channel !== current.channel) {
|
|
281
|
+
invalidDevelopmentScenario(
|
|
282
|
+
`minimumVersion must use the ${current.channel} channel`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
const compared = compareDevelopmentManagedVersions(current, minimum);
|
|
286
|
+
if (step.outcome === "allowed" && compared < 0) {
|
|
287
|
+
invalidDevelopmentScenario(
|
|
288
|
+
"allowed requires currentVersion to meet minimumVersion"
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
if (step.outcome === "upgradeRequired" && compared >= 0) {
|
|
292
|
+
invalidDevelopmentScenario(
|
|
293
|
+
"upgradeRequired requires currentVersion below minimumVersion"
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function resolveDesktopUpdateDevelopmentPolicyScenario(input) {
|
|
299
|
+
if (!readDevelopmentEnabled(input.env)) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
const scenario = {
|
|
303
|
+
policySteps: resolvePolicySteps(input.env)
|
|
304
|
+
};
|
|
305
|
+
return Object.freeze({
|
|
306
|
+
policySteps: Object.freeze(
|
|
307
|
+
scenario.policySteps.map(
|
|
308
|
+
(step) => Object.freeze(
|
|
309
|
+
"minimum" in step ? {
|
|
310
|
+
...step,
|
|
311
|
+
minimum: Object.freeze({ ...step.minimum })
|
|
312
|
+
} : { ...step }
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/development/policyChecker.ts
|
|
320
|
+
function developmentChannel(currentVersion) {
|
|
321
|
+
if (/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-rc\.(0|[1-9]\d*)$/u.test(
|
|
322
|
+
currentVersion
|
|
323
|
+
)) {
|
|
324
|
+
return "rc";
|
|
325
|
+
}
|
|
326
|
+
if (/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u.test(
|
|
327
|
+
currentVersion
|
|
328
|
+
)) {
|
|
329
|
+
return "stable";
|
|
330
|
+
}
|
|
331
|
+
return "unmanaged";
|
|
332
|
+
}
|
|
333
|
+
function abortError() {
|
|
334
|
+
const error = new Error("development minimum-version policy check aborted");
|
|
335
|
+
error.name = "AbortError";
|
|
336
|
+
return error;
|
|
337
|
+
}
|
|
338
|
+
function waitForAbort(signal) {
|
|
339
|
+
if (signal.aborted) {
|
|
340
|
+
return Promise.reject(abortError());
|
|
341
|
+
}
|
|
342
|
+
return new Promise((_resolve, reject) => {
|
|
343
|
+
signal.addEventListener("abort", () => reject(abortError()), {
|
|
344
|
+
once: true
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
function responseForStep(request, step, revision) {
|
|
349
|
+
const channel = developmentChannel(request.currentVersion);
|
|
350
|
+
const base = {
|
|
351
|
+
...request,
|
|
352
|
+
channel,
|
|
353
|
+
minimumVersion: "",
|
|
354
|
+
policyRevision: `development-policy-${revision}`,
|
|
355
|
+
policySource: ""
|
|
356
|
+
};
|
|
357
|
+
const minimumVersion = (minimum) => minimum.kind === "configured" ? minimum.version : request.currentVersion;
|
|
358
|
+
switch (step.outcome) {
|
|
359
|
+
case "allowed":
|
|
360
|
+
return validateMinimumVersionResponse(
|
|
361
|
+
{
|
|
362
|
+
...base,
|
|
363
|
+
channel,
|
|
364
|
+
decision: "allowed",
|
|
365
|
+
minimumVersion: minimumVersion(step.minimum),
|
|
366
|
+
policySource: step.policySource,
|
|
367
|
+
reason: "meetsMinimum"
|
|
368
|
+
},
|
|
369
|
+
request
|
|
370
|
+
);
|
|
371
|
+
case "upgradeRequired":
|
|
372
|
+
return validateMinimumVersionResponse(
|
|
373
|
+
{
|
|
374
|
+
...base,
|
|
375
|
+
channel,
|
|
376
|
+
decision: "upgradeRequired",
|
|
377
|
+
minimumVersion: minimumVersion(step.minimum),
|
|
378
|
+
policySource: step.policySource,
|
|
379
|
+
reason: "belowMinimum"
|
|
380
|
+
},
|
|
381
|
+
request
|
|
382
|
+
);
|
|
383
|
+
case "disabled":
|
|
384
|
+
return validateMinimumVersionResponse(
|
|
385
|
+
{
|
|
386
|
+
...base,
|
|
387
|
+
decision: "notApplicable",
|
|
388
|
+
reason: "productDisabled"
|
|
389
|
+
},
|
|
390
|
+
request
|
|
391
|
+
);
|
|
392
|
+
case "unsupported":
|
|
393
|
+
return validateMinimumVersionResponse(
|
|
394
|
+
{
|
|
395
|
+
...base,
|
|
396
|
+
decision: "notApplicable",
|
|
397
|
+
reason: "unsupportedRelease"
|
|
398
|
+
},
|
|
399
|
+
request
|
|
400
|
+
);
|
|
401
|
+
case "unmanagedPrerelease":
|
|
402
|
+
return validateMinimumVersionResponse(
|
|
403
|
+
{
|
|
404
|
+
...base,
|
|
405
|
+
channel: "unmanaged",
|
|
406
|
+
decision: "notApplicable",
|
|
407
|
+
reason: "unmanagedPrerelease"
|
|
408
|
+
},
|
|
409
|
+
request
|
|
410
|
+
);
|
|
411
|
+
case "error":
|
|
412
|
+
case "timeout":
|
|
413
|
+
throw new Error(`cannot synthesize ${step.outcome} as a policy response`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
function createDevelopmentMinimumVersionChecker(policy, options = {}) {
|
|
417
|
+
const checkIndexes = /* @__PURE__ */ new Map();
|
|
418
|
+
return async (request, signal) => {
|
|
419
|
+
if (options.expectedCurrentVersion && request.currentVersion !== options.expectedCurrentVersion) {
|
|
420
|
+
throw new Error(
|
|
421
|
+
`development policy expected currentVersion ${options.expectedCurrentVersion}, received ${request.currentVersion}`
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
const requestIdentity = [
|
|
425
|
+
request.product,
|
|
426
|
+
request.platform,
|
|
427
|
+
request.architecture
|
|
428
|
+
].join(":");
|
|
429
|
+
const checkIndex = checkIndexes.get(requestIdentity) ?? 0;
|
|
430
|
+
const stepIndex = Math.min(checkIndex, policy.policySteps.length - 1);
|
|
431
|
+
const step = policy.policySteps[stepIndex];
|
|
432
|
+
checkIndexes.set(requestIdentity, checkIndex + 1);
|
|
433
|
+
if (step.outcome === "timeout") {
|
|
434
|
+
return await waitForAbort(signal);
|
|
435
|
+
}
|
|
436
|
+
if (step.outcome === "error") {
|
|
437
|
+
throw new Error(step.message);
|
|
438
|
+
}
|
|
439
|
+
validateDevelopmentPolicyScenarioForCurrentVersion(
|
|
440
|
+
{ policySteps: [step] },
|
|
441
|
+
request.currentVersion
|
|
442
|
+
);
|
|
443
|
+
return responseForStep(request, step, stepIndex + 1);
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// src/development/mockServer.ts
|
|
448
|
+
import {
|
|
449
|
+
createServer
|
|
450
|
+
} from "node:http";
|
|
451
|
+
var maximumRequestBodyBytes = 64 * 1024;
|
|
452
|
+
var minimumVersionPath = "/api/desktop/v1/public/desktop-version/check";
|
|
453
|
+
var InvalidDevelopmentMockRequestError = class extends Error {
|
|
454
|
+
constructor(message) {
|
|
455
|
+
super(message);
|
|
456
|
+
this.name = "InvalidDevelopmentMockRequestError";
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
function invalidRequest(message) {
|
|
460
|
+
throw new InvalidDevelopmentMockRequestError(message);
|
|
461
|
+
}
|
|
462
|
+
function writeJson(response, statusCode, value) {
|
|
463
|
+
const body = JSON.stringify(value);
|
|
464
|
+
response.writeHead(statusCode, {
|
|
465
|
+
"content-length": Buffer.byteLength(body),
|
|
466
|
+
"content-type": "application/json; charset=utf-8"
|
|
467
|
+
});
|
|
468
|
+
response.end(body);
|
|
469
|
+
}
|
|
470
|
+
async function readJson(request) {
|
|
471
|
+
let size = 0;
|
|
472
|
+
const chunks = [];
|
|
473
|
+
for await (const value of request) {
|
|
474
|
+
const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
475
|
+
size += chunk.byteLength;
|
|
476
|
+
if (size > maximumRequestBodyBytes) {
|
|
477
|
+
return invalidRequest("request body exceeds 64 KiB");
|
|
478
|
+
}
|
|
479
|
+
chunks.push(chunk);
|
|
480
|
+
}
|
|
481
|
+
try {
|
|
482
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
483
|
+
} catch {
|
|
484
|
+
return invalidRequest("request body must contain valid JSON");
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function normalizeRequest(value) {
|
|
488
|
+
if (!value || typeof value !== "object") {
|
|
489
|
+
return invalidRequest("request must be an object");
|
|
490
|
+
}
|
|
491
|
+
const candidate = value;
|
|
492
|
+
if (candidate.product !== "tsh-desktop" && candidate.product !== "tutti-desktop") {
|
|
493
|
+
return invalidRequest("request has an invalid product");
|
|
494
|
+
}
|
|
495
|
+
if (candidate.platform !== "macos" && candidate.platform !== "windows" && candidate.platform !== "linux") {
|
|
496
|
+
return invalidRequest("request has an invalid platform");
|
|
497
|
+
}
|
|
498
|
+
if (candidate.architecture !== "arm64" && candidate.architecture !== "x64") {
|
|
499
|
+
return invalidRequest("request has an invalid architecture");
|
|
500
|
+
}
|
|
501
|
+
if (typeof candidate.currentVersion !== "string" || candidate.currentVersion.trim() === "") {
|
|
502
|
+
return invalidRequest("request has an invalid currentVersion");
|
|
503
|
+
}
|
|
504
|
+
return {
|
|
505
|
+
architecture: candidate.architecture,
|
|
506
|
+
currentVersion: candidate.currentVersion,
|
|
507
|
+
platform: candidate.platform,
|
|
508
|
+
product: candidate.product
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
async function startDesktopUpdateDevelopmentMockServer(input) {
|
|
512
|
+
const checker = createDevelopmentMinimumVersionChecker(input.policy);
|
|
513
|
+
const server = createServer(async (request, response) => {
|
|
514
|
+
if (request.url === "/healthz") {
|
|
515
|
+
if (request.method !== "GET") {
|
|
516
|
+
response.writeHead(405, { allow: "GET" });
|
|
517
|
+
response.end();
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
writeJson(response, 200, { status: "ok" });
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (request.url !== minimumVersionPath) {
|
|
524
|
+
writeJson(response, 404, { error: "not found" });
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
if (request.method !== "POST") {
|
|
528
|
+
response.writeHead(405, { allow: "POST" });
|
|
529
|
+
response.end();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (!String(request.headers["content-type"] ?? "").toLowerCase().startsWith("application/json")) {
|
|
533
|
+
writeJson(response, 415, {
|
|
534
|
+
error: "content-type must be application/json"
|
|
535
|
+
});
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
const abortController = new AbortController();
|
|
539
|
+
request.once("aborted", () => abortController.abort());
|
|
540
|
+
response.once("close", () => {
|
|
541
|
+
if (!response.writableEnded) {
|
|
542
|
+
abortController.abort();
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
try {
|
|
546
|
+
const policyRequest = normalizeRequest(await readJson(request));
|
|
547
|
+
const result = await checker(policyRequest, abortController.signal);
|
|
548
|
+
if (!response.destroyed) {
|
|
549
|
+
writeJson(response, 200, result);
|
|
550
|
+
}
|
|
551
|
+
} catch (error) {
|
|
552
|
+
if (abortController.signal.aborted || response.destroyed) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
556
|
+
const statusCode = error instanceof InvalidDevelopmentMockRequestError ? 400 : 500;
|
|
557
|
+
writeJson(response, statusCode, { error: message });
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
const port = input.port ?? 0;
|
|
561
|
+
if (!Number.isSafeInteger(port) || port < 0 || port > 65535) {
|
|
562
|
+
throw new Error("development mock server port must be between 0 and 65535");
|
|
563
|
+
}
|
|
564
|
+
await new Promise((resolve, reject) => {
|
|
565
|
+
server.once("error", reject);
|
|
566
|
+
server.listen(port, "127.0.0.1", () => {
|
|
567
|
+
server.removeListener("error", reject);
|
|
568
|
+
resolve();
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
const address = server.address();
|
|
572
|
+
if (!address || typeof address === "string") {
|
|
573
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
574
|
+
throw new Error("development mock server did not expose a TCP address");
|
|
575
|
+
}
|
|
576
|
+
return {
|
|
577
|
+
baseUrl: `http://127.0.0.1:${address.port}`,
|
|
578
|
+
close: () => new Promise((resolve, reject) => {
|
|
579
|
+
server.close((error) => {
|
|
580
|
+
if (error) {
|
|
581
|
+
reject(error);
|
|
582
|
+
} else {
|
|
583
|
+
resolve();
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
})
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
export {
|
|
591
|
+
desktopUpdateAdmissionDevelopmentEnvironment,
|
|
592
|
+
invalidDevelopmentScenario,
|
|
593
|
+
readRequiredDevelopmentEnvironment,
|
|
594
|
+
readDevelopmentEnabled,
|
|
595
|
+
validateStrictDevelopmentSemVer,
|
|
596
|
+
parseDevelopmentManagedVersion,
|
|
597
|
+
compareDevelopmentManagedVersions,
|
|
598
|
+
validateDevelopmentPolicyScenarioForCurrentVersion,
|
|
599
|
+
resolveDesktopUpdateDevelopmentPolicyScenario,
|
|
600
|
+
createDevelopmentMinimumVersionChecker,
|
|
601
|
+
startDesktopUpdateDevelopmentMockServer
|
|
602
|
+
};
|
|
603
|
+
//# sourceMappingURL=chunk-ZQTIYDGZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/development/environment.ts","../src/development/version.ts","../src/development/policyScenario.ts","../src/development/policyChecker.ts","../src/development/mockServer.ts"],"sourcesContent":["export const desktopUpdateAdmissionDevelopmentEnvironment = {\n currentVersion: \"DESKTOP_UPDATE_ADMISSION_CURRENT_VERSION\",\n development: \"DESKTOP_UPDATE_ADMISSION_DEV\",\n download: \"DESKTOP_UPDATE_ADMISSION_DOWNLOAD\",\n foregroundIntervalMs: \"DESKTOP_UPDATE_ADMISSION_FOREGROUND_INTERVAL_MS\",\n install: \"DESKTOP_UPDATE_ADMISSION_INSTALL\",\n latestVersion: \"DESKTOP_UPDATE_ADMISSION_LATEST_VERSION\",\n minimumVersion: \"DESKTOP_UPDATE_ADMISSION_MINIMUM_VERSION\",\n mockServerUrl: \"DESKTOP_UPDATE_ADMISSION_MOCK_SERVER_URL\",\n policy: \"DESKTOP_UPDATE_ADMISSION_POLICY\",\n policySequence: \"DESKTOP_UPDATE_ADMISSION_POLICY_SEQUENCE\",\n scenario: \"DESKTOP_UPDATE_ADMISSION_SCENARIO\",\n transport: \"DESKTOP_UPDATE_ADMISSION_TRANSPORT\",\n updater: \"DESKTOP_UPDATE_ADMISSION_UPDATER\"\n} as const;\n\nexport function invalidDevelopmentScenario(message: string): never {\n throw new Error(`invalid desktop update development scenario: ${message}`);\n}\n\nexport function readRequiredDevelopmentEnvironment(\n env: Readonly<Record<string, string | undefined>>,\n name: string\n): string {\n const value = env[name]?.trim();\n if (!value) {\n return invalidDevelopmentScenario(`${name} is required`);\n }\n return value;\n}\n\nexport function readDevelopmentEnabled(\n env: Readonly<Record<string, string | undefined>>\n): boolean {\n const name = desktopUpdateAdmissionDevelopmentEnvironment.development;\n const value = env[name]?.trim().toLowerCase();\n if (!value || [\"0\", \"false\", \"no\", \"off\"].includes(value)) {\n return false;\n }\n if ([\"1\", \"true\", \"yes\", \"on\"].includes(value)) {\n return true;\n }\n return invalidDevelopmentScenario(`${name} must be a boolean flag`);\n}\n","import type { DesktopUpdateChannel } from \"../contracts/index.ts\";\nimport { invalidDevelopmentScenario } from \"./environment.ts\";\n\nconst strictSemVerPattern =\n /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?$/u;\nconst managedStablePattern =\n /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/u;\nconst managedRcPattern =\n /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)-rc\\.(0|[1-9]\\d*)$/u;\n\nexport type DevelopmentManagedVersion = {\n channel: DesktopUpdateChannel;\n core: [string, string, string];\n rc: string | null;\n};\n\nexport function validateStrictDevelopmentSemVer(\n value: string,\n name: string\n): string {\n const normalized = value.trim();\n const match = strictSemVerPattern.exec(normalized);\n if (!match) {\n return invalidDevelopmentScenario(`${name} must be valid SemVer`);\n }\n const prerelease = match[4];\n if (prerelease) {\n for (const identifier of prerelease.split(\".\")) {\n if (\n /^\\d+$/u.test(identifier) &&\n identifier.length > 1 &&\n identifier[0] === \"0\"\n ) {\n return invalidDevelopmentScenario(`${name} must be valid SemVer`);\n }\n }\n }\n return normalized;\n}\n\nexport function parseDevelopmentManagedVersion(\n value: string\n): DevelopmentManagedVersion | null {\n const stable = managedStablePattern.exec(value);\n if (stable) {\n return {\n channel: \"stable\",\n core: [stable[1]!, stable[2]!, stable[3]!],\n rc: null\n };\n }\n const rc = managedRcPattern.exec(value);\n if (!rc) {\n return null;\n }\n return {\n channel: \"rc\",\n core: [rc[1]!, rc[2]!, rc[3]!],\n rc: rc[4]!\n };\n}\n\nfunction compareNumericIdentifier(left: string, right: string): number {\n if (left.length !== right.length) {\n return left.length < right.length ? -1 : 1;\n }\n return left === right ? 0 : left < right ? -1 : 1;\n}\n\nexport function compareDevelopmentManagedVersions(\n left: DevelopmentManagedVersion,\n right: DevelopmentManagedVersion\n): number {\n for (const index of [0, 1, 2] as const) {\n const compared = compareNumericIdentifier(\n left.core[index],\n right.core[index]\n );\n if (compared !== 0) {\n return compared;\n }\n }\n if (left.rc === right.rc) {\n return 0;\n }\n if (left.rc === null) {\n return 1;\n }\n if (right.rc === null) {\n return -1;\n }\n return compareNumericIdentifier(left.rc, right.rc);\n}\n","import {\n desktopUpdateAdmissionDevelopmentEnvironment,\n invalidDevelopmentScenario,\n readDevelopmentEnabled\n} from \"./environment.ts\";\nimport {\n compareDevelopmentManagedVersions,\n parseDevelopmentManagedVersion,\n validateStrictDevelopmentSemVer\n} from \"./version.ts\";\n\nexport type DesktopUpdateDevelopmentPolicyOutcome =\n | \"allowed\"\n | \"upgradeRequired\"\n | \"disabled\"\n | \"unsupported\"\n | \"unmanagedPrerelease\"\n | \"error\"\n | \"timeout\";\n\nexport type DesktopUpdateDevelopmentPolicyMinimum =\n | {\n kind: \"configured\";\n version: string;\n }\n | {\n kind: \"requestCurrentVersion\";\n };\n\nexport type DesktopUpdateDevelopmentPolicyStep =\n | {\n outcome: \"allowed\" | \"upgradeRequired\";\n minimum: DesktopUpdateDevelopmentPolicyMinimum;\n policySource: \"defaultMinimum\" | \"platformOverride\";\n }\n | {\n outcome: \"disabled\" | \"unsupported\" | \"unmanagedPrerelease\";\n }\n | {\n outcome: \"error\";\n message: string;\n }\n | {\n outcome: \"timeout\";\n };\n\nexport interface DesktopUpdateDevelopmentPolicyScenario {\n policySteps: readonly DesktopUpdateDevelopmentPolicyStep[];\n}\n\nfunction parseOutcome(value: string): DesktopUpdateDevelopmentPolicyOutcome {\n switch (value) {\n case \"allowed\":\n case \"upgradeRequired\":\n case \"disabled\":\n case \"unsupported\":\n case \"unmanagedPrerelease\":\n case \"error\":\n case \"timeout\":\n return value;\n default:\n return invalidDevelopmentScenario(\n `unknown policy outcome ${JSON.stringify(value)}`\n );\n }\n}\n\nfunction configuredMinimum(\n value: string,\n name: string\n): DesktopUpdateDevelopmentPolicyMinimum {\n const version = validateStrictDevelopmentSemVer(value, name);\n if (!parseDevelopmentManagedVersion(version)) {\n return invalidDevelopmentScenario(\n `${name} must be a managed stable or RC version`\n );\n }\n return { kind: \"configured\", version };\n}\n\nfunction createPolicyStep(\n token: string,\n fallbackMinimumVersion: string | undefined\n): DesktopUpdateDevelopmentPolicyStep {\n const [rawOutcome, rawMinimumVersion, ...extra] = token.split(\"@\");\n if (!rawOutcome || extra.length > 0) {\n return invalidDevelopmentScenario(\n `invalid policy step ${JSON.stringify(token)}`\n );\n }\n const outcome = parseOutcome(rawOutcome.trim());\n if (outcome === \"allowed\" || outcome === \"upgradeRequired\") {\n return {\n minimum: configuredMinimum(\n rawMinimumVersion?.trim() || fallbackMinimumVersion || \"\",\n desktopUpdateAdmissionDevelopmentEnvironment.minimumVersion\n ),\n outcome,\n policySource: \"defaultMinimum\"\n };\n }\n if (rawMinimumVersion !== undefined) {\n return invalidDevelopmentScenario(\n `policy outcome ${outcome} must not include a minimum version`\n );\n }\n if (outcome === \"error\") {\n return {\n message: \"Development minimum-version policy check failed\",\n outcome\n };\n }\n return { outcome };\n}\n\nfunction createPresetPolicySteps(\n name: string,\n minimumVersion: string | undefined\n): readonly DesktopUpdateDevelopmentPolicyStep[] {\n const requiredMinimum = (): DesktopUpdateDevelopmentPolicyMinimum =>\n configuredMinimum(\n minimumVersion || \"\",\n desktopUpdateAdmissionDevelopmentEnvironment.minimumVersion\n );\n switch (name) {\n case \"startup-force-success\":\n case \"startup-updater-unavailable\":\n case \"startup-target-below-minimum\":\n case \"startup-download-error\":\n return [\n {\n minimum: requiredMinimum(),\n outcome: \"upgradeRequired\",\n policySource: \"defaultMinimum\"\n }\n ];\n case \"startup-policy-timeout\":\n return [{ outcome: \"timeout\" }];\n case \"retry-policy-released\":\n return [\n {\n minimum: requiredMinimum(),\n outcome: \"upgradeRequired\",\n policySource: \"defaultMinimum\"\n },\n { outcome: \"disabled\" }\n ];\n case \"foreground-upgrade-required\":\n return [\n {\n minimum: { kind: \"requestCurrentVersion\" },\n outcome: \"allowed\",\n policySource: \"defaultMinimum\"\n },\n {\n minimum: requiredMinimum(),\n outcome: \"upgradeRequired\",\n policySource: \"defaultMinimum\"\n }\n ];\n default:\n return invalidDevelopmentScenario(\n `unknown named scenario ${JSON.stringify(name)}`\n );\n }\n}\n\nfunction resolvePolicySteps(\n env: Readonly<Record<string, string | undefined>>\n): readonly DesktopUpdateDevelopmentPolicyStep[] {\n const names = desktopUpdateAdmissionDevelopmentEnvironment;\n const fallbackMinimumVersion = env[names.minimumVersion]?.trim();\n const preset = env[names.scenario]?.trim();\n if (preset) {\n for (const conflictingName of [names.policy, names.policySequence]) {\n if (env[conflictingName]?.trim()) {\n return invalidDevelopmentScenario(\n `${names.scenario} and ${conflictingName} are mutually exclusive`\n );\n }\n }\n return createPresetPolicySteps(preset, fallbackMinimumVersion);\n }\n const sequence = env[names.policySequence]?.trim();\n const single = env[names.policy]?.trim();\n if (sequence && single) {\n return invalidDevelopmentScenario(\n `${names.policy} and ${names.policySequence} are mutually exclusive`\n );\n }\n const rawSteps = sequence\n ? sequence.split(\",\").map((value) => value.trim())\n : single\n ? [single]\n : invalidDevelopmentScenario(\n `${names.policy} or ${names.policySequence} is required`\n );\n if (rawSteps.some((value) => value.length === 0)) {\n return invalidDevelopmentScenario(\n `${names.policySequence} contains an empty step`\n );\n }\n return rawSteps.map((token) =>\n createPolicyStep(token, fallbackMinimumVersion)\n );\n}\n\nfunction configuredVersion(\n minimum: DesktopUpdateDevelopmentPolicyMinimum,\n currentVersion: string\n): string {\n return minimum.kind === \"configured\" ? minimum.version : currentVersion;\n}\n\nexport function validateDevelopmentPolicyScenarioForCurrentVersion(\n scenario: DesktopUpdateDevelopmentPolicyScenario,\n currentVersion: string\n): void {\n const normalizedCurrent = validateStrictDevelopmentSemVer(\n currentVersion,\n desktopUpdateAdmissionDevelopmentEnvironment.currentVersion\n );\n const current = parseDevelopmentManagedVersion(normalizedCurrent);\n for (const step of scenario.policySteps) {\n if (step.outcome === \"unmanagedPrerelease\") {\n if (current) {\n invalidDevelopmentScenario(\n \"unmanagedPrerelease requires an unmanaged currentVersion\"\n );\n }\n continue;\n }\n if (step.outcome !== \"allowed\" && step.outcome !== \"upgradeRequired\") {\n if (\n (step.outcome === \"disabled\" || step.outcome === \"unsupported\") &&\n !current\n ) {\n invalidDevelopmentScenario(\n `${step.outcome} requires a managed currentVersion`\n );\n }\n continue;\n }\n if (!current) {\n invalidDevelopmentScenario(\n `${step.outcome} requires a managed currentVersion`\n );\n }\n const minimumVersion = configuredVersion(step.minimum, normalizedCurrent);\n const minimum = parseDevelopmentManagedVersion(minimumVersion);\n if (!minimum || minimum.channel !== current.channel) {\n invalidDevelopmentScenario(\n `minimumVersion must use the ${current.channel} channel`\n );\n }\n const compared = compareDevelopmentManagedVersions(current, minimum);\n if (step.outcome === \"allowed\" && compared < 0) {\n invalidDevelopmentScenario(\n \"allowed requires currentVersion to meet minimumVersion\"\n );\n }\n if (step.outcome === \"upgradeRequired\" && compared >= 0) {\n invalidDevelopmentScenario(\n \"upgradeRequired requires currentVersion below minimumVersion\"\n );\n }\n }\n}\n\nexport function resolveDesktopUpdateDevelopmentPolicyScenario(input: {\n env: Readonly<Record<string, string | undefined>>;\n}): DesktopUpdateDevelopmentPolicyScenario | null {\n if (!readDevelopmentEnabled(input.env)) {\n return null;\n }\n const scenario: DesktopUpdateDevelopmentPolicyScenario = {\n policySteps: resolvePolicySteps(input.env)\n };\n return Object.freeze({\n policySteps: Object.freeze(\n scenario.policySteps.map((step) =>\n Object.freeze(\n \"minimum\" in step\n ? {\n ...step,\n minimum: Object.freeze({ ...step.minimum })\n }\n : { ...step }\n )\n )\n )\n });\n}\n","import type {\n DesktopProduct,\n MinimumVersionCheckRequest,\n MinimumVersionCheckResponse\n} from \"../contracts/index.ts\";\nimport { validateMinimumVersionResponse } from \"../core/index.ts\";\nimport type {\n DesktopUpdateDevelopmentPolicyMinimum,\n DesktopUpdateDevelopmentPolicyScenario,\n DesktopUpdateDevelopmentPolicyStep\n} from \"./policyScenario.ts\";\nimport { validateDevelopmentPolicyScenarioForCurrentVersion } from \"./policyScenario.ts\";\n\nfunction developmentChannel(\n currentVersion: string\n): \"stable\" | \"rc\" | \"unmanaged\" {\n if (\n /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)-rc\\.(0|[1-9]\\d*)$/u.test(\n currentVersion\n )\n ) {\n return \"rc\";\n }\n if (\n /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/u.test(\n currentVersion\n )\n ) {\n return \"stable\";\n }\n return \"unmanaged\";\n}\n\nfunction abortError(): Error {\n const error = new Error(\"development minimum-version policy check aborted\");\n error.name = \"AbortError\";\n return error;\n}\n\nfunction waitForAbort(signal: AbortSignal): Promise<never> {\n if (signal.aborted) {\n return Promise.reject(abortError());\n }\n return new Promise<never>((_resolve, reject) => {\n signal.addEventListener(\"abort\", () => reject(abortError()), {\n once: true\n });\n });\n}\n\nfunction responseForStep<TProduct extends DesktopProduct>(\n request: MinimumVersionCheckRequest<TProduct>,\n step: DesktopUpdateDevelopmentPolicyStep,\n revision: number\n): MinimumVersionCheckResponse<TProduct> {\n const channel = developmentChannel(request.currentVersion);\n const base = {\n ...request,\n channel,\n minimumVersion: \"\",\n policyRevision: `development-policy-${revision}`,\n policySource: \"\"\n } as const;\n const minimumVersion = (\n minimum: DesktopUpdateDevelopmentPolicyMinimum\n ): string =>\n minimum.kind === \"configured\" ? minimum.version : request.currentVersion;\n switch (step.outcome) {\n case \"allowed\":\n return validateMinimumVersionResponse(\n {\n ...base,\n channel,\n decision: \"allowed\",\n minimumVersion: minimumVersion(step.minimum),\n policySource: step.policySource,\n reason: \"meetsMinimum\"\n },\n request\n );\n case \"upgradeRequired\":\n return validateMinimumVersionResponse(\n {\n ...base,\n channel,\n decision: \"upgradeRequired\",\n minimumVersion: minimumVersion(step.minimum),\n policySource: step.policySource,\n reason: \"belowMinimum\"\n },\n request\n );\n case \"disabled\":\n return validateMinimumVersionResponse(\n {\n ...base,\n decision: \"notApplicable\",\n reason: \"productDisabled\"\n },\n request\n );\n case \"unsupported\":\n return validateMinimumVersionResponse(\n {\n ...base,\n decision: \"notApplicable\",\n reason: \"unsupportedRelease\"\n },\n request\n );\n case \"unmanagedPrerelease\":\n return validateMinimumVersionResponse(\n {\n ...base,\n channel: \"unmanaged\",\n decision: \"notApplicable\",\n reason: \"unmanagedPrerelease\"\n },\n request\n );\n case \"error\":\n case \"timeout\":\n throw new Error(`cannot synthesize ${step.outcome} as a policy response`);\n }\n}\n\nexport function createDevelopmentMinimumVersionChecker(\n policy: DesktopUpdateDevelopmentPolicyScenario,\n options: {\n expectedCurrentVersion?: string;\n } = {}\n): <TProduct extends DesktopProduct>(\n request: MinimumVersionCheckRequest<TProduct>,\n signal: AbortSignal\n) => Promise<MinimumVersionCheckResponse<TProduct>> {\n const checkIndexes = new Map<string, number>();\n return async <TProduct extends DesktopProduct>(\n request: MinimumVersionCheckRequest<TProduct>,\n signal: AbortSignal\n ): Promise<MinimumVersionCheckResponse<TProduct>> => {\n if (\n options.expectedCurrentVersion &&\n request.currentVersion !== options.expectedCurrentVersion\n ) {\n throw new Error(\n `development policy expected currentVersion ${options.expectedCurrentVersion}, received ${request.currentVersion}`\n );\n }\n const requestIdentity = [\n request.product,\n request.platform,\n request.architecture\n ].join(\":\");\n const checkIndex = checkIndexes.get(requestIdentity) ?? 0;\n const stepIndex = Math.min(checkIndex, policy.policySteps.length - 1);\n const step = policy.policySteps[stepIndex]!;\n checkIndexes.set(requestIdentity, checkIndex + 1);\n if (step.outcome === \"timeout\") {\n return await waitForAbort(signal);\n }\n if (step.outcome === \"error\") {\n throw new Error(step.message);\n }\n validateDevelopmentPolicyScenarioForCurrentVersion(\n { policySteps: [step] },\n request.currentVersion\n );\n return responseForStep(request, step, stepIndex + 1);\n };\n}\n","import {\n createServer,\n type IncomingMessage,\n type ServerResponse\n} from \"node:http\";\nimport type {\n DesktopProduct,\n MinimumVersionCheckRequest\n} from \"../contracts/index.ts\";\nimport { createDevelopmentMinimumVersionChecker } from \"./policyChecker.ts\";\nimport type { DesktopUpdateDevelopmentPolicyScenario } from \"./policyScenario.ts\";\n\nconst maximumRequestBodyBytes = 64 * 1_024;\nconst minimumVersionPath = \"/api/desktop/v1/public/desktop-version/check\";\n\nexport interface DesktopUpdateDevelopmentMockServer {\n readonly baseUrl: string;\n close(): Promise<void>;\n}\n\nclass InvalidDevelopmentMockRequestError extends Error {\n public constructor(message: string) {\n super(message);\n this.name = \"InvalidDevelopmentMockRequestError\";\n }\n}\n\nfunction invalidRequest(message: string): never {\n throw new InvalidDevelopmentMockRequestError(message);\n}\n\nfunction writeJson(\n response: ServerResponse,\n statusCode: number,\n value: unknown\n): void {\n const body = JSON.stringify(value);\n response.writeHead(statusCode, {\n \"content-length\": Buffer.byteLength(body),\n \"content-type\": \"application/json; charset=utf-8\"\n });\n response.end(body);\n}\n\nasync function readJson(request: IncomingMessage): Promise<unknown> {\n let size = 0;\n const chunks: Buffer[] = [];\n for await (const value of request) {\n const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);\n size += chunk.byteLength;\n if (size > maximumRequestBodyBytes) {\n return invalidRequest(\"request body exceeds 64 KiB\");\n }\n chunks.push(chunk);\n }\n try {\n return JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n } catch {\n return invalidRequest(\"request body must contain valid JSON\");\n }\n}\n\nfunction normalizeRequest(\n value: unknown\n): MinimumVersionCheckRequest<DesktopProduct> {\n if (!value || typeof value !== \"object\") {\n return invalidRequest(\"request must be an object\");\n }\n const candidate = value as Record<string, unknown>;\n if (\n candidate.product !== \"tsh-desktop\" &&\n candidate.product !== \"tutti-desktop\"\n ) {\n return invalidRequest(\"request has an invalid product\");\n }\n if (\n candidate.platform !== \"macos\" &&\n candidate.platform !== \"windows\" &&\n candidate.platform !== \"linux\"\n ) {\n return invalidRequest(\"request has an invalid platform\");\n }\n if (candidate.architecture !== \"arm64\" && candidate.architecture !== \"x64\") {\n return invalidRequest(\"request has an invalid architecture\");\n }\n if (\n typeof candidate.currentVersion !== \"string\" ||\n candidate.currentVersion.trim() === \"\"\n ) {\n return invalidRequest(\"request has an invalid currentVersion\");\n }\n return {\n architecture: candidate.architecture,\n currentVersion: candidate.currentVersion,\n platform: candidate.platform,\n product: candidate.product\n };\n}\n\nexport async function startDesktopUpdateDevelopmentMockServer(input: {\n port?: number;\n policy: DesktopUpdateDevelopmentPolicyScenario;\n}): Promise<DesktopUpdateDevelopmentMockServer> {\n const checker = createDevelopmentMinimumVersionChecker(input.policy);\n const server = createServer(async (request, response) => {\n if (request.url === \"/healthz\") {\n if (request.method !== \"GET\") {\n response.writeHead(405, { allow: \"GET\" });\n response.end();\n return;\n }\n writeJson(response, 200, { status: \"ok\" });\n return;\n }\n if (request.url !== minimumVersionPath) {\n writeJson(response, 404, { error: \"not found\" });\n return;\n }\n if (request.method !== \"POST\") {\n response.writeHead(405, { allow: \"POST\" });\n response.end();\n return;\n }\n if (\n !String(request.headers[\"content-type\"] ?? \"\")\n .toLowerCase()\n .startsWith(\"application/json\")\n ) {\n writeJson(response, 415, {\n error: \"content-type must be application/json\"\n });\n return;\n }\n const abortController = new AbortController();\n request.once(\"aborted\", () => abortController.abort());\n response.once(\"close\", () => {\n if (!response.writableEnded) {\n abortController.abort();\n }\n });\n try {\n const policyRequest = normalizeRequest(await readJson(request));\n const result = await checker(policyRequest, abortController.signal);\n if (!response.destroyed) {\n writeJson(response, 200, result);\n }\n } catch (error) {\n if (abortController.signal.aborted || response.destroyed) {\n return;\n }\n const message = error instanceof Error ? error.message : String(error);\n const statusCode =\n error instanceof InvalidDevelopmentMockRequestError ? 400 : 500;\n writeJson(response, statusCode, { error: message });\n }\n });\n const port = input.port ?? 0;\n if (!Number.isSafeInteger(port) || port < 0 || port > 65_535) {\n throw new Error(\"development mock server port must be between 0 and 65535\");\n }\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, \"127.0.0.1\", () => {\n server.removeListener(\"error\", reject);\n resolve();\n });\n });\n const address = server.address();\n if (!address || typeof address === \"string\") {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n throw new Error(\"development mock server did not expose a TCP address\");\n }\n return {\n baseUrl: `http://127.0.0.1:${address.port}`,\n close: () =>\n new Promise<void>((resolve, reject) => {\n server.close((error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n })\n };\n}\n"],"mappings":"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","names":[]}
|