@hot-updater/plugin-core 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.
@@ -4,11 +4,29 @@ const require_createDatabasePlugin = require("./createDatabasePlugin.cjs");
4
4
  const require_filterCompatibleAppVersions = require("./filterCompatibleAppVersions.cjs");
5
5
  const require_queryBundles = require("./queryBundles.cjs");
6
6
  const require_paginateBundles = require("./paginateBundles.cjs");
7
- let _hot_updater_js = require("@hot-updater/js");
7
+ const require_resolveUpdateInfoFromBundles = require("./resolveUpdateInfoFromBundles.cjs");
8
8
  let es_toolkit = require("es-toolkit");
9
9
  let semver = require("semver");
10
10
  semver = require_runtime.__toESM(semver);
11
11
  //#region src/createBlobDatabasePlugin.ts
12
+ const STORAGE_OPERATION_CONCURRENCY = 8;
13
+ async function mapWithConcurrency(items, concurrency, mapper) {
14
+ const results = [];
15
+ let nextIndex = 0;
16
+ const workerCount = Math.min(concurrency, items.length);
17
+ await Promise.all(Array.from({ length: workerCount }, async () => {
18
+ while (true) {
19
+ const index = nextIndex;
20
+ nextIndex += 1;
21
+ if (index >= items.length) break;
22
+ results[index] = await mapper(items[index], index);
23
+ }
24
+ }));
25
+ return results;
26
+ }
27
+ async function forEachWithConcurrency(items, concurrency, mapper) {
28
+ await mapWithConcurrency(items, concurrency, mapper);
29
+ }
12
30
  function removeBundleInternalKeys(bundle) {
13
31
  const { _updateJsonKey, _oldUpdateJsonKey, ...pureBundle } = bundle;
14
32
  return pureBundle;
@@ -57,6 +75,12 @@ const MANAGEMENT_INDEX_PREFIX = "_index";
57
75
  const MANAGEMENT_INDEX_VERSION = 1;
58
76
  const DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE = 128;
59
77
  const ALL_SCOPE_CACHE_KEY = "*|*";
78
+ function summarizeManagementIndexArtifacts(artifacts) {
79
+ return {
80
+ pagesWritten: artifacts.pages.size,
81
+ scopesWritten: artifacts.scopes.length
82
+ };
83
+ }
60
84
  function resolveManagementIndexPageSize(config) {
61
85
  const pageSize = config.managementIndexPageSize ?? DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE;
62
86
  if (!Number.isInteger(pageSize) || pageSize < 1) throw new Error("managementIndexPageSize must be a positive integer.");
@@ -188,11 +212,18 @@ function buildManagementIndexArtifacts(allBundles, pageSize) {
188
212
  const createBlobDatabasePlugin = ({ name, factory }) => {
189
213
  return (config, hooks) => {
190
214
  const managementIndexPageSize = resolveManagementIndexPageSize(config);
191
- const { listObjects, loadObject, uploadObject, deleteObject, invalidatePaths, apiBasePath } = factory(config);
215
+ const { listObjects, loadObject, uploadObject, deleteObject, shouldSkipLoadObjectError, invalidatePaths, apiBasePath } = factory(config);
192
216
  const bundlesMap = /* @__PURE__ */ new Map();
193
217
  const pendingBundlesMap = /* @__PURE__ */ new Map();
194
218
  const managementRootCache = /* @__PURE__ */ new Map();
195
- const PLATFORMS = ["ios", "android"];
219
+ const loadOptionalObject = async (key) => {
220
+ try {
221
+ return await loadObject(key);
222
+ } catch (error) {
223
+ if (shouldSkipLoadObjectError?.(error, key)) return null;
224
+ throw error;
225
+ }
226
+ };
196
227
  const getAllManagementArtifact = (artifacts) => {
197
228
  const allArtifact = artifacts.scopes.find((scope) => scope.cacheKey === ALL_SCOPE_CACHE_KEY);
198
229
  if (!allArtifact) throw new Error("all-bundles management index artifact not found");
@@ -209,7 +240,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
209
240
  };
210
241
  const loadStoredManagementRoot = async (scope) => {
211
242
  const cacheKey = getManagementScopeCacheKey(scope);
212
- const storedRoot = await loadObject(getManagementRootKey(scope));
243
+ const storedRoot = await loadOptionalObject(getManagementRootKey(scope));
213
244
  if (storedRoot) {
214
245
  managementRootCache.set(cacheKey, storedRoot);
215
246
  return storedRoot;
@@ -219,7 +250,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
219
250
  };
220
251
  const loadManagementPage = async (descriptor, pageCache) => {
221
252
  if (pageCache?.has(descriptor.key)) return pageCache.get(descriptor.key) ?? null;
222
- const page = await loadObject(descriptor.key);
253
+ const page = await loadOptionalObject(descriptor.key);
223
254
  pageCache?.set(descriptor.key, page);
224
255
  return page;
225
256
  };
@@ -242,13 +273,13 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
242
273
  return allBundles;
243
274
  };
244
275
  const persistManagementIndexArtifacts = async (nextArtifacts, previousArtifacts) => {
245
- for (const [key, page] of nextArtifacts.pages.entries()) await uploadObject(key, page);
246
- for (const scope of nextArtifacts.scopes) await uploadObject(scope.rootKey, scope.root);
276
+ await forEachWithConcurrency(Array.from(nextArtifacts.pages.entries()), STORAGE_OPERATION_CONCURRENCY, ([key, page]) => uploadObject(key, page));
277
+ await forEachWithConcurrency(nextArtifacts.scopes, STORAGE_OPERATION_CONCURRENCY, (scope) => uploadObject(scope.rootKey, scope.root));
247
278
  if (!previousArtifacts) return;
248
279
  const nextPageKeys = new Set(nextArtifacts.pages.keys());
249
280
  const nextRootKeys = new Set(nextArtifacts.scopes.map((scope) => scope.rootKey));
250
- for (const [key] of previousArtifacts.pages.entries()) if (!nextPageKeys.has(key)) await deleteObject(key).catch(() => {});
251
- for (const scope of previousArtifacts.scopes) if (!nextRootKeys.has(scope.rootKey)) await deleteObject(scope.rootKey).catch(() => {});
281
+ await forEachWithConcurrency(Array.from(previousArtifacts.pages.keys()).filter((key) => !nextPageKeys.has(key)), STORAGE_OPERATION_CONCURRENCY, (key) => deleteObject(key).catch(() => {}));
282
+ await forEachWithConcurrency(previousArtifacts.scopes.filter((scope) => !nextRootKeys.has(scope.rootKey)), STORAGE_OPERATION_CONCURRENCY, (scope) => deleteObject(scope.rootKey).catch(() => {}));
252
283
  };
253
284
  const ensureAllManagementRoot = async () => {
254
285
  const storedAllRoot = await loadStoredManagementRoot({});
@@ -281,6 +312,26 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
281
312
  const loadCurrentBundlesForIndexRebuild = async () => {
282
313
  return loadAllBundlesForManagementFallback();
283
314
  };
315
+ const loadBundlesFromCanonicalManifests = async () => {
316
+ return sortManagedBundles((await reloadBundles()).map((bundle) => removeBundleInternalKeys(bundle)));
317
+ };
318
+ const loadStoredBundlesForIndexRebuild = loadBundlesFromCanonicalManifests;
319
+ const loadCanonicalBundlesForIndexRepair = loadBundlesFromCanonicalManifests;
320
+ const compareBundleIndex = ({ canonicalBundles, indexedBundles, rootMissing }) => {
321
+ const canonicalIds = new Set(canonicalBundles.map((bundle) => bundle.id));
322
+ const indexedIds = new Set(indexedBundles?.map((bundle) => bundle.id) ?? []);
323
+ const missingBundleIds = Array.from(canonicalIds).filter((id) => !indexedIds.has(id)).sort((left, right) => right.localeCompare(left));
324
+ const extraBundleIds = Array.from(indexedIds).filter((id) => !canonicalIds.has(id)).sort((left, right) => right.localeCompare(left));
325
+ return {
326
+ status: missingBundleIds.length === 0 && extraBundleIds.length === 0 && !rootMissing ? "ok" : rootMissing ? "missing" : "stale",
327
+ canonicalBundles: canonicalBundles.length,
328
+ indexedBundles: indexedBundles?.length ?? 0,
329
+ missingBundles: missingBundleIds.length,
330
+ extraBundles: extraBundleIds.length,
331
+ missingBundleIds: missingBundleIds.slice(0, 20),
332
+ extraBundleIds: extraBundleIds.slice(0, 20)
333
+ };
334
+ };
284
335
  const findPageIndexContainingId = (pages, id) => {
285
336
  return pages.findIndex((page) => id.localeCompare(page.firstId) <= 0 && id.localeCompare(page.lastId) >= 0);
286
337
  };
@@ -456,16 +507,15 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
456
507
  };
457
508
  async function reloadBundles() {
458
509
  bundlesMap.clear();
459
- const filePromises = (await listObjects("")).filter((key) => /^[^/]+\/(?:ios|android)\/[^/]+\/update\.json$/.test(key)).map(async (key) => {
460
- return (await loadObject(key) ?? []).map((bundle) => ({
510
+ const allBundles = (await mapWithConcurrency((await listObjects("")).filter((key) => /^[^/]+\/(?:ios|android)\/[^/]+\/update\.json$/.test(key)), STORAGE_OPERATION_CONCURRENCY, async (key) => {
511
+ return (await loadOptionalObject(key) ?? []).map((bundle) => ({
461
512
  ...bundle,
462
513
  _updateJsonKey: key
463
514
  }));
464
- });
465
- const allBundles = (await Promise.all(filePromises)).flat();
515
+ })).flat();
466
516
  for (const bundle of allBundles) bundlesMap.set(bundle.id, bundle);
467
517
  for (const [id, bundle] of pendingBundlesMap.entries()) bundlesMap.set(id, bundle);
468
- return (0, es_toolkit.orderBy)(allBundles, [(v) => v.id], ["desc"]);
518
+ return (0, es_toolkit.orderBy)(Array.from(bundlesMap.values()), [(v) => v.id], ["desc"]);
469
519
  }
470
520
  /**
471
521
  * Updates target-app-versions.json for each channel on the given platform.
@@ -491,35 +541,44 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
491
541
  const updateKeys = keysByChannel[channel];
492
542
  const targetKey = `${channel}/${platform}/target-app-versions.json`;
493
543
  const currentVersions = updateKeys.map((key) => key.split("/")[2]);
494
- const oldTargetVersions = await loadObject(targetKey) ?? [];
544
+ const oldTargetVersions = await loadOptionalObject(targetKey) ?? [];
495
545
  const newTargetVersions = oldTargetVersions.filter((v) => currentVersions.includes(v));
496
546
  for (const v of currentVersions) if (!newTargetVersions.includes(v)) newTargetVersions.push(v);
497
547
  if (JSON.stringify(oldTargetVersions) !== JSON.stringify(newTargetVersions)) await uploadObject(targetKey, newTargetVersions);
498
548
  }
499
549
  }
500
- const getAppVersionUpdateInfo = async ({ appVersion, bundleId, channel = "production", cohort, minBundleId, platform }) => {
501
- const matchingVersions = require_filterCompatibleAppVersions.filterCompatibleAppVersions(await loadObject(`${channel}/${platform}/target-app-versions.json`) ?? [], appVersion);
502
- return (0, _hot_updater_js.getUpdateInfo)((await Promise.allSettled(matchingVersions.map(async (targetAppVersion) => {
503
- return await loadObject(`${channel}/${platform}/${normalizeTargetAppVersion(targetAppVersion) ?? targetAppVersion}/update.json`) ?? [];
504
- }))).filter((entry) => entry.status === "fulfilled").flatMap((entry) => entry.value), {
505
- _updateStrategy: "appVersion",
506
- appVersion,
507
- bundleId,
508
- channel,
509
- cohort,
510
- minBundleId,
511
- platform
550
+ const getAppVersionUpdateInfo = async ({ appVersion, bundleId, channel = "production", cohort, minBundleId, platform }, context) => {
551
+ const bundles = (await mapWithConcurrency(require_filterCompatibleAppVersions.filterCompatibleAppVersions(await loadOptionalObject(`${channel}/${platform}/target-app-versions.json`) ?? [], appVersion), STORAGE_OPERATION_CONCURRENCY, async (targetAppVersion) => {
552
+ return await loadOptionalObject(`${channel}/${platform}/${normalizeTargetAppVersion(targetAppVersion) ?? targetAppVersion}/update.json`) ?? [];
553
+ })).flat();
554
+ return require_resolveUpdateInfoFromBundles.resolveUpdateInfoFromBundles({
555
+ args: {
556
+ _updateStrategy: "appVersion",
557
+ appVersion,
558
+ bundleId,
559
+ channel,
560
+ cohort,
561
+ minBundleId,
562
+ platform
563
+ },
564
+ bundles,
565
+ context
512
566
  });
513
567
  };
514
- const getFingerprintUpdateInfo = async ({ bundleId, channel = "production", cohort, fingerprintHash, minBundleId, platform }) => {
515
- return (0, _hot_updater_js.getUpdateInfo)(await loadObject(`${channel}/${platform}/${fingerprintHash}/update.json`) ?? [], {
516
- _updateStrategy: "fingerprint",
517
- bundleId,
518
- channel,
519
- cohort,
520
- fingerprintHash,
521
- minBundleId,
522
- platform
568
+ const getFingerprintUpdateInfo = async ({ bundleId, channel = "production", cohort, fingerprintHash, minBundleId, platform }, context) => {
569
+ const bundles = await loadOptionalObject(`${channel}/${platform}/${fingerprintHash}/update.json`) ?? [];
570
+ return require_resolveUpdateInfoFromBundles.resolveUpdateInfoFromBundles({
571
+ args: {
572
+ _updateStrategy: "fingerprint",
573
+ bundleId,
574
+ channel,
575
+ cohort,
576
+ fingerprintHash,
577
+ minBundleId,
578
+ platform
579
+ },
580
+ bundles,
581
+ context
523
582
  });
524
583
  };
525
584
  const addAppVersionInvalidationPaths = (pathsToInvalidate, { platform, channel, targetAppVersion }) => {
@@ -541,7 +600,35 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
541
600
  targetAppVersion
542
601
  });
543
602
  };
544
- return require_createDatabasePlugin.createDatabasePlugin({
603
+ const bundleIndexDiagnostics = {
604
+ async check() {
605
+ const canonicalBundles = await loadCanonicalBundlesForIndexRepair();
606
+ const allRoot = await loadStoredManagementRoot({});
607
+ return compareBundleIndex({
608
+ canonicalBundles,
609
+ indexedBundles: allRoot ? await loadAllBundlesFromRoot(allRoot) : null,
610
+ rootMissing: !allRoot
611
+ });
612
+ },
613
+ async repair() {
614
+ const canonicalBundles = await loadCanonicalBundlesForIndexRepair();
615
+ const previousRoot = await loadStoredManagementRoot({});
616
+ const previousBundles = previousRoot ? await loadAllBundlesFromRoot(previousRoot) : null;
617
+ const previousArtifacts = previousRoot && previousBundles ? buildManagementIndexArtifacts(previousBundles, previousRoot.pageSize) : void 0;
618
+ const nextArtifacts = buildManagementIndexArtifacts(canonicalBundles, managementIndexPageSize);
619
+ const indexedObjectKeys = await listObjects(`${MANAGEMENT_INDEX_PREFIX}/`);
620
+ const nextObjectKeys = new Set([...nextArtifacts.pages.keys(), ...nextArtifacts.scopes.map((scope) => scope.rootKey)]);
621
+ await persistManagementIndexArtifacts(nextArtifacts, previousArtifacts);
622
+ await forEachWithConcurrency(indexedObjectKeys.filter((key) => !nextObjectKeys.has(key)), STORAGE_OPERATION_CONCURRENCY, (key) => deleteObject(key).catch(() => {}));
623
+ replaceManagementRootCache(nextArtifacts);
624
+ return {
625
+ scannedBundles: canonicalBundles.length,
626
+ indexedBundles: canonicalBundles.length,
627
+ ...summarizeManagementIndexArtifacts(nextArtifacts)
628
+ };
629
+ }
630
+ };
631
+ const createPlugin = require_createDatabasePlugin.createDatabasePlugin({
545
632
  name,
546
633
  factory: () => ({
547
634
  supportsCursorPagination: true,
@@ -565,9 +652,9 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
565
652
  if (!matchedBundle) return null;
566
653
  return removeBundleInternalKeys(matchedBundle);
567
654
  },
568
- async getUpdateInfo(args) {
569
- if (args._updateStrategy === "appVersion") return getAppVersionUpdateInfo(args);
570
- return getFingerprintUpdateInfo(args);
655
+ async getUpdateInfo(args, context) {
656
+ if (args._updateStrategy === "appVersion") return getAppVersionUpdateInfo(args, context);
657
+ return getFingerprintUpdateInfo(args, context);
571
658
  },
572
659
  async getBundles(options) {
573
660
  const { where, limit, offset, orderBy, cursor } = options;
@@ -600,11 +687,8 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
600
687
  const changedBundlesByKey = {};
601
688
  const removalsByKey = {};
602
689
  const pathsToInvalidate = /* @__PURE__ */ new Set();
603
- let isTargetAppVersionChanged = false;
604
- let isChannelChanged = false;
690
+ const targetVersionPlatforms = /* @__PURE__ */ new Set();
605
691
  for (const { operation, data } of changedSets) {
606
- if (data.targetAppVersion !== void 0) isTargetAppVersionChanged = true;
607
- if (operation === "update" && data.channel !== void 0) isChannelChanged = true;
608
692
  if (operation === "insert") {
609
693
  const target = resolveStorageTarget(data);
610
694
  const key = `${data.channel}/${data.platform}/${target}/update.json`;
@@ -616,6 +700,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
616
700
  pendingBundlesMap.set(data.id, bundleWithKey);
617
701
  changedBundlesByKey[key] = changedBundlesByKey[key] || [];
618
702
  changedBundlesByKey[key].push(removeBundleInternalKeys(bundleWithKey));
703
+ if (data.targetAppVersion !== void 0) targetVersionPlatforms.add(data.platform);
619
704
  addLookupInvalidationPaths(pathsToInvalidate, data);
620
705
  continue;
621
706
  }
@@ -628,6 +713,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
628
713
  const key = bundle._updateJsonKey;
629
714
  removalsByKey[key] = removalsByKey[key] || [];
630
715
  removalsByKey[key].push(bundle.id);
716
+ if (bundle.targetAppVersion !== void 0) targetVersionPlatforms.add(bundle.platform);
631
717
  addLookupInvalidationPaths(pathsToInvalidate, bundle);
632
718
  continue;
633
719
  }
@@ -659,6 +745,10 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
659
745
  channel: nextChannel
660
746
  });
661
747
  }
748
+ if (bundle.targetAppVersion !== void 0 || updatedBundle.targetAppVersion !== void 0) {
749
+ targetVersionPlatforms.add(bundle.platform);
750
+ targetVersionPlatforms.add(updatedBundle.platform);
751
+ }
662
752
  addLookupInvalidationPaths(pathsToInvalidate, updatedBundle);
663
753
  if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle.targetAppVersion) addLookupInvalidationPaths(pathsToInvalidate, bundle);
664
754
  continue;
@@ -672,14 +762,14 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
672
762
  if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle.targetAppVersion) addLookupInvalidationPaths(pathsToInvalidate, bundle);
673
763
  }
674
764
  }
675
- for (const oldKey of Object.keys(removalsByKey)) await (async () => {
676
- const updatedBundles = (await loadObject(oldKey) ?? []).filter((b) => !removalsByKey[oldKey].includes(b.id));
765
+ await forEachWithConcurrency(Object.keys(removalsByKey), STORAGE_OPERATION_CONCURRENCY, async (oldKey) => {
766
+ const updatedBundles = (await loadOptionalObject(oldKey) ?? []).filter((b) => !removalsByKey[oldKey].includes(b.id));
677
767
  updatedBundles.sort((a, b) => b.id.localeCompare(a.id));
678
768
  if (updatedBundles.length === 0) await deleteObject(oldKey);
679
769
  else await uploadObject(oldKey, updatedBundles);
680
- })();
681
- for (const key of Object.keys(changedBundlesByKey)) await (async () => {
682
- const currentBundles = await loadObject(key) ?? [];
770
+ });
771
+ await forEachWithConcurrency(Object.keys(changedBundlesByKey), STORAGE_OPERATION_CONCURRENCY, async (key) => {
772
+ const currentBundles = await loadOptionalObject(key) ?? [];
683
773
  const pureBundles = changedBundlesByKey[key].map((bundle) => bundle);
684
774
  for (const changedBundle of pureBundles) {
685
775
  const index = currentBundles.findIndex((b) => b.id === changedBundle.id);
@@ -688,10 +778,11 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
688
778
  }
689
779
  currentBundles.sort((a, b) => b.id.localeCompare(a.id));
690
780
  await uploadObject(key, currentBundles);
691
- })();
692
- if (isTargetAppVersionChanged || isChannelChanged) for (const platform of PLATFORMS) await updateTargetVersionsForPlatform(platform);
693
- const currentIndexBundles = await loadCurrentBundlesForIndexRebuild();
694
- const nextIndexMap = new Map(currentIndexBundles.map((bundle) => [bundle.id, bundle]));
781
+ });
782
+ if (targetVersionPlatforms.size > 0) await Promise.all(Array.from(targetVersionPlatforms).map((platform) => updateTargetVersionsForPlatform(platform)));
783
+ const previousIndexBundles = await loadCurrentBundlesForIndexRebuild();
784
+ const storedIndexBundles = await loadStoredBundlesForIndexRebuild();
785
+ const nextIndexMap = new Map(storedIndexBundles.map((bundle) => [bundle.id, bundle]));
695
786
  for (const { operation, data } of changedSets) {
696
787
  if (operation === "delete") {
697
788
  nextIndexMap.delete(data.id);
@@ -700,7 +791,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
700
791
  nextIndexMap.set(data.id, data);
701
792
  }
702
793
  const nextIndexBundles = sortManagedBundles(Array.from(nextIndexMap.values()));
703
- const previousArtifacts = buildManagementIndexArtifacts(currentIndexBundles, managementIndexPageSize);
794
+ const previousArtifacts = buildManagementIndexArtifacts(previousIndexBundles, managementIndexPageSize);
704
795
  const nextArtifacts = buildManagementIndexArtifacts(nextIndexBundles, managementIndexPageSize);
705
796
  await persistManagementIndexArtifacts(nextArtifacts, previousArtifacts);
706
797
  replaceManagementRootCache(nextArtifacts);
@@ -711,6 +802,15 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
711
802
  }
712
803
  })
