hot-updater 0.32.0 → 0.33.0
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.mjs +227 -97
- package/package.json +14 -14
package/dist/index.mjs
CHANGED
|
@@ -32,6 +32,7 @@ import { constants as constants$2, createBrotliCompress } from "zlib";
|
|
|
32
32
|
import { assertNodeStoragePlugin, getContentAddressedAssetStoragePath } from "@hot-updater/plugin-core";
|
|
33
33
|
import { createBundleDiff } from "@hot-updater/server";
|
|
34
34
|
import net from "node:net";
|
|
35
|
+
import { setTimeout as setTimeout$1 } from "timers/promises";
|
|
35
36
|
import { createJiti } from "jiti";
|
|
36
37
|
//#region ../../node_modules/.pnpm/commander@14.0.0/node_modules/commander/lib/error.js
|
|
37
38
|
var require_error = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
@@ -30956,6 +30957,8 @@ const LIST_COLUMNS = [
|
|
|
30956
30957
|
}
|
|
30957
30958
|
];
|
|
30958
30959
|
const DEFAULT_LIMIT = 20;
|
|
30960
|
+
const DELETE_VERIFY_ATTEMPTS = 12;
|
|
30961
|
+
const DELETE_VERIFY_DELAY_MS = 1e3;
|
|
30959
30962
|
const formatRow = (bundle) => {
|
|
30960
30963
|
const out = {};
|
|
30961
30964
|
for (const field of LIST_FIELDS) {
|
|
@@ -31145,7 +31148,7 @@ const handleBundleDelete = async (bundleId, options = {}) => {
|
|
|
31145
31148
|
}
|
|
31146
31149
|
await databasePlugin.deleteBundle(bundle);
|
|
31147
31150
|
await databasePlugin.commitBundle();
|
|
31148
|
-
if (await databasePlugin
|
|
31151
|
+
if (!await waitForDeletedBundle(databasePlugin, bundleId)) {
|
|
31149
31152
|
p.log.error(`Verification failed: ${bundleId} still exists.`);
|
|
31150
31153
|
process.exit(1);
|
|
31151
31154
|
}
|
|
@@ -31155,6 +31158,13 @@ const handleBundleDelete = async (bundleId, options = {}) => {
|
|
|
31155
31158
|
await safeOnUnmount$2(databasePlugin);
|
|
31156
31159
|
}
|
|
31157
31160
|
};
|
|
31161
|
+
async function waitForDeletedBundle(databasePlugin, bundleId) {
|
|
31162
|
+
for (let attempt = 0; attempt < DELETE_VERIFY_ATTEMPTS; attempt += 1) {
|
|
31163
|
+
if (!await databasePlugin.getBundleById(bundleId)) return true;
|
|
31164
|
+
if (attempt < DELETE_VERIFY_ATTEMPTS - 1) await setTimeout$1(DELETE_VERIFY_DELAY_MS);
|
|
31165
|
+
}
|
|
31166
|
+
return false;
|
|
31167
|
+
}
|
|
31158
31168
|
//#endregion
|
|
31159
31169
|
//#region ../../node_modules/.pnpm/es-toolkit@1.32.0/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs
|
|
31160
31170
|
function isPlainObject(value) {
|
|
@@ -31728,7 +31738,7 @@ var require_subset = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
31728
31738
|
module.exports = subset;
|
|
31729
31739
|
}));
|
|
31730
31740
|
//#endregion
|
|
31731
|
-
//#region src/commands/
|
|
31741
|
+
//#region src/commands/doctorInfrastructureTargets.ts
|
|
31732
31742
|
var import_semver = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
31733
31743
|
const internalRe = require_re();
|
|
31734
31744
|
const constants = require_constants$1();
|
|
@@ -31782,15 +31792,7 @@ var import_semver = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
|
|
|
31782
31792
|
rcompareIdentifiers: identifiers.rcompareIdentifiers
|
|
31783
31793
|
};
|
|
31784
31794
|
})))(), 1);
|
|
31785
|
-
const
|
|
31786
|
-
"hot-updater init",
|
|
31787
|
-
"hot-updater db migrate",
|
|
31788
|
-
"hot-updater db generate"
|
|
31789
|
-
];
|
|
31790
|
-
const FINGERPRINT_RECOVERY_COMMANDS = ["npx hot-updater fingerprint create"];
|
|
31791
|
-
const EXPORT_PUBLIC_KEY_COMMANDS = ["npx hot-updater keys export-public"];
|
|
31792
|
-
const REMOVE_PUBLIC_KEY_COMMANDS = ["npx hot-updater keys remove"];
|
|
31793
|
-
const INFRASTRUCTURE_UPDATE_TARGETS = [
|
|
31795
|
+
const UPDATE_TARGETS = [
|
|
31794
31796
|
{
|
|
31795
31797
|
version: "0.13.0",
|
|
31796
31798
|
note: "Initial provider infrastructure migrations"
|
|
@@ -31818,35 +31820,51 @@ const INFRASTRUCTURE_UPDATE_TARGETS = [
|
|
|
31818
31820
|
{
|
|
31819
31821
|
version: "0.32.0",
|
|
31820
31822
|
note: "Content-addressed manifest asset routing"
|
|
31823
|
+
},
|
|
31824
|
+
{
|
|
31825
|
+
version: "0.33.0",
|
|
31826
|
+
note: "provider update checks reuse selected bundles"
|
|
31821
31827
|
}
|
|
31822
31828
|
];
|
|
31823
|
-
const
|
|
31824
|
-
const target =
|
|
31825
|
-
if (!target) throw new Error(
|
|
31826
|
-
return target
|
|
31829
|
+
const getTargetAt = ({ index, label, targets }) => {
|
|
31830
|
+
const target = targets.at(index);
|
|
31831
|
+
if (!target) throw new Error(`${label} must not be empty`);
|
|
31832
|
+
return target;
|
|
31827
31833
|
};
|
|
31828
|
-
|
|
31829
|
-
|
|
31830
|
-
|
|
31831
|
-
|
|
31832
|
-
|
|
31833
|
-
|
|
31834
|
-
|
|
31835
|
-
|
|
31836
|
-
const comparableVersionA = import_semver.validRange(versionA) ? import_semver.minVersion(versionA) : null;
|
|
31837
|
-
const comparableVersionB = import_semver.validRange(versionB) ? import_semver.minVersion(versionB) : null;
|
|
31838
|
-
if (comparableVersionA && comparableVersionB && comparableVersionA.prerelease.length === 0 && comparableVersionB.prerelease.length === 0 && comparableVersionA.major === comparableVersionB.major && comparableVersionA.minor === comparableVersionB.minor) return true;
|
|
31839
|
-
const options = { includePrerelease: true };
|
|
31840
|
-
if (import_semver.valid(versionA) && import_semver.validRange(versionB) && import_semver.satisfies(versionA, versionB, options)) return true;
|
|
31841
|
-
if (import_semver.valid(versionB) && import_semver.validRange(versionA) && import_semver.satisfies(versionB, versionA, options)) return true;
|
|
31842
|
-
return false;
|
|
31843
|
-
}
|
|
31844
|
-
function getRequiredInfrastructureVersion(hotUpdaterVersion = getInfrastructureTargetVersionAt(-1)) {
|
|
31834
|
+
const getLatestKnownTargetVersion = () => {
|
|
31835
|
+
return getTargetAt({
|
|
31836
|
+
index: -1,
|
|
31837
|
+
label: "UPDATE_TARGETS",
|
|
31838
|
+
targets: UPDATE_TARGETS
|
|
31839
|
+
}).version;
|
|
31840
|
+
};
|
|
31841
|
+
const getRequiredTarget = ({ hotUpdaterVersion, targets }) => {
|
|
31845
31842
|
const current = import_semver.coerce(hotUpdaterVersion)?.version;
|
|
31846
|
-
if (!current) return
|
|
31847
|
-
let
|
|
31848
|
-
for (const target of
|
|
31849
|
-
return
|
|
31843
|
+
if (!current) return null;
|
|
31844
|
+
let requiredTarget = null;
|
|
31845
|
+
for (const target of targets) if (import_semver.lte(target.version, current)) requiredTarget = target;
|
|
31846
|
+
return requiredTarget ?? getTargetAt({
|
|
31847
|
+
index: 0,
|
|
31848
|
+
label: "UPDATE_TARGETS",
|
|
31849
|
+
targets
|
|
31850
|
+
});
|
|
31851
|
+
};
|
|
31852
|
+
function getRequiredInfrastructureVersion(hotUpdaterVersion = getTargetAt({
|
|
31853
|
+
index: -1,
|
|
31854
|
+
label: "UPDATE_TARGETS",
|
|
31855
|
+
targets: UPDATE_TARGETS
|
|
31856
|
+
}).version) {
|
|
31857
|
+
return getRequiredUpdateTarget(hotUpdaterVersion).version;
|
|
31858
|
+
}
|
|
31859
|
+
function getRequiredUpdateTarget(hotUpdaterVersion = getLatestKnownTargetVersion()) {
|
|
31860
|
+
return getRequiredTarget({
|
|
31861
|
+
hotUpdaterVersion,
|
|
31862
|
+
targets: UPDATE_TARGETS
|
|
31863
|
+
}) ?? getTargetAt({
|
|
31864
|
+
index: -1,
|
|
31865
|
+
label: "UPDATE_TARGETS",
|
|
31866
|
+
targets: UPDATE_TARGETS
|
|
31867
|
+
});
|
|
31850
31868
|
}
|
|
31851
31869
|
function isInfrastructureUpdateRequired({ serverVersion, requiredVersion = getRequiredInfrastructureVersion() }) {
|
|
31852
31870
|
const normalizedServerVersion = import_semver.valid(serverVersion);
|
|
@@ -31854,6 +31872,13 @@ function isInfrastructureUpdateRequired({ serverVersion, requiredVersion = getRe
|
|
|
31854
31872
|
if (!normalizedServerVersion || !normalizedRequiredVersion) throw new Error("Invalid infrastructure version");
|
|
31855
31873
|
return import_semver.lt(normalizedServerVersion, normalizedRequiredVersion);
|
|
31856
31874
|
}
|
|
31875
|
+
//#endregion
|
|
31876
|
+
//#region src/commands/doctorInfrastructure.ts
|
|
31877
|
+
const INFRASTRUCTURE_RECOVERY_COMMANDS = [
|
|
31878
|
+
"hot-updater init",
|
|
31879
|
+
"hot-updater db migrate",
|
|
31880
|
+
"hot-updater db generate"
|
|
31881
|
+
];
|
|
31857
31882
|
function resolveVersionEndpoint(serverBaseUrl) {
|
|
31858
31883
|
const url = new URL(serverBaseUrl.trim());
|
|
31859
31884
|
const pathname = url.pathname.replace(/\/+$/, "");
|
|
@@ -31862,11 +31887,83 @@ function resolveVersionEndpoint(serverBaseUrl) {
|
|
|
31862
31887
|
url.pathname = `${pathname}/version`;
|
|
31863
31888
|
return url.toString();
|
|
31864
31889
|
}
|
|
31865
|
-
const createInfrastructureRemediation = () =>
|
|
31866
|
-
|
|
31867
|
-
|
|
31868
|
-
|
|
31869
|
-
|
|
31890
|
+
const createInfrastructureRemediation = () => {
|
|
31891
|
+
return {
|
|
31892
|
+
fixability: "blocked",
|
|
31893
|
+
reason: "Server infrastructure changes usually need provider credentials, environment variables, and redeploy access.",
|
|
31894
|
+
commands: [...INFRASTRUCTURE_RECOVERY_COMMANDS]
|
|
31895
|
+
};
|
|
31896
|
+
};
|
|
31897
|
+
async function checkInfrastructureStatus({ serverBaseUrl, fetchImpl = fetch, requiredTarget = getRequiredUpdateTarget() }) {
|
|
31898
|
+
const versionEndpoint = resolveVersionEndpoint(serverBaseUrl);
|
|
31899
|
+
const baseUrl = serverBaseUrl.trim();
|
|
31900
|
+
const requiredVersion = requiredTarget.version;
|
|
31901
|
+
try {
|
|
31902
|
+
const response = await fetchImpl(versionEndpoint, { headers: { Accept: "application/json" } });
|
|
31903
|
+
if (!response.ok) {
|
|
31904
|
+
if (response.status === 404) return {
|
|
31905
|
+
baseUrl,
|
|
31906
|
+
versionEndpoint,
|
|
31907
|
+
requiredVersion,
|
|
31908
|
+
needsUpdate: true,
|
|
31909
|
+
updateReason: "Version endpoint not found"
|
|
31910
|
+
};
|
|
31911
|
+
return {
|
|
31912
|
+
baseUrl,
|
|
31913
|
+
versionEndpoint,
|
|
31914
|
+
requiredVersion,
|
|
31915
|
+
error: `Version endpoint returned ${response.status}`
|
|
31916
|
+
};
|
|
31917
|
+
}
|
|
31918
|
+
const data = await response.json();
|
|
31919
|
+
if (typeof data.version !== "string") return {
|
|
31920
|
+
baseUrl,
|
|
31921
|
+
versionEndpoint,
|
|
31922
|
+
requiredVersion,
|
|
31923
|
+
error: "Version endpoint response must include a string version"
|
|
31924
|
+
};
|
|
31925
|
+
const needsUpdate = isInfrastructureUpdateRequired({
|
|
31926
|
+
serverVersion: data.version,
|
|
31927
|
+
requiredVersion
|
|
31928
|
+
});
|
|
31929
|
+
return {
|
|
31930
|
+
baseUrl,
|
|
31931
|
+
versionEndpoint,
|
|
31932
|
+
serverVersion: data.version,
|
|
31933
|
+
requiredVersion,
|
|
31934
|
+
needsUpdate,
|
|
31935
|
+
updateReason: needsUpdate ? requiredTarget.note : void 0
|
|
31936
|
+
};
|
|
31937
|
+
} catch (error) {
|
|
31938
|
+
return {
|
|
31939
|
+
baseUrl,
|
|
31940
|
+
versionEndpoint,
|
|
31941
|
+
requiredVersion,
|
|
31942
|
+
error: error instanceof Error ? error.message : String(error)
|
|
31943
|
+
};
|
|
31944
|
+
}
|
|
31945
|
+
}
|
|
31946
|
+
//#endregion
|
|
31947
|
+
//#region src/commands/doctor.ts
|
|
31948
|
+
const FINGERPRINT_RECOVERY_COMMANDS = ["npx hot-updater fingerprint create"];
|
|
31949
|
+
const EXPORT_PUBLIC_KEY_COMMANDS = ["npx hot-updater keys export-public"];
|
|
31950
|
+
const REMOVE_PUBLIC_KEY_COMMANDS = ["npx hot-updater keys remove"];
|
|
31951
|
+
/**
|
|
31952
|
+
* Checks if two versions (or version and range) are compatible.
|
|
31953
|
+
* @param versionA - First version or range string.
|
|
31954
|
+
* @param versionB - Second version or range string.
|
|
31955
|
+
* @returns True if compatible, false otherwise.
|
|
31956
|
+
*/
|
|
31957
|
+
function areVersionsCompatible(versionA, versionB) {
|
|
31958
|
+
if (versionA === versionB) return true;
|
|
31959
|
+
const comparableVersionA = import_semver.validRange(versionA) ? import_semver.minVersion(versionA) : null;
|
|
31960
|
+
const comparableVersionB = import_semver.validRange(versionB) ? import_semver.minVersion(versionB) : null;
|
|
31961
|
+
if (comparableVersionA && comparableVersionB && comparableVersionA.prerelease.length === 0 && comparableVersionB.prerelease.length === 0 && comparableVersionA.major === comparableVersionB.major && comparableVersionA.minor === comparableVersionB.minor) return true;
|
|
31962
|
+
const options = { includePrerelease: true };
|
|
31963
|
+
if (import_semver.valid(versionA) && import_semver.validRange(versionB) && import_semver.satisfies(versionA, versionB, options)) return true;
|
|
31964
|
+
if (import_semver.valid(versionB) && import_semver.validRange(versionA) && import_semver.satisfies(versionB, versionA, options)) return true;
|
|
31965
|
+
return false;
|
|
31966
|
+
}
|
|
31870
31967
|
const toRelativePath = (cwd, filePath) => path.relative(cwd, filePath);
|
|
31871
31968
|
const resolveProjectPath = (cwd, filePath) => path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
|
|
31872
31969
|
const findNativeFiles = ({ cwd, platform, pattern }) => {
|
|
@@ -32125,49 +32222,55 @@ async function checkNativeStatus({ cwd }) {
|
|
|
32125
32222
|
issues
|
|
32126
32223
|
};
|
|
32127
32224
|
}
|
|
32128
|
-
async function
|
|
32129
|
-
const versionEndpoint = resolveVersionEndpoint(serverBaseUrl);
|
|
32130
|
-
const baseUrl = serverBaseUrl.trim();
|
|
32225
|
+
async function checkBundleIndexStatus({ fix }) {
|
|
32131
32226
|
try {
|
|
32132
|
-
const
|
|
32133
|
-
|
|
32134
|
-
|
|
32135
|
-
|
|
32136
|
-
|
|
32137
|
-
|
|
32138
|
-
|
|
32139
|
-
|
|
32227
|
+
const database = await (await loadConfig(null)).database();
|
|
32228
|
+
try {
|
|
32229
|
+
const bundleIndexDiagnostics = database.diagnostics?.bundleIndex;
|
|
32230
|
+
if (!bundleIndexDiagnostics) {
|
|
32231
|
+
if (!fix) return;
|
|
32232
|
+
return {
|
|
32233
|
+
adapterName: database.name,
|
|
32234
|
+
status: "not-applicable"
|
|
32235
|
+
};
|
|
32236
|
+
}
|
|
32237
|
+
const health = await bundleIndexDiagnostics.check();
|
|
32238
|
+
if (health?.status === "ok") return {
|
|
32239
|
+
adapterName: database.name,
|
|
32240
|
+
status: "ok",
|
|
32241
|
+
repairAvailable: bundleIndexDiagnostics.repair !== void 0,
|
|
32242
|
+
health
|
|
32243
|
+
};
|
|
32244
|
+
if (!fix || !bundleIndexDiagnostics.repair) return {
|
|
32245
|
+
adapterName: database.name,
|
|
32246
|
+
status: health.status,
|
|
32247
|
+
repairAvailable: bundleIndexDiagnostics.repair !== void 0,
|
|
32248
|
+
health
|
|
32249
|
+
};
|
|
32250
|
+
const repair = await bundleIndexDiagnostics.repair();
|
|
32251
|
+
const postRepairHealth = await bundleIndexDiagnostics.check();
|
|
32252
|
+
if (postRepairHealth.status !== "ok") return {
|
|
32253
|
+
adapterName: database.name,
|
|
32254
|
+
status: postRepairHealth.status,
|
|
32255
|
+
repairAvailable: true,
|
|
32256
|
+
health,
|
|
32257
|
+
postRepairHealth,
|
|
32258
|
+
repair
|
|
32140
32259
|
};
|
|
32141
32260
|
return {
|
|
32142
|
-
|
|
32143
|
-
|
|
32144
|
-
|
|
32145
|
-
|
|
32261
|
+
adapterName: database.name,
|
|
32262
|
+
status: "repaired",
|
|
32263
|
+
repairAvailable: true,
|
|
32264
|
+
health,
|
|
32265
|
+
postRepairHealth,
|
|
32266
|
+
repair
|
|
32146
32267
|
};
|
|
32268
|
+
} finally {
|
|
32269
|
+
await database.onUnmount?.();
|
|
32147
32270
|
}
|
|
32148
|
-
const data = await response.json();
|
|
32149
|
-
if (typeof data.version !== "string") return {
|
|
32150
|
-
baseUrl,
|
|
32151
|
-
versionEndpoint,
|
|
32152
|
-
requiredVersion,
|
|
32153
|
-
error: "Version endpoint response must include a string version"
|
|
32154
|
-
};
|
|
32155
|
-
const needsUpdate = isInfrastructureUpdateRequired({
|
|
32156
|
-
serverVersion: data.version,
|
|
32157
|
-
requiredVersion
|
|
32158
|
-
});
|
|
32159
|
-
return {
|
|
32160
|
-
baseUrl,
|
|
32161
|
-
versionEndpoint,
|
|
32162
|
-
serverVersion: data.version,
|
|
32163
|
-
requiredVersion,
|
|
32164
|
-
needsUpdate
|
|
32165
|
-
};
|
|
32166
32271
|
} catch (error) {
|
|
32167
32272
|
return {
|
|
32168
|
-
|
|
32169
|
-
versionEndpoint,
|
|
32170
|
-
requiredVersion,
|
|
32273
|
+
status: "error",
|
|
32171
32274
|
error: error.message
|
|
32172
32275
|
};
|
|
32173
32276
|
}
|
|
@@ -32179,7 +32282,7 @@ async function checkInfrastructureStatus({ serverBaseUrl, fetchImpl = fetch, req
|
|
|
32179
32282
|
*/
|
|
32180
32283
|
async function doctor(options = {}) {
|
|
32181
32284
|
try {
|
|
32182
|
-
const { cwd = getCwd(), serverBaseUrl, fetch: fetchImpl } = options;
|
|
32285
|
+
const { cwd = getCwd(), serverBaseUrl, fetch: fetchImpl, fix = false } = options;
|
|
32183
32286
|
const packageResult = await readPackageUp(cwd);
|
|
32184
32287
|
if (!packageResult) return {
|
|
32185
32288
|
success: false,
|
|
@@ -32213,15 +32316,17 @@ async function doctor(options = {}) {
|
|
|
32213
32316
|
details.infrastructure = await checkInfrastructureStatus({
|
|
32214
32317
|
serverBaseUrl,
|
|
32215
32318
|
fetchImpl,
|
|
32216
|
-
|
|
32319
|
+
requiredTarget: getRequiredUpdateTarget(hotUpdaterVersion)
|
|
32217
32320
|
});
|
|
32218
32321
|
if (details.infrastructure.error !== void 0 || details.infrastructure.needsUpdate === true) details.infrastructure.remediation = createInfrastructureRemediation();
|
|
32219
32322
|
}
|
|
32220
32323
|
if (hasReactNativePackage) details.native = await checkNativeStatus({ cwd });
|
|
32324
|
+
details.bundleIndex = await checkBundleIndexStatus({ fix });
|
|
32221
32325
|
if (versionMismatches.length > 0) details.versionMismatches = versionMismatches;
|
|
32222
32326
|
const hasInfrastructureIssue = details.infrastructure?.error !== void 0 || details.infrastructure?.needsUpdate === true;
|
|
32223
32327
|
const hasNativeIssue = details.native?.issues.some((issue) => issue.type === "error") === true;
|
|
32224
|
-
|
|
32328
|
+
const hasBundleIndexIssue = details.bundleIndex?.status === "error" || details.bundleIndex?.status === "missing" || details.bundleIndex?.status === "stale";
|
|
32329
|
+
if (versionMismatches.length > 0 || hasInfrastructureIssue || hasNativeIssue || hasBundleIndexIssue) return {
|
|
32225
32330
|
success: false,
|
|
32226
32331
|
details
|
|
32227
32332
|
};
|
|
@@ -32233,6 +32338,10 @@ async function doctor(options = {}) {
|
|
|
32233
32338
|
success: true,
|
|
32234
32339
|
details
|
|
32235
32340
|
};
|
|
32341
|
+
if (details.bundleIndex) return {
|
|
32342
|
+
success: true,
|
|
32343
|
+
details
|
|
32344
|
+
};
|
|
32236
32345
|
return true;
|
|
32237
32346
|
} catch (error) {
|
|
32238
32347
|
return {
|
|
@@ -32267,15 +32376,21 @@ const promptServerBaseUrl = async () => {
|
|
|
32267
32376
|
const trimmed = serverBaseUrl.trim();
|
|
32268
32377
|
return trimmed ? trimmed : void 0;
|
|
32269
32378
|
};
|
|
32270
|
-
const handleDoctor = async ({ serverBaseUrl, json = false } = {}) => {
|
|
32379
|
+
const handleDoctor = async ({ serverBaseUrl, json = false, fix = false } = {}) => {
|
|
32271
32380
|
if (json) {
|
|
32272
|
-
const result = normalizeDoctorResult(await doctor({
|
|
32381
|
+
const result = normalizeDoctorResult(await doctor({
|
|
32382
|
+
serverBaseUrl,
|
|
32383
|
+
fix
|
|
32384
|
+
}));
|
|
32273
32385
|
console.log(JSON.stringify(result, null, 2));
|
|
32274
32386
|
if (!result.success) process.exit(1);
|
|
32275
32387
|
return;
|
|
32276
32388
|
}
|
|
32277
32389
|
p.intro("Hot Updater doctor");
|
|
32278
|
-
const result = await doctor({
|
|
32390
|
+
const result = await doctor({
|
|
32391
|
+
serverBaseUrl: serverBaseUrl ?? await promptServerBaseUrl(),
|
|
32392
|
+
fix
|
|
32393
|
+
});
|
|
32279
32394
|
if (result === true) {
|
|
32280
32395
|
p.log.success("All checks passed.");
|
|
32281
32396
|
p.outro("Healthy.");
|
|
@@ -32302,12 +32417,10 @@ const handleDoctor = async ({ serverBaseUrl, json = false } = {}) => {
|
|
|
32302
32417
|
if (infrastructure.updateReason) p.log.info(`Reason: ${infrastructure.updateReason}`);
|
|
32303
32418
|
} else if (infrastructure.error) p.log.error(`Infrastructure check failed: ${infrastructure.error}`);
|
|
32304
32419
|
else p.log.success("Infrastructure is up to date.");
|
|
32305
|
-
if (infrastructure.remediation)
|
|
32306
|
-
ui.
|
|
32307
|
-
"
|
|
32308
|
-
|
|
32309
|
-
"then redeploy server"
|
|
32310
|
-
]))]));
|
|
32420
|
+
if (infrastructure.remediation) {
|
|
32421
|
+
const recoveryLines = infrastructure.remediation.commands.map((command, index) => ui.kv(index === 0 ? "Command" : `Command ${index + 1}`, ui.command(command)));
|
|
32422
|
+
p.log.message(ui.block("Recovery", recoveryLines));
|
|
32423
|
+
}
|
|
32311
32424
|
}
|
|
32312
32425
|
if (details?.native) {
|
|
32313
32426
|
const native = details.native;
|
|
@@ -32328,6 +32441,23 @@ const handleDoctor = async ({ serverBaseUrl, json = false } = {}) => {
|
|
|
32328
32441
|
p.log.info(issue.resolution);
|
|
32329
32442
|
}
|
|
32330
32443
|
}
|
|
32444
|
+
if (details?.bundleIndex) {
|
|
32445
|
+
const bundleIndex = details.bundleIndex;
|
|
32446
|
+
const lines = [ui.kv("Status", bundleIndex.status), ...bundleIndex.adapterName ? [ui.kv("Adapter", bundleIndex.adapterName)] : []];
|
|
32447
|
+
if (bundleIndex.health) lines.push(ui.kv("Canonical", bundleIndex.health.canonicalBundles), ui.kv("Indexed", bundleIndex.health.indexedBundles), ui.kv("Missing", bundleIndex.health.missingBundles), ui.kv("Extra", bundleIndex.health.extraBundles));
|
|
32448
|
+
if (bundleIndex.postRepairHealth) lines.push(ui.kv("Post-repair", bundleIndex.postRepairHealth.status), ui.kv("Post missing", bundleIndex.postRepairHealth.missingBundles), ui.kv("Post extra", bundleIndex.postRepairHealth.extraBundles));
|
|
32449
|
+
if (bundleIndex.repair) lines.push(ui.kv("Written", bundleIndex.repair.indexedBundles), ui.kv("Pages", bundleIndex.repair.pagesWritten), ui.kv("Scopes", bundleIndex.repair.scopesWritten));
|
|
32450
|
+
p.log.message(ui.block("Bundle index", lines));
|
|
32451
|
+
if (bundleIndex.status === "error") p.log.error(`Bundle index check failed: ${bundleIndex.error}`);
|
|
32452
|
+
else if (bundleIndex.status === "repaired") p.log.success("Bundle index repaired.");
|
|
32453
|
+
else if (bundleIndex.status === "ok") p.log.success("Bundle index is healthy.");
|
|
32454
|
+
else if (bundleIndex.status === "not-applicable") p.log.info("Bundle index repair is not supported by this adapter.");
|
|
32455
|
+
else if (bundleIndex.repair) p.log.error("Bundle index repair completed, but the index is still out of sync.");
|
|
32456
|
+
else if (bundleIndex.repairAvailable) {
|
|
32457
|
+
p.log.warn("Bundle index is out of sync.");
|
|
32458
|
+
p.log.info(`Run ${ui.command("hot-updater doctor --fix")} to repair it.`);
|
|
32459
|
+
} else p.log.error("Bundle index is out of sync and repair is not available.");
|
|
32460
|
+
}
|
|
32331
32461
|
if (details?.versionMismatches && details.versionMismatches.length > 0) {
|
|
32332
32462
|
p.log.warn("Version mismatches found:");
|
|
32333
32463
|
for (const mismatch of details.versionMismatches) p.log.error(`${mismatch.packageName}: ${mismatch.currentVersion} (expected ${mismatch.expectedVersion})`);
|
|
@@ -53014,17 +53144,17 @@ const DEFAULT_CONFIG_BASENAMES = [
|
|
|
53014
53144
|
path.join("src", "hotUpdater"),
|
|
53015
53145
|
path.join("src", "db")
|
|
53016
53146
|
];
|
|
53017
|
-
const findDefaultConfigPath = () => {
|
|
53147
|
+
const findDefaultConfigPath = (cwd) => {
|
|
53018
53148
|
for (const basename of DEFAULT_CONFIG_BASENAMES) for (const ext of SUPPORTED_CONFIG_EXTENSIONS) {
|
|
53019
|
-
const candidate = path.resolve(
|
|
53149
|
+
const candidate = path.resolve(cwd, `${basename}.${ext}`);
|
|
53020
53150
|
if (existsSync(candidate)) return candidate;
|
|
53021
53151
|
}
|
|
53022
53152
|
return null;
|
|
53023
53153
|
};
|
|
53024
|
-
const resolveConfigPath = (configPath) => {
|
|
53154
|
+
const resolveConfigPath = (configPath, cwd) => {
|
|
53025
53155
|
const trimmedConfigPath = configPath.trim();
|
|
53026
|
-
if (trimmedConfigPath) return path.resolve(
|
|
53027
|
-
const defaultConfigPath = findDefaultConfigPath();
|
|
53156
|
+
if (trimmedConfigPath) return path.resolve(cwd, trimmedConfigPath);
|
|
53157
|
+
const defaultConfigPath = findDefaultConfigPath(cwd);
|
|
53028
53158
|
if (defaultConfigPath) return defaultConfigPath;
|
|
53029
53159
|
p.log.error("Could not find a Hot Updater config file.");
|
|
53030
53160
|
p.log.message(ui.block("Examples", [
|
|
@@ -53037,8 +53167,8 @@ const resolveConfigPath = (configPath) => {
|
|
|
53037
53167
|
/**
|
|
53038
53168
|
* Load and validate hotUpdater instance from config file
|
|
53039
53169
|
*/
|
|
53040
|
-
async function loadHotUpdater(configPath) {
|
|
53041
|
-
const absoluteConfigPath = resolveConfigPath(configPath);
|
|
53170
|
+
async function loadHotUpdater(configPath, options = {}) {
|
|
53171
|
+
const absoluteConfigPath = resolveConfigPath(configPath, options.cwd ?? process.cwd());
|
|
53042
53172
|
if (!existsSync(absoluteConfigPath)) {
|
|
53043
53173
|
p.log.error(ui.line(["Config file not found:", ui.path(absoluteConfigPath)]));
|
|
53044
53174
|
process.exit(1);
|
|
@@ -54180,7 +54310,7 @@ const parseRolloutCohortCount = (value) => {
|
|
|
54180
54310
|
const program = new Command$1();
|
|
54181
54311
|
program.name("hot-updater").description(banner(version)).version(version);
|
|
54182
54312
|
program.command("init").description("Initialize Hot Updater").action(init);
|
|
54183
|
-
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)").option("--json", "output machine-readable doctor result").action(handleDoctor);
|
|
54313
|
+
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)").option("--fix", "automatically fix safe issues").option("--json", "output machine-readable doctor result").action(handleDoctor);
|
|
54184
54314
|
const fingerprintCommand = program.command("fingerprint").description("Generate fingerprint");
|
|
54185
54315
|
fingerprintCommand.action(handleFingerprint);
|
|
54186
54316
|
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.
|
|
4
|
+
"version": "0.33.0",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.19.0"
|
|
7
7
|
},
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"jiti": "2.6.1",
|
|
49
|
-
"@hot-updater/android-helper": "0.
|
|
50
|
-
"@hot-updater/apple-helper": "0.
|
|
51
|
-
"@hot-updater/cli-tools": "0.
|
|
52
|
-
"@hot-updater/
|
|
53
|
-
"@hot-updater/
|
|
54
|
-
"@hot-updater/plugin-core": "0.
|
|
55
|
-
"@hot-updater/server": "0.
|
|
49
|
+
"@hot-updater/android-helper": "0.33.0",
|
|
50
|
+
"@hot-updater/apple-helper": "0.33.0",
|
|
51
|
+
"@hot-updater/cli-tools": "0.33.0",
|
|
52
|
+
"@hot-updater/console": "0.33.0",
|
|
53
|
+
"@hot-updater/core": "0.33.0",
|
|
54
|
+
"@hot-updater/plugin-core": "0.33.0",
|
|
55
|
+
"@hot-updater/server": "0.33.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@bacons/xcode": "1.0.0-alpha.24",
|
|
@@ -75,12 +75,12 @@
|
|
|
75
75
|
"tsdown": "0.21.6",
|
|
76
76
|
"sql-formatter": "15.6.10",
|
|
77
77
|
"typescript": "6.0.2",
|
|
78
|
-
"@hot-updater/aws": "0.
|
|
79
|
-
"@hot-updater/cloudflare": "0.
|
|
80
|
-
"@hot-updater/
|
|
81
|
-
"@hot-updater/
|
|
82
|
-
"@hot-updater/supabase": "0.
|
|
83
|
-
"@hot-updater/
|
|
78
|
+
"@hot-updater/aws": "0.33.0",
|
|
79
|
+
"@hot-updater/cloudflare": "0.33.0",
|
|
80
|
+
"@hot-updater/server": "0.33.0",
|
|
81
|
+
"@hot-updater/test-utils": "0.33.0",
|
|
82
|
+
"@hot-updater/supabase": "0.33.0",
|
|
83
|
+
"@hot-updater/firebase": "0.33.0"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
86
|
"@expo/fingerprint": "*",
|