@hot-updater/plugin-core 0.29.4 → 0.29.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/createBlobDatabasePlugin.cjs +480 -41
- package/dist/createBlobDatabasePlugin.d.cts +4 -1
- package/dist/createBlobDatabasePlugin.d.mts +4 -1
- package/dist/createBlobDatabasePlugin.mjs +480 -41
- package/dist/createDatabasePlugin.cjs +170 -3
- package/dist/createDatabasePlugin.d.cts +6 -2
- package/dist/createDatabasePlugin.d.mts +6 -2
- package/dist/createDatabasePlugin.mjs +170 -3
- package/dist/createDatabasePluginGetUpdateInfo.cjs +28 -0
- package/dist/createDatabasePluginGetUpdateInfo.d.cts +27 -0
- package/dist/createDatabasePluginGetUpdateInfo.d.mts +27 -0
- package/dist/createDatabasePluginGetUpdateInfo.mjs +27 -0
- package/dist/index.cjs +6 -2
- package/dist/index.d.cts +5 -3
- package/dist/index.d.mts +5 -3
- package/dist/index.mjs +5 -3
- package/dist/paginateBundles.cjs +53 -0
- package/dist/paginateBundles.d.cts +18 -0
- package/dist/paginateBundles.d.mts +18 -0
- package/dist/paginateBundles.mjs +53 -0
- package/dist/types/index.d.cts +28 -3
- package/dist/types/index.d.mts +28 -3
- package/package.json +4 -3
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
|
|
2
2
|
const require_calculatePagination = require("./calculatePagination.cjs");
|
|
3
3
|
const require_createDatabasePlugin = require("./createDatabasePlugin.cjs");
|
|
4
|
+
const require_filterCompatibleAppVersions = require("./filterCompatibleAppVersions.cjs");
|
|
4
5
|
const require_queryBundles = require("./queryBundles.cjs");
|
|
6
|
+
const require_paginateBundles = require("./paginateBundles.cjs");
|
|
7
|
+
let _hot_updater_js = require("@hot-updater/js");
|
|
5
8
|
let es_toolkit = require("es-toolkit");
|
|
6
9
|
let semver = require("semver");
|
|
7
10
|
semver = require_runtime.__toESM(semver);
|
|
@@ -46,6 +49,135 @@ function resolveStorageTarget({ targetAppVersion, fingerprintHash }) {
|
|
|
46
49
|
if (!target) throw new Error("target not found");
|
|
47
50
|
return target;
|
|
48
51
|
}
|
|
52
|
+
const DEFAULT_DESC_ORDER = {
|
|
53
|
+
field: "id",
|
|
54
|
+
direction: "desc"
|
|
55
|
+
};
|
|
56
|
+
const MANAGEMENT_INDEX_PREFIX = "_index";
|
|
57
|
+
const MANAGEMENT_INDEX_VERSION = 1;
|
|
58
|
+
const DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE = 128;
|
|
59
|
+
const ALL_SCOPE_CACHE_KEY = "*|*";
|
|
60
|
+
function resolveManagementIndexPageSize(config) {
|
|
61
|
+
const pageSize = config.managementIndexPageSize ?? DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE;
|
|
62
|
+
if (!Number.isInteger(pageSize) || pageSize < 1) throw new Error("managementIndexPageSize must be a positive integer.");
|
|
63
|
+
return pageSize;
|
|
64
|
+
}
|
|
65
|
+
function sortManagedBundles(bundles, orderBy = DEFAULT_DESC_ORDER) {
|
|
66
|
+
return require_queryBundles.sortBundles(bundles, orderBy);
|
|
67
|
+
}
|
|
68
|
+
function isDefaultManagementOrder(orderBy) {
|
|
69
|
+
return orderBy === void 0 || orderBy.field === DEFAULT_DESC_ORDER.field && orderBy.direction === DEFAULT_DESC_ORDER.direction;
|
|
70
|
+
}
|
|
71
|
+
function hasUnsupportedManagementFilters(where) {
|
|
72
|
+
if (!where) return false;
|
|
73
|
+
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);
|
|
74
|
+
}
|
|
75
|
+
function getSupportedManagementScope(where, orderBy) {
|
|
76
|
+
if (!isDefaultManagementOrder(orderBy) || hasUnsupportedManagementFilters(where)) return null;
|
|
77
|
+
return {
|
|
78
|
+
channel: where?.channel,
|
|
79
|
+
platform: where?.platform
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function encodeScopePart(value) {
|
|
83
|
+
return encodeURIComponent(value);
|
|
84
|
+
}
|
|
85
|
+
function getManagementScopeCacheKey({ channel, platform }) {
|
|
86
|
+
return `${channel ?? "*"}|${platform ?? "*"}`;
|
|
87
|
+
}
|
|
88
|
+
function getManagementScopePrefix({ channel, platform }) {
|
|
89
|
+
if (channel && platform) return `${MANAGEMENT_INDEX_PREFIX}/channel/${encodeScopePart(channel)}/platform/${platform}`;
|
|
90
|
+
if (channel) return `${MANAGEMENT_INDEX_PREFIX}/channel/${encodeScopePart(channel)}`;
|
|
91
|
+
if (platform) return `${MANAGEMENT_INDEX_PREFIX}/platform/${platform}`;
|
|
92
|
+
return `${MANAGEMENT_INDEX_PREFIX}/all`;
|
|
93
|
+
}
|
|
94
|
+
function getManagementRootKey(scope) {
|
|
95
|
+
return `${getManagementScopePrefix(scope)}/root.json`;
|
|
96
|
+
}
|
|
97
|
+
function getManagementPageKey(scope, pageIndex) {
|
|
98
|
+
return `${getManagementScopePrefix(scope)}/pages/${String(pageIndex).padStart(4, "0")}.json`;
|
|
99
|
+
}
|
|
100
|
+
function createBundleWithUpdateJsonKey(bundle) {
|
|
101
|
+
const target = resolveStorageTarget(bundle);
|
|
102
|
+
return {
|
|
103
|
+
...bundle,
|
|
104
|
+
_updateJsonKey: `${bundle.channel}/${bundle.platform}/${target}/update.json`
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function getPageStartOffsets(pages) {
|
|
108
|
+
const startOffsets = [];
|
|
109
|
+
let offset = 0;
|
|
110
|
+
for (const page of pages) {
|
|
111
|
+
startOffsets.push(offset);
|
|
112
|
+
offset += page.count;
|
|
113
|
+
}
|
|
114
|
+
return startOffsets;
|
|
115
|
+
}
|
|
116
|
+
function createEmptyManagementResult(limit) {
|
|
117
|
+
return {
|
|
118
|
+
data: [],
|
|
119
|
+
pagination: require_calculatePagination.calculatePagination(0, {
|
|
120
|
+
limit,
|
|
121
|
+
offset: 0
|
|
122
|
+
})
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function buildManagementIndexArtifacts(allBundles, pageSize) {
|
|
126
|
+
const sortedAllBundles = sortManagedBundles(allBundles);
|
|
127
|
+
const pages = /* @__PURE__ */ new Map();
|
|
128
|
+
const scopes = [];
|
|
129
|
+
const channels = [...new Set(sortedAllBundles.map((bundle) => bundle.channel))].sort();
|
|
130
|
+
const addScope = (scope, scopeBundles, options) => {
|
|
131
|
+
if (!options?.includeChannels && scopeBundles.length === 0) return;
|
|
132
|
+
const pageKeys = [];
|
|
133
|
+
const pageDescriptors = [];
|
|
134
|
+
for (let pageIndex = 0; pageIndex * pageSize < scopeBundles.length; pageIndex++) {
|
|
135
|
+
const page = scopeBundles.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize);
|
|
136
|
+
const key = getManagementPageKey(scope, pageIndex);
|
|
137
|
+
pages.set(key, page);
|
|
138
|
+
pageKeys.push(key);
|
|
139
|
+
pageDescriptors.push({
|
|
140
|
+
key,
|
|
141
|
+
count: page.length,
|
|
142
|
+
firstId: page[0].id,
|
|
143
|
+
lastId: page.at(-1).id
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const root = {
|
|
147
|
+
version: MANAGEMENT_INDEX_VERSION,
|
|
148
|
+
pageSize,
|
|
149
|
+
total: scopeBundles.length,
|
|
150
|
+
pages: pageDescriptors,
|
|
151
|
+
...options?.includeChannels ? { channels } : {}
|
|
152
|
+
};
|
|
153
|
+
scopes.push({
|
|
154
|
+
cacheKey: getManagementScopeCacheKey(scope),
|
|
155
|
+
rootKey: getManagementRootKey(scope),
|
|
156
|
+
root,
|
|
157
|
+
pageKeys
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
addScope({}, sortedAllBundles, { includeChannels: true });
|
|
161
|
+
for (const channel of channels) {
|
|
162
|
+
const channelBundles = sortedAllBundles.filter((bundle) => bundle.channel === channel);
|
|
163
|
+
addScope({ channel }, channelBundles);
|
|
164
|
+
for (const platform of ["ios", "android"]) {
|
|
165
|
+
const scopedBundles = channelBundles.filter((bundle) => bundle.platform === platform);
|
|
166
|
+
addScope({
|
|
167
|
+
channel,
|
|
168
|
+
platform
|
|
169
|
+
}, scopedBundles);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (const platform of ["ios", "android"]) {
|
|
173
|
+
const platformBundles = sortedAllBundles.filter((bundle) => bundle.platform === platform);
|
|
174
|
+
addScope({ platform }, platformBundles);
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
pages,
|
|
178
|
+
scopes
|
|
179
|
+
};
|
|
180
|
+
}
|
|
49
181
|
/**
|
|
50
182
|
* Creates a blob storage-based database plugin with lazy initialization.
|
|
51
183
|
*
|
|
@@ -55,22 +187,282 @@ function resolveStorageTarget({ targetAppVersion, fingerprintHash }) {
|
|
|
55
187
|
*/
|
|
56
188
|
const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
57
189
|
return (config, hooks) => {
|
|
190
|
+
const managementIndexPageSize = resolveManagementIndexPageSize(config);
|
|
58
191
|
const { listObjects, loadObject, uploadObject, deleteObject, invalidatePaths, apiBasePath } = factory(config);
|
|
59
192
|
const bundlesMap = /* @__PURE__ */ new Map();
|
|
60
193
|
const pendingBundlesMap = /* @__PURE__ */ new Map();
|
|
194
|
+
const managementRootCache = /* @__PURE__ */ new Map();
|
|
61
195
|
const PLATFORMS = ["ios", "android"];
|
|
196
|
+
const getAllManagementArtifact = (artifacts) => {
|
|
197
|
+
const allArtifact = artifacts.scopes.find((scope) => scope.cacheKey === ALL_SCOPE_CACHE_KEY);
|
|
198
|
+
if (!allArtifact) throw new Error("all-bundles management index artifact not found");
|
|
199
|
+
return allArtifact;
|
|
200
|
+
};
|
|
201
|
+
const replaceManagementRootCache = (artifacts) => {
|
|
202
|
+
managementRootCache.clear();
|
|
203
|
+
for (const scope of artifacts.scopes) managementRootCache.set(scope.cacheKey, scope.root);
|
|
204
|
+
};
|
|
205
|
+
const createHydratedBundle = (bundle) => {
|
|
206
|
+
const hydratedBundle = createBundleWithUpdateJsonKey(bundle);
|
|
207
|
+
bundlesMap.set(hydratedBundle.id, hydratedBundle);
|
|
208
|
+
return hydratedBundle;
|
|
209
|
+
};
|
|
210
|
+
const loadStoredManagementRoot = async (scope) => {
|
|
211
|
+
const cacheKey = getManagementScopeCacheKey(scope);
|
|
212
|
+
const storedRoot = await loadObject(getManagementRootKey(scope));
|
|
213
|
+
if (storedRoot) {
|
|
214
|
+
managementRootCache.set(cacheKey, storedRoot);
|
|
215
|
+
return storedRoot;
|
|
216
|
+
}
|
|
217
|
+
managementRootCache.delete(cacheKey);
|
|
218
|
+
return null;
|
|
219
|
+
};
|
|
220
|
+
const loadManagementPage = async (descriptor, pageCache) => {
|
|
221
|
+
if (pageCache?.has(descriptor.key)) return pageCache.get(descriptor.key) ?? null;
|
|
222
|
+
const page = await loadObject(descriptor.key);
|
|
223
|
+
pageCache?.set(descriptor.key, page);
|
|
224
|
+
return page;
|
|
225
|
+
};
|
|
226
|
+
const loadBundleFromManagementRoot = async (root, bundleId) => {
|
|
227
|
+
const pageIndex = findPageIndexContainingId(root.pages, bundleId);
|
|
228
|
+
if (pageIndex < 0) return null;
|
|
229
|
+
const descriptor = root.pages[pageIndex];
|
|
230
|
+
const page = await loadManagementPage(descriptor);
|
|
231
|
+
if (!page) return null;
|
|
232
|
+
return page.find((item) => item.id === bundleId) ?? null;
|
|
233
|
+
};
|
|
234
|
+
const loadAllBundlesFromRoot = async (root) => {
|
|
235
|
+
const allBundles = [];
|
|
236
|
+
const pageCache = /* @__PURE__ */ new Map();
|
|
237
|
+
for (const descriptor of root.pages) {
|
|
238
|
+
const page = await loadManagementPage(descriptor, pageCache);
|
|
239
|
+
if (!page) return null;
|
|
240
|
+
allBundles.push(...page);
|
|
241
|
+
}
|
|
242
|
+
return allBundles;
|
|
243
|
+
};
|
|
244
|
+
const persistManagementIndexArtifacts = async (nextArtifacts, previousArtifacts) => {
|
|
245
|
+
for (const [key, page] of nextArtifacts.pages.entries()) await uploadObject(key, page);
|
|
246
|
+
for (const scope of nextArtifacts.scopes) await uploadObject(scope.rootKey, scope.root);
|
|
247
|
+
if (!previousArtifacts) return;
|
|
248
|
+
const nextPageKeys = new Set(nextArtifacts.pages.keys());
|
|
249
|
+
const nextRootKeys = new Set(nextArtifacts.scopes.map((scope) => scope.rootKey));
|
|
250
|
+
for (const [key] of previousArtifacts.pages.entries()) if (!nextPageKeys.has(key)) await deleteObject(key).catch(() => {});
|
|
251
|
+
for (const scope of previousArtifacts.scopes) if (!nextRootKeys.has(scope.rootKey)) await deleteObject(scope.rootKey).catch(() => {});
|
|
252
|
+
};
|
|
253
|
+
const ensureAllManagementRoot = async () => {
|
|
254
|
+
const storedAllRoot = await loadStoredManagementRoot({});
|
|
255
|
+
if (storedAllRoot && storedAllRoot.pageSize === managementIndexPageSize) return storedAllRoot;
|
|
256
|
+
const rebuiltBundles = sortManagedBundles((await reloadBundles()).map((bundle) => removeBundleInternalKeys(bundle)));
|
|
257
|
+
const nextArtifacts = buildManagementIndexArtifacts(rebuiltBundles, managementIndexPageSize);
|
|
258
|
+
await persistManagementIndexArtifacts(nextArtifacts, storedAllRoot ? buildManagementIndexArtifacts(rebuiltBundles, storedAllRoot.pageSize) : void 0);
|
|
259
|
+
replaceManagementRootCache(nextArtifacts);
|
|
260
|
+
return getAllManagementArtifact(nextArtifacts).root;
|
|
261
|
+
};
|
|
262
|
+
const loadManagementScopeRoot = async (scope) => {
|
|
263
|
+
const cacheKey = getManagementScopeCacheKey(scope);
|
|
264
|
+
if (cacheKey === ALL_SCOPE_CACHE_KEY) return ensureAllManagementRoot();
|
|
265
|
+
const storedRoot = await loadStoredManagementRoot(scope);
|
|
266
|
+
if (storedRoot) return storedRoot;
|
|
267
|
+
await ensureAllManagementRoot();
|
|
268
|
+
const storedScopedRoot = await loadStoredManagementRoot(scope);
|
|
269
|
+
if (storedScopedRoot) return storedScopedRoot;
|
|
270
|
+
managementRootCache.set(cacheKey, null);
|
|
271
|
+
return null;
|
|
272
|
+
};
|
|
273
|
+
const loadAllBundlesForManagementFallback = async () => {
|
|
274
|
+
const allRoot = await loadManagementScopeRoot({});
|
|
275
|
+
if (allRoot) {
|
|
276
|
+
const pagedBundles = await loadAllBundlesFromRoot(allRoot);
|
|
277
|
+
if (pagedBundles) return pagedBundles;
|
|
278
|
+
}
|
|
279
|
+
return sortManagedBundles((await reloadBundles()).map((bundle) => removeBundleInternalKeys(bundle)));
|
|
280
|
+
};
|
|
281
|
+
const loadCurrentBundlesForIndexRebuild = async () => {
|
|
282
|
+
return loadAllBundlesForManagementFallback();
|
|
283
|
+
};
|
|
284
|
+
const findPageIndexContainingId = (pages, id) => {
|
|
285
|
+
return pages.findIndex((page) => id.localeCompare(page.firstId) <= 0 && id.localeCompare(page.lastId) >= 0);
|
|
286
|
+
};
|
|
287
|
+
const readPagedBundles = async ({ root, limit, offset, cursor }) => {
|
|
288
|
+
if (root.total === 0 || root.pages.length === 0) return createEmptyManagementResult(limit);
|
|
289
|
+
const pageStartOffsets = getPageStartOffsets(root.pages);
|
|
290
|
+
const pageCache = /* @__PURE__ */ new Map();
|
|
291
|
+
if (offset !== void 0) {
|
|
292
|
+
const normalizedOffset = Math.max(0, offset);
|
|
293
|
+
if (normalizedOffset >= root.total) return {
|
|
294
|
+
data: [],
|
|
295
|
+
pagination: require_calculatePagination.calculatePagination(root.total, {
|
|
296
|
+
limit,
|
|
297
|
+
offset: normalizedOffset
|
|
298
|
+
})
|
|
299
|
+
};
|
|
300
|
+
let pageIndex = 0;
|
|
301
|
+
for (let index = pageStartOffsets.length - 1; index >= 0; index--) if ((pageStartOffsets[index] ?? 0) <= normalizedOffset) {
|
|
302
|
+
pageIndex = index;
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
const startInPage = normalizedOffset - (pageStartOffsets[pageIndex] ?? 0);
|
|
306
|
+
const data = [];
|
|
307
|
+
for (let currentPageIndex = pageIndex; currentPageIndex < root.pages.length && (limit <= 0 || data.length < limit); currentPageIndex++) {
|
|
308
|
+
const descriptor = root.pages[currentPageIndex];
|
|
309
|
+
const page = await loadManagementPage(descriptor, pageCache);
|
|
310
|
+
if (!page) return require_paginateBundles.paginateBundles({
|
|
311
|
+
bundles: await loadAllBundlesForManagementFallback(),
|
|
312
|
+
limit,
|
|
313
|
+
offset: normalizedOffset
|
|
314
|
+
});
|
|
315
|
+
data.push(...currentPageIndex === pageIndex ? page.slice(startInPage) : page);
|
|
316
|
+
}
|
|
317
|
+
const paginatedData = limit > 0 ? data.slice(0, limit) : data;
|
|
318
|
+
return {
|
|
319
|
+
data: paginatedData,
|
|
320
|
+
pagination: {
|
|
321
|
+
...require_calculatePagination.calculatePagination(root.total, {
|
|
322
|
+
limit,
|
|
323
|
+
offset: normalizedOffset
|
|
324
|
+
}),
|
|
325
|
+
...paginatedData.length > 0 && normalizedOffset + paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {},
|
|
326
|
+
...paginatedData.length > 0 && normalizedOffset > 0 ? { previousCursor: paginatedData[0]?.id } : {}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
if (cursor?.after) {
|
|
331
|
+
let pageIndex = root.pages.findIndex((page) => {
|
|
332
|
+
const containsCursor = cursor.after.localeCompare(page.firstId) <= 0 && cursor.after.localeCompare(page.lastId) >= 0;
|
|
333
|
+
const wholePageEligible = cursor.after.localeCompare(page.firstId) > 0;
|
|
334
|
+
return containsCursor || wholePageEligible;
|
|
335
|
+
});
|
|
336
|
+
if (pageIndex < 0) return {
|
|
337
|
+
data: [],
|
|
338
|
+
pagination: {
|
|
339
|
+
...require_calculatePagination.calculatePagination(root.total, {
|
|
340
|
+
limit,
|
|
341
|
+
offset: root.total
|
|
342
|
+
}),
|
|
343
|
+
previousCursor: cursor.after
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
const data = [];
|
|
347
|
+
let startIndex = null;
|
|
348
|
+
while (pageIndex < root.pages.length && (limit <= 0 || data.length < limit)) {
|
|
349
|
+
const descriptor = root.pages[pageIndex];
|
|
350
|
+
const page = await loadManagementPage(descriptor, pageCache);
|
|
351
|
+
if (!page) return require_paginateBundles.paginateBundles({
|
|
352
|
+
bundles: await loadAllBundlesForManagementFallback(),
|
|
353
|
+
limit,
|
|
354
|
+
cursor
|
|
355
|
+
});
|
|
356
|
+
const containsCursor = cursor.after.localeCompare(descriptor.firstId) <= 0 && cursor.after.localeCompare(descriptor.lastId) >= 0;
|
|
357
|
+
let eligiblePageBundles = page;
|
|
358
|
+
if (containsCursor) {
|
|
359
|
+
const startInPage = page.findIndex((bundle) => bundle.id.localeCompare(cursor.after) < 0);
|
|
360
|
+
if (startInPage < 0) eligiblePageBundles = [];
|
|
361
|
+
else {
|
|
362
|
+
eligiblePageBundles = page.slice(startInPage);
|
|
363
|
+
startIndex ??= (pageStartOffsets[pageIndex] ?? 0) + startInPage;
|
|
364
|
+
}
|
|
365
|
+
} else if (eligiblePageBundles.length > 0) startIndex ??= pageStartOffsets[pageIndex] ?? 0;
|
|
366
|
+
data.push(...eligiblePageBundles);
|
|
367
|
+
if (limit > 0 && data.length >= limit) break;
|
|
368
|
+
pageIndex += 1;
|
|
369
|
+
}
|
|
370
|
+
const paginatedData = limit > 0 ? data.slice(0, limit) : data;
|
|
371
|
+
const resolvedStartIndex = startIndex ?? root.total;
|
|
372
|
+
return {
|
|
373
|
+
data: paginatedData,
|
|
374
|
+
pagination: {
|
|
375
|
+
...require_calculatePagination.calculatePagination(root.total, {
|
|
376
|
+
limit,
|
|
377
|
+
offset: resolvedStartIndex
|
|
378
|
+
}),
|
|
379
|
+
...paginatedData.length > 0 && resolvedStartIndex + paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {},
|
|
380
|
+
...paginatedData.length > 0 && resolvedStartIndex > 0 ? { previousCursor: paginatedData[0]?.id } : {}
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
if (cursor?.before) {
|
|
385
|
+
let pageIndex = -1;
|
|
386
|
+
for (let index = root.pages.length - 1; index >= 0; index--) {
|
|
387
|
+
const page = root.pages[index];
|
|
388
|
+
const containsCursor = cursor.before.localeCompare(page.firstId) <= 0 && cursor.before.localeCompare(page.lastId) >= 0;
|
|
389
|
+
const wholePageEligible = cursor.before.localeCompare(page.lastId) < 0;
|
|
390
|
+
if (containsCursor || wholePageEligible) {
|
|
391
|
+
pageIndex = index;
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (pageIndex < 0) return createEmptyManagementResult(limit);
|
|
396
|
+
let startIndex = null;
|
|
397
|
+
let collected = [];
|
|
398
|
+
while (pageIndex >= 0 && (limit <= 0 || collected.length < limit)) {
|
|
399
|
+
const descriptor = root.pages[pageIndex];
|
|
400
|
+
const page = await loadManagementPage(descriptor, pageCache);
|
|
401
|
+
if (!page) return require_paginateBundles.paginateBundles({
|
|
402
|
+
bundles: await loadAllBundlesForManagementFallback(),
|
|
403
|
+
limit,
|
|
404
|
+
cursor
|
|
405
|
+
});
|
|
406
|
+
const eligiblePageBundles = cursor.before.localeCompare(descriptor.firstId) <= 0 && cursor.before.localeCompare(descriptor.lastId) >= 0 ? page.filter((bundle) => bundle.id.localeCompare(cursor.before) > 0) : page;
|
|
407
|
+
collected = [...eligiblePageBundles, ...collected];
|
|
408
|
+
if (eligiblePageBundles.length > 0) startIndex = pageStartOffsets[pageIndex] ?? 0;
|
|
409
|
+
if (limit > 0 && collected.length >= limit) break;
|
|
410
|
+
pageIndex -= 1;
|
|
411
|
+
}
|
|
412
|
+
if (startIndex === null || collected.length === 0) return createEmptyManagementResult(limit);
|
|
413
|
+
let paginatedData = collected;
|
|
414
|
+
if (limit > 0 && collected.length > limit) {
|
|
415
|
+
const dropCount = collected.length - limit;
|
|
416
|
+
paginatedData = collected.slice(dropCount);
|
|
417
|
+
startIndex += dropCount;
|
|
418
|
+
}
|
|
419
|
+
const pagination = require_calculatePagination.calculatePagination(root.total, {
|
|
420
|
+
limit,
|
|
421
|
+
offset: startIndex
|
|
422
|
+
});
|
|
423
|
+
return {
|
|
424
|
+
data: paginatedData,
|
|
425
|
+
pagination: {
|
|
426
|
+
...pagination,
|
|
427
|
+
...paginatedData.length > 0 && startIndex + paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {},
|
|
428
|
+
...paginatedData.length > 0 && startIndex > 0 ? { previousCursor: paginatedData[0]?.id } : {}
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
const pageIndex = 0;
|
|
433
|
+
const startInPage = 0;
|
|
434
|
+
const data = [];
|
|
435
|
+
for (let currentPageIndex = pageIndex; currentPageIndex < root.pages.length && (limit <= 0 || data.length < limit); currentPageIndex++) {
|
|
436
|
+
const descriptor = root.pages[currentPageIndex];
|
|
437
|
+
const page = await loadManagementPage(descriptor, pageCache);
|
|
438
|
+
if (!page) return require_paginateBundles.paginateBundles({
|
|
439
|
+
bundles: await loadAllBundlesForManagementFallback(),
|
|
440
|
+
limit,
|
|
441
|
+
cursor
|
|
442
|
+
});
|
|
443
|
+
data.push(...currentPageIndex === pageIndex ? page.slice(startInPage) : page);
|
|
444
|
+
}
|
|
445
|
+
const paginatedData = limit > 0 ? data.slice(0, limit) : data;
|
|
446
|
+
return {
|
|
447
|
+
data: paginatedData,
|
|
448
|
+
pagination: {
|
|
449
|
+
...require_calculatePagination.calculatePagination(root.total, {
|
|
450
|
+
limit,
|
|
451
|
+
offset: 0
|
|
452
|
+
}),
|
|
453
|
+
...paginatedData.length > 0 && paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {}
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
};
|
|
62
457
|
async function reloadBundles() {
|
|
63
458
|
bundlesMap.clear();
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}));
|
|
70
|
-
});
|
|
71
|
-
return (await Promise.all(filePromises)).flat();
|
|
459
|
+
const filePromises = (await listObjects("")).filter((key) => /^[^/]+\/(?:ios|android)\/[^/]+\/update\.json$/.test(key)).map(async (key) => {
|
|
460
|
+
return (await loadObject(key) ?? []).map((bundle) => ({
|
|
461
|
+
...bundle,
|
|
462
|
+
_updateJsonKey: key
|
|
463
|
+
}));
|
|
72
464
|
});
|
|
73
|
-
const allBundles = (await Promise.all(
|
|
465
|
+
const allBundles = (await Promise.all(filePromises)).flat();
|
|
74
466
|
for (const bundle of allBundles) bundlesMap.set(bundle.id, bundle);
|
|
75
467
|
for (const [id, bundle] of pendingBundlesMap.entries()) bundlesMap.set(id, bundle);
|
|
76
468
|
return (0, es_toolkit.orderBy)(allBundles, [(v) => v.id], ["desc"]);
|
|
@@ -105,17 +497,31 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
105
497
|
if (JSON.stringify(oldTargetVersions) !== JSON.stringify(newTargetVersions)) await uploadObject(targetKey, newTargetVersions);
|
|
106
498
|
}
|
|
107
499
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
500
|
+
const getAppVersionUpdateInfo = async ({ appVersion, bundleId, channel = "production", cohort, minBundleId, platform }) => {
|
|
501
|
+
const matchingVersions = require_filterCompatibleAppVersions.filterCompatibleAppVersions(await loadObject(`${channel}/${platform}/target-app-versions.json`) ?? [], appVersion);
|
|
502
|
+
return (0, _hot_updater_js.getUpdateInfo)((await Promise.allSettled(matchingVersions.map(async (targetAppVersion) => {
|
|
503
|
+
return await loadObject(`${channel}/${platform}/${normalizeTargetAppVersion(targetAppVersion) ?? targetAppVersion}/update.json`) ?? [];
|
|
504
|
+
}))).filter((entry) => entry.status === "fulfilled").flatMap((entry) => entry.value), {
|
|
505
|
+
_updateStrategy: "appVersion",
|
|
506
|
+
appVersion,
|
|
507
|
+
bundleId,
|
|
508
|
+
channel,
|
|
509
|
+
cohort,
|
|
510
|
+
minBundleId,
|
|
511
|
+
platform
|
|
512
|
+
});
|
|
513
|
+
};
|
|
514
|
+
const getFingerprintUpdateInfo = async ({ bundleId, channel = "production", cohort, fingerprintHash, minBundleId, platform }) => {
|
|
515
|
+
return (0, _hot_updater_js.getUpdateInfo)(await loadObject(`${channel}/${platform}/${fingerprintHash}/update.json`) ?? [], {
|
|
516
|
+
_updateStrategy: "fingerprint",
|
|
517
|
+
bundleId,
|
|
518
|
+
channel,
|
|
519
|
+
cohort,
|
|
520
|
+
fingerprintHash,
|
|
521
|
+
minBundleId,
|
|
522
|
+
platform
|
|
523
|
+
});
|
|
524
|
+
};
|
|
119
525
|
const addAppVersionInvalidationPaths = (pathsToInvalidate, { platform, channel, targetAppVersion }) => {
|
|
120
526
|
if (!isExactVersion(targetAppVersion)) {
|
|
121
527
|
pathsToInvalidate.add(`${apiBasePath}/app-version/${platform}/*`);
|
|
@@ -138,37 +544,56 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
138
544
|
return require_createDatabasePlugin.createDatabasePlugin({
|
|
139
545
|
name,
|
|
140
546
|
factory: () => ({
|
|
547
|
+
supportsCursorPagination: true,
|
|
141
548
|
async getBundleById(bundleId) {
|
|
142
549
|
const pendingBundle = pendingBundlesMap.get(bundleId);
|
|
143
550
|
if (pendingBundle) return removeBundleInternalKeys(pendingBundle);
|
|
144
551
|
const bundle = bundlesMap.get(bundleId);
|
|
145
552
|
if (bundle) return removeBundleInternalKeys(bundle);
|
|
146
|
-
|
|
553
|
+
const allRoot = await loadManagementScopeRoot({});
|
|
554
|
+
if (allRoot) {
|
|
555
|
+
const matchedBundle = await loadBundleFromManagementRoot(allRoot, bundleId);
|
|
556
|
+
if (matchedBundle) return removeBundleInternalKeys(createHydratedBundle(matchedBundle));
|
|
557
|
+
managementRootCache.delete(ALL_SCOPE_CACHE_KEY);
|
|
558
|
+
const refreshedAllRoot = await loadStoredManagementRoot({});
|
|
559
|
+
if (refreshedAllRoot) {
|
|
560
|
+
const refreshedBundle = await loadBundleFromManagementRoot(refreshedAllRoot, bundleId);
|
|
561
|
+
if (refreshedBundle) return removeBundleInternalKeys(createHydratedBundle(refreshedBundle));
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
const matchedBundle = (await reloadBundles()).find((item) => item.id === bundleId);
|
|
565
|
+
if (!matchedBundle) return null;
|
|
566
|
+
return removeBundleInternalKeys(matchedBundle);
|
|
567
|
+
},
|
|
568
|
+
async getUpdateInfo(args) {
|
|
569
|
+
if (args._updateStrategy === "appVersion") return getAppVersionUpdateInfo(args);
|
|
570
|
+
return getFingerprintUpdateInfo(args);
|
|
147
571
|
},
|
|
148
572
|
async getBundles(options) {
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (limit) paginatedData = paginatedData.slice(0, limit);
|
|
157
|
-
return {
|
|
158
|
-
data: paginatedData,
|
|
159
|
-
pagination: require_calculatePagination.calculatePagination(total, {
|
|
573
|
+
const { where, limit, offset, orderBy, cursor } = options;
|
|
574
|
+
const scope = getSupportedManagementScope(where, orderBy);
|
|
575
|
+
if (scope) {
|
|
576
|
+
const root = await loadManagementScopeRoot(scope);
|
|
577
|
+
if (!root) return createEmptyManagementResult(limit);
|
|
578
|
+
return readPagedBundles({
|
|
579
|
+
root,
|
|
160
580
|
limit,
|
|
161
|
-
offset
|
|
162
|
-
|
|
163
|
-
|
|
581
|
+
offset,
|
|
582
|
+
cursor
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
let allBundles = await loadAllBundlesForManagementFallback();
|
|
586
|
+
if (where) allBundles = allBundles.filter((bundle) => require_queryBundles.bundleMatchesQueryWhere(bundle, where));
|
|
587
|
+
return require_paginateBundles.paginateBundles({
|
|
588
|
+
bundles: allBundles,
|
|
589
|
+
limit,
|
|
590
|
+
offset,
|
|
591
|
+
cursor,
|
|
592
|
+
orderBy
|
|
593
|
+
});
|
|
164
594
|
},
|
|
165
595
|
async getChannels() {
|
|
166
|
-
|
|
167
|
-
const result = await this.getBundles({
|
|
168
|
-
limit: total,
|
|
169
|
-
offset: 0
|
|
170
|
-
});
|
|
171
|
-
return [...new Set(result.data.map((bundle) => bundle.channel))];
|
|
596
|
+
return (await loadManagementScopeRoot({}))?.channels ?? [];
|
|
172
597
|
},
|
|
173
598
|
async commitBundle({ changedSets }) {
|
|
174
599
|
if (changedSets.length === 0) return;
|
|
@@ -265,6 +690,20 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
265
690
|
await uploadObject(key, currentBundles);
|
|
266
691
|
})();
|
|
267
692
|
if (isTargetAppVersionChanged || isChannelChanged) for (const platform of PLATFORMS) await updateTargetVersionsForPlatform(platform);
|
|
693
|
+
const currentIndexBundles = await loadCurrentBundlesForIndexRebuild();
|
|
694
|
+
const nextIndexMap = new Map(currentIndexBundles.map((bundle) => [bundle.id, bundle]));
|
|
695
|
+
for (const { operation, data } of changedSets) {
|
|
696
|
+
if (operation === "delete") {
|
|
697
|
+
nextIndexMap.delete(data.id);
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
nextIndexMap.set(data.id, data);
|
|
701
|
+
}
|
|
702
|
+
const nextIndexBundles = sortManagedBundles(Array.from(nextIndexMap.values()));
|
|
703
|
+
const previousArtifacts = buildManagementIndexArtifacts(currentIndexBundles, managementIndexPageSize);
|
|
704
|
+
const nextArtifacts = buildManagementIndexArtifacts(nextIndexBundles, managementIndexPageSize);
|
|
705
|
+
await persistManagementIndexArtifacts(nextArtifacts, previousArtifacts);
|
|
706
|
+
replaceManagementRootCache(nextArtifacts);
|
|
268
707
|
const encondedPaths = /* @__PURE__ */ new Set();
|
|
269
708
|
for (const path of pathsToInvalidate) encondedPaths.add(encodeURI(path));
|
|
270
709
|
await invalidatePaths(Array.from(encondedPaths));
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { DatabasePlugin, DatabasePluginHooks } from "./types/index.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/createBlobDatabasePlugin.d.ts
|
|
4
|
+
interface BlobDatabasePluginConfig {
|
|
5
|
+
managementIndexPageSize?: number;
|
|
6
|
+
}
|
|
4
7
|
interface BlobOperations {
|
|
5
8
|
listObjects: (prefix: string) => Promise<string[]>;
|
|
6
9
|
loadObject: <T>(key: string) => Promise<T | null>;
|
|
@@ -24,4 +27,4 @@ declare const createBlobDatabasePlugin: <TConfig>({
|
|
|
24
27
|
factory: (config: TConfig) => BlobOperations;
|
|
25
28
|
}) => (config: TConfig, hooks?: DatabasePluginHooks) => () => DatabasePlugin<unknown>;
|
|
26
29
|
//#endregion
|
|
27
|
-
export { BlobOperations, createBlobDatabasePlugin };
|
|
30
|
+
export { BlobDatabasePluginConfig, BlobOperations, createBlobDatabasePlugin };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { DatabasePlugin, DatabasePluginHooks } from "./types/index.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/createBlobDatabasePlugin.d.ts
|
|
4
|
+
interface BlobDatabasePluginConfig {
|
|
5
|
+
managementIndexPageSize?: number;
|
|
6
|
+
}
|
|
4
7
|
interface BlobOperations {
|
|
5
8
|
listObjects: (prefix: string) => Promise<string[]>;
|
|
6
9
|
loadObject: <T>(key: string) => Promise<T | null>;
|
|
@@ -24,4 +27,4 @@ declare const createBlobDatabasePlugin: <TConfig>({
|
|
|
24
27
|
factory: (config: TConfig) => BlobOperations;
|
|
25
28
|
}) => (config: TConfig, hooks?: DatabasePluginHooks) => () => DatabasePlugin<unknown>;
|
|
26
29
|
//#endregion
|
|
27
|
-
export { BlobOperations, createBlobDatabasePlugin };
|
|
30
|
+
export { BlobDatabasePluginConfig, BlobOperations, createBlobDatabasePlugin };
|