@hot-updater/vercel 1.0.5 → 1.0.7

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.
@@ -17558,7 +17558,7 @@ const postgres = (config) => {
17558
17558
  };
17559
17559
  //#endregion
17560
17560
  //#region ../../packages/server/dist/version.mjs
17561
- const HOT_UPDATER_SERVER_VERSION = "1.0.5";
17561
+ const HOT_UPDATER_SERVER_VERSION = "1.0.7";
17562
17562
  //#endregion
17563
17563
  //#region ../../packages/server/dist/handlerVersionRoutes.mjs
17564
17564
  const createVersionRouteHandlers = (plugins = []) => {
@@ -17847,6 +17847,85 @@ async function resolveManifestArtifacts({ currentBundle, resolveFileUrl, readSto
17847
17847
  ...archiveUrl ? { archiveUrl } : {}
17848
17848
  };
17849
17849
  }
17850
+ async function loadTargetPlan({ currentBundle, readStorageText, targetBundle }) {
17851
+ const manifestStorageUri = getManifestStorageUri(targetBundle);
17852
+ const manifestFileHash = getManifestFileHash(targetBundle);
17853
+ const assetBaseStorageUri = getAssetBaseStorageUri(targetBundle);
17854
+ if (!manifestStorageUri || !manifestFileHash || !assetBaseStorageUri) return null;
17855
+ const targetManifest = await fetchBundleManifest(manifestStorageUri, readStorageText);
17856
+ if (!targetManifest) return null;
17857
+ return {
17858
+ manifestFileHash,
17859
+ manifestStorageUri,
17860
+ plan: createManifestArtifactPlan({
17861
+ assetBaseStorageUri,
17862
+ currentBundle,
17863
+ targetBundle,
17864
+ targetManifest
17865
+ }),
17866
+ targetManifest
17867
+ };
17868
+ }
17869
+ async function resolveManifestArtifactsV2({ currentBundle, resolveFileUrl, readStorageText, targetBundle }) {
17870
+ const target = await loadTargetPlan({
17871
+ currentBundle,
17872
+ readStorageText,
17873
+ targetBundle
17874
+ });
17875
+ if (!target) return null;
17876
+ const manifestUrl = await resolveFileUrl(target.manifestStorageUri);
17877
+ if (!manifestUrl) return null;
17878
+ const patched = target.plan.assets.find((asset) => asset.patch !== null);
17879
+ let patch;
17880
+ if (patched?.patch && !(patched.file.byteSize !== null && patched.patch.byteSize !== null && patched.patch.byteSize >= patched.file.byteSize)) try {
17881
+ const patchUrl = await resolveFileUrl(patched.patch.storageUri);
17882
+ if (patchUrl) patch = {
17883
+ algorithm: "bsdiff",
17884
+ assetPath: patched.assetPath,
17885
+ baseBundleId: patched.patch.baseBundleId,
17886
+ baseFileHash: patched.patch.baseFileHash,
17887
+ patchFileHash: patched.patch.patchFileHash,
17888
+ patchUrl,
17889
+ ...patched.patch.byteSize === null ? {} : { byteSize: patched.patch.byteSize }
17890
+ };
17891
+ } catch {}
17892
+ let archiveUrl = null;
17893
+ const archive = target.targetManifest.archive;
17894
+ if (archive && isContentAddressedAssetFileHash(archive.downloadFileHash) && (asByteSize(archive.downloadByteSize) ?? 0) > 0 && (asByteSize(archive.tarByteSize) ?? 0) > 0) try {
17895
+ archiveUrl = await resolveFileUrl(getBundleArchiveStorageUri({
17896
+ manifestStorageUri: target.manifestStorageUri,
17897
+ bundleId: targetBundle.id
17898
+ }));
17899
+ } catch {}
17900
+ return {
17901
+ artifactProtocolVersion: 2,
17902
+ manifestFileHash: target.manifestFileHash,
17903
+ manifestUrl,
17904
+ ...archiveUrl ? { archiveUrl } : {},
17905
+ ...patch ? { patch } : {}
17906
+ };
17907
+ }
17908
+ async function resolveManifestArtifactFiles({ paths, resolveFileUrl, readStorageText, targetBundle }) {
17909
+ if (paths.length === 0 || paths.length > 100 || new Set(paths).size !== paths.length) return null;
17910
+ const target = await loadTargetPlan({
17911
+ currentBundle: null,
17912
+ readStorageText,
17913
+ targetBundle
17914
+ });
17915
+ if (!target) return null;
17916
+ const planned = new Map(target.plan.assets.map((asset) => [asset.assetPath, asset]));
17917
+ const requested = paths.map((path) => planned.get(path));
17918
+ if (requested.some((asset) => asset === void 0)) return null;
17919
+ const files = await Promise.all(requested.map(async (asset) => {
17920
+ const url = await resolveFileUrl(asset.file.storageUri);
17921
+ if (!url) return null;
17922
+ const file = { url };
17923
+ if (asset.file.compression) file.compression = asset.file.compression;
17924
+ return [asset.assetPath, file];
17925
+ }));
17926
+ if (files.some((file) => file === null)) return null;
17927
+ return { files: Object.fromEntries(files.filter((file) => file !== null)) };
17928
+ }
17850
17929
  //#endregion
17851
17930
  //#region ../../node_modules/.pnpm/verkit@0.4.0/node_modules/verkit/dist/comparison-DenM3wCn.js
17852
17931
  const LETTER_DASH_NUMBER = "[a-zA-Z0-9-]";
@@ -18308,20 +18387,33 @@ const createCoreReads = (db, storage) => {
18308
18387
  const row = await db.findOne("release_catalogs", { scope_key: scopeKey });
18309
18388
  return projectReleaseCatalogRow(row === null ? null : toCatalogRow(row), input, scopeKey);
18310
18389
  },
18311
- async getArtifactInfo(targetBundleId, currentBundleId, artifactProtocolVersion) {
18390
+ getArtifactInfo: (async (targetBundleId, currentBundleId, artifactProtocolVersion) => {
18312
18391
  const { readStorageText } = storage;
18313
- if (artifactProtocolVersion !== 1 || readStorageText === void 0) return null;
18392
+ if (artifactProtocolVersion !== 1 && artifactProtocolVersion !== 2 || readStorageText === void 0) return null;
18314
18393
  const withCurrent = currentBundleId !== NIL_UUID;
18315
18394
  const [[target, current], patch] = await Promise.all([db.findByKeys("bundles", withCurrent ? [{ id: targetBundleId }, { id: currentBundleId }] : [{ id: targetBundleId }]), withCurrent ? db.findOne("bundle_patches", {
18316
18395
  bundle_id: targetBundleId,
18317
18396
  base_bundle_id: currentBundleId
18318
18397
  }) : null]);
18319
18398
  if (target === null || target === void 0) return null;
18320
- return resolveManifestArtifacts({
18399
+ const input = {
18321
18400
  currentBundle: current ? rowToBundle(toBundleRow(current)) : null,
18322
18401
  readStorageText,
18323
18402
  resolveFileUrl: storage.resolveFileUrl,
18324
18403
  targetBundle: rowToBundle(toBundleRow(target), patch === null ? [] : [toPatchRow(patch)])
18404
+ };
18405
+ return artifactProtocolVersion === 2 ? resolveManifestArtifactsV2(input) : resolveManifestArtifacts(input);
18406
+ }),
18407
+ async getArtifactFiles(bundleId, paths) {
18408
+ const { readStorageText } = storage;
18409
+ if (readStorageText === void 0) return null;
18410
+ const row = await db.findOne("bundles", { id: bundleId });
18411
+ if (row === null) return null;
18412
+ return resolveManifestArtifactFiles({
18413
+ paths,
18414
+ readStorageText,
18415
+ resolveFileUrl: storage.resolveFileUrl,
18416
+ targetBundle: rowToBundle(toBundleRow(row))
18325
18417
  });
18326
18418
  },
18327
18419
  async getBundle(id) {
@@ -19598,11 +19690,23 @@ const ADMIN_ROUTES = [
19598
19690
  //#region ../../packages/server/dist/handlerReleaseCatalogRoutes.mjs
19599
19691
  const CATALOG_CONTENT_TYPE = "application/vnd.hot-updater.release-catalog+json; version=1";
19600
19692
  const ARTIFACT_CONTENT_TYPE = "application/vnd.hot-updater.artifact+json; version=1";
19693
+ const ARTIFACT_V2_CONTENT_TYPE = "application/vnd.hot-updater.artifact+json; version=2";
19601
19694
  const ORIGIN_CACHE_TTL_MS = 5e3;
19602
19695
  const ORIGIN_CACHE_MAX_ENTRIES = 128;
19603
19696
  const NO_CATALOG_HEADER = "x-hot-updater-catalog";
19604
19697
  const CATALOG_CACHE_CONTROL = "public, max-age=0, s-maxage=5";
19605
19698
  const CATALOG_CDN_CACHE_CONTROL = "public, max-age=5, stale-while-revalidate=5, stale-if-error=0";
19699
+ const readArtifactFilePaths = async (request) => {
19700
+ let body;
19701
+ try {
19702
+ body = await request.json();
19703
+ } catch {
19704
+ return null;
19705
+ }
19706
+ const paths = body?.paths;
19707
+ if (!Array.isArray(paths) || paths.length === 0 || paths.length > 100 || !paths.every((path) => typeof path === "string" && path.length > 0) || new Set(paths).size !== paths.length) return null;
19708
+ return paths;
19709
+ };
19606
19710
  const privateNotFound = () => Response.json({ error: "Not found" }, {
19607
19711
  status: 404,
19608
19712
  headers: { "cache-control": "private, no-store" }
@@ -19712,6 +19816,30 @@ const createReleaseCatalogRouteHandlers = () => {
19712
19816
  if (invalid) return invalid;
19713
19817
  return catalogResponse(await loadCatalog(`fingerprint:${JSON.stringify(input)}`, () => core.getReleaseCatalog(input)), request);
19714
19818
  },
19819
+ artifactV2: async (params, _request, { core }) => {
19820
+ const info = await core.getArtifactInfo(requireRouteParam(params, "targetBundleId"), requireRouteParam(params, "currentBundleId"), 2);
19821
+ if (info === null) return privateNotFound();
19822
+ return new Response(JSON.stringify(info), {
19823
+ status: 200,
19824
+ headers: {
19825
+ "cache-control": "private, no-store",
19826
+ "content-type": ARTIFACT_V2_CONTENT_TYPE
19827
+ }
19828
+ });
19829
+ },
19830
+ artifactFilesV2: async (params, request, { core }) => {
19831
+ const paths = await readArtifactFilePaths(request);
19832
+ if (paths === null) return badRequest(`paths must be 1 to 100 unique strings`);
19833
+ const files = await core.getArtifactFiles(requireRouteParam(params, "targetBundleId"), paths);
19834
+ if (files === null) return privateNotFound();
19835
+ return new Response(JSON.stringify(files), {
19836
+ status: 200,
19837
+ headers: {
19838
+ "cache-control": "private, no-store",
19839
+ "content-type": ARTIFACT_V2_CONTENT_TYPE
19840
+ }
19841
+ });
19842
+ },
19715
19843
  artifactV1: async (params, _request, { core }) => {
19716
19844
  const info = await core.getArtifactInfo(requireRouteParam(params, "targetBundleId"), requireRouteParam(params, "currentBundleId"), 1);
19717
19845
  if (info === null) return privateNotFound();
@@ -19862,6 +19990,8 @@ function createHotUpdaterHandlers({ api, clientPolicy, endpoints = [], plugins =
19862
19990
  addClientRoute("GET", "/release-catalogs/app-version/:platform/:channelKey/:appVersion", "appVersionReleaseCatalog");
19863
19991
  addClientRoute("GET", "/release-catalogs/fingerprint/:platform/:channelKey/:fingerprintHash", "fingerprintReleaseCatalog");
19864
19992
  addClientRoute("GET", "/artifacts/v1/:targetBundleId/from/:currentBundleId", "artifactV1");
19993
+ addClientRoute("GET", "/artifacts/v2/:targetBundleId/from/:currentBundleId", "artifactV2");
19994
+ addClientRoute("POST", "/artifacts/v2/:targetBundleId/files", "artifactFilesV2");
19865
19995
  const adminRouter = createRouter();
19866
19996
  const addAdminRoute = mount(adminRouter, true);
19867
19997
  addAdminRoute("GET", "/version", "adminVersion");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/vercel",
3
3
  "type": "module",
4
- "version": "1.0.5",
4
+ "version": "1.0.7",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.mjs",
@@ -41,11 +41,11 @@
41
41
  "@vercel/cli-auth": "0.3.10",
42
42
  "@vercel/cli-config": "0.3.1",
43
43
  "pg": "8.13.1",
44
- "@hot-updater/cli-tools": "1.0.5",
45
- "@hot-updater/postgres": "1.0.5",
46
- "@hot-updater/protocol": "1.0.5",
47
- "@hot-updater/plugin-core": "1.0.5",
48
- "@hot-updater/server": "1.0.5"
44
+ "@hot-updater/cli-tools": "1.0.7",
45
+ "@hot-updater/postgres": "1.0.7",
46
+ "@hot-updater/server": "1.0.7",
47
+ "@hot-updater/plugin-core": "1.0.7",
48
+ "@hot-updater/protocol": "1.0.7"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^20",
@@ -54,7 +54,7 @@
54
54
  "execa": "9.5.2",
55
55
  "kysely": "0.28.17",
56
56
  "vitest": "4.1.4",
57
- "@hot-updater/test-utils": "1.0.5"
57
+ "@hot-updater/test-utils": "1.0.7"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "tsdown",