713
804
  })({}, hooks);
805
+ return () => {
806
+ const plugin = createPlugin();
807
+ Object.defineProperty(plugin, "diagnostics", {
808
+ configurable: true,
809
+ enumerable: true,
810
+ value: { bundleIndex: bundleIndexDiagnostics }
811
+ });
812
+ return plugin;
813
+ };
714
814
  };
715
815
  };
716
816
  //#endregion
@@ -9,6 +9,7 @@ interface BlobOperations {
9
9
  loadObject: <T>(key: string) => Promise<T | null>;
10
10
  uploadObject: <T>(key: string, data: T) => Promise<void>;
11
11
  deleteObject: (key: string) => Promise<void>;
12
+ shouldSkipLoadObjectError?: (error: unknown, key: string) => boolean;
12
13
  invalidatePaths: (paths: string[]) => Promise<void>;
13
14
  apiBasePath: string;
14
15
  }
@@ -9,6 +9,7 @@ interface BlobOperations {
9
9
  loadObject: <T>(key: string) => Promise<T | null>;
10
10
  uploadObject: <T>(key: string, data: T) => Promise<void>;
11
11
  deleteObject: (key: string) => Promise<void>;
12
+ shouldSkipLoadObjectError?: (error: unknown, key: string) => boolean;
12
13
  invalidatePaths: (paths: string[]) => Promise<void>;
13
14
  apiBasePath: string;
14
15
  }
