@hot-updater/cloudflare 1.0.4 → 1.0.6
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/package.json +6 -6
- package/worker/dist/README.md +1 -1
- package/worker/dist/index.js +142 -4
- package/worker/dist/index.js.map +2 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/cloudflare",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.6",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
"cloudflare": "4.2.0",
|
|
56
56
|
"hono": "4.12.34",
|
|
57
57
|
"uuidv7": "^1.0.2",
|
|
58
|
-
"@hot-updater/protocol": "1.0.
|
|
59
|
-
"@hot-updater/
|
|
60
|
-
"@hot-updater/
|
|
61
|
-
"@hot-updater/
|
|
58
|
+
"@hot-updater/protocol": "1.0.6",
|
|
59
|
+
"@hot-updater/cli-tools": "1.0.6",
|
|
60
|
+
"@hot-updater/plugin-core": "1.0.6",
|
|
61
|
+
"@hot-updater/server": "1.0.6"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@cloudflare/vitest-pool-workers": "0.13.0",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"vitest": "4.1.4",
|
|
75
75
|
"wrangler": "^4.69.0",
|
|
76
76
|
"xdg-app-paths": "^8.3.0",
|
|
77
|
-
"@hot-updater/test-utils": "1.0.
|
|
77
|
+
"@hot-updater/test-utils": "1.0.6"
|
|
78
78
|
},
|
|
79
79
|
"scripts": {
|
|
80
80
|
"build": "tsdown && pnpm build:worker",
|
package/worker/dist/README.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
This folder contains the built output assets for the worker "hot-updater" generated at 2026-10-
|
|
1
|
+
This folder contains the built output assets for the worker "hot-updater" generated at 2026-10-14T16:15:36.859Z.
|
package/worker/dist/index.js
CHANGED
|
@@ -604,6 +604,8 @@ var NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
|
604
604
|
var UUID_V7_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
605
605
|
var isUUIDv7 = /* @__PURE__ */ __name((value) => typeof value === "string" && UUID_V7_PATTERN.test(value), "isUUIDv7");
|
|
606
606
|
var ARTIFACT_PROTOCOL_VERSION = 1;
|
|
607
|
+
var ARTIFACT_FILES_PROTOCOL_VERSION = 2;
|
|
608
|
+
var ARTIFACT_FILES_REQUEST_LIMIT = 100;
|
|
607
609
|
|
|
608
610
|
// ../plugin-core/dist/databaseRows.mjs
|
|
609
611
|
var releaseRowToRelease = /* @__PURE__ */ __name((row) => ({
|
|
@@ -4510,7 +4512,7 @@ var createSqlAdapter = /* @__PURE__ */ __name(({ executor, tablePrefix = "", max
|
|
|
4510
4512
|
}, "createSqlAdapter");
|
|
4511
4513
|
|
|
4512
4514
|
// ../../packages/server/dist/package.mjs
|
|
4513
|
-
var version = "1.0.
|
|
4515
|
+
var version = "1.0.6";
|
|
4514
4516
|
|
|
4515
4517
|
// ../../packages/server/dist/version.mjs
|
|
4516
4518
|
var HOT_UPDATER_SERVER_VERSION = version;
|
|
@@ -4808,6 +4810,90 @@ async function resolveManifestArtifacts({ currentBundle, resolveFileUrl, readSto
|
|
|
4808
4810
|
};
|
|
4809
4811
|
}
|
|
4810
4812
|
__name(resolveManifestArtifacts, "resolveManifestArtifacts");
|
|
4813
|
+
async function loadTargetPlan({ currentBundle, readStorageText, targetBundle }) {
|
|
4814
|
+
const manifestStorageUri = getManifestStorageUri(targetBundle);
|
|
4815
|
+
const manifestFileHash = getManifestFileHash(targetBundle);
|
|
4816
|
+
const assetBaseStorageUri = getAssetBaseStorageUri(targetBundle);
|
|
4817
|
+
if (!manifestStorageUri || !manifestFileHash || !assetBaseStorageUri) return null;
|
|
4818
|
+
const targetManifest = await fetchBundleManifest(manifestStorageUri, readStorageText);
|
|
4819
|
+
if (!targetManifest) return null;
|
|
4820
|
+
return {
|
|
4821
|
+
manifestFileHash,
|
|
4822
|
+
manifestStorageUri,
|
|
4823
|
+
plan: createManifestArtifactPlan({
|
|
4824
|
+
assetBaseStorageUri,
|
|
4825
|
+
currentBundle,
|
|
4826
|
+
targetBundle,
|
|
4827
|
+
targetManifest
|
|
4828
|
+
}),
|
|
4829
|
+
targetManifest
|
|
4830
|
+
};
|
|
4831
|
+
}
|
|
4832
|
+
__name(loadTargetPlan, "loadTargetPlan");
|
|
4833
|
+
async function resolveManifestArtifactsV2({ currentBundle, resolveFileUrl, readStorageText, targetBundle }) {
|
|
4834
|
+
const target = await loadTargetPlan({
|
|
4835
|
+
currentBundle,
|
|
4836
|
+
readStorageText,
|
|
4837
|
+
targetBundle
|
|
4838
|
+
});
|
|
4839
|
+
if (!target) return null;
|
|
4840
|
+
const manifestUrl = await resolveFileUrl(target.manifestStorageUri);
|
|
4841
|
+
if (!manifestUrl) return null;
|
|
4842
|
+
const patched = target.plan.assets.find((asset) => asset.patch !== null);
|
|
4843
|
+
let patch;
|
|
4844
|
+
if (patched?.patch && !(patched.file.byteSize !== null && patched.patch.byteSize !== null && patched.patch.byteSize >= patched.file.byteSize)) try {
|
|
4845
|
+
const patchUrl = await resolveFileUrl(patched.patch.storageUri);
|
|
4846
|
+
if (patchUrl) patch = {
|
|
4847
|
+
algorithm: "bsdiff",
|
|
4848
|
+
assetPath: patched.assetPath,
|
|
4849
|
+
baseBundleId: patched.patch.baseBundleId,
|
|
4850
|
+
baseFileHash: patched.patch.baseFileHash,
|
|
4851
|
+
patchFileHash: patched.patch.patchFileHash,
|
|
4852
|
+
patchUrl,
|
|
4853
|
+
...patched.patch.byteSize === null ? {} : { byteSize: patched.patch.byteSize }
|
|
4854
|
+
};
|
|
4855
|
+
} catch {
|
|
4856
|
+
}
|
|
4857
|
+
let archiveUrl = null;
|
|
4858
|
+
const archive = target.targetManifest.archive;
|
|
4859
|
+
if (archive && isContentAddressedAssetFileHash(archive.downloadFileHash) && (asByteSize(archive.downloadByteSize) ?? 0) > 0 && (asByteSize(archive.tarByteSize) ?? 0) > 0) try {
|
|
4860
|
+
archiveUrl = await resolveFileUrl(getBundleArchiveStorageUri({
|
|
4861
|
+
manifestStorageUri: target.manifestStorageUri,
|
|
4862
|
+
bundleId: targetBundle.id
|
|
4863
|
+
}));
|
|
4864
|
+
} catch {
|
|
4865
|
+
}
|
|
4866
|
+
return {
|
|
4867
|
+
artifactProtocolVersion: ARTIFACT_FILES_PROTOCOL_VERSION,
|
|
4868
|
+
manifestFileHash: target.manifestFileHash,
|
|
4869
|
+
manifestUrl,
|
|
4870
|
+
...archiveUrl ? { archiveUrl } : {},
|
|
4871
|
+
...patch ? { patch } : {}
|
|
4872
|
+
};
|
|
4873
|
+
}
|
|
4874
|
+
__name(resolveManifestArtifactsV2, "resolveManifestArtifactsV2");
|
|
4875
|
+
async function resolveManifestArtifactFiles({ paths, resolveFileUrl, readStorageText, targetBundle }) {
|
|
4876
|
+
if (paths.length === 0 || paths.length > ARTIFACT_FILES_REQUEST_LIMIT || new Set(paths).size !== paths.length) return null;
|
|
4877
|
+
const target = await loadTargetPlan({
|
|
4878
|
+
currentBundle: null,
|
|
4879
|
+
readStorageText,
|
|
4880
|
+
targetBundle
|
|
4881
|
+
});
|
|
4882
|
+
if (!target) return null;
|
|
4883
|
+
const planned = new Map(target.plan.assets.map((asset) => [asset.assetPath, asset]));
|
|
4884
|
+
const requested = paths.map((path) => planned.get(path));
|
|
4885
|
+
if (requested.some((asset) => asset === void 0)) return null;
|
|
4886
|
+
const files = await Promise.all(requested.map(async (asset) => {
|
|
4887
|
+
const url = await resolveFileUrl(asset.file.storageUri);
|
|
4888
|
+
if (!url) return null;
|
|
4889
|
+
const file = { url };
|
|
4890
|
+
if (asset.file.compression) file.compression = asset.file.compression;
|
|
4891
|
+
return [asset.assetPath, file];
|
|
4892
|
+
}));
|
|
4893
|
+
if (files.some((file) => file === null)) return null;
|
|
4894
|
+
return { files: Object.fromEntries(files.filter((file) => file !== null)) };
|
|
4895
|
+
}
|
|
4896
|
+
__name(resolveManifestArtifactFiles, "resolveManifestArtifactFiles");
|
|
4811
4897
|
|
|
4812
4898
|
// ../../node_modules/.pnpm/verkit@0.4.0/node_modules/verkit/dist/comparison-DenM3wCn.js
|
|
4813
4899
|
var LETTER_DASH_NUMBER3 = "[a-zA-Z0-9-]";
|
|
@@ -5309,20 +5395,34 @@ var createCoreReads = /* @__PURE__ */ __name((db, storage) => {
|
|
|
5309
5395
|
return projectReleaseCatalogRow(row === null ? null : toCatalogRow(row), input, scopeKey);
|
|
5310
5396
|
},
|
|
5311
5397
|
/** Artifact resolution: one batch read of both bundles and one unique read of their patch. */
|
|
5312
|
-
async
|
|
5398
|
+
getArtifactInfo: /* @__PURE__ */ __name((async (targetBundleId, currentBundleId, artifactProtocolVersion) => {
|
|
5313
5399
|
const { readStorageText } = storage;
|
|
5314
|
-
if (artifactProtocolVersion !== ARTIFACT_PROTOCOL_VERSION || readStorageText === void 0) return null;
|
|
5400
|
+
if (artifactProtocolVersion !== ARTIFACT_PROTOCOL_VERSION && artifactProtocolVersion !== ARTIFACT_FILES_PROTOCOL_VERSION || readStorageText === void 0) return null;
|
|
5315
5401
|
const withCurrent = currentBundleId !== NIL_UUID;
|
|
5316
5402
|
const [[target, current], patch] = await Promise.all([db.findByKeys("bundles", withCurrent ? [{ id: targetBundleId }, { id: currentBundleId }] : [{ id: targetBundleId }]), withCurrent ? db.findOne("bundle_patches", {
|
|
5317
5403
|
bundle_id: targetBundleId,
|
|
5318
5404
|
base_bundle_id: currentBundleId
|
|
5319
5405
|
}) : null]);
|
|
5320
5406
|
if (target === null || target === void 0) return null;
|
|
5321
|
-
|
|
5407
|
+
const input = {
|
|
5322
5408
|
currentBundle: current ? rowToBundle(toBundleRow(current)) : null,
|
|
5323
5409
|
readStorageText,
|
|
5324
5410
|
resolveFileUrl: storage.resolveFileUrl,
|
|
5325
5411
|
targetBundle: rowToBundle(toBundleRow(target), patch === null ? [] : [toPatchRow(patch)])
|
|
5412
|
+
};
|
|
5413
|
+
return artifactProtocolVersion === ARTIFACT_FILES_PROTOCOL_VERSION ? resolveManifestArtifactsV2(input) : resolveManifestArtifacts(input);
|
|
5414
|
+
}), "getArtifactInfo"),
|
|
5415
|
+
/** A Bundle's file URLs: one point read of the bundle and its manifest. */
|
|
5416
|
+
async getArtifactFiles(bundleId, paths) {
|
|
5417
|
+
const { readStorageText } = storage;
|
|
5418
|
+
if (readStorageText === void 0) return null;
|
|
5419
|
+
const row = await db.findOne("bundles", { id: bundleId });
|
|
5420
|
+
if (row === null) return null;
|
|
5421
|
+
return resolveManifestArtifactFiles({
|
|
5422
|
+
paths,
|
|
5423
|
+
readStorageText,
|
|
5424
|
+
resolveFileUrl: storage.resolveFileUrl,
|
|
5425
|
+
targetBundle: rowToBundle(toBundleRow(row))
|
|
5326
5426
|
});
|
|
5327
5427
|
},
|
|
5328
5428
|
/** A bundle, its patches, and how many patches start from it. */
|
|
@@ -6619,11 +6719,23 @@ var ADMIN_ROUTES = [
|
|
|
6619
6719
|
// ../../packages/server/dist/handlerReleaseCatalogRoutes.mjs
|
|
6620
6720
|
var CATALOG_CONTENT_TYPE = "application/vnd.hot-updater.release-catalog+json; version=1";
|
|
6621
6721
|
var ARTIFACT_CONTENT_TYPE = "application/vnd.hot-updater.artifact+json; version=1";
|
|
6722
|
+
var ARTIFACT_V2_CONTENT_TYPE = "application/vnd.hot-updater.artifact+json; version=2";
|
|
6622
6723
|
var ORIGIN_CACHE_TTL_MS = 5e3;
|
|
6623
6724
|
var ORIGIN_CACHE_MAX_ENTRIES = 128;
|
|
6624
6725
|
var NO_CATALOG_HEADER = "x-hot-updater-catalog";
|
|
6625
6726
|
var CATALOG_CACHE_CONTROL = "public, max-age=0, s-maxage=5";
|
|
6626
6727
|
var CATALOG_CDN_CACHE_CONTROL = "public, max-age=5, stale-while-revalidate=5, stale-if-error=0";
|
|
6728
|
+
var readArtifactFilePaths = /* @__PURE__ */ __name(async (request) => {
|
|
6729
|
+
let body2;
|
|
6730
|
+
try {
|
|
6731
|
+
body2 = await request.json();
|
|
6732
|
+
} catch {
|
|
6733
|
+
return null;
|
|
6734
|
+
}
|
|
6735
|
+
const paths = body2?.paths;
|
|
6736
|
+
if (!Array.isArray(paths) || paths.length === 0 || paths.length > ARTIFACT_FILES_REQUEST_LIMIT || !paths.every((path) => typeof path === "string" && path.length > 0) || new Set(paths).size !== paths.length) return null;
|
|
6737
|
+
return paths;
|
|
6738
|
+
}, "readArtifactFilePaths");
|
|
6627
6739
|
var privateNotFound = /* @__PURE__ */ __name(() => Response.json({ error: "Not found" }, {
|
|
6628
6740
|
status: 404,
|
|
6629
6741
|
headers: { "cache-control": "private, no-store" }
|
|
@@ -6733,6 +6845,30 @@ var createReleaseCatalogRouteHandlers = /* @__PURE__ */ __name(() => {
|
|
|
6733
6845
|
if (invalid) return invalid;
|
|
6734
6846
|
return catalogResponse(await loadCatalog(`fingerprint:${JSON.stringify(input)}`, () => core.getReleaseCatalog(input)), request);
|
|
6735
6847
|
}, "fingerprintReleaseCatalog"),
|
|
6848
|
+
artifactV2: /* @__PURE__ */ __name(async (params, _request, { core }) => {
|
|
6849
|
+
const info = await core.getArtifactInfo(requireRouteParam(params, "targetBundleId"), requireRouteParam(params, "currentBundleId"), 2);
|
|
6850
|
+
if (info === null) return privateNotFound();
|
|
6851
|
+
return new Response(JSON.stringify(info), {
|
|
6852
|
+
status: 200,
|
|
6853
|
+
headers: {
|
|
6854
|
+
"cache-control": "private, no-store",
|
|
6855
|
+
"content-type": ARTIFACT_V2_CONTENT_TYPE
|
|
6856
|
+
}
|
|
6857
|
+
});
|
|
6858
|
+
}, "artifactV2"),
|
|
6859
|
+
artifactFilesV2: /* @__PURE__ */ __name(async (params, request, { core }) => {
|
|
6860
|
+
const paths = await readArtifactFilePaths(request);
|
|
6861
|
+
if (paths === null) return badRequest(`paths must be 1 to ${ARTIFACT_FILES_REQUEST_LIMIT} unique strings`);
|
|
6862
|
+
const files = await core.getArtifactFiles(requireRouteParam(params, "targetBundleId"), paths);
|
|
6863
|
+
if (files === null) return privateNotFound();
|
|
6864
|
+
return new Response(JSON.stringify(files), {
|
|
6865
|
+
status: 200,
|
|
6866
|
+
headers: {
|
|
6867
|
+
"cache-control": "private, no-store",
|
|
6868
|
+
"content-type": ARTIFACT_V2_CONTENT_TYPE
|
|
6869
|
+
}
|
|
6870
|
+
});
|
|
6871
|
+
}, "artifactFilesV2"),
|
|
6736
6872
|
artifactV1: /* @__PURE__ */ __name(async (params, _request, { core }) => {
|
|
6737
6873
|
const info = await core.getArtifactInfo(requireRouteParam(params, "targetBundleId"), requireRouteParam(params, "currentBundleId"), 1);
|
|
6738
6874
|
if (info === null) return privateNotFound();
|
|
@@ -6886,6 +7022,8 @@ function createHotUpdaterHandlers({ api, clientPolicy, endpoints = [], plugins =
|
|
|
6886
7022
|
addClientRoute("GET", "/release-catalogs/app-version/:platform/:channelKey/:appVersion", "appVersionReleaseCatalog");
|
|
6887
7023
|
addClientRoute("GET", "/release-catalogs/fingerprint/:platform/:channelKey/:fingerprintHash", "fingerprintReleaseCatalog");
|
|
6888
7024
|
addClientRoute("GET", "/artifacts/v1/:targetBundleId/from/:currentBundleId", "artifactV1");
|
|
7025
|
+
addClientRoute("GET", "/artifacts/v2/:targetBundleId/from/:currentBundleId", "artifactV2");
|
|
7026
|
+
addClientRoute("POST", "/artifacts/v2/:targetBundleId/files", "artifactFilesV2");
|
|
6889
7027
|
const adminRouter = createRouter();
|
|
6890
7028
|
const addAdminRoute = mount(adminRouter, true);
|
|
6891
7029
|
addAdminRoute("GET", "/version", "adminVersion");
|