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.
Files changed (3) hide show
  1. package/dist/index.cjs +166 -45
  2. package/dist/index.mjs +151 -30
  3. 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$20 = require("node: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$20.existsSync(executableFile)) return;
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$20.existsSync(localBin)) return localBin;
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$20.existsSync(`${localBin}${ext}`));
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$20.realpathSync(this._scriptPath);
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$19 = require("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$19.stat(path, function(er, stat) {
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$19.statSync(path), path, options);
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$18 = require("fs");
4666
+ var fs$17 = require("fs");
4667
4667
  function isexe(path, options, cb) {
4668
- fs$18.stat(path, function(er, stat) {
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$18.statSync(path), options);
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$17 = require("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$17.openSync(command, "r");
4896
- fs$17.readSync(fd, buffer, 0, size, 0);
4897
- fs$17.closeSync(fd);
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 cwd - Current working directory (optional)
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(cwd = (0, _hot_updater_cli_tools.getCwd)()) {
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
- if (versionMismatches.length > 0) return {
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
- * Fix version mismatches in package.json
36357
- * This is a separate utility function for CLI usage
36358
- */
36359
- async function fixVersionMismatches(packageJsonPath, versionMismatches) {
36360
- const packageResult = await fs.default.promises.readFile(packageJsonPath, "utf-8");
36361
- if (!packageResult) throw new Error("Could not read package.json");
36362
- const packageJson = JSON.parse(packageResult);
36363
- for (const mismatch of versionMismatches) if (packageJson.dependencies?.[mismatch.packageName]) packageJson.dependencies[mismatch.packageName] = mismatch.expectedVersion;
36364
- else if (packageJson.devDependencies?.[mismatch.packageName]) packageJson.devDependencies[mismatch.packageName] = mismatch.expectedVersion;
36365
- const content = `${JSON.stringify(packageJson, null, 2)}\n`;
36366
- await fs.default.promises.writeFile(packageJsonPath, content);
36367
- }
36368
- const handleDoctor = async ({ fix }) => {
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
- if (fix && details.packageJsonPath) try {
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("-f, --fix", "fix the issues", false).action(handleDoctor);
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 cwd - Current working directory (optional)
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(cwd = getCwd()) {
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
- if (versionMismatches.length > 0) return {
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
- * Fix version mismatches in package.json
36337
- * This is a separate utility function for CLI usage
36338
- */
36339
- async function fixVersionMismatches(packageJsonPath, versionMismatches) {
36340
- const packageResult = await fs$1.promises.readFile(packageJsonPath, "utf-8");
36341
- if (!packageResult) throw new Error("Could not read package.json");
36342
- const packageJson = JSON.parse(packageResult);
36343
- for (const mismatch of versionMismatches) if (packageJson.dependencies?.[mismatch.packageName]) packageJson.dependencies[mismatch.packageName] = mismatch.expectedVersion;
36344
- else if (packageJson.devDependencies?.[mismatch.packageName]) packageJson.devDependencies[mismatch.packageName] = mismatch.expectedVersion;
36345
- const content = `${JSON.stringify(packageJson, null, 2)}\n`;
36346
- await fs$1.promises.writeFile(packageJsonPath, content);
36347
- }
36348
- const handleDoctor = async ({ fix }) => {
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
- if (fix && details.packageJsonPath) try {
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("-f, --fix", "fix the issues", false).action(handleDoctor);
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.0",
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.0",
51
- "@hot-updater/apple-helper": "0.30.0",
52
- "@hot-updater/cli-tools": "0.30.0",
53
- "@hot-updater/console": "0.30.0",
54
- "@hot-updater/core": "0.30.0",
55
- "@hot-updater/plugin-core": "0.30.0",
56
- "@hot-updater/server": "0.30.0"
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/aws": "0.30.0",
77
- "@hot-updater/cloudflare": "0.30.0",
78
- "@hot-updater/firebase": "0.30.0",
79
- "@hot-updater/server": "0.30.0",
80
- "@hot-updater/supabase": "0.30.0",
81
- "@hot-updater/test-utils": "0.30.0"
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": "*",