@@ -3,10 +3,28 @@ import { createDatabasePlugin } from "./createDatabasePlugin.mjs";
3
3
  import { filterCompatibleAppVersions } from "./filterCompatibleAppVersions.mjs";
4
4
  import { bundleMatchesQueryWhere, sortBundles } from "./queryBundles.mjs";
5
5
  import { paginateBundles } from "./paginateBundles.mjs";
6
- import { getUpdateInfo } from "@hot-updater/js";
6
+ import { resolveUpdateInfoFromBundles } from "./resolveUpdateInfoFromBundles.mjs";
7
7
  import { orderBy } from "es-toolkit";
8
8
  import semver from "semver";
9
9
  //#region src/createBlobDatabasePlugin.ts
10
+ const STORAGE_OPERATION_CONCURRENCY = 8;
11
+ async function mapWithConcurrency(items, concurrency, mapper) {
12
+ const results = [];
13
+ let nextIndex = 0;
14
+ const workerCount = Math.min(concurrency, items.length);
15
+ await Promise.all(Array.from({ length: workerCount }, async () => {
16
+ while (true) {
17
+ const index = nextIndex;
18
+ nextIndex += 1;
19
+ if (index >= items.length) break;
20
+ results[index] = await mapper(items[index], index);
21
+ }
22
+ }));
23
+ return results;
24
+ }
25
+ async function forEachWithConcurrency(items, concurrency, mapper) {
26
+ await mapWithConcurrency(items, concurrency, mapper);
27
+ }
10
28
  function removeBundleInternalKeys(bundle) {
11
29
  const { _updateJsonKey, _oldUpdateJsonKey, ...pureBundle } = bundle;
12
30
  return pureBundle;
@@ -55,6 +73,12 @@ const MANAGEMENT_INDEX_PREFIX = "_index";
55
73
  const MANAGEMENT_INDEX_VERSION = 1;
56
74
  const DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE = 128;
57
75
  const ALL_SCOPE_CACHE_KEY = "*|*";
76
+ function summarizeManagementIndexArtifacts(artifacts) {
77
+ return {
78
+ pagesWritten: artifacts.pages.size,
79
+ scopesWritten: artifacts.scopes.length
80
+ };
81
+ }
58
82
  function resolveManagementIndexPageSize(config) {
59
83
  const pageSize = config.managementIndexPageSize ?? DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE;
60
84
  if (!Number.isInteger(pageSize) || pageSize < 1) throw new Error("managementIndexPageSize must be a positive integer.");
@@ -186,11 +210,18 @@ function buildManagementIndexArtifacts(allBundles, pageSize) {
186
210
  const createBlobDatabasePlugin = ({ name, factory }) => {
187
211
  return (config, hooks) => {
188
212
  const managementIndexPageSize = resolveManagementIndexPageSize(config);
189
- const { listObjects, loadObject, uploadObject, deleteObject, invalidatePaths, apiBasePath } = factory(config);
213
+ const { listObjects, loadObject, uploadObject, deleteObject, shouldSkipLoadObjectError, invalidatePaths, apiBasePath } = factory(config);
190
214
  const bundlesMap = /* @__PURE__ */ new Map();
191
215
  const pendingBundlesMap = /* @__PURE__ */ new Map();
192
216
  const managementRootCache = /* @__PURE__ */ new Map();
193
- const PLATFORMS = ["ios", "android"];
217
+ const loadOptionalObject = async (key) => {
218
+ try {
219
+ return await loadObject(key);
220
+ } catch (error) {
221
+ if (shouldSkipLoadObjectError?.(error, key)) return null;
222
+ throw error;
223
+ }
224
+ };
194
225
  const getAllManagementArtifact = (artifacts) => {
195
226
  const allArtifact = artifacts.scopes.find((scope) => scope.cacheKey === ALL_SCOPE_CACHE_KEY);
196
227
  if (!allArtifact) throw new Error("all-bundles management index artifact not found");
@@ -207,7 +238,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
207
238
  };
208
239
  const loadStoredManagementRoot = async (scope) => {
209
240
  const cacheKey = getManagementScopeCacheKey(scope);
210
- const storedRoot = await loadObject(getManagementRootKey(scope));
241
+ const storedRoot = await loadOptionalObject(getManagementRootKey(scope));
211
242
  if (storedRoot) {
212
243
  managementRootCache.set(cacheKey, storedRoot);
213
244
  return storedRoot;
@@ -217,7 +248,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
217
248
  };
218
249
  const loadManagementPage = async (descriptor, pageCache) => {
219
250
  if (pageCache?.has(descriptor.key)) return pageCache.get(descriptor.key) ?? null;
220
- const page = await loadObject(descriptor.key);
251
+ const page = await loadOptionalObject(descriptor.key);
221
252
  pageCache?.set(descriptor.key, page);
222
253
  return page;
223
254
  };
@@ -240,13 +271,13 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
240
271
  return allBundles;
241
272
  };
242
273
  const persistManagementIndexArtifacts = async (nextArtifacts, previousArtifacts) => {
243
- for (const [key, page] of nextArtifacts.pages.entries()) await uploadObject(key, page);
244
- for (const scope of nextArtifacts.scopes) await uploadObject(scope.rootKey, scope.root);
274
+ await forEachWithConcurrency(Array.from(nextArtifacts.pages.entries()), STORAGE_OPERATION_CONCURRENCY, ([key, page]) => uploadObject(key, page));
275
+ await forEachWithConcurrency(nextArtifacts.scopes, STORAGE_OPERATION_CONCURRENCY, (scope) => uploadObject(scope.rootKey, scope.root));
245
276
  if (!previousArtifacts) return;
246
277
  const nextPageKeys = new Set(nextArtifacts.pages.keys());
247
278
  const nextRootKeys = new Set(nextArtifacts.scopes.map((scope) => scope.rootKey));
248
- for (const [key] of previousArtifacts.pages.entries()) if (!nextPageKeys.has(key)) await deleteObject(key).catch(() => {});
249
- for (const scope of previousArtifacts.scopes) if (!nextRootKeys.has(scope.rootKey)) await deleteObject(scope.rootKey).catch(() => {});
279
+ await forEachWithConcurrency(Array.from(previousArtifacts.pages.keys()).filter((key) => !nextPageKeys.has(key)), STORAGE_OPERATION_CONCURRENCY, (key) => deleteObject(key).catch(() => {}));
280
+ await forEachWithConcurrency(previousArtifacts.scopes.filter((scope) => !nextRootKeys.has(scope.rootKey)), STORAGE_OPERATION_CONCURRENCY, (scope) => deleteObject(scope.rootKey).catch(() => {}));
250
281
  };
251
282
  const ensureAllManagementRoot = async () => {
252
283
  const storedAllRoot = await loadStoredManagementRoot({});
@@ -279,6 +310,26 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
279
310
  const loadCurrentBundlesForIndexRebuild = async () => {
280
311
  return loadAllBundlesForManagementFallback();
281
312
  };
313
+ const loadBundlesFromCanonicalManifests = async () => {
314
+ return sortManagedBundles((await reloadBundles()).map((bundle) => removeBundleInternalKeys(bundle)));
315
+ };
316
+ const loadStoredBundlesForIndexRebuild = loadBundlesFromCanonicalManifests;
317
+ const loadCanonicalBundlesForIndexRepair = loadBundlesFromCanonicalManifests;
318
+ const compareBundleIndex = ({ canonicalBundles, indexedBundles, rootMissing }) => {
319
+ const canonicalIds = new Set(canonicalBundles.map((bundle) => bundle.id));
320
+ const indexedIds = new Set(indexedBundles?.map((bundle) => bundle.id) ?? []);
321
+ const missingBundleIds = Array.from(canonicalIds).filter((id) => !indexedIds.has(id)).sort((left, right) => right.localeCompare(left));
322
+ const extraBundleIds = Array.from(indexedIds).filter((id) => !canonicalIds.has(id)).sort((left, right) => right.localeCompare(left));
323
+ return {
324
+ status: missingBundleIds.length === 0 && extraBundleIds.length === 0 && !rootMissing ? "ok" : rootMissing ? "missing" : "stale",
325
+ canonicalBundles: canonicalBundles.length,
326
+ indexedBundles: indexedBundles?.length ?? 0,
327
+ missingBundles: missingBundleIds.length,
328
+ extraBundles: extraBundleIds.length,
329
+ missingBundleIds: missingBundleIds.slice(0, 20),
330
+ extraBundleIds: extraBundleIds.slice(0, 20)
331
+ };
332
+ };
282
333
  const findPageIndexContainingId = (pages, id) => {
283
334
  return pages.findIndex((page) => id.localeCompare(page.firstId) <= 0 && id.localeCompare(page.lastId) >= 0);
284
335
  };
@@ -454,16 +505,15 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
454
505
  };
455
506
  async function reloadBundles() {
456
507
  bundlesMap.clear();
457
- const filePromises = (await listObjects("")).filter((key) => /^[^/]+\/(?:ios|android)\/[^/]+\/update\.json$/.test(key)).map(async (key) => {
458
- return (await loadObject(key) ?? []).map((bundle) => ({
508
+ const allBundles = (await mapWithConcurrency((await listObjects("")).filter((key) => /^[^/]+\/(?:ios|android)\/[^/]+\/update\.json$/.test(key)), STORAGE_OPERATION_CONCURRENCY, async (key) => {
509
+ return (await loadOptionalObject(key) ?? []).map((bundle) => ({
459
510
  ...bundle,
460
511
  _updateJsonKey: key
461
512
  }));
462
- });
463
- const allBundles = (await Promise.all(filePromises)).flat();
513
+ })).flat();
464
514
  for (const bundle of allBundles) bundlesMap.set(bundle.id, bundle);
465
515
  for (const [id, bundle] of pendingBundlesMap.entries()) bundlesMap.set(id, bundle);
466
- return orderBy(allBundles, [(v) => v.id], ["desc"]);
516
+ return orderBy(Array.from(bundlesMap.values()), [(v) => v.id], ["desc"]);
467
517
  }
468
518
  /**
469
519
  * Updates target-app-versions.json for each channel on the given platform.
@@ -489,35 +539,44 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
489
539
  const updateKeys = keysByChannel[channel];
490
540
  const targetKey = `${channel}/${platform}/target-app-versions.json`;
491
541
  const currentVersions = updateKeys.map((key) => key.split("/")[2]);
492
- const oldTargetVersions = await loadObject(targetKey) ?? [];
542
+ const oldTargetVersions = await loadOptionalObject(targetKey) ?? [];
493
543
  const newTargetVersions = oldTargetVersions.filter((v) => currentVersions.includes(v));
494
544
  for (const v of currentVersions) if (!newTargetVersions.includes(v)) newTargetVersions.push(v);
495
545
  if (JSON.stringify(oldTargetVersions) !== JSON.stringify(newTargetVersions)) await uploadObject(targetKey, newTargetVersions);
496
546
  }
497
547
  }
498
- const getAppVersionUpdateInfo = async ({ appVersion, bundleId, channel = "production", cohort, minBundleId, platform }) => {
499
- const matchingVersions = filterCompatibleAppVersions(await loadObject(`${channel}/${platform}/target-app-versions.json`) ?? [], appVersion);
500
- return getUpdateInfo((await Promise.allSettled(matchingVersions.map(async (targetAppVersion) => {
501
- return await loadObject(`${channel}/${platform}/${normalizeTargetAppVersion(targetAppVersion) ?? targetAppVersion}/update.json`) ?? [];
502
- }))).filter((entry) => entry.status === "fulfilled").flatMap((entry) => entry.value), {
503
- _updateStrategy: "appVersion",
504
- appVersion,
505
- bundleId,
506
- channel,
507
- cohort,
508
- minBundleId,
509
- platform
548
+ const getAppVersionUpdateInfo = async ({ appVersion, bundleId, channel = "production", cohort, minBundleId, platform }, context) => {
549
+ const bundles = (await mapWithConcurrency(filterCompatibleAppVersions(await loadOptionalObject(`${channel}/${platform}/target-app-versions.json`) ?? [], appVersion), STORAGE_OPERATION_CONCURRENCY, async (targetAppVersion) => {
550
+ return await loadOptionalObject(`${channel}/${platform}/${normalizeTargetAppVersion(targetAppVersion) ?? targetAppVersion}/update.json`) ?? [];
551
+ })).flat();
552
+ return resolveUpdateInfoFromBundles({
553
+ args: {
554
+ _updateStrategy: "appVersion",
555
+ appVersion,
556
+ bundleId,
557
+ channel,
558
+ cohort,
559
+ minBundleId,
560
+ platform
561
+ },
562
+ bundles,
563
+ context
510
564
  });
511
565
  };
512
- const getFingerprintUpdateInfo = async ({ bundleId, channel = "production", cohort, fingerprintHash, minBundleId, platform }) => {
513
- return getUpdateInfo(await loadObject(`${channel}/${platform}/${fingerprintHash}/update.json`) ?? [], {
514
- _updateStrategy: "fingerprint",
515
- bundleId,
516
- channel,
517
- cohort,
518
- fingerprintHash,
519
- minBundleId,
520
- platform
566
+ const getFingerprintUpdateInfo = async ({ bundleId, channel = "production", cohort, fingerprintHash, minBundleId, platform }, context) => {
567
+ const bundles = await loadOptionalObject(`${channel}/${platform}/${fingerprintHash}/update.json`) ?? [];
568
+ return resolveUpdateInfoFromBundles({
569
+ args: {
570
+ _updateStrategy: "fingerprint",
571
+ bundleId,
572
+ channel,
573
+ cohort,
574
+ fingerprintHash,
575
+ minBundleId,
576
+ platform
577
+ },
578
+ bundles,
579
+ context
521
580
  });
522
581
  };
523
582
  const addAppVersionInvalidationPaths = (pathsToInvalidate, { platform, channel, targetAppVersion }) => {
@@ -539,7 +598,35 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
539
598
  targetAppVersion
540
599
  });
541
600
  };
542
- return createDatabasePlugin({
601
+ const bundleIndexDiagnostics = {
602
+ async check() {
603
+ const canonicalBundles = await loadCanonicalBundlesForIndexRepair();
604
+ const allRoot = await loadStoredManagementRoot({});
605
+ return compareBundleIndex({
606
+ canonicalBundles,
607
+ indexedBundles: allRoot ? await loadAllBundlesFromRoot(allRoot) : null,
608
+ rootMissing: !allRoot
609
+ });
610
+ },
611
+ async repair() {
612
+ const canonicalBundles = await loadCanonicalBundlesForIndexRepair();
613
+ const previousRoot = await loadStoredManagementRoot({});
614
+ const previousBundles = previousRoot ? await loadAllBundlesFromRoot(previousRoot) : null;
615
+ const previousArtifacts = previousRoot && previousBundles ? buildManagementIndexArtifacts(previousBundles, previousRoot.pageSize) : void 0;
616
+ const nextArtifacts = buildManagementIndexArtifacts(canonicalBundles, managementIndexPageSize);
617
+ const indexedObjectKeys = await listObjects(`${MANAGEMENT_INDEX_PREFIX}/`);
618
+ const nextObjectKeys = new Set([...nextArtifacts.pages.keys(), ...nextArtifacts.scopes.map((scope) => scope.rootKey)]);
619
+ await persistManagementIndexArtifacts(nextArtifacts, previousArtifacts);
620
+ await forEachWithConcurrency(indexedObjectKeys.filter((key) => !nextObjectKeys.has(key)), STORAGE_OPERATION_CONCURRENCY, (key) => deleteObject(key).catch(() => {}));
621
+ replaceManagementRootCache(nextArtifacts);
622
+ return {
623
+ scannedBundles: canonicalBundles.length,
624
+ indexedBundles: canonicalBundles.length,
625
+ ...summarizeManagementIndexArtifacts(nextArtifacts)
626
+ };
627
+ }
628
+ };
629
+ const createPlugin = createDatabasePlugin({
543
630
  name,
544
631
  factory: () => ({
545
632
  supportsCursorPagination: true,
@@ -563,9 +650,9 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
563
650
  if (!matchedBundle) return null;
564
651
  return removeBundleInternalKeys(matchedBundle);
565
652
  },
566
- async getUpdateInfo(args) {
567
- if (args._updateStrategy === "appVersion") return getAppVersionUpdateInfo(args);
568
- return getFingerprintUpdateInfo(args);
653
+ async getUpdateInfo(args, context) {
654
+ if (args._updateStrategy === "appVersion") return getAppVersionUpdateInfo(args, context);
655
+ return getFingerprintUpdateInfo(args, context);
569
656
  },
570
657
  async getBundles(options) {
571
658
  const { where, limit, offset, orderBy, cursor } = options;
@@ -598,11 +685,8 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
598
685
  const changedBundlesByKey = {};
599
686
  const removalsByKey = {};
600
687
  const pathsToInvalidate = /* @__PURE__ */ new Set();
601
- let isTargetAppVersionChanged = false;
602
- let isChannelChanged = false;
688
+ const targetVersionPlatforms = /* @__PURE__ */ new Set();
603
689
  for (const { operation, data } of changedSets) {
604
- if (data.targetAppVersion !== void 0) isTargetAppVersionChanged = true;
605
- if (operation === "update" && data.channel !== void 0) isChannelChanged = true;
606
690
  if (operation === "insert") {
607
691
  const target = resolveStorageTarget(data);
608
692
  const key = `${data.channel}/${data.platform}/${target}/update.json`;
@@ -614,6 +698,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
614
698
  pendingBundlesMap.set(data.id, bundleWithKey);
615
699
  changedBundlesByKey[key] = changedBundlesByKey[key] || [];
616
700
  changedBundlesByKey[key].push(removeBundleInternalKeys(bundleWithKey));
701
+ if (data.targetAppVersion !== void 0) targetVersionPlatforms.add(data.platform);
617
702
  addLookupInvalidationPaths(pathsToInvalidate, data);
618
703
  continue;
619
704
  }
@@ -626,6 +711,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
626
711
  const key = bundle._updateJsonKey;
627
712
  removalsByKey[key] = removalsByKey[key] || [];
628
713
  removalsByKey[key].push(bundle.id);
714
+ if (bundle.targetAppVersion !== void 0) targetVersionPlatforms.add(bundle.platform);
629
715
  addLookupInvalidationPaths(pathsToInvalidate, bundle);
630
716
  continue;
631
717
  }
@@ -657,6 +743,10 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
657
743
  channel: nextChannel
658
744
  });
659
745
  }
746
+ if (bundle.targetAppVersion !== void 0 || updatedBundle.targetAppVersion !== void 0) {
747
+ targetVersionPlatforms.add(bundle.platform);
748
+ targetVersionPlatforms.add(updatedBundle.platform);
749
+ }
660
750
  addLookupInvalidationPaths(pathsToInvalidate, updatedBundle);
661
751
  if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle.targetAppVersion) addLookupInvalidationPaths(pathsToInvalidate, bundle);
662
752
  continue;
@@ -670,14 +760,14 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
670
760
  if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle.targetAppVersion) addLookupInvalidationPaths(pathsToInvalidate, bundle);
671
761
  }
672
762
  }
673
- for (const oldKey of Object.keys(removalsByKey)) await (async () => {
674
- const updatedBundles = (await loadObject(oldKey) ?? []).filter((b) => !removalsByKey[oldKey].includes(b.id));
763
+ await forEachWithConcurrency(Object.keys(removalsByKey), STORAGE_OPERATION_CONCURRENCY, async (oldKey) => {
764
+ const updatedBundles = (await loadOptionalObject(oldKey) ?? []).filter((b) => !removalsByKey[oldKey].includes(b.id));
675
765
  updatedBundles.sort((a, b) => b.id.localeCompare(a.id));
676
766
  if (updatedBundles.length === 0) await deleteObject(oldKey);
677
767
  else await uploadObject(oldKey, updatedBundles);
678
- })();
679
- for (const key of Object.keys(changedBundlesByKey)) await (async () => {
680
- const currentBundles = await loadObject(key) ?? [];
768
+ });
769
+ await forEachWithConcurrency(Object.keys(changedBundlesByKey), STORAGE_OPERATION_CONCURRENCY, async (key) => {
770
+ const currentBundles = await loadOptionalObject(key) ?? [];
681
771
  const pureBundles = changedBundlesByKey[key].map((bundle) => bundle);
682
772
  for (const changedBundle of pureBundles) {
683
773
  const index = currentBundles.findIndex((b) => b.id === changedBundle.id);
@@ -686,10 +776,11 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
686
776
  }
687
777
  currentBundles.sort((a, b) => b.id.localeCompare(a.id));
688
778
  await uploadObject(key, currentBundles);
689
- })();
690
- if (isTargetAppVersionChanged || isChannelChanged) for (const platform of PLATFORMS) await updateTargetVersionsForPlatform(platform);
691
- const currentIndexBundles = await loadCurrentBundlesForIndexRebuild();
692
- const nextIndexMap = new Map(currentIndexBundles.map((bundle) => [bundle.id, bundle]));
779
+ });
780
+ if (targetVersionPlatforms.size > 0) await Promise.all(Array.from(targetVersionPlatforms).map((platform) => updateTargetVersionsForPlatform(platform)));
781
+ const previousIndexBundles = await loadCurrentBundlesForIndexRebuild();
782
+ const storedIndexBundles = await loadStoredBundlesForIndexRebuild();
783
+ const nextIndexMap = new Map(storedIndexBundles.map((bundle) => [bundle.id, bundle]));
693
784
  for (const { operation, data } of changedSets) {
694
785
  if (operation === "delete") {
695
786
  nextIndexMap.delete(data.id);
@@ -698,7 +789,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
698
789
  nextIndexMap.set(data.id, data);
699
790
  }
700
791
  const nextIndexBundles = sortManagedBundles(Array.from(nextIndexMap.values()));
701
- const previousArtifacts = buildManagementIndexArtifacts(currentIndexBundles, managementIndexPageSize);
792
+ const previousArtifacts = buildManagementIndexArtifacts(previousIndexBundles, managementIndexPageSize);
702
793
  const nextArtifacts = buildManagementIndexArtifacts(nextIndexBundles, managementIndexPageSize);
703
794
  await persistManagementIndexArtifacts(nextArtifacts, previousArtifacts);
704
795
  replaceManagementRootCache(nextArtifacts);
@@ -709,6 +800,15 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
709
800
  }
710
801
  })
