@hot-updater/aws 0.31.4 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/iac/index.cjs +7 -7
- package/dist/iac/index.mjs +1 -1
- package/dist/index.cjs +59 -7
- package/dist/index.d.cts +10 -5
- package/dist/index.d.mts +9 -4
- package/dist/index.mjs +58 -6
- package/dist/lambda/index.cjs +487 -192
- package/package.json +8 -8
package/dist/lambda/index.cjs
CHANGED
|
@@ -6,7 +6,7 @@ var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames$1 = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf$1 = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __commonJSMin$1 = (cb, mod) => () => (mod ||
|
|
9
|
+
var __commonJSMin$1 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
10
10
|
var __copyProps$1 = (to, from, except, desc) => {
|
|
11
11
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames$1(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
12
|
key = keys[i];
|
|
@@ -23,16 +23,16 @@ var __toESM$1 = (mod, isNodeMode, target) => (target = mod != null ? __create$1(
|
|
|
23
23
|
}) : target, mod));
|
|
24
24
|
//#endregion
|
|
25
25
|
let node_path = require("node:path");
|
|
26
|
-
node_path = __toESM$1(node_path
|
|
26
|
+
node_path = __toESM$1(node_path);
|
|
27
27
|
let node_crypto = require("node:crypto");
|
|
28
|
-
node_crypto = __toESM$1(node_crypto
|
|
28
|
+
node_crypto = __toESM$1(node_crypto);
|
|
29
29
|
let _aws_sdk_client_cloudfront = require("@aws-sdk/client-cloudfront");
|
|
30
30
|
let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
|
|
31
31
|
let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
|
|
32
32
|
let fs_promises = require("fs/promises");
|
|
33
|
-
fs_promises = __toESM$1(fs_promises
|
|
33
|
+
fs_promises = __toESM$1(fs_promises);
|
|
34
34
|
let path = require("path");
|
|
35
|
-
path = __toESM$1(path
|
|
35
|
+
path = __toESM$1(path);
|
|
36
36
|
let _aws_sdk_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
|
|
37
37
|
let _aws_sdk_client_ssm = require("@aws-sdk/client-ssm");
|
|
38
38
|
let _aws_sdk_cloudfront_signer = require("@aws-sdk/cloudfront-signer");
|
|
@@ -1440,6 +1440,49 @@ function getContentType(bundlePath) {
|
|
|
1440
1440
|
return src_default.getType(bundlePath) ?? getCompressionMimeType(filename) ?? "application/octet-stream";
|
|
1441
1441
|
}
|
|
1442
1442
|
//#endregion
|
|
1443
|
+
//#region ../plugin-core/dist/contentAddressedAssets.mjs
|
|
1444
|
+
const getContentAddressedAssetStoragePath = ({ assetPath, fileHash }) => {
|
|
1445
|
+
const extension = assetPath.endsWith(".br") ? ".br" : assetPath.includes(".") ? `.${assetPath.split(".").pop()}` : "";
|
|
1446
|
+
return `sha256/${fileHash.slice(0, 2)}/${fileHash}${extension}`;
|
|
1447
|
+
};
|
|
1448
|
+
//#endregion
|
|
1449
|
+
//#region ../plugin-core/dist/legacyAssetStorageLayout.mjs
|
|
1450
|
+
/**
|
|
1451
|
+
* @internal
|
|
1452
|
+
*
|
|
1453
|
+
* Legacy manifest assets were stored below each bundle's `/files` directory
|
|
1454
|
+
* using their manifest-relative path. Keep all old-layout path decisions here
|
|
1455
|
+
* so support can be removed by deleting this module and the entrypoint branch
|
|
1456
|
+
* that imports it.
|
|
1457
|
+
*/
|
|
1458
|
+
const getLegacyManifestAssetStoragePath = ({ assetPath }) => assetPath;
|
|
1459
|
+
//#endregion
|
|
1460
|
+
//#region ../plugin-core/dist/assetStorageLayout.mjs
|
|
1461
|
+
const createStorageUriWithRelativePath = ({ baseStorageUri, relativePath }) => {
|
|
1462
|
+
const storageUrl = new URL(baseStorageUri);
|
|
1463
|
+
storageUrl.pathname = `${storageUrl.pathname.replace(/\/+$/, "")}/${relativePath.replace(/\\/g, "/").split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/")}`;
|
|
1464
|
+
return storageUrl.toString();
|
|
1465
|
+
};
|
|
1466
|
+
const getAssetStorageLayout = (assetBaseStorageUri) => {
|
|
1467
|
+
const pathname = new URL(assetBaseStorageUri).pathname.replace(/\/+$/, "");
|
|
1468
|
+
return pathname.endsWith("/assets") || pathname === "/assets" ? "content-addressed" : "legacy-files";
|
|
1469
|
+
};
|
|
1470
|
+
const getManifestAssetStoragePath = ({ assetBaseStorageUri, assetPath, fileHash }) => {
|
|
1471
|
+
if (getAssetStorageLayout(assetBaseStorageUri) === "content-addressed") return getContentAddressedAssetStoragePath({
|
|
1472
|
+
assetPath,
|
|
1473
|
+
fileHash
|
|
1474
|
+
});
|
|
1475
|
+
return getLegacyManifestAssetStoragePath({ assetPath });
|
|
1476
|
+
};
|
|
1477
|
+
const resolveManifestAssetStorageUri = ({ assetBaseStorageUri, assetPath, fileHash }) => createStorageUriWithRelativePath({
|
|
1478
|
+
baseStorageUri: assetBaseStorageUri,
|
|
1479
|
+
relativePath: getManifestAssetStoragePath({
|
|
1480
|
+
assetBaseStorageUri,
|
|
1481
|
+
assetPath,
|
|
1482
|
+
fileHash
|
|
1483
|
+
})
|
|
1484
|
+
});
|
|
1485
|
+
//#endregion
|
|
1443
1486
|
//#region ../../node_modules/.pnpm/es-toolkit@1.32.0/node_modules/es-toolkit/dist/_internal/compareValues.mjs
|
|
1444
1487
|
function compareValues(a, b, order) {
|
|
1445
1488
|
if (a < b) return order === "asc" ? -1 : 1;
|
|
@@ -2795,7 +2838,6 @@ var require_min_version$2 = /* @__PURE__ */ __commonJSMin$1(((exports, module) =
|
|
|
2795
2838
|
break;
|
|
2796
2839
|
case "<":
|
|
2797
2840
|
case "<=": break;
|
|
2798
|
-
/* istanbul ignore next */
|
|
2799
2841
|
default: throw new Error(`Unexpected operation: ${comparator.operator}`);
|
|
2800
2842
|
}
|
|
2801
2843
|
});
|
|
@@ -3183,6 +3225,24 @@ function paginateBundles({ bundles, limit, offset, cursor, orderBy }) {
|
|
|
3183
3225
|
};
|
|
3184
3226
|
}
|
|
3185
3227
|
//#endregion
|
|
3228
|
+
//#region ../plugin-core/dist/requestUpdateBundleState.mjs
|
|
3229
|
+
const requestUpdateBundleSeeds = /* @__PURE__ */ new WeakMap();
|
|
3230
|
+
const isWeakMapKey = (value) => typeof value === "object" && value !== null || typeof value === "function";
|
|
3231
|
+
const toBundleSeeds = (seeds) => seeds.filter((seed) => !!seed);
|
|
3232
|
+
const seedRequestUpdateBundles = (context, seeds) => {
|
|
3233
|
+
if (!isWeakMapKey(context)) return;
|
|
3234
|
+
const nextSeeds = toBundleSeeds(seeds);
|
|
3235
|
+
if (nextSeeds.length === 0) return;
|
|
3236
|
+
const bundlesById = /* @__PURE__ */ new Map();
|
|
3237
|
+
for (const seed of requestUpdateBundleSeeds.get(context) ?? []) bundlesById.set(seed.id, seed);
|
|
3238
|
+
for (const seed of nextSeeds) bundlesById.set(seed.id, seed);
|
|
3239
|
+
requestUpdateBundleSeeds.set(context, [...bundlesById.values()]);
|
|
3240
|
+
};
|
|
3241
|
+
const getRequestUpdateBundleSeeds = (context) => {
|
|
3242
|
+
if (!isWeakMapKey(context)) return [];
|
|
3243
|
+
return requestUpdateBundleSeeds.get(context) ?? [];
|
|
3244
|
+
};
|
|
3245
|
+
//#endregion
|
|
3186
3246
|
//#region ../../packages/core/dist/index.mjs
|
|
3187
3247
|
const getManifestStorageUri = (bundle) => bundle.manifestStorageUri ?? null;
|
|
3188
3248
|
const getManifestFileHash = (bundle) => bundle.manifestFileHash ?? null;
|
|
@@ -3319,7 +3379,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
3319
3379
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3320
3380
|
var __getProtoOf = Object.getPrototypeOf;
|
|
3321
3381
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
3322
|
-
var __commonJSMin = (cb, mod) => () => (mod ||
|
|
3382
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
3323
3383
|
var __copyProps = (to, from, except, desc) => {
|
|
3324
3384
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
3325
3385
|
key = keys[i];
|
|
@@ -4308,7 +4368,6 @@ var require_min_version$1 = /* @__PURE__ */ __commonJSMin(((exports, module) =>
|
|
|
4308
4368
|
break;
|
|
4309
4369
|
case "<":
|
|
4310
4370
|
case "<=": break;
|
|
4311
|
-
/* istanbul ignore next */
|
|
4312
4371
|
default: throw new Error(`Unexpected operation: ${comparator.operator}`);
|
|
4313
4372
|
}
|
|
4314
4373
|
});
|
|
@@ -4678,7 +4737,34 @@ const day = 3600 * 24;
|
|
|
4678
4737
|
day * 7;
|
|
4679
4738
|
day * 365.25;
|
|
4680
4739
|
//#endregion
|
|
4740
|
+
//#region ../plugin-core/dist/resolveUpdateInfoFromBundles.mjs
|
|
4741
|
+
const findSeedBundle = (bundles, bundleId) => bundles.find((bundle) => bundle.id === bundleId);
|
|
4742
|
+
const resolveUpdateInfoFromBundles = async ({ args, bundles, context }) => {
|
|
4743
|
+
const info = await getUpdateInfo(bundles, args);
|
|
4744
|
+
if (!info) return null;
|
|
4745
|
+
seedRequestUpdateBundles(context, [findSeedBundle(bundles, info.id), args.bundleId === "00000000-0000-0000-0000-000000000000" ? null : findSeedBundle(bundles, args.bundleId)]);
|
|
4746
|
+
return info;
|
|
4747
|
+
};
|
|
4748
|
+
//#endregion
|
|
4681
4749
|
//#region ../plugin-core/dist/createBlobDatabasePlugin.mjs
|
|
4750
|
+
const STORAGE_OPERATION_CONCURRENCY = 8;
|
|
4751
|
+
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
4752
|
+
const results = [];
|
|
4753
|
+
let nextIndex = 0;
|
|
4754
|
+
const workerCount = Math.min(concurrency, items.length);
|
|
4755
|
+
await Promise.all(Array.from({ length: workerCount }, async () => {
|
|
4756
|
+
while (true) {
|
|
4757
|
+
const index = nextIndex;
|
|
4758
|
+
nextIndex += 1;
|
|
4759
|
+
if (index >= items.length) break;
|
|
4760
|
+
results[index] = await mapper(items[index], index);
|
|
4761
|
+
}
|
|
4762
|
+
}));
|
|
4763
|
+
return results;
|
|
4764
|
+
}
|
|
4765
|
+
async function forEachWithConcurrency(items, concurrency, mapper) {
|
|
4766
|
+
await mapWithConcurrency(items, concurrency, mapper);
|
|
4767
|
+
}
|
|
4682
4768
|
function removeBundleInternalKeys(bundle) {
|
|
4683
4769
|
const { _updateJsonKey, _oldUpdateJsonKey, ...pureBundle } = bundle;
|
|
4684
4770
|
return pureBundle;
|
|
@@ -4727,6 +4813,12 @@ const MANAGEMENT_INDEX_PREFIX = "_index";
|
|
|
4727
4813
|
const MANAGEMENT_INDEX_VERSION = 1;
|
|
4728
4814
|
const DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE = 128;
|
|
4729
4815
|
const ALL_SCOPE_CACHE_KEY = "*|*";
|
|
4816
|
+
function summarizeManagementIndexArtifacts(artifacts) {
|
|
4817
|
+
return {
|
|
4818
|
+
pagesWritten: artifacts.pages.size,
|
|
4819
|
+
scopesWritten: artifacts.scopes.length
|
|
4820
|
+
};
|
|
4821
|
+
}
|
|
4730
4822
|
function resolveManagementIndexPageSize(config) {
|
|
4731
4823
|
const pageSize = config.managementIndexPageSize ?? DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE;
|
|
4732
4824
|
if (!Number.isInteger(pageSize) || pageSize < 1) throw new Error("managementIndexPageSize must be a positive integer.");
|
|
@@ -4858,11 +4950,18 @@ function buildManagementIndexArtifacts(allBundles, pageSize) {
|
|
|
4858
4950
|
const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
4859
4951
|
return (config, hooks) => {
|
|
4860
4952
|
const managementIndexPageSize = resolveManagementIndexPageSize(config);
|
|
4861
|
-
const { listObjects, loadObject, uploadObject, deleteObject, invalidatePaths, apiBasePath } = factory(config);
|
|
4953
|
+
const { listObjects, loadObject, uploadObject, deleteObject, shouldSkipLoadObjectError, invalidatePaths, apiBasePath } = factory(config);
|
|
4862
4954
|
const bundlesMap = /* @__PURE__ */ new Map();
|
|
4863
4955
|
const pendingBundlesMap = /* @__PURE__ */ new Map();
|
|
4864
4956
|
const managementRootCache = /* @__PURE__ */ new Map();
|
|
4865
|
-
const
|
|
4957
|
+
const loadOptionalObject = async (key) => {
|
|
4958
|
+
try {
|
|
4959
|
+
return await loadObject(key);
|
|
4960
|
+
} catch (error) {
|
|
4961
|
+
if (shouldSkipLoadObjectError?.(error, key)) return null;
|
|
4962
|
+
throw error;
|
|
4963
|
+
}
|
|
4964
|
+
};
|
|
4866
4965
|
const getAllManagementArtifact = (artifacts) => {
|
|
4867
4966
|
const allArtifact = artifacts.scopes.find((scope) => scope.cacheKey === ALL_SCOPE_CACHE_KEY);
|
|
4868
4967
|
if (!allArtifact) throw new Error("all-bundles management index artifact not found");
|
|
@@ -4879,7 +4978,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
4879
4978
|
};
|
|
4880
4979
|
const loadStoredManagementRoot = async (scope) => {
|
|
4881
4980
|
const cacheKey = getManagementScopeCacheKey(scope);
|
|
4882
|
-
const storedRoot = await
|
|
4981
|
+
const storedRoot = await loadOptionalObject(getManagementRootKey(scope));
|
|
4883
4982
|
if (storedRoot) {
|
|
4884
4983
|
managementRootCache.set(cacheKey, storedRoot);
|
|
4885
4984
|
return storedRoot;
|
|
@@ -4889,7 +4988,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
4889
4988
|
};
|
|
4890
4989
|
const loadManagementPage = async (descriptor, pageCache) => {
|
|
4891
4990
|
if (pageCache?.has(descriptor.key)) return pageCache.get(descriptor.key) ?? null;
|
|
4892
|
-
const page = await
|
|
4991
|
+
const page = await loadOptionalObject(descriptor.key);
|
|
4893
4992
|
pageCache?.set(descriptor.key, page);
|
|
4894
4993
|
return page;
|
|
4895
4994
|
};
|
|
@@ -4912,13 +5011,13 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
4912
5011
|
return allBundles;
|
|
4913
5012
|
};
|
|
4914
5013
|
const persistManagementIndexArtifacts = async (nextArtifacts, previousArtifacts) => {
|
|
4915
|
-
|
|
4916
|
-
|
|
5014
|
+
await forEachWithConcurrency(Array.from(nextArtifacts.pages.entries()), STORAGE_OPERATION_CONCURRENCY, ([key, page]) => uploadObject(key, page));
|
|
5015
|
+
await forEachWithConcurrency(nextArtifacts.scopes, STORAGE_OPERATION_CONCURRENCY, (scope) => uploadObject(scope.rootKey, scope.root));
|
|
4917
5016
|
if (!previousArtifacts) return;
|
|
4918
5017
|
const nextPageKeys = new Set(nextArtifacts.pages.keys());
|
|
4919
5018
|
const nextRootKeys = new Set(nextArtifacts.scopes.map((scope) => scope.rootKey));
|
|
4920
|
-
|
|
4921
|
-
|
|
5019
|
+
await forEachWithConcurrency(Array.from(previousArtifacts.pages.keys()).filter((key) => !nextPageKeys.has(key)), STORAGE_OPERATION_CONCURRENCY, (key) => deleteObject(key).catch(() => {}));
|
|
5020
|
+
await forEachWithConcurrency(previousArtifacts.scopes.filter((scope) => !nextRootKeys.has(scope.rootKey)), STORAGE_OPERATION_CONCURRENCY, (scope) => deleteObject(scope.rootKey).catch(() => {}));
|
|
4922
5021
|
};
|
|
4923
5022
|
const ensureAllManagementRoot = async () => {
|
|
4924
5023
|
const storedAllRoot = await loadStoredManagementRoot({});
|
|
@@ -4951,6 +5050,26 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
4951
5050
|
const loadCurrentBundlesForIndexRebuild = async () => {
|
|
4952
5051
|
return loadAllBundlesForManagementFallback();
|
|
4953
5052
|
};
|
|
5053
|
+
const loadBundlesFromCanonicalManifests = async () => {
|
|
5054
|
+
return sortManagedBundles((await reloadBundles()).map((bundle) => removeBundleInternalKeys(bundle)));
|
|
5055
|
+
};
|
|
5056
|
+
const loadStoredBundlesForIndexRebuild = loadBundlesFromCanonicalManifests;
|
|
5057
|
+
const loadCanonicalBundlesForIndexRepair = loadBundlesFromCanonicalManifests;
|
|
5058
|
+
const compareBundleIndex = ({ canonicalBundles, indexedBundles, rootMissing }) => {
|
|
5059
|
+
const canonicalIds = new Set(canonicalBundles.map((bundle) => bundle.id));
|
|
5060
|
+
const indexedIds = new Set(indexedBundles?.map((bundle) => bundle.id) ?? []);
|
|
5061
|
+
const missingBundleIds = Array.from(canonicalIds).filter((id) => !indexedIds.has(id)).sort((left, right) => right.localeCompare(left));
|
|
5062
|
+
const extraBundleIds = Array.from(indexedIds).filter((id) => !canonicalIds.has(id)).sort((left, right) => right.localeCompare(left));
|
|
5063
|
+
return {
|
|
5064
|
+
status: missingBundleIds.length === 0 && extraBundleIds.length === 0 && !rootMissing ? "ok" : rootMissing ? "missing" : "stale",
|
|
5065
|
+
canonicalBundles: canonicalBundles.length,
|
|
5066
|
+
indexedBundles: indexedBundles?.length ?? 0,
|
|
5067
|
+
missingBundles: missingBundleIds.length,
|
|
5068
|
+
extraBundles: extraBundleIds.length,
|
|
5069
|
+
missingBundleIds: missingBundleIds.slice(0, 20),
|
|
5070
|
+
extraBundleIds: extraBundleIds.slice(0, 20)
|
|
5071
|
+
};
|
|
5072
|
+
};
|
|
4954
5073
|
const findPageIndexContainingId = (pages, id) => {
|
|
4955
5074
|
return pages.findIndex((page) => id.localeCompare(page.firstId) <= 0 && id.localeCompare(page.lastId) >= 0);
|
|
4956
5075
|
};
|
|
@@ -5126,16 +5245,15 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5126
5245
|
};
|
|
5127
5246
|
async function reloadBundles() {
|
|
5128
5247
|
bundlesMap.clear();
|
|
5129
|
-
const
|
|
5130
|
-
return (await
|
|
5248
|
+
const allBundles = (await mapWithConcurrency((await listObjects("")).filter((key) => /^[^/]+\/(?:ios|android)\/[^/]+\/update\.json$/.test(key)), STORAGE_OPERATION_CONCURRENCY, async (key) => {
|
|
5249
|
+
return (await loadOptionalObject(key) ?? []).map((bundle) => ({
|
|
5131
5250
|
...bundle,
|
|
5132
5251
|
_updateJsonKey: key
|
|
5133
5252
|
}));
|
|
5134
|
-
});
|
|
5135
|
-
const allBundles = (await Promise.all(filePromises)).flat();
|
|
5253
|
+
})).flat();
|
|
5136
5254
|
for (const bundle of allBundles) bundlesMap.set(bundle.id, bundle);
|
|
5137
5255
|
for (const [id, bundle] of pendingBundlesMap.entries()) bundlesMap.set(id, bundle);
|
|
5138
|
-
return orderBy(
|
|
5256
|
+
return orderBy(Array.from(bundlesMap.values()), [(v) => v.id], ["desc"]);
|
|
5139
5257
|
}
|
|
5140
5258
|
/**
|
|
5141
5259
|
* Updates target-app-versions.json for each channel on the given platform.
|
|
@@ -5161,35 +5279,44 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5161
5279
|
const updateKeys = keysByChannel[channel];
|
|
5162
5280
|
const targetKey = `${channel}/${platform}/target-app-versions.json`;
|
|
5163
5281
|
const currentVersions = updateKeys.map((key) => key.split("/")[2]);
|
|
5164
|
-
const oldTargetVersions = await
|
|
5282
|
+
const oldTargetVersions = await loadOptionalObject(targetKey) ?? [];
|
|
5165
5283
|
const newTargetVersions = oldTargetVersions.filter((v) => currentVersions.includes(v));
|
|
5166
5284
|
for (const v of currentVersions) if (!newTargetVersions.includes(v)) newTargetVersions.push(v);
|
|
5167
5285
|
if (JSON.stringify(oldTargetVersions) !== JSON.stringify(newTargetVersions)) await uploadObject(targetKey, newTargetVersions);
|
|
5168
5286
|
}
|
|
5169
5287
|
}
|
|
5170
|
-
const getAppVersionUpdateInfo = async ({ appVersion, bundleId, channel = "production", cohort, minBundleId, platform }) => {
|
|
5171
|
-
const
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5288
|
+
const getAppVersionUpdateInfo = async ({ appVersion, bundleId, channel = "production", cohort, minBundleId, platform }, context) => {
|
|
5289
|
+
const bundles = (await mapWithConcurrency(filterCompatibleAppVersions(await loadOptionalObject(`${channel}/${platform}/target-app-versions.json`) ?? [], appVersion), STORAGE_OPERATION_CONCURRENCY, async (targetAppVersion) => {
|
|
5290
|
+
return await loadOptionalObject(`${channel}/${platform}/${normalizeTargetAppVersion(targetAppVersion) ?? targetAppVersion}/update.json`) ?? [];
|
|
5291
|
+
})).flat();
|
|
5292
|
+
return resolveUpdateInfoFromBundles({
|
|
5293
|
+
args: {
|
|
5294
|
+
_updateStrategy: "appVersion",
|
|
5295
|
+
appVersion,
|
|
5296
|
+
bundleId,
|
|
5297
|
+
channel,
|
|
5298
|
+
cohort,
|
|
5299
|
+
minBundleId,
|
|
5300
|
+
platform
|
|
5301
|
+
},
|
|
5302
|
+
bundles,
|
|
5303
|
+
context
|
|
5182
5304
|
});
|
|
5183
5305
|
};
|
|
5184
|
-
const getFingerprintUpdateInfo = async ({ bundleId, channel = "production", cohort, fingerprintHash, minBundleId, platform }) => {
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5306
|
+
const getFingerprintUpdateInfo = async ({ bundleId, channel = "production", cohort, fingerprintHash, minBundleId, platform }, context) => {
|
|
5307
|
+
const bundles = await loadOptionalObject(`${channel}/${platform}/${fingerprintHash}/update.json`) ?? [];
|
|
5308
|
+
return resolveUpdateInfoFromBundles({
|
|
5309
|
+
args: {
|
|
5310
|
+
_updateStrategy: "fingerprint",
|
|
5311
|
+
bundleId,
|
|
5312
|
+
channel,
|
|
5313
|
+
cohort,
|
|
5314
|
+
fingerprintHash,
|
|
5315
|
+
minBundleId,
|
|
5316
|
+
platform
|
|
5317
|
+
},
|
|
5318
|
+
bundles,
|
|
5319
|
+
context
|
|
5193
5320
|
});
|
|
5194
5321
|
};
|
|
5195
5322
|
const addAppVersionInvalidationPaths = (pathsToInvalidate, { platform, channel, targetAppVersion }) => {
|
|
@@ -5211,7 +5338,35 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5211
5338
|
targetAppVersion
|
|
5212
5339
|
});
|
|
5213
5340
|
};
|
|
5214
|
-
|
|
5341
|
+
const bundleIndexDiagnostics = {
|
|
5342
|
+
async check() {
|
|
5343
|
+
const canonicalBundles = await loadCanonicalBundlesForIndexRepair();
|
|
5344
|
+
const allRoot = await loadStoredManagementRoot({});
|
|
5345
|
+
return compareBundleIndex({
|
|
5346
|
+
canonicalBundles,
|
|
5347
|
+
indexedBundles: allRoot ? await loadAllBundlesFromRoot(allRoot) : null,
|
|
5348
|
+
rootMissing: !allRoot
|
|
5349
|
+
});
|
|
5350
|
+
},
|
|
5351
|
+
async repair() {
|
|
5352
|
+
const canonicalBundles = await loadCanonicalBundlesForIndexRepair();
|
|
5353
|
+
const previousRoot = await loadStoredManagementRoot({});
|
|
5354
|
+
const previousBundles = previousRoot ? await loadAllBundlesFromRoot(previousRoot) : null;
|
|
5355
|
+
const previousArtifacts = previousRoot && previousBundles ? buildManagementIndexArtifacts(previousBundles, previousRoot.pageSize) : void 0;
|
|
5356
|
+
const nextArtifacts = buildManagementIndexArtifacts(canonicalBundles, managementIndexPageSize);
|
|
5357
|
+
const indexedObjectKeys = await listObjects(`${MANAGEMENT_INDEX_PREFIX}/`);
|
|
5358
|
+
const nextObjectKeys = new Set([...nextArtifacts.pages.keys(), ...nextArtifacts.scopes.map((scope) => scope.rootKey)]);
|
|
5359
|
+
await persistManagementIndexArtifacts(nextArtifacts, previousArtifacts);
|
|
5360
|
+
await forEachWithConcurrency(indexedObjectKeys.filter((key) => !nextObjectKeys.has(key)), STORAGE_OPERATION_CONCURRENCY, (key) => deleteObject(key).catch(() => {}));
|
|
5361
|
+
replaceManagementRootCache(nextArtifacts);
|
|
5362
|
+
return {
|
|
5363
|
+
scannedBundles: canonicalBundles.length,
|
|
5364
|
+
indexedBundles: canonicalBundles.length,
|
|
5365
|
+
...summarizeManagementIndexArtifacts(nextArtifacts)
|
|
5366
|
+
};
|
|
5367
|
+
}
|
|
5368
|
+
};
|
|
5369
|
+
const createPlugin = createDatabasePlugin({
|
|
5215
5370
|
name,
|
|
5216
5371
|
factory: () => ({
|
|
5217
5372
|
supportsCursorPagination: true,
|
|
@@ -5235,9 +5390,9 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5235
5390
|
if (!matchedBundle) return null;
|
|
5236
5391
|
return removeBundleInternalKeys(matchedBundle);
|
|
5237
5392
|
},
|
|
5238
|
-
async getUpdateInfo(args) {
|
|
5239
|
-
if (args._updateStrategy === "appVersion") return getAppVersionUpdateInfo(args);
|
|
5240
|
-
return getFingerprintUpdateInfo(args);
|
|
5393
|
+
async getUpdateInfo(args, context) {
|
|
5394
|
+
if (args._updateStrategy === "appVersion") return getAppVersionUpdateInfo(args, context);
|
|
5395
|
+
return getFingerprintUpdateInfo(args, context);
|
|
5241
5396
|
},
|
|
5242
5397
|
async getBundles(options) {
|
|
5243
5398
|
const { where, limit, offset, orderBy, cursor } = options;
|
|
@@ -5270,11 +5425,8 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5270
5425
|
const changedBundlesByKey = {};
|
|
5271
5426
|
const removalsByKey = {};
|
|
5272
5427
|
const pathsToInvalidate = /* @__PURE__ */ new Set();
|
|
5273
|
-
|
|
5274
|
-
let isChannelChanged = false;
|
|
5428
|
+
const targetVersionPlatforms = /* @__PURE__ */ new Set();
|
|
5275
5429
|
for (const { operation, data } of changedSets) {
|
|
5276
|
-
if (data.targetAppVersion !== void 0) isTargetAppVersionChanged = true;
|
|
5277
|
-
if (operation === "update" && data.channel !== void 0) isChannelChanged = true;
|
|
5278
5430
|
if (operation === "insert") {
|
|
5279
5431
|
const target = resolveStorageTarget(data);
|
|
5280
5432
|
const key = `${data.channel}/${data.platform}/${target}/update.json`;
|
|
@@ -5286,6 +5438,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5286
5438
|
pendingBundlesMap.set(data.id, bundleWithKey);
|
|
5287
5439
|
changedBundlesByKey[key] = changedBundlesByKey[key] || [];
|
|
5288
5440
|
changedBundlesByKey[key].push(removeBundleInternalKeys(bundleWithKey));
|
|
5441
|
+
if (data.targetAppVersion !== void 0) targetVersionPlatforms.add(data.platform);
|
|
5289
5442
|
addLookupInvalidationPaths(pathsToInvalidate, data);
|
|
5290
5443
|
continue;
|
|
5291
5444
|
}
|
|
@@ -5298,6 +5451,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5298
5451
|
const key = bundle._updateJsonKey;
|
|
5299
5452
|
removalsByKey[key] = removalsByKey[key] || [];
|
|
5300
5453
|
removalsByKey[key].push(bundle.id);
|
|
5454
|
+
if (bundle.targetAppVersion !== void 0) targetVersionPlatforms.add(bundle.platform);
|
|
5301
5455
|
addLookupInvalidationPaths(pathsToInvalidate, bundle);
|
|
5302
5456
|
continue;
|
|
5303
5457
|
}
|
|
@@ -5329,6 +5483,10 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5329
5483
|
channel: nextChannel
|
|
5330
5484
|
});
|
|
5331
5485
|
}
|
|
5486
|
+
if (bundle.targetAppVersion !== void 0 || updatedBundle.targetAppVersion !== void 0) {
|
|
5487
|
+
targetVersionPlatforms.add(bundle.platform);
|
|
5488
|
+
targetVersionPlatforms.add(updatedBundle.platform);
|
|
5489
|
+
}
|
|
5332
5490
|
addLookupInvalidationPaths(pathsToInvalidate, updatedBundle);
|
|
5333
5491
|
if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle.targetAppVersion) addLookupInvalidationPaths(pathsToInvalidate, bundle);
|
|
5334
5492
|
continue;
|
|
@@ -5342,14 +5500,14 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5342
5500
|
if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle.targetAppVersion) addLookupInvalidationPaths(pathsToInvalidate, bundle);
|
|
5343
5501
|
}
|
|
5344
5502
|
}
|
|
5345
|
-
|
|
5346
|
-
const updatedBundles = (await
|
|
5503
|
+
await forEachWithConcurrency(Object.keys(removalsByKey), STORAGE_OPERATION_CONCURRENCY, async (oldKey) => {
|
|
5504
|
+
const updatedBundles = (await loadOptionalObject(oldKey) ?? []).filter((b) => !removalsByKey[oldKey].includes(b.id));
|
|
5347
5505
|
updatedBundles.sort((a, b) => b.id.localeCompare(a.id));
|
|
5348
5506
|
if (updatedBundles.length === 0) await deleteObject(oldKey);
|
|
5349
5507
|
else await uploadObject(oldKey, updatedBundles);
|
|
5350
|
-
})
|
|
5351
|
-
|
|
5352
|
-
const currentBundles = await
|
|
5508
|
+
});
|
|
5509
|
+
await forEachWithConcurrency(Object.keys(changedBundlesByKey), STORAGE_OPERATION_CONCURRENCY, async (key) => {
|
|
5510
|
+
const currentBundles = await loadOptionalObject(key) ?? [];
|
|
5353
5511
|
const pureBundles = changedBundlesByKey[key].map((bundle) => bundle);
|
|
5354
5512
|
for (const changedBundle of pureBundles) {
|
|
5355
5513
|
const index = currentBundles.findIndex((b) => b.id === changedBundle.id);
|
|
@@ -5358,10 +5516,11 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5358
5516
|
}
|
|
5359
5517
|
currentBundles.sort((a, b) => b.id.localeCompare(a.id));
|
|
5360
5518
|
await uploadObject(key, currentBundles);
|
|
5361
|
-
})
|
|
5362
|
-
if (
|
|
5363
|
-
const
|
|
5364
|
-
const
|
|
5519
|
+
});
|
|
5520
|
+
if (targetVersionPlatforms.size > 0) await Promise.all(Array.from(targetVersionPlatforms).map((platform) => updateTargetVersionsForPlatform(platform)));
|
|
5521
|
+
const previousIndexBundles = await loadCurrentBundlesForIndexRebuild();
|
|
5522
|
+
const storedIndexBundles = await loadStoredBundlesForIndexRebuild();
|
|
5523
|
+
const nextIndexMap = new Map(storedIndexBundles.map((bundle) => [bundle.id, bundle]));
|
|
5365
5524
|
for (const { operation, data } of changedSets) {
|
|
5366
5525
|
if (operation === "delete") {
|
|
5367
5526
|
nextIndexMap.delete(data.id);
|
|
@@ -5370,7 +5529,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5370
5529
|
nextIndexMap.set(data.id, data);
|
|
5371
5530
|
}
|
|
5372
5531
|
const nextIndexBundles = sortManagedBundles(Array.from(nextIndexMap.values()));
|
|
5373
|
-
const previousArtifacts = buildManagementIndexArtifacts(
|
|
5532
|
+
const previousArtifacts = buildManagementIndexArtifacts(previousIndexBundles, managementIndexPageSize);
|
|
5374
5533
|
const nextArtifacts = buildManagementIndexArtifacts(nextIndexBundles, managementIndexPageSize);
|
|
5375
5534
|
await persistManagementIndexArtifacts(nextArtifacts, previousArtifacts);
|
|
5376
5535
|
replaceManagementRootCache(nextArtifacts);
|
|
@@ -5381,6 +5540,15 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
5381
5540
|
}
|
|
5382
5541
|
})
|
|
5383
5542
|
})({}, hooks);
|
|
5543
|
+
return () => {
|
|
5544
|
+
const plugin = createPlugin();
|
|
5545
|
+
Object.defineProperty(plugin, "diagnostics", {
|
|
5546
|
+
configurable: true,
|
|
5547
|
+
enumerable: true,
|
|
5548
|
+
value: { bundleIndex: bundleIndexDiagnostics }
|
|
5549
|
+
});
|
|
5550
|
+
return plugin;
|
|
5551
|
+
};
|
|
5384
5552
|
};
|
|
5385
5553
|
};
|
|
5386
5554
|
//#endregion
|
|
@@ -5436,6 +5604,9 @@ const createProfiledStoragePlugin = ({ createProfiles, name, profileShape, suppo
|
|
|
5436
5604
|
async downloadFile(storageUri, filePath) {
|
|
5437
5605
|
return requireNodeProfile().downloadFile(storageUri, filePath);
|
|
5438
5606
|
},
|
|
5607
|
+
async exists(storageUri) {
|
|
5608
|
+
return requireNodeProfile().exists(storageUri);
|
|
5609
|
+
},
|
|
5439
5610
|
async upload(key, filePath) {
|
|
5440
5611
|
return requireNodeProfile().upload(key, filePath);
|
|
5441
5612
|
}
|
|
@@ -5519,6 +5690,34 @@ function assertRuntimeStoragePlugin(plugin) {
|
|
|
5519
5690
|
if (!isRuntimeStoragePlugin(plugin)) throw createMissingProfileError(plugin, "runtime");
|
|
5520
5691
|
}
|
|
5521
5692
|
//#endregion
|
|
5693
|
+
//#region ../../packages/server/src/db/requestBundleIdentityMap.ts
|
|
5694
|
+
const createRequestBundleIdentityMap = ({ context, loadBundleById, seeds }) => {
|
|
5695
|
+
const bundles = /* @__PURE__ */ new Map();
|
|
5696
|
+
const pendingBundles = /* @__PURE__ */ new Map();
|
|
5697
|
+
for (const seed of seeds) if (seed) bundles.set(seed.id, seed);
|
|
5698
|
+
const get = async (bundleId) => {
|
|
5699
|
+
const cachedBundle = bundles.get(bundleId);
|
|
5700
|
+
if (cachedBundle) return cachedBundle;
|
|
5701
|
+
const pendingBundle = pendingBundles.get(bundleId);
|
|
5702
|
+
if (pendingBundle) return pendingBundle;
|
|
5703
|
+
const lookup = loadBundleById(bundleId, context).then((bundle) => {
|
|
5704
|
+
pendingBundles.delete(bundleId);
|
|
5705
|
+
if (bundle) bundles.set(bundle.id, bundle);
|
|
5706
|
+
return bundle;
|
|
5707
|
+
}, (error) => {
|
|
5708
|
+
pendingBundles.delete(bundleId);
|
|
5709
|
+
throw error;
|
|
5710
|
+
});
|
|
5711
|
+
pendingBundles.set(bundleId, lookup);
|
|
5712
|
+
return lookup;
|
|
5713
|
+
};
|
|
5714
|
+
const peek = (bundleId) => bundles.get(bundleId) ?? null;
|
|
5715
|
+
return {
|
|
5716
|
+
get,
|
|
5717
|
+
peek
|
|
5718
|
+
};
|
|
5719
|
+
};
|
|
5720
|
+
//#endregion
|
|
5522
5721
|
//#region ../../packages/server/src/db/schemaEnhancements.ts
|
|
5523
5722
|
const normalizeNullableString = (value) => {
|
|
5524
5723
|
if (value === null || value === void 0) return null;
|
|
@@ -5554,11 +5753,6 @@ const isBundleManifest = (value) => {
|
|
|
5554
5753
|
return typeof manifestAsset.fileHash === "string" && (manifestAsset.signature === void 0 || typeof manifestAsset.signature === "string");
|
|
5555
5754
|
});
|
|
5556
5755
|
};
|
|
5557
|
-
const createChildStorageUri = (baseStorageUri, relativePath) => {
|
|
5558
|
-
const baseUrl = new URL(baseStorageUri);
|
|
5559
|
-
baseUrl.pathname = `${baseUrl.pathname.replace(/\/+$/, "")}/${relativePath.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/")}`;
|
|
5560
|
-
return baseUrl.toString();
|
|
5561
|
-
};
|
|
5562
5756
|
async function fetchBundleManifest(storageUri, readStorageText, resolveFileUrl, context) {
|
|
5563
5757
|
const [storageText, fileUrl] = await Promise.all([readStorageText(storageUri, context), resolveFileUrl(storageUri, context)]);
|
|
5564
5758
|
if (storageText === null) return null;
|
|
@@ -5586,7 +5780,11 @@ async function resolveChangedAssets({ assetBaseStorageUri, currentManifest, curr
|
|
|
5586
5780
|
const changedEntries = await Promise.all(Object.entries(targetManifest.assets).map(async ([assetPath, asset]) => {
|
|
5587
5781
|
if ((currentManifest?.assets[assetPath])?.fileHash === asset.fileHash) return null;
|
|
5588
5782
|
const usesBrotliAsset = BR_COMPRESSED_ASSET_PATH_RE.test(assetPath);
|
|
5589
|
-
const storageUri =
|
|
5783
|
+
const storageUri = resolveManifestAssetStorageUri({
|
|
5784
|
+
assetBaseStorageUri,
|
|
5785
|
+
assetPath: usesBrotliAsset ? `${assetPath}.br` : assetPath,
|
|
5786
|
+
fileHash: asset.fileHash
|
|
5787
|
+
});
|
|
5590
5788
|
const patch = patchDescriptor?.assetPath === assetPath ? patchDescriptor.patch : null;
|
|
5591
5789
|
let fileUrl = null;
|
|
5592
5790
|
try {
|
|
@@ -5756,109 +5954,148 @@ function createPluginDatabaseCore(getPlugin, resolveFileUrl, options) {
|
|
|
5756
5954
|
enabled: true,
|
|
5757
5955
|
id: { gte: minBundleId }
|
|
5758
5956
|
});
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
return bundle.enabled && bundle.platform === args.platform && bundle.channel === channel && bundle.id.localeCompare(minBundleId) >= 0 && !!bundle.targetAppVersion && semverSatisfies$1(bundle.targetAppVersion, args.appVersion);
|
|
5791
|
-
}
|
|
5792
|
-
});
|
|
5793
|
-
},
|
|
5794
|
-
async getAppUpdateInfo(args, context) {
|
|
5795
|
-
const info = await this.getUpdateInfo(args, context);
|
|
5796
|
-
if (!info) return null;
|
|
5797
|
-
const { storageUri, ...rest } = info;
|
|
5798
|
-
const readStorageText = options?.readStorageText;
|
|
5799
|
-
if (info.id === "00000000-0000-0000-0000-000000000000" || !readStorageText) {
|
|
5800
|
-
const fileUrl = await resolveFileUrl(storageUri ?? null, context);
|
|
5801
|
-
return {
|
|
5802
|
-
...rest,
|
|
5803
|
-
fileUrl
|
|
5804
|
-
};
|
|
5957
|
+
const api = {
|
|
5958
|
+
async getBundleById(id, context) {
|
|
5959
|
+
return getPlugin().getBundleById(id, context);
|
|
5960
|
+
},
|
|
5961
|
+
async getUpdateInfo(args, context) {
|
|
5962
|
+
const directGetUpdateInfo = getPlugin().getUpdateInfo;
|
|
5963
|
+
if (directGetUpdateInfo) return context === void 0 ? await directGetUpdateInfo(args) : await directGetUpdateInfo(args, context);
|
|
5964
|
+
const channel = args.channel ?? "production";
|
|
5965
|
+
const minBundleId = args.minBundleId ?? "00000000-0000-0000-0000-000000000000";
|
|
5966
|
+
const baseWhere = getBaseWhere({
|
|
5967
|
+
platform: args.platform,
|
|
5968
|
+
channel,
|
|
5969
|
+
minBundleId
|
|
5970
|
+
});
|
|
5971
|
+
if (args._updateStrategy === "fingerprint") return findUpdateInfoByScanning({
|
|
5972
|
+
args,
|
|
5973
|
+
queryWhere: {
|
|
5974
|
+
...baseWhere,
|
|
5975
|
+
fingerprintHash: args.fingerprintHash
|
|
5976
|
+
},
|
|
5977
|
+
context,
|
|
5978
|
+
isCandidate: (bundle) => {
|
|
5979
|
+
return bundle.enabled && bundle.platform === args.platform && bundle.channel === channel && bundle.id.localeCompare(minBundleId) >= 0 && bundle.fingerprintHash === args.fingerprintHash;
|
|
5980
|
+
}
|
|
5981
|
+
});
|
|
5982
|
+
return findUpdateInfoByScanning({
|
|
5983
|
+
args,
|
|
5984
|
+
queryWhere: { ...baseWhere },
|
|
5985
|
+
context,
|
|
5986
|
+
isCandidate: (bundle) => {
|
|
5987
|
+
return bundle.enabled && bundle.platform === args.platform && bundle.channel === channel && bundle.id.localeCompare(minBundleId) >= 0 && !!bundle.targetAppVersion && semverSatisfies$1(bundle.targetAppVersion, args.appVersion);
|
|
5805
5988
|
}
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5989
|
+
});
|
|
5990
|
+
},
|
|
5991
|
+
async getAppUpdateInfo(args, context) {
|
|
5992
|
+
const info = await this.getUpdateInfo(args, context);
|
|
5993
|
+
if (!info) return null;
|
|
5994
|
+
const { storageUri, ...rest } = info;
|
|
5995
|
+
const readStorageText = options?.readStorageText;
|
|
5996
|
+
if (info.id === "00000000-0000-0000-0000-000000000000" || !readStorageText) {
|
|
5997
|
+
const fileUrl = await resolveFileUrl(storageUri ?? null, context);
|
|
5998
|
+
return {
|
|
5812
5999
|
...rest,
|
|
5813
6000
|
fileUrl
|
|
5814
6001
|
};
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
return
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
}
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
6002
|
+
}
|
|
6003
|
+
const requestBundleSeeds = getRequestUpdateBundleSeeds(context);
|
|
6004
|
+
const requestBundles = createRequestBundleIdentityMap({
|
|
6005
|
+
context,
|
|
6006
|
+
loadBundleById: (bundleId, requestContext) => getPlugin().getBundleById(bundleId, requestContext),
|
|
6007
|
+
seeds: requestBundleSeeds
|
|
6008
|
+
});
|
|
6009
|
+
const getCurrentBundle = () => {
|
|
6010
|
+
if (args.bundleId === "00000000-0000-0000-0000-000000000000") return null;
|
|
6011
|
+
const seededCurrentBundle = requestBundles.peek(args.bundleId);
|
|
6012
|
+
if (seededCurrentBundle || requestBundleSeeds.length > 0) return seededCurrentBundle;
|
|
6013
|
+
return requestBundles.get(args.bundleId);
|
|
6014
|
+
};
|
|
6015
|
+
const [fileUrl, targetBundle, currentBundle] = await Promise.all([
|
|
6016
|
+
resolveFileUrl(storageUri ?? null, context),
|
|
6017
|
+
requestBundles.get(info.id),
|
|
6018
|
+
getCurrentBundle()
|
|
6019
|
+
]);
|
|
6020
|
+
const baseResponse = {
|
|
6021
|
+
...rest,
|
|
6022
|
+
fileUrl
|
|
6023
|
+
};
|
|
6024
|
+
const manifestArtifacts = await resolveManifestArtifacts({
|
|
6025
|
+
currentBundle,
|
|
6026
|
+
resolveFileUrl,
|
|
6027
|
+
readStorageText,
|
|
6028
|
+
targetBundle,
|
|
6029
|
+
context
|
|
6030
|
+
});
|
|
6031
|
+
if (!manifestArtifacts) return baseResponse;
|
|
6032
|
+
return {
|
|
6033
|
+
...baseResponse,
|
|
6034
|
+
...manifestArtifacts
|
|
6035
|
+
};
|
|
6036
|
+
},
|
|
6037
|
+
async getChannels(context) {
|
|
6038
|
+
return getPlugin().getChannels(context);
|
|
6039
|
+
},
|
|
6040
|
+
async getBundles(options, context) {
|
|
6041
|
+
return getPlugin().getBundles(options, context);
|
|
6042
|
+
},
|
|
6043
|
+
async insertBundle(bundle, context) {
|
|
6044
|
+
assertBundlePersistenceConstraints(bundle);
|
|
6045
|
+
await runWithMutationPlugin(async (plugin) => {
|
|
6046
|
+
await plugin.appendBundle(bundle, context);
|
|
6047
|
+
await plugin.commitBundle(context);
|
|
6048
|
+
});
|
|
6049
|
+
},
|
|
6050
|
+
async updateBundleById(bundleId, newBundle, context) {
|
|
6051
|
+
await runWithMutationPlugin(async (plugin) => {
|
|
6052
|
+
const current = await plugin.getBundleById(bundleId, context);
|
|
6053
|
+
if (!current) throw new Error("targetBundleId not found");
|
|
6054
|
+
assertBundlePersistenceConstraints({
|
|
6055
|
+
...current,
|
|
6056
|
+
...newBundle
|
|
5851
6057
|
});
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
6058
|
+
await plugin.updateBundle(bundleId, newBundle, context);
|
|
6059
|
+
await plugin.commitBundle(context);
|
|
6060
|
+
});
|
|
6061
|
+
},
|
|
6062
|
+
async deleteBundleById(bundleId, context) {
|
|
6063
|
+
await runWithMutationPlugin(async (plugin) => {
|
|
6064
|
+
const bundle = await plugin.getBundleById(bundleId, context);
|
|
6065
|
+
if (!bundle) return;
|
|
6066
|
+
await plugin.deleteBundle(bundle, context);
|
|
6067
|
+
await plugin.commitBundle(context);
|
|
6068
|
+
});
|
|
6069
|
+
}
|
|
6070
|
+
};
|
|
6071
|
+
Object.defineProperty(api, "diagnostics", {
|
|
6072
|
+
configurable: true,
|
|
6073
|
+
enumerable: true,
|
|
6074
|
+
get() {
|
|
6075
|
+
const diagnostics = getPlugin().diagnostics;
|
|
6076
|
+
if (!diagnostics) {
|
|
6077
|
+
Object.defineProperty(this, "diagnostics", {
|
|
6078
|
+
configurable: true,
|
|
6079
|
+
enumerable: true,
|
|
6080
|
+
value: void 0
|
|
5859
6081
|
});
|
|
6082
|
+
return;
|
|
5860
6083
|
}
|
|
5861
|
-
|
|
6084
|
+
const wrappedDiagnostics = {};
|
|
6085
|
+
if (diagnostics.bundleIndex) wrappedDiagnostics.bundleIndex = {
|
|
6086
|
+
check: (context) => getPlugin().diagnostics.bundleIndex.check(context),
|
|
6087
|
+
...diagnostics.bundleIndex.repair ? { repair: (context) => getPlugin().diagnostics.bundleIndex.repair(context) } : {}
|
|
6088
|
+
};
|
|
6089
|
+
Object.defineProperty(this, "diagnostics", {
|
|
6090
|
+
configurable: true,
|
|
6091
|
+
enumerable: true,
|
|
6092
|
+
value: wrappedDiagnostics
|
|
6093
|
+
});
|
|
6094
|
+
return wrappedDiagnostics;
|
|
6095
|
+
}
|
|
6096
|
+
});
|
|
6097
|
+
return {
|
|
6098
|
+
api,
|
|
5862
6099
|
adapterName: getPlugin().name,
|
|
5863
6100
|
createMigrator: () => {
|
|
5864
6101
|
throw new Error("createMigrator is only available for Kysely/Prisma/Drizzle database adapters.");
|
|
@@ -6924,7 +7161,6 @@ var require_min_version = /* @__PURE__ */ __commonJSMin$1(((exports, module) =>
|
|
|
6924
7161
|
break;
|
|
6925
7162
|
case "<":
|
|
6926
7163
|
case "<=": break;
|
|
6927
|
-
/* istanbul ignore next */
|
|
6928
7164
|
default: throw new Error(`Unexpected operation: ${comparator.operator}`);
|
|
6929
7165
|
}
|
|
6930
7166
|
});
|
|
@@ -7274,7 +7510,7 @@ function findRoute(router, method, path) {
|
|
|
7274
7510
|
}
|
|
7275
7511
|
//#endregion
|
|
7276
7512
|
//#region ../../packages/server/src/version.ts
|
|
7277
|
-
const HOT_UPDATER_SERVER_VERSION = "0.
|
|
7513
|
+
const HOT_UPDATER_SERVER_VERSION = "0.33.0";
|
|
7278
7514
|
//#endregion
|
|
7279
7515
|
//#region ../../packages/server/src/handler.ts
|
|
7280
7516
|
var HandlerBadRequestError = class extends Error {
|
|
@@ -7285,6 +7521,8 @@ var HandlerBadRequestError = class extends Error {
|
|
|
7285
7521
|
};
|
|
7286
7522
|
const SDK_VERSION_HEADER = "Hot-Updater-SDK-Version";
|
|
7287
7523
|
const EXPLICIT_NO_UPDATE_MIN_SDK_VERSION = "0.31.0";
|
|
7524
|
+
const DEFAULT_BUNDLE_LIST_LIMIT = 50;
|
|
7525
|
+
const MAX_BUNDLE_LIST_LIMIT = 100;
|
|
7288
7526
|
const supportsExplicitNoUpdateResponse = (request) => {
|
|
7289
7527
|
const sdkVersion = request.headers.get(SDK_VERSION_HEADER)?.trim();
|
|
7290
7528
|
if (!sdkVersion) return false;
|
|
@@ -7334,6 +7572,13 @@ const parseStringArraySearchParam = (url, key) => {
|
|
|
7334
7572
|
const values = url.searchParams.getAll(key);
|
|
7335
7573
|
return values.length > 0 ? values : void 0;
|
|
7336
7574
|
};
|
|
7575
|
+
const parsePositiveIntegerSearchParam = (url, key, defaultValue, maxValue) => {
|
|
7576
|
+
const value = url.searchParams.get(key);
|
|
7577
|
+
if (value === null) return defaultValue;
|
|
7578
|
+
const parsed = Number(value);
|
|
7579
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > maxValue) throw new HandlerBadRequestError(`The '${key}' query parameter must be a positive integer between 1 and ${maxValue}.`);
|
|
7580
|
+
return parsed;
|
|
7581
|
+
};
|
|
7337
7582
|
const requirePlatformParam = (params) => {
|
|
7338
7583
|
const platform = requireRouteParam(params, "platform");
|
|
7339
7584
|
if (!isPlatform(platform)) throw new HandlerBadRequestError(`Invalid platform: ${platform}. Expected 'ios' or 'android'.`);
|
|
@@ -7402,7 +7647,7 @@ const handleGetBundles = async (_params, request, api, context) => {
|
|
|
7402
7647
|
const url = new URL(request.url);
|
|
7403
7648
|
const channel = url.searchParams.get("channel") ?? void 0;
|
|
7404
7649
|
const platform = url.searchParams.get("platform");
|
|
7405
|
-
const limit =
|
|
7650
|
+
const limit = parsePositiveIntegerSearchParam(url, "limit", DEFAULT_BUNDLE_LIST_LIMIT, MAX_BUNDLE_LIST_LIMIT);
|
|
7406
7651
|
const pageParam = url.searchParams.get("page");
|
|
7407
7652
|
const offset = url.searchParams.get("offset");
|
|
7408
7653
|
const after = url.searchParams.get("after") ?? void 0;
|
|
@@ -7504,18 +7749,19 @@ const routes = {
|
|
|
7504
7749
|
*/
|
|
7505
7750
|
function createHandler(api, options = {}) {
|
|
7506
7751
|
const basePath = options.basePath ?? "/api";
|
|
7507
|
-
const
|
|
7508
|
-
|
|
7509
|
-
|
|
7752
|
+
const routeOptions = {
|
|
7753
|
+
updateCheck: options.routes?.updateCheck ?? true,
|
|
7754
|
+
bundles: options.routes?.bundles ?? false
|
|
7755
|
+
};
|
|
7510
7756
|
const router = createRouter();
|
|
7511
|
-
|
|
7512
|
-
if (
|
|
7757
|
+
addRoute(router, "GET", "/version", "version");
|
|
7758
|
+
if (routeOptions.updateCheck) {
|
|
7513
7759
|
addRoute(router, "GET", "/fingerprint/:platform/:fingerprintHash/:channel/:minBundleId/:bundleId", "fingerprintUpdateWithCohort");
|
|
7514
7760
|
addRoute(router, "GET", "/fingerprint/:platform/:fingerprintHash/:channel/:minBundleId/:bundleId/:cohort", "fingerprintUpdateWithCohort");
|
|
7515
7761
|
addRoute(router, "GET", "/app-version/:platform/:appVersion/:channel/:minBundleId/:bundleId", "appVersionUpdateWithCohort");
|
|
7516
7762
|
addRoute(router, "GET", "/app-version/:platform/:appVersion/:channel/:minBundleId/:bundleId/:cohort", "appVersionUpdateWithCohort");
|
|
7517
7763
|
}
|
|
7518
|
-
if (
|
|
7764
|
+
if (routeOptions.bundles) {
|
|
7519
7765
|
addRoute(router, "GET", "/api/bundles/channels", "getChannels");
|
|
7520
7766
|
addRoute(router, "GET", "/api/bundles/:id", "getBundle");
|
|
7521
7767
|
addRoute(router, "GET", "/api/bundles", "getBundles");
|
|
@@ -7556,7 +7802,7 @@ function createHandler(api, options = {}) {
|
|
|
7556
7802
|
}
|
|
7557
7803
|
//#endregion
|
|
7558
7804
|
//#region ../../packages/server/src/route.ts
|
|
7559
|
-
const normalizeBasePath = (basePath) => {
|
|
7805
|
+
const normalizeBasePath$1 = (basePath) => {
|
|
7560
7806
|
if (!basePath || basePath === "/") return "/";
|
|
7561
7807
|
return basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
7562
7808
|
};
|
|
@@ -7607,7 +7853,7 @@ const createStorageAccess = (storagePlugins) => {
|
|
|
7607
7853
|
//#region ../../packages/server/src/runtime.ts
|
|
7608
7854
|
function createHotUpdater(options) {
|
|
7609
7855
|
const database = options.database;
|
|
7610
|
-
const basePath = normalizeBasePath(options.basePath ?? "/api");
|
|
7856
|
+
const basePath = normalizeBasePath$1(options.basePath ?? "/api");
|
|
7611
7857
|
const { readStorageText, resolveFileUrl } = createStorageAccess((options.storages ?? options.storagePlugins ?? []).map((plugin) => {
|
|
7612
7858
|
const storagePlugin = typeof plugin === "function" ? plugin() : plugin;
|
|
7613
7859
|
assertRuntimeStoragePlugin(storagePlugin);
|
|
@@ -7619,23 +7865,21 @@ function createHotUpdater(options) {
|
|
|
7619
7865
|
createMutationPlugin: () => database(),
|
|
7620
7866
|
readStorageText
|
|
7621
7867
|
} : { readStorageText });
|
|
7622
|
-
const
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
routes: options.routes
|
|
7627
|
-
}),
|
|
7628
|
-
adapterName: core.adapterName
|
|
7629
|
-
};
|
|
7868
|
+
const internalHandler = createHandler(core.api, {
|
|
7869
|
+
basePath,
|
|
7870
|
+
routes: options.routes
|
|
7871
|
+
});
|
|
7630
7872
|
const handler = (request, context, ...extraArgs) => {
|
|
7631
|
-
if (extraArgs.length > 0) return
|
|
7632
|
-
return
|
|
7873
|
+
if (extraArgs.length > 0) return internalHandler(request);
|
|
7874
|
+
return internalHandler(request, context);
|
|
7633
7875
|
};
|
|
7634
|
-
|
|
7635
|
-
...api,
|
|
7876
|
+
const api = {
|
|
7636
7877
|
basePath,
|
|
7878
|
+
adapterName: core.adapterName,
|
|
7637
7879
|
handler
|
|
7638
7880
|
};
|
|
7881
|
+
Object.defineProperties(api, Object.getOwnPropertyDescriptors(core.api));
|
|
7882
|
+
return api;
|
|
7639
7883
|
}
|
|
7640
7884
|
//#endregion
|
|
7641
7885
|
//#region ../../node_modules/.pnpm/hono@4.12.9/node_modules/hono/dist/compose.js
|
|
@@ -9530,6 +9774,37 @@ const streamToString = (stream) => {
|
|
|
9530
9774
|
const DEFAULT_INVALIDATION_POLL_INTERVAL_MS = 2e3;
|
|
9531
9775
|
const DEFAULT_INVALIDATION_TIMEOUT_MS = 300 * 1e3;
|
|
9532
9776
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
9777
|
+
const getS3ErrorProperty = (error, key) => {
|
|
9778
|
+
if (typeof error !== "object" || error === null) return;
|
|
9779
|
+
const value = error[key];
|
|
9780
|
+
return typeof value === "string" ? value : void 0;
|
|
9781
|
+
};
|
|
9782
|
+
const isArchivedS3ObjectError = (error) => {
|
|
9783
|
+
if (!(error instanceof Error)) return false;
|
|
9784
|
+
return error.name === "InvalidObjectState" || getS3ErrorProperty(error, "Code") === "InvalidObjectState";
|
|
9785
|
+
};
|
|
9786
|
+
const createArchivedS3ObjectError = ({ bucket, key, error }) => {
|
|
9787
|
+
const storageClass = getS3ErrorProperty(error, "StorageClass") ?? "archived storage";
|
|
9788
|
+
const nextError = new Error(`S3 object "${key}" in bucket "${bucket}" is archived (${storageClass}) and cannot be read. Restore the object in S3 or exclude Hot Updater metadata from lifecycle archival: "_index/**", "**/target-app-versions.json", and "**/update.json".`, { cause: error });
|
|
9789
|
+
nextError.name = "S3ArchivedObjectError";
|
|
9790
|
+
return nextError;
|
|
9791
|
+
};
|
|
9792
|
+
function normalizeBasePath(basePath) {
|
|
9793
|
+
return basePath?.replace(/^\/+|\/+$/g, "") ?? "";
|
|
9794
|
+
}
|
|
9795
|
+
function createDatabaseKeyBuilder(basePath) {
|
|
9796
|
+
const normalizedBasePath = normalizeBasePath(basePath);
|
|
9797
|
+
const toStorageKey = (key) => [normalizedBasePath, key].filter(Boolean).join("/");
|
|
9798
|
+
const fromStorageKey = (key) => {
|
|
9799
|
+
if (!normalizedBasePath) return key;
|
|
9800
|
+
const prefix = `${normalizedBasePath}/`;
|
|
9801
|
+
return key.startsWith(prefix) ? key.slice(prefix.length) : key;
|
|
9802
|
+
};
|
|
9803
|
+
return {
|
|
9804
|
+
fromStorageKey,
|
|
9805
|
+
toStorageKey
|
|
9806
|
+
};
|
|
9807
|
+
}
|
|
9533
9808
|
/**
|
|
9534
9809
|
* Loads JSON data from S3.
|
|
9535
9810
|
* Returns null if NoSuchKey error occurs.
|
|
@@ -9545,6 +9820,11 @@ async function loadJsonFromS3(client, bucket, key) {
|
|
|
9545
9820
|
return JSON.parse(bodyContents);
|
|
9546
9821
|
} catch (e) {
|
|
9547
9822
|
if (e instanceof _aws_sdk_client_s3.NoSuchKey) return null;
|
|
9823
|
+
if (isArchivedS3ObjectError(e)) throw createArchivedS3ObjectError({
|
|
9824
|
+
bucket,
|
|
9825
|
+
key,
|
|
9826
|
+
error: e
|
|
9827
|
+
});
|
|
9548
9828
|
throw e;
|
|
9549
9829
|
}
|
|
9550
9830
|
}
|
|
@@ -9628,18 +9908,20 @@ async function invalidateCloudFront(client, distributionId, paths, options) {
|
|
|
9628
9908
|
const s3Database = createBlobDatabasePlugin({
|
|
9629
9909
|
name: "s3Database",
|
|
9630
9910
|
factory: (config) => {
|
|
9631
|
-
const { bucketName, cloudfrontDistributionId, apiBasePath = "/api/check-update", shouldWaitForInvalidation = false, ...s3Config } = config;
|
|
9911
|
+
const { basePath, bucketName, cloudfrontDistributionId, apiBasePath = "/api/check-update", shouldWaitForInvalidation = false, ...s3Config } = config;
|
|
9632
9912
|
const client = new _aws_sdk_client_s3.S3Client(applyS3RuntimeAwsConfig(s3Config));
|
|
9913
|
+
const { fromStorageKey, toStorageKey } = createDatabaseKeyBuilder(basePath);
|
|
9633
9914
|
const cloudfrontClient = cloudfrontDistributionId ? new _aws_sdk_client_cloudfront.CloudFrontClient({
|
|
9634
9915
|
credentials: s3Config.credentials,
|
|
9635
9916
|
region: s3Config.region
|
|
9636
9917
|
}) : void 0;
|
|
9637
9918
|
return {
|
|
9638
9919
|
apiBasePath,
|
|
9639
|
-
listObjects: (prefix) => listObjectsInS3(client, bucketName, prefix),
|
|
9640
|
-
loadObject: (key) => loadJsonFromS3(client, bucketName, key),
|
|
9641
|
-
uploadObject: (key, data) => uploadJsonToS3(client, bucketName, key, data),
|
|
9642
|
-
deleteObject: (key) => deleteObjectInS3(client, bucketName, key),
|
|
9920
|
+
listObjects: (prefix) => listObjectsInS3(client, bucketName, toStorageKey(prefix)).then((keys) => keys.map(fromStorageKey)),
|
|
9921
|
+
loadObject: (key) => loadJsonFromS3(client, bucketName, toStorageKey(key)),
|
|
9922
|
+
uploadObject: (key, data) => uploadJsonToS3(client, bucketName, toStorageKey(key), data),
|
|
9923
|
+
deleteObject: (key) => deleteObjectInS3(client, bucketName, toStorageKey(key)),
|
|
9924
|
+
shouldSkipLoadObjectError: (error) => error instanceof Error && error.name === "S3ArchivedObjectError",
|
|
9643
9925
|
invalidatePaths: (pathsToInvalidate) => {
|
|
9644
9926
|
if (cloudfrontClient && cloudfrontDistributionId && pathsToInvalidate.length > 0) return invalidateCloudFront(cloudfrontClient, cloudfrontDistributionId, pathsToInvalidate, { shouldWaitForInvalidation });
|
|
9645
9927
|
return Promise.resolve();
|
|
@@ -9696,6 +9978,20 @@ const s3Storage = createUniversalStoragePlugin({
|
|
|
9696
9978
|
if (!response.Bucket || !response.Key) throw new Error("Upload Failed");
|
|
9697
9979
|
return { storageUri: `s3://${bucketName}/${Key}` };
|
|
9698
9980
|
},
|
|
9981
|
+
async exists(storageUri) {
|
|
9982
|
+
const { bucket, key } = parseStorageUri(storageUri, "s3");
|
|
9983
|
+
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
9984
|
+
try {
|
|
9985
|
+
await client.send(new _aws_sdk_client_s3.HeadObjectCommand({
|
|
9986
|
+
Bucket: bucketName,
|
|
9987
|
+
Key: key
|
|
9988
|
+
}));
|
|
9989
|
+
return true;
|
|
9990
|
+
} catch (error) {
|
|
9991
|
+
if (error instanceof Error && (error.name === "NotFound" || error.name === "NoSuchKey")) return false;
|
|
9992
|
+
throw error;
|
|
9993
|
+
}
|
|
9994
|
+
},
|
|
9699
9995
|
async downloadFile(storageUri, filePath) {
|
|
9700
9996
|
const { bucket, key } = parseStorageUri(storageUri, "s3");
|
|
9701
9997
|
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
@@ -9848,7 +10144,6 @@ const hotUpdater = createHotUpdater({
|
|
|
9848
10144
|
basePath: HOT_UPDATER_BASE_PATH,
|
|
9849
10145
|
routes: {
|
|
9850
10146
|
updateCheck: true,
|
|
9851
|
-
version: true,
|
|
9852
10147
|
bundles: false
|
|
9853
10148
|
}
|
|
9854
10149
|
});
|