@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/cloudflare",
3
3
  "type": "module",
4
- "version": "1.0.4",
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.4",
59
- "@hot-updater/plugin-core": "1.0.4",
60
- "@hot-updater/server": "1.0.4",
61
- "@hot-updater/cli-tools": "1.0.4"
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.4"
77
+ "@hot-updater/test-utils": "1.0.6"
78
78
  },
79
79
  "scripts": {
80
80
  "build": "tsdown && pnpm build:worker",
@@ -1 +1 @@
1
- This folder contains the built output assets for the worker "hot-updater" generated at 2026-10-14T05:14:27.689Z.
1
+ This folder contains the built output assets for the worker "hot-updater" generated at 2026-10-14T16:15:36.859Z.
@@ -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.4";
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 getArtifactInfo(targetBundleId, currentBundleId, artifactProtocolVersion) {
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
- return resolveManifestArtifacts({
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");