hot-updater 0.30.0 → 0.30.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/dist/index.cjs +166 -45
- package/dist/index.mjs +151 -30
- package/package.json +14 -14
package/dist/index.cjs
CHANGED
|
@@ -1059,7 +1059,7 @@ var require_command = /* @__PURE__ */ require_keyGeneration.__commonJSMin(((expo
|
|
|
1059
1059
|
const EventEmitter$1 = require("node:events").EventEmitter;
|
|
1060
1060
|
const childProcess$1 = require("node:child_process");
|
|
1061
1061
|
const path$28 = require("node:path");
|
|
1062
|
-
const fs$
|
|
1062
|
+
const fs$19 = require("node:fs");
|
|
1063
1063
|
const process$15 = require("node:process");
|
|
1064
1064
|
const { Argument, humanReadableArgName } = require_argument();
|
|
1065
1065
|
const { CommanderError } = require_error();
|
|
@@ -1952,7 +1952,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1952
1952
|
* @param {string} subcommandName
|
|
1953
1953
|
*/
|
|
1954
1954
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
1955
|
-
if (fs$
|
|
1955
|
+
if (fs$19.existsSync(executableFile)) return;
|
|
1956
1956
|
const executableMissing = `'${executableFile}' does not exist
|
|
1957
1957
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
1958
1958
|
- if the default executable name is not suitable, use the executableFile option to supply a custom name or path
|
|
@@ -1976,9 +1976,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1976
1976
|
];
|
|
1977
1977
|
function findFile(baseDir, baseName) {
|
|
1978
1978
|
const localBin = path$28.resolve(baseDir, baseName);
|
|
1979
|
-
if (fs$
|
|
1979
|
+
if (fs$19.existsSync(localBin)) return localBin;
|
|
1980
1980
|
if (sourceExt.includes(path$28.extname(baseName))) return void 0;
|
|
1981
|
-
const foundExt = sourceExt.find((ext) => fs$
|
|
1981
|
+
const foundExt = sourceExt.find((ext) => fs$19.existsSync(`${localBin}${ext}`));
|
|
1982
1982
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1983
1983
|
}
|
|
1984
1984
|
this._checkForMissingMandatoryOptions();
|
|
@@ -1988,7 +1988,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1988
1988
|
if (this._scriptPath) {
|
|
1989
1989
|
let resolvedScriptPath;
|
|
1990
1990
|
try {
|
|
1991
|
-
resolvedScriptPath = fs$
|
|
1991
|
+
resolvedScriptPath = fs$19.realpathSync(this._scriptPath);
|
|
1992
1992
|
} catch {
|
|
1993
1993
|
resolvedScriptPath = this._scriptPath;
|
|
1994
1994
|
}
|
|
@@ -4633,7 +4633,7 @@ const handleCommand = (filePath, rawArguments, rawOptions) => {
|
|
|
4633
4633
|
var require_windows = /* @__PURE__ */ require_keyGeneration.__commonJSMin(((exports, module) => {
|
|
4634
4634
|
module.exports = isexe;
|
|
4635
4635
|
isexe.sync = sync;
|
|
4636
|
-
var fs$
|
|
4636
|
+
var fs$18 = require("fs");
|
|
4637
4637
|
function checkPathExt(path, options) {
|
|
4638
4638
|
var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
|
|
4639
4639
|
if (!pathext) return true;
|
|
@@ -4650,12 +4650,12 @@ var require_windows = /* @__PURE__ */ require_keyGeneration.__commonJSMin(((expo
|
|
|
4650
4650
|
return checkPathExt(path, options);
|
|
4651
4651
|
}
|
|
4652
4652
|
function isexe(path, options, cb) {
|
|
4653
|
-
fs$
|
|
4653
|
+
fs$18.stat(path, function(er, stat) {
|
|
4654
4654
|
cb(er, er ? false : checkStat(stat, path, options));
|
|
4655
4655
|
});
|
|
4656
4656
|
}
|
|
4657
4657
|
function sync(path, options) {
|
|
4658
|
-
return checkStat(fs$
|
|
4658
|
+
return checkStat(fs$18.statSync(path), path, options);
|
|
4659
4659
|
}
|
|
4660
4660
|
}));
|
|
4661
4661
|
//#endregion
|
|
@@ -4663,14 +4663,14 @@ var require_windows = /* @__PURE__ */ require_keyGeneration.__commonJSMin(((expo
|
|
|
4663
4663
|
var require_mode = /* @__PURE__ */ require_keyGeneration.__commonJSMin(((exports, module) => {
|
|
4664
4664
|
module.exports = isexe;
|
|
4665
4665
|
isexe.sync = sync;
|
|
4666
|
-
var fs$
|
|
4666
|
+
var fs$17 = require("fs");
|
|
4667
4667
|
function isexe(path, options, cb) {
|
|
4668
|
-
fs$
|
|
4668
|
+
fs$17.stat(path, function(er, stat) {
|
|
4669
4669
|
cb(er, er ? false : checkStat(stat, options));
|
|
4670
4670
|
});
|
|
4671
4671
|
}
|
|
4672
4672
|
function sync(path, options) {
|
|
4673
|
-
return checkStat(fs$
|
|
4673
|
+
return checkStat(fs$17.statSync(path), options);
|
|
4674
4674
|
}
|
|
4675
4675
|
function checkStat(stat, options) {
|
|
4676
4676
|
return stat.isFile() && checkMode(stat, options);
|
|
@@ -4885,16 +4885,16 @@ var require_shebang_command = /* @__PURE__ */ require_keyGeneration.__commonJSMi
|
|
|
4885
4885
|
//#endregion
|
|
4886
4886
|
//#region ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js
|
|
4887
4887
|
var require_readShebang = /* @__PURE__ */ require_keyGeneration.__commonJSMin(((exports, module) => {
|
|
4888
|
-
const fs$
|
|
4888
|
+
const fs$16 = require("fs");
|
|
4889
4889
|
const shebangCommand = require_shebang_command();
|
|
4890
4890
|
function readShebang(command) {
|
|
4891
4891
|
const size = 150;
|
|
4892
4892
|
const buffer = Buffer.alloc(size);
|
|
4893
4893
|
let fd;
|
|
4894
4894
|
try {
|
|
4895
|
-
fd = fs$
|
|
4896
|
-
fs$
|
|
4897
|
-
fs$
|
|
4895
|
+
fd = fs$16.openSync(command, "r");
|
|
4896
|
+
fs$16.readSync(fd, buffer, 0, size, 0);
|
|
4897
|
+
fs$16.closeSync(fd);
|
|
4898
4898
|
} catch (e) {}
|
|
4899
4899
|
return shebangCommand(buffer.toString());
|
|
4900
4900
|
}
|
|
@@ -36291,6 +36291,33 @@ var require_semver = /* @__PURE__ */ require_keyGeneration.__commonJSMin(((expor
|
|
|
36291
36291
|
//#endregion
|
|
36292
36292
|
//#region src/commands/doctor.ts
|
|
36293
36293
|
var import_semver = /* @__PURE__ */ require_keyGeneration.__toESM(require_semver(), 1);
|
|
36294
|
+
const INFRASTRUCTURE_UPDATE_TARGETS = [
|
|
36295
|
+
{
|
|
36296
|
+
version: "0.13.0",
|
|
36297
|
+
note: "Initial provider infrastructure migrations"
|
|
36298
|
+
},
|
|
36299
|
+
{
|
|
36300
|
+
version: "0.18.0",
|
|
36301
|
+
note: "Provider infrastructure migration"
|
|
36302
|
+
},
|
|
36303
|
+
{
|
|
36304
|
+
version: "0.21.0",
|
|
36305
|
+
note: "ORM schema version target"
|
|
36306
|
+
},
|
|
36307
|
+
{
|
|
36308
|
+
version: "0.29.0",
|
|
36309
|
+
note: "Rollout infrastructure fields"
|
|
36310
|
+
},
|
|
36311
|
+
{
|
|
36312
|
+
version: "0.30.0",
|
|
36313
|
+
note: "Target cohort rollout behavior"
|
|
36314
|
+
}
|
|
36315
|
+
];
|
|
36316
|
+
const getInfrastructureTargetVersionAt = (index) => {
|
|
36317
|
+
const target = INFRASTRUCTURE_UPDATE_TARGETS.at(index);
|
|
36318
|
+
if (!target) throw new Error("INFRASTRUCTURE_UPDATE_TARGETS must not be empty");
|
|
36319
|
+
return target.version;
|
|
36320
|
+
};
|
|
36294
36321
|
/**
|
|
36295
36322
|
* Checks if two versions (or version and range) are compatible.
|
|
36296
36323
|
* @param versionA - First version or range string.
|
|
@@ -36304,13 +36331,82 @@ function areVersionsCompatible(versionA, versionB) {
|
|
|
36304
36331
|
if (import_semver.valid(versionB) && import_semver.validRange(versionA) && import_semver.satisfies(versionB, versionA, options)) return true;
|
|
36305
36332
|
return false;
|
|
36306
36333
|
}
|
|
36334
|
+
function getRequiredInfrastructureVersion(hotUpdaterVersion = getInfrastructureTargetVersionAt(-1)) {
|
|
36335
|
+
const current = import_semver.coerce(hotUpdaterVersion)?.version;
|
|
36336
|
+
if (!current) return getInfrastructureTargetVersionAt(-1);
|
|
36337
|
+
let requiredVersion = getInfrastructureTargetVersionAt(0);
|
|
36338
|
+
for (const target of INFRASTRUCTURE_UPDATE_TARGETS) if (import_semver.lte(target.version, current)) requiredVersion = target.version;
|
|
36339
|
+
return requiredVersion;
|
|
36340
|
+
}
|
|
36341
|
+
function isInfrastructureUpdateRequired({ serverVersion, requiredVersion = getRequiredInfrastructureVersion() }) {
|
|
36342
|
+
const normalizedServerVersion = import_semver.valid(serverVersion);
|
|
36343
|
+
const normalizedRequiredVersion = import_semver.valid(requiredVersion);
|
|
36344
|
+
if (!normalizedServerVersion || !normalizedRequiredVersion) throw new Error("Invalid infrastructure version");
|
|
36345
|
+
return import_semver.lt(normalizedServerVersion, normalizedRequiredVersion);
|
|
36346
|
+
}
|
|
36347
|
+
function resolveVersionEndpoint(serverBaseUrl) {
|
|
36348
|
+
const url = new URL(serverBaseUrl.trim());
|
|
36349
|
+
const pathname = url.pathname.replace(/\/+$/, "");
|
|
36350
|
+
url.hash = "";
|
|
36351
|
+
url.search = "";
|
|
36352
|
+
url.pathname = `${pathname}/version`;
|
|
36353
|
+
return url.toString();
|
|
36354
|
+
}
|
|
36355
|
+
async function checkInfrastructureStatus({ serverBaseUrl, fetchImpl = fetch, requiredVersion = getRequiredInfrastructureVersion() }) {
|
|
36356
|
+
const versionEndpoint = resolveVersionEndpoint(serverBaseUrl);
|
|
36357
|
+
const baseUrl = serverBaseUrl.trim();
|
|
36358
|
+
try {
|
|
36359
|
+
const response = await fetchImpl(versionEndpoint, { headers: { Accept: "application/json" } });
|
|
36360
|
+
if (!response.ok) {
|
|
36361
|
+
if (response.status === 404) return {
|
|
36362
|
+
baseUrl,
|
|
36363
|
+
versionEndpoint,
|
|
36364
|
+
requiredVersion,
|
|
36365
|
+
needsUpdate: true,
|
|
36366
|
+
updateReason: "Version endpoint not found"
|
|
36367
|
+
};
|
|
36368
|
+
return {
|
|
36369
|
+
baseUrl,
|
|
36370
|
+
versionEndpoint,
|
|
36371
|
+
requiredVersion,
|
|
36372
|
+
error: `Version endpoint returned ${response.status}`
|
|
36373
|
+
};
|
|
36374
|
+
}
|
|
36375
|
+
const data = await response.json();
|
|
36376
|
+
if (typeof data.version !== "string") return {
|
|
36377
|
+
baseUrl,
|
|
36378
|
+
versionEndpoint,
|
|
36379
|
+
requiredVersion,
|
|
36380
|
+
error: "Version endpoint response must include a string version"
|
|
36381
|
+
};
|
|
36382
|
+
const needsUpdate = isInfrastructureUpdateRequired({
|
|
36383
|
+
serverVersion: data.version,
|
|
36384
|
+
requiredVersion
|
|
36385
|
+
});
|
|
36386
|
+
return {
|
|
36387
|
+
baseUrl,
|
|
36388
|
+
versionEndpoint,
|
|
36389
|
+
serverVersion: data.version,
|
|
36390
|
+
requiredVersion,
|
|
36391
|
+
needsUpdate
|
|
36392
|
+
};
|
|
36393
|
+
} catch (error) {
|
|
36394
|
+
return {
|
|
36395
|
+
baseUrl,
|
|
36396
|
+
versionEndpoint,
|
|
36397
|
+
requiredVersion,
|
|
36398
|
+
error: error.message
|
|
36399
|
+
};
|
|
36400
|
+
}
|
|
36401
|
+
}
|
|
36307
36402
|
/**
|
|
36308
36403
|
* Performs health check on Hot Updater installation
|
|
36309
|
-
* @param
|
|
36404
|
+
* @param options - Doctor check options
|
|
36310
36405
|
* @returns true if everything is healthy, or DoctorResult with details if there are issues
|
|
36311
36406
|
*/
|
|
36312
|
-
async function doctor(
|
|
36407
|
+
async function doctor(options = {}) {
|
|
36313
36408
|
try {
|
|
36409
|
+
const { cwd = (0, _hot_updater_cli_tools.getCwd)(), serverBaseUrl, fetch: fetchImpl } = options;
|
|
36314
36410
|
const packageResult = await readPackageUp({ cwd });
|
|
36315
36411
|
if (!packageResult) return {
|
|
36316
36412
|
success: false,
|
|
@@ -36339,11 +36435,21 @@ async function doctor(cwd = (0, _hot_updater_cli_tools.getCwd)()) {
|
|
|
36339
36435
|
packageJsonPath,
|
|
36340
36436
|
installedHotUpdaterPackages: hotUpdaterPackages
|
|
36341
36437
|
};
|
|
36438
|
+
if (serverBaseUrl) details.infrastructure = await checkInfrastructureStatus({
|
|
36439
|
+
serverBaseUrl,
|
|
36440
|
+
fetchImpl,
|
|
36441
|
+
requiredVersion: getRequiredInfrastructureVersion(hotUpdaterVersion)
|
|
36442
|
+
});
|
|
36342
36443
|
if (versionMismatches.length > 0) details.versionMismatches = versionMismatches;
|
|
36343
|
-
|
|
36444
|
+
const hasInfrastructureIssue = details.infrastructure?.error !== void 0 || details.infrastructure?.needsUpdate === true;
|
|
36445
|
+
if (versionMismatches.length > 0 || hasInfrastructureIssue) return {
|
|
36344
36446
|
success: false,
|
|
36345
36447
|
details
|
|
36346
36448
|
};
|
|
36449
|
+
if (details.infrastructure) return {
|
|
36450
|
+
success: true,
|
|
36451
|
+
details
|
|
36452
|
+
};
|
|
36347
36453
|
return true;
|
|
36348
36454
|
} catch (error) {
|
|
36349
36455
|
return {
|
|
@@ -36352,22 +36458,31 @@ async function doctor(cwd = (0, _hot_updater_cli_tools.getCwd)()) {
|
|
|
36352
36458
|
};
|
|
36353
36459
|
}
|
|
36354
36460
|
}
|
|
36355
|
-
|
|
36356
|
-
|
|
36357
|
-
|
|
36358
|
-
|
|
36359
|
-
|
|
36360
|
-
|
|
36361
|
-
|
|
36362
|
-
|
|
36363
|
-
|
|
36364
|
-
|
|
36365
|
-
|
|
36366
|
-
|
|
36367
|
-
}
|
|
36368
|
-
|
|
36461
|
+
const promptServerBaseUrl = async () => {
|
|
36462
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
36463
|
+
const serverBaseUrl = await _hot_updater_cli_tools.p.text({
|
|
36464
|
+
message: "Server base URL for infrastructure check (Enter to skip)",
|
|
36465
|
+
placeholder: "https://example.com/api/check-update",
|
|
36466
|
+
validate(value) {
|
|
36467
|
+
const trimmed = value?.trim() ?? "";
|
|
36468
|
+
if (!trimmed) return;
|
|
36469
|
+
try {
|
|
36470
|
+
new URL(trimmed);
|
|
36471
|
+
} catch {
|
|
36472
|
+
return "Enter a valid URL";
|
|
36473
|
+
}
|
|
36474
|
+
}
|
|
36475
|
+
});
|
|
36476
|
+
if (_hot_updater_cli_tools.p.isCancel(serverBaseUrl)) {
|
|
36477
|
+
_hot_updater_cli_tools.p.cancel("Doctor check cancelled");
|
|
36478
|
+
process.exit(0);
|
|
36479
|
+
}
|
|
36480
|
+
const trimmed = serverBaseUrl.trim();
|
|
36481
|
+
return trimmed ? trimmed : void 0;
|
|
36482
|
+
};
|
|
36483
|
+
const handleDoctor = async ({ serverBaseUrl } = {}) => {
|
|
36369
36484
|
_hot_updater_cli_tools.p.intro("Checking the health of Hot Updater.");
|
|
36370
|
-
const result = await doctor();
|
|
36485
|
+
const result = await doctor({ serverBaseUrl: serverBaseUrl ?? await promptServerBaseUrl() });
|
|
36371
36486
|
if (result === true) {
|
|
36372
36487
|
_hot_updater_cli_tools.p.log.success("✅ All Hot Updater checks passed!");
|
|
36373
36488
|
_hot_updater_cli_tools.p.outro("Hot Updater is healthy.");
|
|
@@ -36379,22 +36494,28 @@ const handleDoctor = async ({ fix }) => {
|
|
|
36379
36494
|
return;
|
|
36380
36495
|
}
|
|
36381
36496
|
const { details } = result;
|
|
36497
|
+
let shouldExitWithFailure = !result.success;
|
|
36382
36498
|
if (details?.hotUpdaterVersion) _hot_updater_cli_tools.p.log.info(`hot-updater CLI version: ${details.hotUpdaterVersion}`);
|
|
36499
|
+
if (details?.infrastructure) {
|
|
36500
|
+
const infrastructure = details.infrastructure;
|
|
36501
|
+
_hot_updater_cli_tools.p.log.info(`Server version endpoint: ${infrastructure.versionEndpoint}`);
|
|
36502
|
+
if (infrastructure.serverVersion) {
|
|
36503
|
+
_hot_updater_cli_tools.p.log.info(`Server version: ${infrastructure.serverVersion}`);
|
|
36504
|
+
_hot_updater_cli_tools.p.log.info(`Required infrastructure version: ${infrastructure.requiredVersion}`);
|
|
36505
|
+
}
|
|
36506
|
+
if (infrastructure.needsUpdate) {
|
|
36507
|
+
_hot_updater_cli_tools.p.log.error(`Infrastructure update required. Deploy server infrastructure version ${infrastructure.requiredVersion} or newer.`);
|
|
36508
|
+
if (infrastructure.updateReason) _hot_updater_cli_tools.p.log.info(`Reason: ${infrastructure.updateReason}`);
|
|
36509
|
+
} else if (infrastructure.error) _hot_updater_cli_tools.p.log.error(`Infrastructure check failed: ${infrastructure.error}`);
|
|
36510
|
+
else _hot_updater_cli_tools.p.log.success("✅ Server infrastructure is up to date.");
|
|
36511
|
+
}
|
|
36383
36512
|
if (details?.versionMismatches && details.versionMismatches.length > 0) {
|
|
36384
36513
|
_hot_updater_cli_tools.p.log.warn("Version mismatches found:");
|
|
36385
36514
|
for (const mismatch of details.versionMismatches) _hot_updater_cli_tools.p.log.error(`❌ ${mismatch.packageName}: ${mismatch.currentVersion} (expected ${mismatch.expectedVersion})`);
|
|
36386
|
-
|
|
36387
|
-
await fixVersionMismatches(details.packageJsonPath, details.versionMismatches);
|
|
36388
|
-
_hot_updater_cli_tools.p.log.success("✅ Fixed version mismatches in package.json");
|
|
36389
|
-
_hot_updater_cli_tools.p.log.info("Run your package manager to install the updated versions.");
|
|
36390
|
-
} catch (error) {
|
|
36391
|
-
_hot_updater_cli_tools.p.log.error(`Failed to fix versions: ${error.message}`);
|
|
36392
|
-
}
|
|
36393
|
-
else if (!fix) {
|
|
36394
|
-
_hot_updater_cli_tools.p.log.info("Run with --fix to automatically update versions.");
|
|
36395
|
-
process.exit(1);
|
|
36396
|
-
}
|
|
36515
|
+
_hot_updater_cli_tools.p.log.info("Update every Hot Updater package to the same compatible version, then run your package manager install command.");
|
|
36397
36516
|
}
|
|
36517
|
+
if (shouldExitWithFailure) process.exit(1);
|
|
36518
|
+
_hot_updater_cli_tools.p.log.success("✅ All Hot Updater checks passed!");
|
|
36398
36519
|
_hot_updater_cli_tools.p.outro("Doctor check complete.");
|
|
36399
36520
|
};
|
|
36400
36521
|
//#endregion
|
|
@@ -37516,7 +37637,7 @@ const DEFAULT_CHANNEL = "production";
|
|
|
37516
37637
|
const program = new Command();
|
|
37517
37638
|
program.name("hot-updater").description((0, _hot_updater_cli_tools.banner)(version)).version(version);
|
|
37518
37639
|
program.command("init").description("Initialize Hot Updater").action(init);
|
|
37519
|
-
program.command("doctor").description("Check the health of Hot Updater").option("-
|
|
37640
|
+
program.command("doctor").description("Check the health of Hot Updater").option("--server-base-url <url>", "server base URL used by update checks (doctor appends /version)").action(handleDoctor);
|
|
37520
37641
|
const fingerprintCommand = program.command("fingerprint").description("Generate fingerprint");
|
|
37521
37642
|
fingerprintCommand.action(handleFingerprint);
|
|
37522
37643
|
fingerprintCommand.command("create").description("Create fingerprint").action(handleCreateFingerprint);
|
package/dist/index.mjs
CHANGED
|
@@ -36271,6 +36271,33 @@ var import_semver = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
|
|
|
36271
36271
|
rcompareIdentifiers: identifiers.rcompareIdentifiers
|
|
36272
36272
|
};
|
|
36273
36273
|
})))(), 1);
|
|
36274
|
+
const INFRASTRUCTURE_UPDATE_TARGETS = [
|
|
36275
|
+
{
|
|
36276
|
+
version: "0.13.0",
|
|
36277
|
+
note: "Initial provider infrastructure migrations"
|
|
36278
|
+
},
|
|
36279
|
+
{
|
|
36280
|
+
version: "0.18.0",
|
|
36281
|
+
note: "Provider infrastructure migration"
|
|
36282
|
+
},
|
|
36283
|
+
{
|
|
36284
|
+
version: "0.21.0",
|
|
36285
|
+
note: "ORM schema version target"
|
|
36286
|
+
},
|
|
36287
|
+
{
|
|
36288
|
+
version: "0.29.0",
|
|
36289
|
+
note: "Rollout infrastructure fields"
|
|
36290
|
+
},
|
|
36291
|
+
{
|
|
36292
|
+
version: "0.30.0",
|
|
36293
|
+
note: "Target cohort rollout behavior"
|
|
36294
|
+
}
|
|
36295
|
+
];
|
|
36296
|
+
const getInfrastructureTargetVersionAt = (index) => {
|
|
36297
|
+
const target = INFRASTRUCTURE_UPDATE_TARGETS.at(index);
|
|
36298
|
+
if (!target) throw new Error("INFRASTRUCTURE_UPDATE_TARGETS must not be empty");
|
|
36299
|
+
return target.version;
|
|
36300
|
+
};
|
|
36274
36301
|
/**
|
|
36275
36302
|
* Checks if two versions (or version and range) are compatible.
|
|
36276
36303
|
* @param versionA - First version or range string.
|
|
@@ -36284,13 +36311,82 @@ function areVersionsCompatible(versionA, versionB) {
|
|
|
36284
36311
|
if (import_semver.valid(versionB) && import_semver.validRange(versionA) && import_semver.satisfies(versionB, versionA, options)) return true;
|
|
36285
36312
|
return false;
|
|
36286
36313
|
}
|
|
36314
|
+
function getRequiredInfrastructureVersion(hotUpdaterVersion = getInfrastructureTargetVersionAt(-1)) {
|
|
36315
|
+
const current = import_semver.coerce(hotUpdaterVersion)?.version;
|
|
36316
|
+
if (!current) return getInfrastructureTargetVersionAt(-1);
|
|
36317
|
+
let requiredVersion = getInfrastructureTargetVersionAt(0);
|
|
36318
|
+
for (const target of INFRASTRUCTURE_UPDATE_TARGETS) if (import_semver.lte(target.version, current)) requiredVersion = target.version;
|
|
36319
|
+
return requiredVersion;
|
|
36320
|
+
}
|
|
36321
|
+
function isInfrastructureUpdateRequired({ serverVersion, requiredVersion = getRequiredInfrastructureVersion() }) {
|
|
36322
|
+
const normalizedServerVersion = import_semver.valid(serverVersion);
|
|
36323
|
+
const normalizedRequiredVersion = import_semver.valid(requiredVersion);
|
|
36324
|
+
if (!normalizedServerVersion || !normalizedRequiredVersion) throw new Error("Invalid infrastructure version");
|
|
36325
|
+
return import_semver.lt(normalizedServerVersion, normalizedRequiredVersion);
|
|
36326
|
+
}
|
|
36327
|
+
function resolveVersionEndpoint(serverBaseUrl) {
|
|
36328
|
+
const url = new URL(serverBaseUrl.trim());
|
|
36329
|
+
const pathname = url.pathname.replace(/\/+$/, "");
|
|
36330
|
+
url.hash = "";
|
|
36331
|
+
url.search = "";
|
|
36332
|
+
url.pathname = `${pathname}/version`;
|
|
36333
|
+
return url.toString();
|
|
36334
|
+
}
|
|
36335
|
+
async function checkInfrastructureStatus({ serverBaseUrl, fetchImpl = fetch, requiredVersion = getRequiredInfrastructureVersion() }) {
|
|
36336
|
+
const versionEndpoint = resolveVersionEndpoint(serverBaseUrl);
|
|
36337
|
+
const baseUrl = serverBaseUrl.trim();
|
|
36338
|
+
try {
|
|
36339
|
+
const response = await fetchImpl(versionEndpoint, { headers: { Accept: "application/json" } });
|
|
36340
|
+
if (!response.ok) {
|
|
36341
|
+
if (response.status === 404) return {
|
|
36342
|
+
baseUrl,
|
|
36343
|
+
versionEndpoint,
|
|
36344
|
+
requiredVersion,
|
|
36345
|
+
needsUpdate: true,
|
|
36346
|
+
updateReason: "Version endpoint not found"
|
|
36347
|
+
};
|
|
36348
|
+
return {
|
|
36349
|
+
baseUrl,
|
|
36350
|
+
versionEndpoint,
|
|
36351
|
+
requiredVersion,
|
|
36352
|
+
error: `Version endpoint returned ${response.status}`
|
|
36353
|
+
};
|
|
36354
|
+
}
|
|
36355
|
+
const data = await response.json();
|
|
36356
|
+
if (typeof data.version !== "string") return {
|
|
36357
|
+
baseUrl,
|
|
36358
|
+
versionEndpoint,
|
|
36359
|
+
requiredVersion,
|
|
36360
|
+
error: "Version endpoint response must include a string version"
|
|
36361
|
+
};
|
|
36362
|
+
const needsUpdate = isInfrastructureUpdateRequired({
|
|
36363
|
+
serverVersion: data.version,
|
|
36364
|
+
requiredVersion
|
|
36365
|
+
});
|
|
36366
|
+
return {
|
|
36367
|
+
baseUrl,
|
|
36368
|
+
versionEndpoint,
|
|
36369
|
+
serverVersion: data.version,
|
|
36370
|
+
requiredVersion,
|
|
36371
|
+
needsUpdate
|
|
36372
|
+
};
|
|
36373
|
+
} catch (error) {
|
|
36374
|
+
return {
|
|
36375
|
+
baseUrl,
|
|
36376
|
+
versionEndpoint,
|
|
36377
|
+
requiredVersion,
|
|
36378
|
+
error: error.message
|
|
36379
|
+
};
|
|
36380
|
+
}
|
|
36381
|
+
}
|
|
36287
36382
|
/**
|
|
36288
36383
|
* Performs health check on Hot Updater installation
|
|
36289
|
-
* @param
|
|
36384
|
+
* @param options - Doctor check options
|
|
36290
36385
|
* @returns true if everything is healthy, or DoctorResult with details if there are issues
|
|
36291
36386
|
*/
|
|
36292
|
-
async function doctor(
|
|
36387
|
+
async function doctor(options = {}) {
|
|
36293
36388
|
try {
|
|
36389
|
+
const { cwd = getCwd(), serverBaseUrl, fetch: fetchImpl } = options;
|
|
36294
36390
|
const packageResult = await readPackageUp({ cwd });
|
|
36295
36391
|
if (!packageResult) return {
|
|
36296
36392
|
success: false,
|
|
@@ -36319,11 +36415,21 @@ async function doctor(cwd = getCwd()) {
|
|
|
36319
36415
|
packageJsonPath,
|
|
36320
36416
|
installedHotUpdaterPackages: hotUpdaterPackages
|
|
36321
36417
|
};
|
|
36418
|
+
if (serverBaseUrl) details.infrastructure = await checkInfrastructureStatus({
|
|
36419
|
+
serverBaseUrl,
|
|
36420
|
+
fetchImpl,
|
|
36421
|
+
requiredVersion: getRequiredInfrastructureVersion(hotUpdaterVersion)
|
|
36422
|
+
});
|
|
36322
36423
|
if (versionMismatches.length > 0) details.versionMismatches = versionMismatches;
|
|
36323
|
-
|
|
36424
|
+
const hasInfrastructureIssue = details.infrastructure?.error !== void 0 || details.infrastructure?.needsUpdate === true;
|
|
36425
|
+
if (versionMismatches.length > 0 || hasInfrastructureIssue) return {
|
|
36324
36426
|
success: false,
|
|
36325
36427
|
details
|
|
36326
36428
|
};
|
|
36429
|
+
if (details.infrastructure) return {
|
|
36430
|
+
success: true,
|
|
36431
|
+
details
|
|
36432
|
+
};
|
|
36327
36433
|
return true;
|
|
36328
36434
|
} catch (error) {
|
|
36329
36435
|
return {
|
|
@@ -36332,22 +36438,31 @@ async function doctor(cwd = getCwd()) {
|
|
|
36332
36438
|
};
|
|
36333
36439
|
}
|
|
36334
36440
|
}
|
|
36335
|
-
|
|
36336
|
-
|
|
36337
|
-
|
|
36338
|
-
|
|
36339
|
-
|
|
36340
|
-
|
|
36341
|
-
|
|
36342
|
-
|
|
36343
|
-
|
|
36344
|
-
|
|
36345
|
-
|
|
36346
|
-
|
|
36347
|
-
}
|
|
36348
|
-
|
|
36441
|
+
const promptServerBaseUrl = async () => {
|
|
36442
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
36443
|
+
const serverBaseUrl = await p.text({
|
|
36444
|
+
message: "Server base URL for infrastructure check (Enter to skip)",
|
|
36445
|
+
placeholder: "https://example.com/api/check-update",
|
|
36446
|
+
validate(value) {
|
|
36447
|
+
const trimmed = value?.trim() ?? "";
|
|
36448
|
+
if (!trimmed) return;
|
|
36449
|
+
try {
|
|
36450
|
+
new URL(trimmed);
|
|
36451
|
+
} catch {
|
|
36452
|
+
return "Enter a valid URL";
|
|
36453
|
+
}
|
|
36454
|
+
}
|
|
36455
|
+
});
|
|
36456
|
+
if (p.isCancel(serverBaseUrl)) {
|
|
36457
|
+
p.cancel("Doctor check cancelled");
|
|
36458
|
+
process.exit(0);
|
|
36459
|
+
}
|
|
36460
|
+
const trimmed = serverBaseUrl.trim();
|
|
36461
|
+
return trimmed ? trimmed : void 0;
|
|
36462
|
+
};
|
|
36463
|
+
const handleDoctor = async ({ serverBaseUrl } = {}) => {
|
|
36349
36464
|
p.intro("Checking the health of Hot Updater.");
|
|
36350
|
-
const result = await doctor();
|
|
36465
|
+
const result = await doctor({ serverBaseUrl: serverBaseUrl ?? await promptServerBaseUrl() });
|
|
36351
36466
|
if (result === true) {
|
|
36352
36467
|
p.log.success("✅ All Hot Updater checks passed!");
|
|
36353
36468
|
p.outro("Hot Updater is healthy.");
|
|
@@ -36359,22 +36474,28 @@ const handleDoctor = async ({ fix }) => {
|
|
|
36359
36474
|
return;
|
|
36360
36475
|
}
|
|
36361
36476
|
const { details } = result;
|
|
36477
|
+
let shouldExitWithFailure = !result.success;
|
|
36362
36478
|
if (details?.hotUpdaterVersion) p.log.info(`hot-updater CLI version: ${details.hotUpdaterVersion}`);
|
|
36479
|
+
if (details?.infrastructure) {
|
|
36480
|
+
const infrastructure = details.infrastructure;
|
|
36481
|
+
p.log.info(`Server version endpoint: ${infrastructure.versionEndpoint}`);
|
|
36482
|
+
if (infrastructure.serverVersion) {
|
|
36483
|
+
p.log.info(`Server version: ${infrastructure.serverVersion}`);
|
|
36484
|
+
p.log.info(`Required infrastructure version: ${infrastructure.requiredVersion}`);
|
|
36485
|
+
}
|
|
36486
|
+
if (infrastructure.needsUpdate) {
|
|
36487
|
+
p.log.error(`Infrastructure update required. Deploy server infrastructure version ${infrastructure.requiredVersion} or newer.`);
|
|
36488
|
+
if (infrastructure.updateReason) p.log.info(`Reason: ${infrastructure.updateReason}`);
|
|
36489
|
+
} else if (infrastructure.error) p.log.error(`Infrastructure check failed: ${infrastructure.error}`);
|
|
36490
|
+
else p.log.success("✅ Server infrastructure is up to date.");
|
|
36491
|
+
}
|
|
36363
36492
|
if (details?.versionMismatches && details.versionMismatches.length > 0) {
|
|
36364
36493
|
p.log.warn("Version mismatches found:");
|
|
36365
36494
|
for (const mismatch of details.versionMismatches) p.log.error(`❌ ${mismatch.packageName}: ${mismatch.currentVersion} (expected ${mismatch.expectedVersion})`);
|
|
36366
|
-
|
|
36367
|
-
await fixVersionMismatches(details.packageJsonPath, details.versionMismatches);
|
|
36368
|
-
p.log.success("✅ Fixed version mismatches in package.json");
|
|
36369
|
-
p.log.info("Run your package manager to install the updated versions.");
|
|
36370
|
-
} catch (error) {
|
|
36371
|
-
p.log.error(`Failed to fix versions: ${error.message}`);
|
|
36372
|
-
}
|
|
36373
|
-
else if (!fix) {
|
|
36374
|
-
p.log.info("Run with --fix to automatically update versions.");
|
|
36375
|
-
process.exit(1);
|
|
36376
|
-
}
|
|
36495
|
+
p.log.info("Update every Hot Updater package to the same compatible version, then run your package manager install command.");
|
|
36377
36496
|
}
|
|
36497
|
+
if (shouldExitWithFailure) process.exit(1);
|
|
36498
|
+
p.log.success("✅ All Hot Updater checks passed!");
|
|
36378
36499
|
p.outro("Doctor check complete.");
|
|
36379
36500
|
};
|
|
36380
36501
|
//#endregion
|
|
@@ -37496,7 +37617,7 @@ const DEFAULT_CHANNEL = "production";
|
|
|
37496
37617
|
const program = new Command();
|
|
37497
37618
|
program.name("hot-updater").description(banner(version)).version(version);
|
|
37498
37619
|
program.command("init").description("Initialize Hot Updater").action(init);
|
|
37499
|
-
program.command("doctor").description("Check the health of Hot Updater").option("-
|
|
37620
|
+
program.command("doctor").description("Check the health of Hot Updater").option("--server-base-url <url>", "server base URL used by update checks (doctor appends /version)").action(handleDoctor);
|
|
37500
37621
|
const fingerprintCommand = program.command("fingerprint").description("Generate fingerprint");
|
|
37501
37622
|
fingerprintCommand.action(handleFingerprint);
|
|
37502
37623
|
fingerprintCommand.command("create").description("Create fingerprint").action(handleCreateFingerprint);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hot-updater",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.30.
|
|
4
|
+
"version": "0.30.2",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hot-updater": "./dist/index.cjs"
|
|
7
7
|
},
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
"jiti": "2.6.1",
|
|
48
48
|
"kysely": "0.28.9",
|
|
49
49
|
"sql-formatter": "15.6.10",
|
|
50
|
-
"@hot-updater/android-helper": "0.30.
|
|
51
|
-
"@hot-updater/
|
|
52
|
-
"@hot-updater/
|
|
53
|
-
"@hot-updater/console": "0.30.
|
|
54
|
-
"@hot-updater/core": "0.30.
|
|
55
|
-
"@hot-updater/
|
|
56
|
-
"@hot-updater/server": "0.30.
|
|
50
|
+
"@hot-updater/android-helper": "0.30.2",
|
|
51
|
+
"@hot-updater/cli-tools": "0.30.2",
|
|
52
|
+
"@hot-updater/apple-helper": "0.30.2",
|
|
53
|
+
"@hot-updater/console": "0.30.2",
|
|
54
|
+
"@hot-updater/plugin-core": "0.30.2",
|
|
55
|
+
"@hot-updater/core": "0.30.2",
|
|
56
|
+
"@hot-updater/server": "0.30.2"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@bacons/xcode": "1.0.0-alpha.24",
|
|
@@ -73,12 +73,12 @@
|
|
|
73
73
|
"read-package-up": "^11.0.0",
|
|
74
74
|
"semver": "^7.6.3",
|
|
75
75
|
"typescript": "6.0.2",
|
|
76
|
-
"@hot-updater/
|
|
77
|
-
"@hot-updater/
|
|
78
|
-
"@hot-updater/firebase": "0.30.
|
|
79
|
-
"@hot-updater/server": "0.30.
|
|
80
|
-
"@hot-updater/
|
|
81
|
-
"@hot-updater/
|
|
76
|
+
"@hot-updater/cloudflare": "0.30.2",
|
|
77
|
+
"@hot-updater/aws": "0.30.2",
|
|
78
|
+
"@hot-updater/firebase": "0.30.2",
|
|
79
|
+
"@hot-updater/server": "0.30.2",
|
|
80
|
+
"@hot-updater/test-utils": "0.30.2",
|
|
81
|
+
"@hot-updater/supabase": "0.30.2"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
84
|
"@hot-updater/aws": "*",
|