711
802
  })({}, hooks);
803
+ return () => {
804
+ const plugin = createPlugin();
805
+ Object.defineProperty(plugin, "diagnostics", {
806
+ configurable: true,
807
+ enumerable: true,
808
+ value: { bundleIndex: bundleIndexDiagnostics }
809
+ });
810
+ return plugin;
811
+ };
712
812
  };
713
813
  };
714
814
  //#endregion
@@ -1,6 +1,6 @@
1
1
  require("./_virtual/_rolldown/runtime.cjs");
2
2
  const require_filterCompatibleAppVersions = require("./filterCompatibleAppVersions.cjs");
3
- let _hot_updater_js = require("@hot-updater/js");
3
+ const require_resolveUpdateInfoFromBundles = require("./resolveUpdateInfoFromBundles.cjs");
4
4
  let _hot_updater_core = require("@hot-updater/core");
5
5
  //#region src/createDatabasePluginGetUpdateInfo.ts
6
6
  const normalizeAppVersionArgs = (args) => ({
@@ -18,10 +18,18 @@ const createDatabasePluginGetUpdateInfo = ({ getBundlesByFingerprint, getBundles
18
18
  if (args._updateStrategy === "appVersion") {
19
19
  const normalizedArgs = normalizeAppVersionArgs(args);
20
20
  const compatibleAppVersions = require_filterCompatibleAppVersions.filterCompatibleAppVersions(await listTargetAppVersions(normalizedArgs, context), normalizedArgs.appVersion);
21
- return (0, _hot_updater_js.getUpdateInfo)(compatibleAppVersions.length > 0 ? await getBundlesByTargetAppVersions(normalizedArgs, compatibleAppVersions, context) : [], normalizedArgs);
21
+ return require_resolveUpdateInfoFromBundles.resolveUpdateInfoFromBundles({
22
+ args: normalizedArgs,
23
+ bundles: compatibleAppVersions.length > 0 ? await getBundlesByTargetAppVersions(normalizedArgs, compatibleAppVersions, context) : [],
24
+ context
25
+ });
22
26
  }
23
27
  const normalizedArgs = normalizeFingerprintArgs(args);
24
- return (0, _hot_updater_js.getUpdateInfo)(await getBundlesByFingerprint(normalizedArgs, context), normalizedArgs);
28
+ return require_resolveUpdateInfoFromBundles.resolveUpdateInfoFromBundles({
29
+ args: normalizedArgs,
30
+ bundles: await getBundlesByFingerprint(normalizedArgs, context),
31
+ context
32
+ });
25
33
  };
26
34
  };
27
35
  //#endregion
@@ -1,5 +1,5 @@
1
1
  import { filterCompatibleAppVersions } from "./filterCompatibleAppVersions.mjs";
2
- import { getUpdateInfo } from "@hot-updater/js";
2
+ import { resolveUpdateInfoFromBundles } from "./resolveUpdateInfoFromBundles.mjs";
3
3
  import { NIL_UUID } from "@hot-updater/core";
4
4
  //#region src/createDatabasePluginGetUpdateInfo.ts
5
5
  const normalizeAppVersionArgs = (args) => ({
@@ -17,10 +17,18 @@ const createDatabasePluginGetUpdateInfo = ({ getBundlesByFingerprint, getBundles
17
17
  if (args._updateStrategy === "appVersion") {
18
18
  const normalizedArgs = normalizeAppVersionArgs(args);
19
19
  const compatibleAppVersions = filterCompatibleAppVersions(await listTargetAppVersions(normalizedArgs, context), normalizedArgs.appVersion);
20
- return getUpdateInfo(compatibleAppVersions.length > 0 ? await getBundlesByTargetAppVersions(normalizedArgs, compatibleAppVersions, context) : [], normalizedArgs);
20
+ return resolveUpdateInfoFromBundles({
21
+ args: normalizedArgs,
22
+ bundles: compatibleAppVersions.length > 0 ? await getBundlesByTargetAppVersions(normalizedArgs, compatibleAppVersions, context) : [],
23
+ context
24
+ });
21
25
  }
22
26
  const normalizedArgs = normalizeFingerprintArgs(args);
23
- return getUpdateInfo(await getBundlesByFingerprint(normalizedArgs, context), normalizedArgs);
27
+ return resolveUpdateInfoFromBundles({
28
+ args: normalizedArgs,
29
+ bundles: await getBundlesByFingerprint(normalizedArgs, context),
30
+ context
31
+ });
24
32
  };
25
33
  };
26
34
  //#endregion
package/dist/index.cjs CHANGED
@@ -8,6 +8,8 @@ const require_semverSatisfies = require("./semverSatisfies.cjs");
8
8
  const require_filterCompatibleAppVersions = require("./filterCompatibleAppVersions.cjs");
9
9
  const require_queryBundles = require("./queryBundles.cjs");
10
10
  const require_paginateBundles = require("./paginateBundles.cjs");
11
+ const require_requestUpdateBundleState = require("./requestUpdateBundleState.cjs");
12
+ const require_resolveUpdateInfoFromBundles = require("./resolveUpdateInfoFromBundles.cjs");
11
13
  const require_createBlobDatabasePlugin = require("./createBlobDatabasePlugin.cjs");
12
14
  const require_createDatabasePluginGetUpdateInfo = require("./createDatabasePluginGetUpdateInfo.cjs");
13
15
  const require_createStorageKeyBuilder = require("./createStorageKeyBuilder.cjs");
@@ -41,12 +43,14 @@ exports.getCompressionMimeType = require_compressionFormat.getCompressionMimeTyp
41
43
  exports.getContentAddressedAssetStoragePath = require_contentAddressedAssets.getContentAddressedAssetStoragePath;
42
44
  exports.getContentType = require_compressionFormat.getContentType;
43
45
  exports.getManifestAssetStoragePath = require_assetStorageLayout.getManifestAssetStoragePath;
46
+ exports.getRequestUpdateBundleSeeds = require_requestUpdateBundleState.getRequestUpdateBundleSeeds;
44
47
  exports.isContentAddressedAssetBaseStorageUri = require_assetStorageLayout.isContentAddressedAssetBaseStorageUri;
45
48
  exports.isNodeStoragePlugin = require_storageProfile.isNodeStoragePlugin;
46
49
  exports.isRuntimeStoragePlugin = require_storageProfile.isRuntimeStoragePlugin;
47
50
  exports.paginateBundles = require_paginateBundles.paginateBundles;
48
51
  exports.parseStorageUri = require_parseStorageUri.parseStorageUri;
49
52
  exports.resolveManifestAssetStorageUri = require_assetStorageLayout.resolveManifestAssetStorageUri;
53
+ exports.resolveUpdateInfoFromBundles = require_resolveUpdateInfoFromBundles.resolveUpdateInfoFromBundles;
50
54
  exports.semverSatisfies = require_semverSatisfies.semverSatisfies;
51
55
  exports.sortBundles = require_queryBundles.sortBundles;
52
56
  exports.supportedIosPlatforms = require_index.supportedIosPlatforms;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BuiltIns, HasMultipleCallSignatures, Primitive, RequiredDeep } from "./types/utils.cjs";
2
- import { AppVersionGetBundlesArgs, ApplePlatform, BasePluginArgs, BuildPlugin, BuildPluginConfig, Bundle, ConfigInput, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabasePlugin, DatabasePluginHooks, FingerprintGetBundlesArgs, GetBundlesArgs, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, Platform, PlatformConfig, RequestEnvContext, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, UpdateInfo, supportedIosPlatforms } from "./types/index.cjs";
2
+ import { AppVersionGetBundlesArgs, ApplePlatform, BasePluginArgs, BuildPlugin, BuildPluginConfig, Bundle, BundleIndexDiagnostics, BundleIndexHealth, BundleIndexRepairResult, ConfigInput, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabaseDiagnostics, DatabasePlugin, DatabasePluginHooks, FingerprintGetBundlesArgs, GetBundlesArgs, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, Platform, PlatformConfig, RequestEnvContext, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, UpdateInfo, supportedIosPlatforms } from "./types/index.cjs";
3
3
  import { PaginationOptions, calculatePagination } from "./calculatePagination.cjs";
4
4
  import { CompressionFormat, CompressionFormatInfo, detectCompressionFormat, getCompressionMimeType, getContentType } from "./compressionFormat.cjs";
5
5
  import { AssetStorageLayout, createStorageUriWithRelativePath, getAssetStorageLayout, getManifestAssetStoragePath, isContentAddressedAssetBaseStorageUri, resolveManifestAssetStorageUri } from "./assetStorageLayout.cjs";
@@ -14,7 +14,9 @@ import { generateMinBundleId } from "./generateMinBundleId.cjs";
14
14
  import { ParsedStorageUri, parseStorageUri } from "./parseStorageUri.cjs";
15
15
  import { paginateBundles } from "./paginateBundles.cjs";
16
16
  import { bundleIdMatchesFilter, bundleMatchesQueryWhere, sortBundles } from "./queryBundles.cjs";
17
+ import { getRequestUpdateBundleSeeds } from "./requestUpdateBundleState.cjs";
18
+ import { ResolveUpdateInfoFromBundlesOptions, resolveUpdateInfoFromBundles } from "./resolveUpdateInfoFromBundles.cjs";
17
19
  import { semverSatisfies } from "./semverSatisfies.cjs";
18
20
  import { assertNodeStoragePlugin, assertRuntimeStoragePlugin, isNodeStoragePlugin, isRuntimeStoragePlugin } from "./storageProfile.cjs";
19
21
  import { createUUIDv7, createUUIDv7WithSameTimestamp, extractTimestampFromUUIDv7 } from "./uuidv7.cjs";
20
- export { AbstractDatabasePlugin, AppVersionGetBundlesArgs, ApplePlatform, AssetStorageLayout, BasePluginArgs, BlobDatabasePluginConfig, BlobOperations, BuildPlugin, BuildPluginConfig, BuiltIns, Bundle, CompressionFormat, CompressionFormatInfo, ConfigInput, CreateDatabasePluginGetUpdateInfoOptions, CreateDatabasePluginOptions, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabasePlugin, DatabasePluginHooks, FingerprintGetBundlesArgs, GetBundlesArgs, HasMultipleCallSignatures, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, PaginationOptions, ParsedStorageUri, Platform, PlatformConfig, Primitive, RequestEnvContext, RequiredDeep, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, UpdateInfo, assertNodeStoragePlugin, assertRuntimeStoragePlugin, bundleIdMatchesFilter, bundleMatchesQueryWhere, calculatePagination, createBlobDatabasePlugin, createDatabasePlugin, createDatabasePluginGetUpdateInfo, createNodeStoragePlugin, createRuntimeStoragePlugin, createStorageKeyBuilder, createStorageUriWithRelativePath, createUUIDv7, createUUIDv7WithSameTimestamp, createUniversalStoragePlugin, detectCompressionFormat, extractTimestampFromUUIDv7, filterCompatibleAppVersions, generateMinBundleId, getAssetStorageLayout, getCompressionMimeType, getContentAddressedAssetStoragePath, getContentType, getManifestAssetStoragePath, isContentAddressedAssetBaseStorageUri, isNodeStoragePlugin, isRuntimeStoragePlugin, paginateBundles, parseStorageUri, resolveManifestAssetStorageUri, semverSatisfies, sortBundles, supportedIosPlatforms };
22
+ export { AbstractDatabasePlugin, AppVersionGetBundlesArgs, ApplePlatform, AssetStorageLayout, BasePluginArgs, BlobDatabasePluginConfig, BlobOperations, BuildPlugin, BuildPluginConfig, BuiltIns, Bundle, BundleIndexDiagnostics, BundleIndexHealth, BundleIndexRepairResult, CompressionFormat, CompressionFormatInfo, ConfigInput, CreateDatabasePluginGetUpdateInfoOptions, CreateDatabasePluginOptions, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabaseDiagnostics, DatabasePlugin, DatabasePluginHooks, FingerprintGetBundlesArgs, GetBundlesArgs, HasMultipleCallSignatures, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, PaginationOptions, ParsedStorageUri, Platform, PlatformConfig, Primitive, RequestEnvContext, RequiredDeep, ResolveUpdateInfoFromBundlesOptions, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, UpdateInfo, assertNodeStoragePlugin, assertRuntimeStoragePlugin, bundleIdMatchesFilter, bundleMatchesQueryWhere, calculatePagination, createBlobDatabasePlugin, createDatabasePlugin, createDatabasePluginGetUpdateInfo, createNodeStoragePlugin, createRuntimeStoragePlugin, createStorageKeyBuilder, createStorageUriWithRelativePath, createUUIDv7, createUUIDv7WithSameTimestamp, createUniversalStoragePlugin, detectCompressionFormat, extractTimestampFromUUIDv7, filterCompatibleAppVersions, generateMinBundleId, getAssetStorageLayout, getCompressionMimeType, getContentAddressedAssetStoragePath, getContentType, getManifestAssetStoragePath, getRequestUpdateBundleSeeds, isContentAddressedAssetBaseStorageUri, isNodeStoragePlugin, isRuntimeStoragePlugin, paginateBundles, parseStorageUri, resolveManifestAssetStorageUri, resolveUpdateInfoFromBundles, semverSatisfies, sortBundles, supportedIosPlatforms };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BuiltIns, HasMultipleCallSignatures, Primitive, RequiredDeep } from "./types/utils.mjs";
2
- import { AppVersionGetBundlesArgs, ApplePlatform, BasePluginArgs, BuildPlugin, BuildPluginConfig, Bundle, ConfigInput, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabasePlugin, DatabasePluginHooks, FingerprintGetBundlesArgs, GetBundlesArgs, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, Platform, PlatformConfig, RequestEnvContext, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, UpdateInfo, supportedIosPlatforms } from "./types/index.mjs";
2
+ import { AppVersionGetBundlesArgs, ApplePlatform, BasePluginArgs, BuildPlugin, BuildPluginConfig, Bundle, BundleIndexDiagnostics, BundleIndexHealth, BundleIndexRepairResult, ConfigInput, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabaseDiagnostics, DatabasePlugin, DatabasePluginHooks, FingerprintGetBundlesArgs, GetBundlesArgs, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, Platform, PlatformConfig, RequestEnvContext, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, UpdateInfo, supportedIosPlatforms } from "./types/index.mjs";
3
3
  import { PaginationOptions, calculatePagination } from "./calculatePagination.mjs";
4
4
  import { CompressionFormat, CompressionFormatInfo, detectCompressionFormat, getCompressionMimeType, getContentType } from "./compressionFormat.mjs";
5
5
  import { AssetStorageLayout, createStorageUriWithRelativePath, getAssetStorageLayout, getManifestAssetStoragePath, isContentAddressedAssetBaseStorageUri, resolveManifestAssetStorageUri } from "./assetStorageLayout.mjs";
@@ -14,7 +14,9 @@ import { generateMinBundleId } from "./generateMinBundleId.mjs";
14
14
  import { ParsedStorageUri, parseStorageUri } from "./parseStorageUri.mjs";
15
15
  import { paginateBundles } from "./paginateBundles.mjs";
16
16
  import { bundleIdMatchesFilter, bundleMatchesQueryWhere, sortBundles } from "./queryBundles.mjs";
17
+ import { getRequestUpdateBundleSeeds } from "./requestUpdateBundleState.mjs";
18
+ import { ResolveUpdateInfoFromBundlesOptions, resolveUpdateInfoFromBundles } from "./resolveUpdateInfoFromBundles.mjs";
17
19
  import { semverSatisfies } from "./semverSatisfies.mjs";
18
20
  import { assertNodeStoragePlugin, assertRuntimeStoragePlugin, isNodeStoragePlugin, isRuntimeStoragePlugin } from "./storageProfile.mjs";
19
21
  import { createUUIDv7, createUUIDv7WithSameTimestamp, extractTimestampFromUUIDv7 } from "./uuidv7.mjs";
20
- export { AbstractDatabasePlugin, AppVersionGetBundlesArgs, ApplePlatform, AssetStorageLayout, BasePluginArgs, BlobDatabasePluginConfig, BlobOperations, BuildPlugin, BuildPluginConfig, BuiltIns, Bundle, CompressionFormat, CompressionFormatInfo, ConfigInput, CreateDatabasePluginGetUpdateInfoOptions, CreateDatabasePluginOptions, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabasePlugin, DatabasePluginHooks, FingerprintGetBundlesArgs, GetBundlesArgs, HasMultipleCallSignatures, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, PaginationOptions, ParsedStorageUri, Platform, PlatformConfig, Primitive, RequestEnvContext, RequiredDeep, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, UpdateInfo, assertNodeStoragePlugin, assertRuntimeStoragePlugin, bundleIdMatchesFilter, bundleMatchesQueryWhere, calculatePagination, createBlobDatabasePlugin, createDatabasePlugin, createDatabasePluginGetUpdateInfo, createNodeStoragePlugin, createRuntimeStoragePlugin, createStorageKeyBuilder, createStorageUriWithRelativePath, createUUIDv7, createUUIDv7WithSameTimestamp, createUniversalStoragePlugin, detectCompressionFormat, extractTimestampFromUUIDv7, filterCompatibleAppVersions, generateMinBundleId, getAssetStorageLayout, getCompressionMimeType, getContentAddressedAssetStoragePath, getContentType, getManifestAssetStoragePath, isContentAddressedAssetBaseStorageUri, isNodeStoragePlugin, isRuntimeStoragePlugin, paginateBundles, parseStorageUri, resolveManifestAssetStorageUri, semverSatisfies, sortBundles, supportedIosPlatforms };
22
+ export { AbstractDatabasePlugin, AppVersionGetBundlesArgs, ApplePlatform, AssetStorageLayout, BasePluginArgs, BlobDatabasePluginConfig, BlobOperations, BuildPlugin, BuildPluginConfig, BuiltIns, Bundle, BundleIndexDiagnostics, BundleIndexHealth, BundleIndexRepairResult, CompressionFormat, CompressionFormatInfo, ConfigInput, CreateDatabasePluginGetUpdateInfoOptions, CreateDatabasePluginOptions, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabaseDiagnostics, DatabasePlugin, DatabasePluginHooks, FingerprintGetBundlesArgs, GetBundlesArgs, HasMultipleCallSignatures, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, PaginationOptions, ParsedStorageUri, Platform, PlatformConfig, Primitive, RequestEnvContext, RequiredDeep, ResolveUpdateInfoFromBundlesOptions, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, UpdateInfo, assertNodeStoragePlugin, assertRuntimeStoragePlugin, bundleIdMatchesFilter, bundleMatchesQueryWhere, calculatePagination, createBlobDatabasePlugin, createDatabasePlugin, createDatabasePluginGetUpdateInfo, createNodeStoragePlugin, createRuntimeStoragePlugin, createStorageKeyBuilder, createStorageUriWithRelativePath, createUUIDv7, createUUIDv7WithSameTimestamp, createUniversalStoragePlugin, detectCompressionFormat, extractTimestampFromUUIDv7, filterCompatibleAppVersions, generateMinBundleId, getAssetStorageLayout, getCompressionMimeType, getContentAddressedAssetStoragePath, getContentType, getManifestAssetStoragePath, getRequestUpdateBundleSeeds, isContentAddressedAssetBaseStorageUri, isNodeStoragePlugin, isRuntimeStoragePlugin, paginateBundles, parseStorageUri, resolveManifestAssetStorageUri, resolveUpdateInfoFromBundles, semverSatisfies, sortBundles, supportedIosPlatforms };
package/dist/index.mjs CHANGED
@@ -7,6 +7,8 @@ import { semverSatisfies } from "./semverSatisfies.mjs";
7
7
  import { filterCompatibleAppVersions } from "./filterCompatibleAppVersions.mjs";
8
8
  import { bundleIdMatchesFilter, bundleMatchesQueryWhere, sortBundles } from "./queryBundles.mjs";
9
9
  import { paginateBundles } from "./paginateBundles.mjs";
10
+ import { getRequestUpdateBundleSeeds } from "./requestUpdateBundleState.mjs";
11
+ import { resolveUpdateInfoFromBundles } from "./resolveUpdateInfoFromBundles.mjs";
10
12
  import { createBlobDatabasePlugin } from "./createBlobDatabasePlugin.mjs";
11
13
  import { createDatabasePluginGetUpdateInfo } from "./createDatabasePluginGetUpdateInfo.mjs";
12
14
  import { createStorageKeyBuilder } from "./createStorageKeyBuilder.mjs";
@@ -16,4 +18,4 @@ import { parseStorageUri } from "./parseStorageUri.mjs";
16
18
  import { assertNodeStoragePlugin, assertRuntimeStoragePlugin, isNodeStoragePlugin, isRuntimeStoragePlugin } from "./storageProfile.mjs";
17
19
  import { supportedIosPlatforms } from "./types/index.mjs";
18
20
  import { createUUIDv7, createUUIDv7WithSameTimestamp, extractTimestampFromUUIDv7 } from "./uuidv7.mjs";
19
- export { assertNodeStoragePlugin, assertRuntimeStoragePlugin, bundleIdMatchesFilter, bundleMatchesQueryWhere, calculatePagination, createBlobDatabasePlugin, createDatabasePlugin, createDatabasePluginGetUpdateInfo, createNodeStoragePlugin, createRuntimeStoragePlugin, createStorageKeyBuilder, createStorageUriWithRelativePath, createUUIDv7, createUUIDv7WithSameTimestamp, createUniversalStoragePlugin, detectCompressionFormat, extractTimestampFromUUIDv7, filterCompatibleAppVersions, generateMinBundleId, getAssetStorageLayout, getCompressionMimeType, getContentAddressedAssetStoragePath, getContentType, getManifestAssetStoragePath, isContentAddressedAssetBaseStorageUri, isNodeStoragePlugin, isRuntimeStoragePlugin, paginateBundles, parseStorageUri, resolveManifestAssetStorageUri, semverSatisfies, sortBundles, supportedIosPlatforms };
21
+ export { assertNodeStoragePlugin, assertRuntimeStoragePlugin, bundleIdMatchesFilter, bundleMatchesQueryWhere, calculatePagination, createBlobDatabasePlugin, createDatabasePlugin, createDatabasePluginGetUpdateInfo, createNodeStoragePlugin, createRuntimeStoragePlugin, createStorageKeyBuilder, createStorageUriWithRelativePath, createUUIDv7, createUUIDv7WithSameTimestamp, createUniversalStoragePlugin, detectCompressionFormat, extractTimestampFromUUIDv7, filterCompatibleAppVersions, generateMinBundleId, getAssetStorageLayout, getCompressionMimeType, getContentAddressedAssetStoragePath, getContentType, getManifestAssetStoragePath, getRequestUpdateBundleSeeds, isContentAddressedAssetBaseStorageUri, isNodeStoragePlugin, isRuntimeStoragePlugin, paginateBundles, parseStorageUri, resolveManifestAssetStorageUri, resolveUpdateInfoFromBundles, semverSatisfies, sortBundles, supportedIosPlatforms };
@@ -0,0 +1,20 @@
1
+ //#region src/requestUpdateBundleState.ts
2
+ const requestUpdateBundleSeeds = /* @__PURE__ */ new WeakMap();
3
+ const isWeakMapKey = (value) => typeof value === "object" && value !== null || typeof value === "function";
4
+ const toBundleSeeds = (seeds) => seeds.filter((seed) => !!seed);
5
+ const seedRequestUpdateBundles = (context, seeds) => {
6
+ if (!isWeakMapKey(context)) return;
7
+ const nextSeeds = toBundleSeeds(seeds);
8
+ if (nextSeeds.length === 0) return;
9
+ const bundlesById = /* @__PURE__ */ new Map();
10
+ for (const seed of requestUpdateBundleSeeds.get(context) ?? []) bundlesById.set(seed.id, seed);
11
+ for (const seed of nextSeeds) bundlesById.set(seed.id, seed);
12
+ requestUpdateBundleSeeds.set(context, [...bundlesById.values()]);
13
+ };
14
+ const getRequestUpdateBundleSeeds = (context) => {
15
+ if (!isWeakMapKey(context)) return [];
16
+ return requestUpdateBundleSeeds.get(context) ?? [];
17
+ };
18
+ //#endregion
19
+ exports.getRequestUpdateBundleSeeds = getRequestUpdateBundleSeeds;
20
+ exports.seedRequestUpdateBundles = seedRequestUpdateBundles;
@@ -0,0 +1,6 @@
1
+ import { Bundle, HotUpdaterContext } from "./types/index.cjs";
2
+
3
+ //#region src/requestUpdateBundleState.d.ts
4
+ declare const getRequestUpdateBundleSeeds: <TContext = unknown>(context: HotUpdaterContext<TContext> | undefined) => readonly Bundle[];
5
+ //#endregion
6
+ export { getRequestUpdateBundleSeeds };
@@ -0,0 +1,6 @@
1
+ import { Bundle, HotUpdaterContext } from "./types/index.mjs";
2
+
3
+ //#region src/requestUpdateBundleState.d.ts
4
+ declare const getRequestUpdateBundleSeeds: <TContext = unknown>(context: HotUpdaterContext<TContext> | undefined) => readonly Bundle[];
5
+ //#endregion
6
+ export { getRequestUpdateBundleSeeds };
@@ -0,0 +1,19 @@
1
+ //#region src/requestUpdateBundleState.ts
2
+ const requestUpdateBundleSeeds = /* @__PURE__ */ new WeakMap();
3
+ const isWeakMapKey = (value) => typeof value === "object" && value !== null || typeof value === "function";
4
+ const toBundleSeeds = (seeds) => seeds.filter((seed) => !!seed);
5
+ const seedRequestUpdateBundles = (context, seeds) => {
6
+ if (!isWeakMapKey(context)) return;
7
+ const nextSeeds = toBundleSeeds(seeds);
8
+ if (nextSeeds.length === 0) return;
9
+ const bundlesById = /* @__PURE__ */ new Map();
10
+ for (const seed of requestUpdateBundleSeeds.get(context) ?? []) bundlesById.set(seed.id, seed);
11
+ for (const seed of nextSeeds) bundlesById.set(seed.id, seed);
12
+ requestUpdateBundleSeeds.set(context, [...bundlesById.values()]);
13
+ };
14
+ const getRequestUpdateBundleSeeds = (context) => {
15
+ if (!isWeakMapKey(context)) return [];
16
+ return requestUpdateBundleSeeds.get(context) ?? [];
17
+ };
18
+ //#endregion
19
+ export { getRequestUpdateBundleSeeds, seedRequestUpdateBundles };
@@ -0,0 +1,14 @@
1
+ require("./_virtual/_rolldown/runtime.cjs");
2
+ const require_requestUpdateBundleState = require("./requestUpdateBundleState.cjs");
3
+ let _hot_updater_core = require("@hot-updater/core");
4
+ let _hot_updater_js = require("@hot-updater/js");
5
+ //#region src/resolveUpdateInfoFromBundles.ts
6
+ const findSeedBundle = (bundles, bundleId) => bundles.find((bundle) => bundle.id === bundleId);
7
+ const resolveUpdateInfoFromBundles = async ({ args, bundles, context }) => {
8
+ const info = await (0, _hot_updater_js.getUpdateInfo)(bundles, args);
9
+ if (!info) return null;
10
+ require_requestUpdateBundleState.seedRequestUpdateBundles(context, [findSeedBundle(bundles, info.id), args.bundleId === _hot_updater_core.NIL_UUID ? null : findSeedBundle(bundles, args.bundleId)]);
11
+ return info;
12
+ };
13
+ //#endregion
14
+ exports.resolveUpdateInfoFromBundles = resolveUpdateInfoFromBundles;
@@ -0,0 +1,16 @@
1
+ import { Bundle as Bundle$1, HotUpdaterContext } from "./types/index.cjs";
2
+ import { GetBundlesArgs, UpdateInfo } from "@hot-updater/core";
3
+
4
+ //#region src/resolveUpdateInfoFromBundles.d.ts
5
+ interface ResolveUpdateInfoFromBundlesOptions<TContext = unknown> {
6
+ readonly args: GetBundlesArgs;
7
+ readonly bundles: Bundle$1[];
8
+ readonly context?: HotUpdaterContext<TContext>;
9
+ }
10
+ declare const resolveUpdateInfoFromBundles: <TContext = unknown>({
11
+ args,
12
+ bundles,
13
+ context
14
+ }: ResolveUpdateInfoFromBundlesOptions<TContext>) => Promise<UpdateInfo | null>;
15
+ //#endregion
16
+ export { ResolveUpdateInfoFromBundlesOptions, resolveUpdateInfoFromBundles };
@@ -0,0 +1,16 @@
1
+ import { Bundle as Bundle$1, HotUpdaterContext } from "./types/index.mjs";
2
+ import { GetBundlesArgs, UpdateInfo } from "@hot-updater/core";
3
+
4
+ //#region src/resolveUpdateInfoFromBundles.d.ts
5
+ interface ResolveUpdateInfoFromBundlesOptions<TContext = unknown> {
6
+ readonly args: GetBundlesArgs;
7
+ readonly bundles: Bundle$1[];
8
+ readonly context?: HotUpdaterContext<TContext>;
9
+ }
10
+ declare const resolveUpdateInfoFromBundles: <TContext = unknown>({
11
+ args,
12
+ bundles,
13
+ context
14
+ }: ResolveUpdateInfoFromBundlesOptions<TContext>) => Promise<UpdateInfo | null>;
15
+ //#endregion
16
+ export { ResolveUpdateInfoFromBundlesOptions, resolveUpdateInfoFromBundles };
@@ -0,0 +1,13 @@
1
+ import { seedRequestUpdateBundles } from "./requestUpdateBundleState.mjs";
2
+ import { NIL_UUID } from "@hot-updater/core";
3
+ import { getUpdateInfo } from "@hot-updater/js";
4
+ //#region src/resolveUpdateInfoFromBundles.ts
5
+ const findSeedBundle = (bundles, bundleId) => bundles.find((bundle) => bundle.id === bundleId);
6
+ const resolveUpdateInfoFromBundles = async ({ args, bundles, context }) => {
7
+ const info = await getUpdateInfo(bundles, args);
8
+ if (!info) return null;
9
+ seedRequestUpdateBundles(context, [findSeedBundle(bundles, info.id), args.bundleId === NIL_UUID ? null : findSeedBundle(bundles, args.bundleId)]);
10
+ return info;
11
+ };
12
+ //#endregion
13
+ export { resolveUpdateInfoFromBundles };
@@ -69,6 +69,28 @@ interface DatabaseBundleQueryOptions {
69
69
  cursor?: DatabaseBundleCursor;
70
70
  orderBy?: DatabaseBundleQueryOrder;
71
71
  }
72
+ interface BundleIndexHealth {
73
+ status: "ok" | "missing" | "stale";
74
+ canonicalBundles: number;
75
+ indexedBundles: number;
76
+ missingBundles: number;
77
+ extraBundles: number;
78
+ missingBundleIds: string[];
79
+ extraBundleIds: string[];
80
+ }
81
+ interface BundleIndexRepairResult {
82
+ scannedBundles: number;
83
+ indexedBundles: number;
84
+ pagesWritten: number;
85
+ scopesWritten: number;
86
+ }
87
+ interface BundleIndexDiagnostics<TContext = unknown> {
88
+ check: (context?: HotUpdaterContext<TContext>) => Promise<BundleIndexHealth>;
89
+ repair?: (context?: HotUpdaterContext<TContext>) => Promise<BundleIndexRepairResult>;
90
+ }
91
+ interface DatabaseDiagnostics<TContext = unknown> {
92
+ bundleIndex?: BundleIndexDiagnostics<TContext>;
93
+ }
72
94
  interface BuildPluginConfig {
73
95
  outDir?: string;
74
96
  }
@@ -80,6 +102,7 @@ interface DatabasePlugin<TContext = unknown> {
80
102
  updateBundle: (targetBundleId: string, newBundle: Partial<Bundle>, context?: HotUpdaterContext<TContext>) => Promise<void>;
81
103
  appendBundle: (insertBundle: Bundle, context?: HotUpdaterContext<TContext>) => Promise<void>;
82
104
  commitBundle: (context?: HotUpdaterContext<TContext>) => Promise<void>;
105
+ diagnostics?: DatabaseDiagnostics<TContext>;
83
106
  onUnmount?: () => Promise<void>;
84
107
  name: string;
85
108
  deleteBundle: (deleteBundle: Bundle, context?: HotUpdaterContext<TContext>) => Promise<void>;
@@ -504,4 +527,4 @@ interface NativeBuildOptions {
504
527
  scheme?: string;
505
528
  }
506
529
  //#endregion
507
- export { type AppVersionGetBundlesArgs$1 as AppVersionGetBundlesArgs, ApplePlatform, BasePluginArgs, BuildPlugin, BuildPluginConfig, type Bundle$1 as Bundle, ConfigInput, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabasePlugin, DatabasePluginHooks, type FingerprintGetBundlesArgs$1 as FingerprintGetBundlesArgs, type GetBundlesArgs$1 as GetBundlesArgs, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, type Platform$1 as Platform, PlatformConfig, RequestEnvContext, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, type UpdateInfo$1 as UpdateInfo, supportedIosPlatforms };
530
+ export { type AppVersionGetBundlesArgs$1 as AppVersionGetBundlesArgs, ApplePlatform, BasePluginArgs, BuildPlugin, BuildPluginConfig, type Bundle$1 as Bundle, BundleIndexDiagnostics, BundleIndexHealth, BundleIndexRepairResult, ConfigInput, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabaseDiagnostics, DatabasePlugin, DatabasePluginHooks, type FingerprintGetBundlesArgs$1 as FingerprintGetBundlesArgs, type GetBundlesArgs$1 as GetBundlesArgs, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, type Platform$1 as Platform, PlatformConfig, RequestEnvContext, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, type UpdateInfo$1 as UpdateInfo, supportedIosPlatforms };
@@ -69,6 +69,28 @@ interface DatabaseBundleQueryOptions {
69
69
  cursor?: DatabaseBundleCursor;
70
70
  orderBy?: DatabaseBundleQueryOrder;
71
71
  }
72
+ interface BundleIndexHealth {
73
+ status: "ok" | "missing" | "stale";
74
+ canonicalBundles: number;
75
+ indexedBundles: number;
76
+ missingBundles: number;
77
+ extraBundles: number;
78
+ missingBundleIds: string[];
79
+ extraBundleIds: string[];
80
+ }
81
+ interface BundleIndexRepairResult {
82
+ scannedBundles: number;
83
+ indexedBundles: number;
84
+ pagesWritten: number;
85
+ scopesWritten: number;
86
+ }
87
+ interface BundleIndexDiagnostics<TContext = unknown> {
88
+ check: (context?: HotUpdaterContext<TContext>) => Promise<BundleIndexHealth>;
89
+ repair?: (context?: HotUpdaterContext<TContext>) => Promise<BundleIndexRepairResult>;
90
+ }
91
+ interface DatabaseDiagnostics<TContext = unknown> {
92
+ bundleIndex?: BundleIndexDiagnostics<TContext>;
93
+ }
72
94
  interface BuildPluginConfig {
73
95
  outDir?: string;
74
96
  }
@@ -80,6 +102,7 @@ interface DatabasePlugin<TContext = unknown> {
80
102
  updateBundle: (targetBundleId: string, newBundle: Partial<Bundle>, context?: HotUpdaterContext<TContext>) => Promise<void>;
81
103
  appendBundle: (insertBundle: Bundle, context?: HotUpdaterContext<TContext>) => Promise<void>;
82
104
  commitBundle: (context?: HotUpdaterContext<TContext>) => Promise<void>;
105
+ diagnostics?: DatabaseDiagnostics<TContext>;
83
106
  onUnmount?: () => Promise<void>;
84
107
  name: string;
85
108
  deleteBundle: (deleteBundle: Bundle, context?: HotUpdaterContext<TContext>) => Promise<void>;
@@ -504,4 +527,4 @@ interface NativeBuildOptions {
504
527
  scheme?: string;
505
528
  }
506
529
  //#endregion
507
- export { type AppVersionGetBundlesArgs$1 as AppVersionGetBundlesArgs, ApplePlatform, BasePluginArgs, BuildPlugin, BuildPluginConfig, type Bundle$1 as Bundle, ConfigInput, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabasePlugin, DatabasePluginHooks, type FingerprintGetBundlesArgs$1 as FingerprintGetBundlesArgs, type GetBundlesArgs$1 as GetBundlesArgs, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, type Platform$1 as Platform, PlatformConfig, RequestEnvContext, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, type UpdateInfo$1 as UpdateInfo, supportedIosPlatforms };
530
+ export { type AppVersionGetBundlesArgs$1 as AppVersionGetBundlesArgs, ApplePlatform, BasePluginArgs, BuildPlugin, BuildPluginConfig, type Bundle$1 as Bundle, BundleIndexDiagnostics, BundleIndexHealth, BundleIndexRepairResult, ConfigInput, DatabaseBundleCursor, DatabaseBundleIdFilter, DatabaseBundleQueryOptions, DatabaseBundleQueryOrder, DatabaseBundleQueryWhere, DatabaseDiagnostics, DatabasePlugin, DatabasePluginHooks, type FingerprintGetBundlesArgs$1 as FingerprintGetBundlesArgs, type GetBundlesArgs$1 as GetBundlesArgs, HotUpdaterContext, IosBuildDestination, NativeBuildAndroidScheme, NativeBuildArgs, NativeBuildIosScheme, NativeBuildOptions, NodeStoragePlugin, NodeStorageProfile, Paginated, PaginatedResult, PaginationInfo, type Platform$1 as Platform, PlatformConfig, RequestEnvContext, RuntimeStoragePlugin, RuntimeStorageProfile, SigningConfig, StoragePlugin, StoragePluginHooks, StoragePluginProfiles, StorageResolveContext, UniversalStoragePlugin, type UpdateInfo$1 as UpdateInfo, supportedIosPlatforms };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hot-updater/plugin-core",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "type": "module",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "sideEffects": false,
@@ -42,14 +42,14 @@
42
42
  "es-toolkit": "^1.32.0",
43
43
  "mime": "^4.0.4",
44
44
  "semver": "^7.7.2",
45
- "@hot-updater/core": "0.32.0",
46
- "@hot-updater/js": "0.32.0"
45
+ "@hot-updater/js": "0.33.0",
46
+ "@hot-updater/core": "0.33.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "^20",
50
50
  "@types/semver": "^7.5.8",
51
51
  "typescript": "6.0.2",
52
- "@hot-updater/test-utils": "0.32.0"
52
+ "@hot-updater/test-utils": "0.33.0"
53
53
  },
54
54
  "scripts": {
55
55
  "build": "tsdown",