@hot-updater/plugin-core 0.32.0 → 0.33.1
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/bundleUnitOfWork.cjs +158 -0
- package/dist/bundleUnitOfWork.mjs +158 -0
- package/dist/bundleUnitOfWorkStore.cjs +15 -0
- package/dist/bundleUnitOfWorkStore.mjs +15 -0
- package/dist/createBlobDatabasePlugin.cjs +149 -493
- package/dist/createBlobDatabasePlugin.d.cts +2 -4
- package/dist/createBlobDatabasePlugin.d.mts +2 -4
- package/dist/createBlobDatabasePlugin.mjs +149 -493
- package/dist/createDatabasePlugin.cjs +71 -32
- package/dist/createDatabasePlugin.mjs +71 -32
- package/dist/createDatabasePluginGetUpdateInfo.cjs +11 -3
- package/dist/createDatabasePluginGetUpdateInfo.mjs +11 -3
- package/dist/index.cjs +6 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +4 -2
- package/dist/requestUpdateBundleState.cjs +26 -0
- package/dist/requestUpdateBundleState.d.cts +12 -0
- package/dist/requestUpdateBundleState.d.mts +12 -0
- package/dist/requestUpdateBundleState.mjs +24 -0
- package/dist/resolveUpdateInfoFromBundles.cjs +14 -0
- package/dist/resolveUpdateInfoFromBundles.d.cts +16 -0
- package/dist/resolveUpdateInfoFromBundles.d.mts +16 -0
- package/dist/resolveUpdateInfoFromBundles.mjs +13 -0
- package/package.json +8 -5
|
@@ -1,12 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { bundleMatchesQueryWhere, sortBundles } from "./queryBundles.mjs";
|
|
2
2
|
import { createDatabasePlugin } from "./createDatabasePlugin.mjs";
|
|
3
3
|
import { filterCompatibleAppVersions } from "./filterCompatibleAppVersions.mjs";
|
|
4
|
-
import { bundleMatchesQueryWhere, sortBundles } from "./queryBundles.mjs";
|
|
5
4
|
import { paginateBundles } from "./paginateBundles.mjs";
|
|
6
|
-
import {
|
|
5
|
+
import { resolveUpdateInfoFromBundles } from "./resolveUpdateInfoFromBundles.mjs";
|
|
7
6
|
import { orderBy } from "es-toolkit";
|
|
8
7
|
import semver from "semver";
|
|
9
8
|
//#region src/createBlobDatabasePlugin.ts
|
|
9
|
+
const STORAGE_OPERATION_CONCURRENCY = 8;
|
|
10
|
+
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
11
|
+
const results = [];
|
|
12
|
+
let nextIndex = 0;
|
|
13
|
+
const workerCount = Math.min(concurrency, items.length);
|
|
14
|
+
await Promise.all(Array.from({ length: workerCount }, async () => {
|
|
15
|
+
while (true) {
|
|
16
|
+
const index = nextIndex;
|
|
17
|
+
nextIndex += 1;
|
|
18
|
+
if (index >= items.length) break;
|
|
19
|
+
results[index] = await mapper(items[index], index);
|
|
20
|
+
}
|
|
21
|
+
}));
|
|
22
|
+
return results;
|
|
23
|
+
}
|
|
24
|
+
async function forEachWithConcurrency(items, concurrency, mapper) {
|
|
25
|
+
await mapWithConcurrency(items, concurrency, mapper);
|
|
26
|
+
}
|
|
10
27
|
function removeBundleInternalKeys(bundle) {
|
|
11
28
|
const { _updateJsonKey, _oldUpdateJsonKey, ...pureBundle } = bundle;
|
|
12
29
|
return pureBundle;
|
|
@@ -47,134 +64,43 @@ function resolveStorageTarget({ targetAppVersion, fingerprintHash }) {
|
|
|
47
64
|
if (!target) throw new Error("target not found");
|
|
48
65
|
return target;
|
|
49
66
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
direction: "desc"
|
|
53
|
-
};
|
|
54
|
-
const MANAGEMENT_INDEX_PREFIX = "_index";
|
|
55
|
-
const MANAGEMENT_INDEX_VERSION = 1;
|
|
56
|
-
const DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE = 128;
|
|
57
|
-
const ALL_SCOPE_CACHE_KEY = "*|*";
|
|
58
|
-
function resolveManagementIndexPageSize(config) {
|
|
59
|
-
const pageSize = config.managementIndexPageSize ?? DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE;
|
|
60
|
-
if (!Number.isInteger(pageSize) || pageSize < 1) throw new Error("managementIndexPageSize must be a positive integer.");
|
|
61
|
-
return pageSize;
|
|
62
|
-
}
|
|
63
|
-
function sortManagedBundles(bundles, orderBy = DEFAULT_DESC_ORDER) {
|
|
64
|
-
return sortBundles(bundles, orderBy);
|
|
65
|
-
}
|
|
66
|
-
function isDefaultManagementOrder(orderBy) {
|
|
67
|
-
return orderBy === void 0 || orderBy.field === DEFAULT_DESC_ORDER.field && orderBy.direction === DEFAULT_DESC_ORDER.direction;
|
|
68
|
-
}
|
|
69
|
-
function hasUnsupportedManagementFilters(where) {
|
|
70
|
-
if (!where) return false;
|
|
71
|
-
return Boolean(where.enabled !== void 0 || where.id !== void 0 || where.targetAppVersion !== void 0 || where.targetAppVersionIn !== void 0 || where.targetAppVersionNotNull !== void 0 || where.fingerprintHash !== void 0);
|
|
67
|
+
function targetVersionMutationKey(bundle) {
|
|
68
|
+
return `${bundle.channel}/${bundle.platform}`;
|
|
72
69
|
}
|
|
73
|
-
function
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
function getTargetVersionMutation(mutations, bundle) {
|
|
71
|
+
const key = targetVersionMutationKey(bundle);
|
|
72
|
+
const existingMutation = mutations.get(key);
|
|
73
|
+
if (existingMutation) return existingMutation;
|
|
74
|
+
const mutation = {
|
|
75
|
+
additions: /* @__PURE__ */ new Set(),
|
|
76
|
+
channel: bundle.channel,
|
|
77
|
+
platform: bundle.platform,
|
|
78
|
+
removals: /* @__PURE__ */ new Set()
|
|
78
79
|
};
|
|
80
|
+
mutations.set(key, mutation);
|
|
81
|
+
return mutation;
|
|
79
82
|
}
|
|
80
|
-
function
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return `${channel ?? "*"}|${platform ?? "*"}`;
|
|
85
|
-
}
|
|
86
|
-
function getManagementScopePrefix({ channel, platform }) {
|
|
87
|
-
if (channel && platform) return `${MANAGEMENT_INDEX_PREFIX}/channel/${encodeScopePart(channel)}/platform/${platform}`;
|
|
88
|
-
if (channel) return `${MANAGEMENT_INDEX_PREFIX}/channel/${encodeScopePart(channel)}`;
|
|
89
|
-
if (platform) return `${MANAGEMENT_INDEX_PREFIX}/platform/${platform}`;
|
|
90
|
-
return `${MANAGEMENT_INDEX_PREFIX}/all`;
|
|
91
|
-
}
|
|
92
|
-
function getManagementRootKey(scope) {
|
|
93
|
-
return `${getManagementScopePrefix(scope)}/root.json`;
|
|
94
|
-
}
|
|
95
|
-
function getManagementPageKey(scope, pageIndex) {
|
|
96
|
-
return `${getManagementScopePrefix(scope)}/pages/${String(pageIndex).padStart(4, "0")}.json`;
|
|
83
|
+
function addTargetVersionAddition(mutations, bundle) {
|
|
84
|
+
const targetAppVersion = normalizeTargetAppVersion(bundle.targetAppVersion);
|
|
85
|
+
if (targetAppVersion == null) return;
|
|
86
|
+
getTargetVersionMutation(mutations, bundle).additions.add(targetAppVersion);
|
|
97
87
|
}
|
|
98
|
-
function
|
|
99
|
-
const
|
|
100
|
-
return
|
|
101
|
-
|
|
102
|
-
_updateJsonKey: `${bundle.channel}/${bundle.platform}/${target}/update.json`
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
function getPageStartOffsets(pages) {
|
|
106
|
-
const startOffsets = [];
|
|
107
|
-
let offset = 0;
|
|
108
|
-
for (const page of pages) {
|
|
109
|
-
startOffsets.push(offset);
|
|
110
|
-
offset += page.count;
|
|
111
|
-
}
|
|
112
|
-
return startOffsets;
|
|
88
|
+
function addTargetVersionRemoval(mutations, bundle) {
|
|
89
|
+
const targetAppVersion = normalizeTargetAppVersion(bundle.targetAppVersion);
|
|
90
|
+
if (targetAppVersion == null) return;
|
|
91
|
+
getTargetVersionMutation(mutations, bundle).removals.add(targetAppVersion);
|
|
113
92
|
}
|
|
114
|
-
function
|
|
115
|
-
return {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
limit,
|
|
119
|
-
offset: 0
|
|
120
|
-
})
|
|
121
|
-
};
|
|
93
|
+
function getManagementListPrefixes(where) {
|
|
94
|
+
if (where?.channel && where.platform) return [`${where.channel}/${where.platform}/`];
|
|
95
|
+
if (where?.channel) return [`${where.channel}/`];
|
|
96
|
+
return [""];
|
|
122
97
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (!options?.includeChannels && scopeBundles.length === 0) return;
|
|
130
|
-
const pageKeys = [];
|
|
131
|
-
const pageDescriptors = [];
|
|
132
|
-
for (let pageIndex = 0; pageIndex * pageSize < scopeBundles.length; pageIndex++) {
|
|
133
|
-
const page = scopeBundles.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize);
|
|
134
|
-
const key = getManagementPageKey(scope, pageIndex);
|
|
135
|
-
pages.set(key, page);
|
|
136
|
-
pageKeys.push(key);
|
|
137
|
-
pageDescriptors.push({
|
|
138
|
-
key,
|
|
139
|
-
count: page.length,
|
|
140
|
-
firstId: page[0].id,
|
|
141
|
-
lastId: page.at(-1).id
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
const root = {
|
|
145
|
-
version: MANAGEMENT_INDEX_VERSION,
|
|
146
|
-
pageSize,
|
|
147
|
-
total: scopeBundles.length,
|
|
148
|
-
pages: pageDescriptors,
|
|
149
|
-
...options?.includeChannels ? { channels } : {}
|
|
150
|
-
};
|
|
151
|
-
scopes.push({
|
|
152
|
-
cacheKey: getManagementScopeCacheKey(scope),
|
|
153
|
-
rootKey: getManagementRootKey(scope),
|
|
154
|
-
root,
|
|
155
|
-
pageKeys
|
|
156
|
-
});
|
|
157
|
-
};
|
|
158
|
-
addScope({}, sortedAllBundles, { includeChannels: true });
|
|
159
|
-
for (const channel of channels) {
|
|
160
|
-
const channelBundles = sortedAllBundles.filter((bundle) => bundle.channel === channel);
|
|
161
|
-
addScope({ channel }, channelBundles);
|
|
162
|
-
for (const platform of ["ios", "android"]) {
|
|
163
|
-
const scopedBundles = channelBundles.filter((bundle) => bundle.platform === platform);
|
|
164
|
-
addScope({
|
|
165
|
-
channel,
|
|
166
|
-
platform
|
|
167
|
-
}, scopedBundles);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
for (const platform of ["ios", "android"]) {
|
|
171
|
-
const platformBundles = sortedAllBundles.filter((bundle) => bundle.platform === platform);
|
|
172
|
-
addScope({ platform }, platformBundles);
|
|
173
|
-
}
|
|
174
|
-
return {
|
|
175
|
-
pages,
|
|
176
|
-
scopes
|
|
177
|
-
};
|
|
98
|
+
const DEFAULT_DESC_ORDER = {
|
|
99
|
+
field: "id",
|
|
100
|
+
direction: "desc"
|
|
101
|
+
};
|
|
102
|
+
function sortManagedBundles(bundles, orderBy = DEFAULT_DESC_ORDER) {
|
|
103
|
+
return sortBundles(bundles, orderBy);
|
|
178
104
|
}
|
|
179
105
|
/**
|
|
180
106
|
* Creates a blob storage-based database plugin with lazy initialization.
|
|
@@ -185,339 +111,90 @@ function buildManagementIndexArtifacts(allBundles, pageSize) {
|
|
|
185
111
|
*/
|
|
186
112
|
const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
187
113
|
return (config, hooks) => {
|
|
188
|
-
const
|
|
189
|
-
const { listObjects, loadObject, uploadObject, deleteObject, invalidatePaths, apiBasePath } = factory(config);
|
|
114
|
+
const { listObjects, loadObject, uploadObject, deleteObject, shouldSkipLoadObjectError, invalidatePaths, apiBasePath } = factory(config);
|
|
190
115
|
const bundlesMap = /* @__PURE__ */ new Map();
|
|
191
116
|
const pendingBundlesMap = /* @__PURE__ */ new Map();
|
|
192
|
-
const
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const replaceManagementRootCache = (artifacts) => {
|
|
200
|
-
managementRootCache.clear();
|
|
201
|
-
for (const scope of artifacts.scopes) managementRootCache.set(scope.cacheKey, scope.root);
|
|
202
|
-
};
|
|
203
|
-
const createHydratedBundle = (bundle) => {
|
|
204
|
-
const hydratedBundle = createBundleWithUpdateJsonKey(bundle);
|
|
205
|
-
bundlesMap.set(hydratedBundle.id, hydratedBundle);
|
|
206
|
-
return hydratedBundle;
|
|
207
|
-
};
|
|
208
|
-
const loadStoredManagementRoot = async (scope) => {
|
|
209
|
-
const cacheKey = getManagementScopeCacheKey(scope);
|
|
210
|
-
const storedRoot = await loadObject(getManagementRootKey(scope));
|
|
211
|
-
if (storedRoot) {
|
|
212
|
-
managementRootCache.set(cacheKey, storedRoot);
|
|
213
|
-
return storedRoot;
|
|
214
|
-
}
|
|
215
|
-
managementRootCache.delete(cacheKey);
|
|
216
|
-
return null;
|
|
217
|
-
};
|
|
218
|
-
const loadManagementPage = async (descriptor, pageCache) => {
|
|
219
|
-
if (pageCache?.has(descriptor.key)) return pageCache.get(descriptor.key) ?? null;
|
|
220
|
-
const page = await loadObject(descriptor.key);
|
|
221
|
-
pageCache?.set(descriptor.key, page);
|
|
222
|
-
return page;
|
|
223
|
-
};
|
|
224
|
-
const loadBundleFromManagementRoot = async (root, bundleId) => {
|
|
225
|
-
const pageIndex = findPageIndexContainingId(root.pages, bundleId);
|
|
226
|
-
if (pageIndex < 0) return null;
|
|
227
|
-
const descriptor = root.pages[pageIndex];
|
|
228
|
-
const page = await loadManagementPage(descriptor);
|
|
229
|
-
if (!page) return null;
|
|
230
|
-
return page.find((item) => item.id === bundleId) ?? null;
|
|
231
|
-
};
|
|
232
|
-
const loadAllBundlesFromRoot = async (root) => {
|
|
233
|
-
const allBundles = [];
|
|
234
|
-
const pageCache = /* @__PURE__ */ new Map();
|
|
235
|
-
for (const descriptor of root.pages) {
|
|
236
|
-
const page = await loadManagementPage(descriptor, pageCache);
|
|
237
|
-
if (!page) return null;
|
|
238
|
-
allBundles.push(...page);
|
|
117
|
+
const locallyDeletedBundleIds = /* @__PURE__ */ new Set();
|
|
118
|
+
const loadOptionalObject = async (key) => {
|
|
119
|
+
try {
|
|
120
|
+
return await loadObject(key);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (shouldSkipLoadObjectError?.(error, key)) return null;
|
|
123
|
+
throw error;
|
|
239
124
|
}
|
|
240
|
-
return allBundles;
|
|
241
|
-
};
|
|
242
|
-
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);
|
|
245
|
-
if (!previousArtifacts) return;
|
|
246
|
-
const nextPageKeys = new Set(nextArtifacts.pages.keys());
|
|
247
|
-
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(() => {});
|
|
250
|
-
};
|
|
251
|
-
const ensureAllManagementRoot = async () => {
|
|
252
|
-
const storedAllRoot = await loadStoredManagementRoot({});
|
|
253
|
-
if (storedAllRoot && storedAllRoot.pageSize === managementIndexPageSize) return storedAllRoot;
|
|
254
|
-
const rebuiltBundles = sortManagedBundles((await reloadBundles()).map((bundle) => removeBundleInternalKeys(bundle)));
|
|
255
|
-
const nextArtifacts = buildManagementIndexArtifacts(rebuiltBundles, managementIndexPageSize);
|
|
256
|
-
await persistManagementIndexArtifacts(nextArtifacts, storedAllRoot ? buildManagementIndexArtifacts(rebuiltBundles, storedAllRoot.pageSize) : void 0);
|
|
257
|
-
replaceManagementRootCache(nextArtifacts);
|
|
258
|
-
return getAllManagementArtifact(nextArtifacts).root;
|
|
259
|
-
};
|
|
260
|
-
const loadManagementScopeRoot = async (scope) => {
|
|
261
|
-
const cacheKey = getManagementScopeCacheKey(scope);
|
|
262
|
-
if (cacheKey === ALL_SCOPE_CACHE_KEY) return ensureAllManagementRoot();
|
|
263
|
-
const storedRoot = await loadStoredManagementRoot(scope);
|
|
264
|
-
if (storedRoot) return storedRoot;
|
|
265
|
-
await ensureAllManagementRoot();
|
|
266
|
-
const storedScopedRoot = await loadStoredManagementRoot(scope);
|
|
267
|
-
if (storedScopedRoot) return storedScopedRoot;
|
|
268
|
-
managementRootCache.set(cacheKey, null);
|
|
269
|
-
return null;
|
|
270
|
-
};
|
|
271
|
-
const loadAllBundlesForManagementFallback = async () => {
|
|
272
|
-
const allRoot = await loadManagementScopeRoot({});
|
|
273
|
-
if (allRoot) {
|
|
274
|
-
const pagedBundles = await loadAllBundlesFromRoot(allRoot);
|
|
275
|
-
if (pagedBundles) return pagedBundles;
|
|
276
|
-
}
|
|
277
|
-
return sortManagedBundles((await reloadBundles()).map((bundle) => removeBundleInternalKeys(bundle)));
|
|
278
|
-
};
|
|
279
|
-
const loadCurrentBundlesForIndexRebuild = async () => {
|
|
280
|
-
return loadAllBundlesForManagementFallback();
|
|
281
125
|
};
|
|
282
|
-
const
|
|
283
|
-
return
|
|
126
|
+
const loadAllBundlesForManagementFallback = async (where) => {
|
|
127
|
+
return sortManagedBundles((await reloadBundles(getManagementListPrefixes(where))).map((bundle) => removeBundleInternalKeys(bundle)));
|
|
284
128
|
};
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (normalizedOffset >= root.total) return {
|
|
292
|
-
data: [],
|
|
293
|
-
pagination: calculatePagination(root.total, {
|
|
294
|
-
limit,
|
|
295
|
-
offset: normalizedOffset
|
|
296
|
-
})
|
|
297
|
-
};
|
|
298
|
-
let pageIndex = 0;
|
|
299
|
-
for (let index = pageStartOffsets.length - 1; index >= 0; index--) if ((pageStartOffsets[index] ?? 0) <= normalizedOffset) {
|
|
300
|
-
pageIndex = index;
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
const startInPage = normalizedOffset - (pageStartOffsets[pageIndex] ?? 0);
|
|
304
|
-
const data = [];
|
|
305
|
-
for (let currentPageIndex = pageIndex; currentPageIndex < root.pages.length && (limit <= 0 || data.length < limit); currentPageIndex++) {
|
|
306
|
-
const descriptor = root.pages[currentPageIndex];
|
|
307
|
-
const page = await loadManagementPage(descriptor, pageCache);
|
|
308
|
-
if (!page) return paginateBundles({
|
|
309
|
-
bundles: await loadAllBundlesForManagementFallback(),
|
|
310
|
-
limit,
|
|
311
|
-
offset: normalizedOffset
|
|
312
|
-
});
|
|
313
|
-
data.push(...currentPageIndex === pageIndex ? page.slice(startInPage) : page);
|
|
314
|
-
}
|
|
315
|
-
const paginatedData = limit > 0 ? data.slice(0, limit) : data;
|
|
316
|
-
return {
|
|
317
|
-
data: paginatedData,
|
|
318
|
-
pagination: {
|
|
319
|
-
...calculatePagination(root.total, {
|
|
320
|
-
limit,
|
|
321
|
-
offset: normalizedOffset
|
|
322
|
-
}),
|
|
323
|
-
...paginatedData.length > 0 && normalizedOffset + paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {},
|
|
324
|
-
...paginatedData.length > 0 && normalizedOffset > 0 ? { previousCursor: paginatedData[0]?.id } : {}
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
if (cursor?.after) {
|
|
329
|
-
let pageIndex = root.pages.findIndex((page) => {
|
|
330
|
-
const containsCursor = cursor.after.localeCompare(page.firstId) <= 0 && cursor.after.localeCompare(page.lastId) >= 0;
|
|
331
|
-
const wholePageEligible = cursor.after.localeCompare(page.firstId) > 0;
|
|
332
|
-
return containsCursor || wholePageEligible;
|
|
333
|
-
});
|
|
334
|
-
if (pageIndex < 0) return {
|
|
335
|
-
data: [],
|
|
336
|
-
pagination: {
|
|
337
|
-
...calculatePagination(root.total, {
|
|
338
|
-
limit,
|
|
339
|
-
offset: root.total
|
|
340
|
-
}),
|
|
341
|
-
previousCursor: cursor.after
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
const data = [];
|
|
345
|
-
let startIndex = null;
|
|
346
|
-
while (pageIndex < root.pages.length && (limit <= 0 || data.length < limit)) {
|
|
347
|
-
const descriptor = root.pages[pageIndex];
|
|
348
|
-
const page = await loadManagementPage(descriptor, pageCache);
|
|
349
|
-
if (!page) return paginateBundles({
|
|
350
|
-
bundles: await loadAllBundlesForManagementFallback(),
|
|
351
|
-
limit,
|
|
352
|
-
cursor
|
|
353
|
-
});
|
|
354
|
-
const containsCursor = cursor.after.localeCompare(descriptor.firstId) <= 0 && cursor.after.localeCompare(descriptor.lastId) >= 0;
|
|
355
|
-
let eligiblePageBundles = page;
|
|
356
|
-
if (containsCursor) {
|
|
357
|
-
const startInPage = page.findIndex((bundle) => bundle.id.localeCompare(cursor.after) < 0);
|
|
358
|
-
if (startInPage < 0) eligiblePageBundles = [];
|
|
359
|
-
else {
|
|
360
|
-
eligiblePageBundles = page.slice(startInPage);
|
|
361
|
-
startIndex ??= (pageStartOffsets[pageIndex] ?? 0) + startInPage;
|
|
362
|
-
}
|
|
363
|
-
} else if (eligiblePageBundles.length > 0) startIndex ??= pageStartOffsets[pageIndex] ?? 0;
|
|
364
|
-
data.push(...eligiblePageBundles);
|
|
365
|
-
if (limit > 0 && data.length >= limit) break;
|
|
366
|
-
pageIndex += 1;
|
|
367
|
-
}
|
|
368
|
-
const paginatedData = limit > 0 ? data.slice(0, limit) : data;
|
|
369
|
-
const resolvedStartIndex = startIndex ?? root.total;
|
|
370
|
-
return {
|
|
371
|
-
data: paginatedData,
|
|
372
|
-
pagination: {
|
|
373
|
-
...calculatePagination(root.total, {
|
|
374
|
-
limit,
|
|
375
|
-
offset: resolvedStartIndex
|
|
376
|
-
}),
|
|
377
|
-
...paginatedData.length > 0 && resolvedStartIndex + paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {},
|
|
378
|
-
...paginatedData.length > 0 && resolvedStartIndex > 0 ? { previousCursor: paginatedData[0]?.id } : {}
|
|
379
|
-
}
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
if (cursor?.before) {
|
|
383
|
-
let pageIndex = -1;
|
|
384
|
-
for (let index = root.pages.length - 1; index >= 0; index--) {
|
|
385
|
-
const page = root.pages[index];
|
|
386
|
-
const containsCursor = cursor.before.localeCompare(page.firstId) <= 0 && cursor.before.localeCompare(page.lastId) >= 0;
|
|
387
|
-
const wholePageEligible = cursor.before.localeCompare(page.lastId) < 0;
|
|
388
|
-
if (containsCursor || wholePageEligible) {
|
|
389
|
-
pageIndex = index;
|
|
390
|
-
break;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
if (pageIndex < 0) return createEmptyManagementResult(limit);
|
|
394
|
-
let startIndex = null;
|
|
395
|
-
let collected = [];
|
|
396
|
-
while (pageIndex >= 0 && (limit <= 0 || collected.length < limit)) {
|
|
397
|
-
const descriptor = root.pages[pageIndex];
|
|
398
|
-
const page = await loadManagementPage(descriptor, pageCache);
|
|
399
|
-
if (!page) return paginateBundles({
|
|
400
|
-
bundles: await loadAllBundlesForManagementFallback(),
|
|
401
|
-
limit,
|
|
402
|
-
cursor
|
|
403
|
-
});
|
|
404
|
-
const eligiblePageBundles = cursor.before.localeCompare(descriptor.firstId) <= 0 && cursor.before.localeCompare(descriptor.lastId) >= 0 ? page.filter((bundle) => bundle.id.localeCompare(cursor.before) > 0) : page;
|
|
405
|
-
collected = [...eligiblePageBundles, ...collected];
|
|
406
|
-
if (eligiblePageBundles.length > 0) startIndex = pageStartOffsets[pageIndex] ?? 0;
|
|
407
|
-
if (limit > 0 && collected.length >= limit) break;
|
|
408
|
-
pageIndex -= 1;
|
|
409
|
-
}
|
|
410
|
-
if (startIndex === null || collected.length === 0) return createEmptyManagementResult(limit);
|
|
411
|
-
let paginatedData = collected;
|
|
412
|
-
if (limit > 0 && collected.length > limit) {
|
|
413
|
-
const dropCount = collected.length - limit;
|
|
414
|
-
paginatedData = collected.slice(dropCount);
|
|
415
|
-
startIndex += dropCount;
|
|
416
|
-
}
|
|
417
|
-
const pagination = calculatePagination(root.total, {
|
|
418
|
-
limit,
|
|
419
|
-
offset: startIndex
|
|
420
|
-
});
|
|
421
|
-
return {
|
|
422
|
-
data: paginatedData,
|
|
423
|
-
pagination: {
|
|
424
|
-
...pagination,
|
|
425
|
-
...paginatedData.length > 0 && startIndex + paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {},
|
|
426
|
-
...paginatedData.length > 0 && startIndex > 0 ? { previousCursor: paginatedData[0]?.id } : {}
|
|
427
|
-
}
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
const pageIndex = 0;
|
|
431
|
-
const startInPage = 0;
|
|
432
|
-
const data = [];
|
|
433
|
-
for (let currentPageIndex = pageIndex; currentPageIndex < root.pages.length && (limit <= 0 || data.length < limit); currentPageIndex++) {
|
|
434
|
-
const descriptor = root.pages[currentPageIndex];
|
|
435
|
-
const page = await loadManagementPage(descriptor, pageCache);
|
|
436
|
-
if (!page) return paginateBundles({
|
|
437
|
-
bundles: await loadAllBundlesForManagementFallback(),
|
|
438
|
-
limit,
|
|
439
|
-
cursor
|
|
129
|
+
const cacheBundlesFromObject = (key, bundles) => {
|
|
130
|
+
for (const bundle of bundles) {
|
|
131
|
+
if (locallyDeletedBundleIds.has(bundle.id) || pendingBundlesMap.has(bundle.id)) continue;
|
|
132
|
+
bundlesMap.set(bundle.id, {
|
|
133
|
+
...bundle,
|
|
134
|
+
_updateJsonKey: key
|
|
440
135
|
});
|
|
441
|
-
data.push(...currentPageIndex === pageIndex ? page.slice(startInPage) : page);
|
|
442
136
|
}
|
|
443
|
-
const paginatedData = limit > 0 ? data.slice(0, limit) : data;
|
|
444
|
-
return {
|
|
445
|
-
data: paginatedData,
|
|
446
|
-
pagination: {
|
|
447
|
-
...calculatePagination(root.total, {
|
|
448
|
-
limit,
|
|
449
|
-
offset: 0
|
|
450
|
-
}),
|
|
451
|
-
...paginatedData.length > 0 && paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {}
|
|
452
|
-
}
|
|
453
|
-
};
|
|
454
137
|
};
|
|
455
|
-
async
|
|
138
|
+
const loadBundleObject = async (key) => {
|
|
139
|
+
const bundles = await loadOptionalObject(key) ?? [];
|
|
140
|
+
cacheBundlesFromObject(key, bundles);
|
|
141
|
+
return bundles;
|
|
142
|
+
};
|
|
143
|
+
async function reloadBundles(prefixes = [""]) {
|
|
456
144
|
bundlesMap.clear();
|
|
457
|
-
|
|
458
|
-
|
|
145
|
+
pendingBundlesMap.clear();
|
|
146
|
+
locallyDeletedBundleIds.clear();
|
|
147
|
+
const allBundles = (await mapWithConcurrency((await mapWithConcurrency(prefixes, STORAGE_OPERATION_CONCURRENCY, (prefix) => listObjects(prefix))).flat().filter((key) => /^[^/]+\/(?:ios|android)\/[^/]+\/update\.json$/.test(key)), STORAGE_OPERATION_CONCURRENCY, async (key) => {
|
|
148
|
+
return (await loadBundleObject(key)).map((bundle) => ({
|
|
459
149
|
...bundle,
|
|
460
150
|
_updateJsonKey: key
|
|
461
151
|
}));
|
|
462
|
-
});
|
|
463
|
-
const allBundles = (await Promise.all(filePromises)).flat();
|
|
152
|
+
})).flat();
|
|
464
153
|
for (const bundle of allBundles) bundlesMap.set(bundle.id, bundle);
|
|
465
154
|
for (const [id, bundle] of pendingBundlesMap.entries()) bundlesMap.set(id, bundle);
|
|
466
|
-
return orderBy(
|
|
155
|
+
return orderBy(Array.from(bundlesMap.values()), [(v) => v.id], ["desc"]);
|
|
467
156
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
* Returns true if the file was updated, false if no changes were made.
|
|
471
|
-
*/
|
|
472
|
-
async function updateTargetVersionsForPlatform(platform) {
|
|
473
|
-
const updateJsonPattern = new RegExp(`^[^/]+/${platform}/[^/]+/update\\.json$`);
|
|
474
|
-
const targetVersionsPattern = new RegExp(`^[^/]+/${platform}/target-app-versions\\.json$`);
|
|
475
|
-
const allKeys = await listObjects("");
|
|
476
|
-
const updateJsonKeys = allKeys.filter((key) => updateJsonPattern.test(key));
|
|
477
|
-
const targetVersionsKeys = allKeys.filter((key) => targetVersionsPattern.test(key));
|
|
478
|
-
const keysByChannel = updateJsonKeys.reduce((acc, key) => {
|
|
479
|
-
const channel = key.split("/")[0];
|
|
480
|
-
acc[channel] = acc[channel] || [];
|
|
481
|
-
acc[channel].push(key);
|
|
482
|
-
return acc;
|
|
483
|
-
}, {});
|
|
484
|
-
for (const key of targetVersionsKeys) {
|
|
485
|
-
const channel = key.split("/")[0];
|
|
486
|
-
if (!keysByChannel[channel]) keysByChannel[channel] = [];
|
|
487
|
-
}
|
|
488
|
-
for (const channel of Object.keys(keysByChannel)) {
|
|
489
|
-
const updateKeys = keysByChannel[channel];
|
|
157
|
+
async function applyTargetVersionMutations(mutations) {
|
|
158
|
+
await Promise.all(Array.from(mutations.values()).map(async ({ additions, channel, platform, removals }) => {
|
|
490
159
|
const targetKey = `${channel}/${platform}/target-app-versions.json`;
|
|
491
|
-
const
|
|
492
|
-
const
|
|
493
|
-
const
|
|
494
|
-
for (const v of currentVersions) if (!newTargetVersions.includes(v)) newTargetVersions.push(v);
|
|
160
|
+
const oldTargetVersions = await loadOptionalObject(targetKey) ?? [];
|
|
161
|
+
const newTargetVersions = oldTargetVersions.filter((version) => !removals.has(version) || additions.has(version));
|
|
162
|
+
for (const version of additions) if (!newTargetVersions.includes(version)) newTargetVersions.push(version);
|
|
495
163
|
if (JSON.stringify(oldTargetVersions) !== JSON.stringify(newTargetVersions)) await uploadObject(targetKey, newTargetVersions);
|
|
496
|
-
}
|
|
164
|
+
}));
|
|
497
165
|
}
|
|
498
|
-
const getAppVersionUpdateInfo = async ({ appVersion, bundleId, channel = "production", cohort, minBundleId, platform }) => {
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
166
|
+
const getAppVersionUpdateInfo = async ({ appVersion, bundleId, channel = "production", cohort, minBundleId, platform }, context) => {
|
|
167
|
+
const bundles = (await mapWithConcurrency(filterCompatibleAppVersions(await loadOptionalObject(`${channel}/${platform}/target-app-versions.json`) ?? [], appVersion), STORAGE_OPERATION_CONCURRENCY, async (targetAppVersion) => {
|
|
168
|
+
return loadBundleObject(`${channel}/${platform}/${normalizeTargetAppVersion(targetAppVersion) ?? targetAppVersion}/update.json`);
|
|
169
|
+
})).flat();
|
|
170
|
+
return resolveUpdateInfoFromBundles({
|
|
171
|
+
args: {
|
|
172
|
+
_updateStrategy: "appVersion",
|
|
173
|
+
appVersion,
|
|
174
|
+
bundleId,
|
|
175
|
+
channel,
|
|
176
|
+
cohort,
|
|
177
|
+
minBundleId,
|
|
178
|
+
platform
|
|
179
|
+
},
|
|
180
|
+
bundles,
|
|
181
|
+
context
|
|
510
182
|
});
|
|
511
183
|
};
|
|
512
|
-
const getFingerprintUpdateInfo = async ({ bundleId, channel = "production", cohort, fingerprintHash, minBundleId, platform }) => {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
184
|
+
const getFingerprintUpdateInfo = async ({ bundleId, channel = "production", cohort, fingerprintHash, minBundleId, platform }, context) => {
|
|
185
|
+
const bundles = await loadBundleObject(`${channel}/${platform}/${fingerprintHash}/update.json`);
|
|
186
|
+
return resolveUpdateInfoFromBundles({
|
|
187
|
+
args: {
|
|
188
|
+
_updateStrategy: "fingerprint",
|
|
189
|
+
bundleId,
|
|
190
|
+
channel,
|
|
191
|
+
cohort,
|
|
192
|
+
fingerprintHash,
|
|
193
|
+
minBundleId,
|
|
194
|
+
platform
|
|
195
|
+
},
|
|
196
|
+
bundles,
|
|
197
|
+
context
|
|
521
198
|
});
|
|
522
199
|
};
|
|
523
200
|
const addAppVersionInvalidationPaths = (pathsToInvalidate, { platform, channel, targetAppVersion }) => {
|
|
@@ -539,48 +216,27 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
539
216
|
targetAppVersion
|
|
540
217
|
});
|
|
541
218
|
};
|
|
542
|
-
|
|
219
|
+
const createPlugin = createDatabasePlugin({
|
|
543
220
|
name,
|
|
544
221
|
factory: () => ({
|
|
545
222
|
supportsCursorPagination: true,
|
|
546
223
|
async getBundleById(bundleId) {
|
|
224
|
+
if (locallyDeletedBundleIds.has(bundleId)) return null;
|
|
547
225
|
const pendingBundle = pendingBundlesMap.get(bundleId);
|
|
548
226
|
if (pendingBundle) return removeBundleInternalKeys(pendingBundle);
|
|
549
227
|
const bundle = bundlesMap.get(bundleId);
|
|
550
228
|
if (bundle) return removeBundleInternalKeys(bundle);
|
|
551
|
-
const allRoot = await loadManagementScopeRoot({});
|
|
552
|
-
if (allRoot) {
|
|
553
|
-
const matchedBundle = await loadBundleFromManagementRoot(allRoot, bundleId);
|
|
554
|
-
if (matchedBundle) return removeBundleInternalKeys(createHydratedBundle(matchedBundle));
|
|
555
|
-
managementRootCache.delete(ALL_SCOPE_CACHE_KEY);
|
|
556
|
-
const refreshedAllRoot = await loadStoredManagementRoot({});
|
|
557
|
-
if (refreshedAllRoot) {
|
|
558
|
-
const refreshedBundle = await loadBundleFromManagementRoot(refreshedAllRoot, bundleId);
|
|
559
|
-
if (refreshedBundle) return removeBundleInternalKeys(createHydratedBundle(refreshedBundle));
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
229
|
const matchedBundle = (await reloadBundles()).find((item) => item.id === bundleId);
|
|
563
230
|
if (!matchedBundle) return null;
|
|
564
231
|
return removeBundleInternalKeys(matchedBundle);
|
|
565
232
|
},
|
|
566
|
-
async getUpdateInfo(args) {
|
|
567
|
-
if (args._updateStrategy === "appVersion") return getAppVersionUpdateInfo(args);
|
|
568
|
-
return getFingerprintUpdateInfo(args);
|
|
233
|
+
async getUpdateInfo(args, context) {
|
|
234
|
+
if (args._updateStrategy === "appVersion") return getAppVersionUpdateInfo(args, context);
|
|
235
|
+
return getFingerprintUpdateInfo(args, context);
|
|
569
236
|
},
|
|
570
237
|
async getBundles(options) {
|
|
571
238
|
const { where, limit, offset, orderBy, cursor } = options;
|
|
572
|
-
|
|
573
|
-
if (scope) {
|
|
574
|
-
const root = await loadManagementScopeRoot(scope);
|
|
575
|
-
if (!root) return createEmptyManagementResult(limit);
|
|
576
|
-
return readPagedBundles({
|
|
577
|
-
root,
|
|
578
|
-
limit,
|
|
579
|
-
offset,
|
|
580
|
-
cursor
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
let allBundles = await loadAllBundlesForManagementFallback();
|
|
239
|
+
let allBundles = await loadAllBundlesForManagementFallback(where);
|
|
584
240
|
if (where) allBundles = allBundles.filter((bundle) => bundleMatchesQueryWhere(bundle, where));
|
|
585
241
|
return paginateBundles({
|
|
586
242
|
bundles: allBundles,
|
|
@@ -591,18 +247,16 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
591
247
|
});
|
|
592
248
|
},
|
|
593
249
|
async getChannels() {
|
|
594
|
-
return (await
|
|
250
|
+
return [...new Set((await loadAllBundlesForManagementFallback()).map((bundle) => bundle.channel))].sort();
|
|
595
251
|
},
|
|
596
252
|
async commitBundle({ changedSets }) {
|
|
597
253
|
if (changedSets.length === 0) return;
|
|
598
254
|
const changedBundlesByKey = {};
|
|
599
255
|
const removalsByKey = {};
|
|
256
|
+
const targetVersionRemovalsByKey = {};
|
|
600
257
|
const pathsToInvalidate = /* @__PURE__ */ new Set();
|
|
601
|
-
|
|
602
|
-
let isChannelChanged = false;
|
|
258
|
+
const targetVersionMutations = /* @__PURE__ */ new Map();
|
|
603
259
|
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
260
|
if (operation === "insert") {
|
|
607
261
|
const target = resolveStorageTarget(data);
|
|
608
262
|
const key = `${data.channel}/${data.platform}/${target}/update.json`;
|
|
@@ -610,10 +264,12 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
610
264
|
...data,
|
|
611
265
|
_updateJsonKey: key
|
|
612
266
|
};
|
|
267
|
+
locallyDeletedBundleIds.delete(data.id);
|
|
613
268
|
bundlesMap.set(data.id, bundleWithKey);
|
|
614
269
|
pendingBundlesMap.set(data.id, bundleWithKey);
|
|
615
270
|
changedBundlesByKey[key] = changedBundlesByKey[key] || [];
|
|
616
271
|
changedBundlesByKey[key].push(removeBundleInternalKeys(bundleWithKey));
|
|
272
|
+
addTargetVersionAddition(targetVersionMutations, data);
|
|
617
273
|
addLookupInvalidationPaths(pathsToInvalidate, data);
|
|
618
274
|
continue;
|
|
619
275
|
}
|
|
@@ -623,9 +279,12 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
623
279
|
if (!bundle) throw new Error("Bundle to delete not found");
|
|
624
280
|
bundlesMap.delete(data.id);
|
|
625
281
|
pendingBundlesMap.delete(data.id);
|
|
282
|
+
locallyDeletedBundleIds.add(data.id);
|
|
626
283
|
const key = bundle._updateJsonKey;
|
|
627
284
|
removalsByKey[key] = removalsByKey[key] || [];
|
|
628
285
|
removalsByKey[key].push(bundle.id);
|
|
286
|
+
targetVersionRemovalsByKey[key] = targetVersionRemovalsByKey[key] || [];
|
|
287
|
+
targetVersionRemovalsByKey[key].push(bundle);
|
|
629
288
|
addLookupInvalidationPaths(pathsToInvalidate, bundle);
|
|
630
289
|
continue;
|
|
631
290
|
}
|
|
@@ -642,11 +301,14 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
642
301
|
const oldKey = bundle._updateJsonKey;
|
|
643
302
|
removalsByKey[oldKey] = removalsByKey[oldKey] || [];
|
|
644
303
|
removalsByKey[oldKey].push(bundle.id);
|
|
304
|
+
targetVersionRemovalsByKey[oldKey] = targetVersionRemovalsByKey[oldKey] || [];
|
|
305
|
+
targetVersionRemovalsByKey[oldKey].push(bundle);
|
|
645
306
|
changedBundlesByKey[newKey] = changedBundlesByKey[newKey] || [];
|
|
646
307
|
updatedBundle._oldUpdateJsonKey = oldKey;
|
|
647
308
|
updatedBundle._updateJsonKey = newKey;
|
|
648
309
|
bundlesMap.set(data.id, updatedBundle);
|
|
649
310
|
pendingBundlesMap.set(data.id, updatedBundle);
|
|
311
|
+
locallyDeletedBundleIds.delete(data.id);
|
|
650
312
|
changedBundlesByKey[newKey].push(removeBundleInternalKeys(updatedBundle));
|
|
651
313
|
const oldChannel = bundle.channel;
|
|
652
314
|
const nextChannel = updatedBundle.channel;
|
|
@@ -657,6 +319,7 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
657
319
|
channel: nextChannel
|
|
658
320
|
});
|
|
659
321
|
}
|
|
322
|
+
addTargetVersionAddition(targetVersionMutations, updatedBundle);
|
|
660
323
|
addLookupInvalidationPaths(pathsToInvalidate, updatedBundle);
|
|
661
324
|
if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle.targetAppVersion) addLookupInvalidationPaths(pathsToInvalidate, bundle);
|
|
662
325
|
continue;
|
|
@@ -664,20 +327,24 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
664
327
|
const currentKey = bundle._updateJsonKey;
|
|
665
328
|
bundlesMap.set(data.id, updatedBundle);
|
|
666
329
|
pendingBundlesMap.set(data.id, updatedBundle);
|
|
330
|
+
locallyDeletedBundleIds.delete(data.id);
|
|
667
331
|
changedBundlesByKey[currentKey] = changedBundlesByKey[currentKey] || [];
|
|
668
332
|
changedBundlesByKey[currentKey].push(removeBundleInternalKeys(updatedBundle));
|
|
669
333
|
addLookupInvalidationPaths(pathsToInvalidate, updatedBundle);
|
|
334
|
+
addTargetVersionAddition(targetVersionMutations, updatedBundle);
|
|
670
335
|
if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle.targetAppVersion) addLookupInvalidationPaths(pathsToInvalidate, bundle);
|
|
671
336
|
}
|
|
672
337
|
}
|
|
673
|
-
|
|
674
|
-
const updatedBundles = (await
|
|
338
|
+
await forEachWithConcurrency(Object.keys(removalsByKey), STORAGE_OPERATION_CONCURRENCY, async (oldKey) => {
|
|
339
|
+
const updatedBundles = (await loadOptionalObject(oldKey) ?? []).filter((b) => !removalsByKey[oldKey].includes(b.id));
|
|
675
340
|
updatedBundles.sort((a, b) => b.id.localeCompare(a.id));
|
|
676
|
-
if (updatedBundles.length === 0)
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
341
|
+
if (updatedBundles.length === 0) {
|
|
342
|
+
await deleteObject(oldKey);
|
|
343
|
+
for (const removedBundle of targetVersionRemovalsByKey[oldKey] ?? []) addTargetVersionRemoval(targetVersionMutations, removedBundle);
|
|
344
|
+
} else await uploadObject(oldKey, updatedBundles);
|
|
345
|
+
});
|
|
346
|
+
await forEachWithConcurrency(Object.keys(changedBundlesByKey), STORAGE_OPERATION_CONCURRENCY, async (key) => {
|
|
347
|
+
const currentBundles = await loadOptionalObject(key) ?? [];
|
|
681
348
|
const pureBundles = changedBundlesByKey[key].map((bundle) => bundle);
|
|
682
349
|
for (const changedBundle of pureBundles) {
|
|
683
350
|
const index = currentBundles.findIndex((b) => b.id === changedBundle.id);
|
|
@@ -686,22 +353,8 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
686
353
|
}
|
|
687
354
|
currentBundles.sort((a, b) => b.id.localeCompare(a.id));
|
|
688
355
|
await uploadObject(key, currentBundles);
|
|
689
|
-
})
|
|
690
|
-
if (
|
|
691
|
-
const currentIndexBundles = await loadCurrentBundlesForIndexRebuild();
|
|
692
|
-
const nextIndexMap = new Map(currentIndexBundles.map((bundle) => [bundle.id, bundle]));
|
|
693
|
-
for (const { operation, data } of changedSets) {
|
|
694
|
-
if (operation === "delete") {
|
|
695
|
-
nextIndexMap.delete(data.id);
|
|
696
|
-
continue;
|
|
697
|
-
}
|
|
698
|
-
nextIndexMap.set(data.id, data);
|
|
699
|
-
}
|
|
700
|
-
const nextIndexBundles = sortManagedBundles(Array.from(nextIndexMap.values()));
|
|
701
|
-
const previousArtifacts = buildManagementIndexArtifacts(currentIndexBundles, managementIndexPageSize);
|
|
702
|
-
const nextArtifacts = buildManagementIndexArtifacts(nextIndexBundles, managementIndexPageSize);
|
|
703
|
-
await persistManagementIndexArtifacts(nextArtifacts, previousArtifacts);
|
|
704
|
-
replaceManagementRootCache(nextArtifacts);
|
|
356
|
+
});
|
|
357
|
+
if (targetVersionMutations.size > 0) await applyTargetVersionMutations(targetVersionMutations);
|
|
705
358
|
const encondedPaths = /* @__PURE__ */ new Set();
|
|
706
359
|
for (const path of pathsToInvalidate) encondedPaths.add(encodeURI(path));
|
|
707
360
|
await invalidatePaths(Array.from(encondedPaths));
|
|
@@ -709,6 +362,9 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
709
362
|
}
|
|
710
363
|
})
|
|
711
364
|
})({}, hooks);
|
|
365
|
+
return () => {
|
|
366
|
+
return createPlugin();
|
|
367
|
+
};
|
|
712
368
|
};
|
|
713
369
|
};
|
|
714
370
|
//#endregion
|