@hot-updater/server 0.32.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/db/index.cjs CHANGED
@@ -29,20 +29,17 @@ function createHotUpdater(options) {
29
29
  resolveFileUrl
30
30
  });
31
31
  const api = {
32
- ...core.api,
32
+ basePath,
33
+ adapterName: core.adapterName,
34
+ createMigrator: core.createMigrator,
35
+ generateSchema: core.generateSchema,
33
36
  handler: require_handler.createHandler(core.api, {
34
37
  basePath,
35
38
  routes: options.routes
36
- }),
37
- adapterName: core.adapterName,
38
- createMigrator: core.createMigrator,
39
- generateSchema: core.generateSchema
40
- };
41
- return {
42
- ...api,
43
- basePath,
44
- handler: api.handler
39
+ })
45
40
  };
41
+ Object.defineProperties(api, Object.getOwnPropertyDescriptors(core.api));
42
+ return api;
46
43
  }
47
44
  //#endregion
48
45
  exports.createHotUpdater = createHotUpdater;
package/dist/db/index.mjs CHANGED
@@ -28,20 +28,17 @@ function createHotUpdater(options) {
28
28
  resolveFileUrl
29
29
  });
30
30
  const api = {
31
- ...core.api,
31
+ basePath,
32
+ adapterName: core.adapterName,
33
+ createMigrator: core.createMigrator,
34
+ generateSchema: core.generateSchema,
32
35
  handler: createHandler(core.api, {
33
36
  basePath,
34
37
  routes: options.routes
35
- }),
36
- adapterName: core.adapterName,
37
- createMigrator: core.createMigrator,
38
- generateSchema: core.generateSchema
39
- };
40
- return {
41
- ...api,
42
- basePath,
43
- handler: api.handler
38
+ })
44
39
  };
40
+ Object.defineProperties(api, Object.getOwnPropertyDescriptors(core.api));
41
+ return api;
45
42
  }
46
43
  //#endregion
47
44
  export { createHotUpdater };
@@ -1,6 +1,7 @@
1
1
  require("../_virtual/_rolldown/runtime.cjs");
2
2
  const require_schemaEnhancements = require("./schemaEnhancements.cjs");
3
3
  const require_updateArtifacts = require("./updateArtifacts.cjs");
4
+ const require_requestBundleIdentityMap = require("./requestBundleIdentityMap.cjs");
4
5
  let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
5
6
  let _hot_updater_core = require("@hot-updater/core");
6
7
  //#region src/db/pluginCore.ts
@@ -111,109 +112,148 @@ function createPluginDatabaseCore(getPlugin, resolveFileUrl, options) {
111
112
  enabled: true,
112
113
  id: { gte: minBundleId }
113
114
  });
114
- return {
115
- api: {
116
- async getBundleById(id, context) {
117
- return getPlugin().getBundleById(id, context);
118
- },
119
- async getUpdateInfo(args, context) {
120
- const directGetUpdateInfo = getPlugin().getUpdateInfo;
121
- if (directGetUpdateInfo) return context === void 0 ? await directGetUpdateInfo(args) : await directGetUpdateInfo(args, context);
122
- const channel = args.channel ?? "production";
123
- const minBundleId = args.minBundleId ?? _hot_updater_core.NIL_UUID;
124
- const baseWhere = getBaseWhere({
125
- platform: args.platform,
126
- channel,
127
- minBundleId
128
- });
129
- if (args._updateStrategy === "fingerprint") return findUpdateInfoByScanning({
130
- args,
131
- queryWhere: {
132
- ...baseWhere,
133
- fingerprintHash: args.fingerprintHash
134
- },
135
- context,
136
- isCandidate: (bundle) => {
137
- return bundle.enabled && bundle.platform === args.platform && bundle.channel === channel && bundle.id.localeCompare(minBundleId) >= 0 && bundle.fingerprintHash === args.fingerprintHash;
138
- }
139
- });
140
- return findUpdateInfoByScanning({
141
- args,
142
- queryWhere: { ...baseWhere },
143
- context,
144
- isCandidate: (bundle) => {
145
- return bundle.enabled && bundle.platform === args.platform && bundle.channel === channel && bundle.id.localeCompare(minBundleId) >= 0 && !!bundle.targetAppVersion && (0, _hot_updater_plugin_core.semverSatisfies)(bundle.targetAppVersion, args.appVersion);
146
- }
147
- });
148
- },
149
- async getAppUpdateInfo(args, context) {
150
- const info = await this.getUpdateInfo(args, context);
151
- if (!info) return null;
152
- const { storageUri, ...rest } = info;
153
- const readStorageText = options?.readStorageText;
154
- if (info.id === _hot_updater_core.NIL_UUID || !readStorageText) {
155
- const fileUrl = await resolveFileUrl(storageUri ?? null, context);
156
- return {
157
- ...rest,
158
- fileUrl
159
- };
115
+ const api = {
116
+ async getBundleById(id, context) {
117
+ return getPlugin().getBundleById(id, context);
118
+ },
119
+ async getUpdateInfo(args, context) {
120
+ const directGetUpdateInfo = getPlugin().getUpdateInfo;
121
+ if (directGetUpdateInfo) return context === void 0 ? await directGetUpdateInfo(args) : await directGetUpdateInfo(args, context);
122
+ const channel = args.channel ?? "production";
123
+ const minBundleId = args.minBundleId ?? _hot_updater_core.NIL_UUID;
124
+ const baseWhere = getBaseWhere({
125
+ platform: args.platform,
126
+ channel,
127
+ minBundleId
128
+ });
129
+ if (args._updateStrategy === "fingerprint") return findUpdateInfoByScanning({
130
+ args,
131
+ queryWhere: {
132
+ ...baseWhere,
133
+ fingerprintHash: args.fingerprintHash
134
+ },
135
+ context,
136
+ isCandidate: (bundle) => {
137
+ return bundle.enabled && bundle.platform === args.platform && bundle.channel === channel && bundle.id.localeCompare(minBundleId) >= 0 && bundle.fingerprintHash === args.fingerprintHash;
138
+ }
139
+ });
140
+ return findUpdateInfoByScanning({
141
+ args,
142
+ queryWhere: { ...baseWhere },
143
+ context,
144
+ isCandidate: (bundle) => {
145
+ return bundle.enabled && bundle.platform === args.platform && bundle.channel === channel && bundle.id.localeCompare(minBundleId) >= 0 && !!bundle.targetAppVersion && (0, _hot_updater_plugin_core.semverSatisfies)(bundle.targetAppVersion, args.appVersion);
160
146
  }
161
- const [fileUrl, targetBundle, currentBundle] = await Promise.all([
162
- resolveFileUrl(storageUri ?? null, context),
163
- getPlugin().getBundleById(info.id, context),
164
- args.bundleId !== _hot_updater_core.NIL_UUID ? getPlugin().getBundleById(args.bundleId, context) : null
165
- ]);
166
- const baseResponse = {
147
+ });
148
+ },
149
+ async getAppUpdateInfo(args, context) {
150
+ const info = await this.getUpdateInfo(args, context);
151
+ if (!info) return null;
152
+ const { storageUri, ...rest } = info;
153
+ const readStorageText = options?.readStorageText;
154
+ if (info.id === _hot_updater_core.NIL_UUID || !readStorageText) {
155
+ const fileUrl = await resolveFileUrl(storageUri ?? null, context);
156
+ return {
167
157
  ...rest,
168
158
  fileUrl
169
159
  };
170
- const manifestArtifacts = await require_updateArtifacts.resolveManifestArtifacts({
171
- currentBundle,
172
- resolveFileUrl,
173
- readStorageText,
174
- targetBundle,
175
- context
176
- });
177
- if (!manifestArtifacts) return baseResponse;
178
- return {
179
- ...baseResponse,
180
- ...manifestArtifacts
181
- };
182
- },
183
- async getChannels(context) {
184
- return getPlugin().getChannels(context);
185
- },
186
- async getBundles(options, context) {
187
- return getPlugin().getBundles(options, context);
188
- },
189
- async insertBundle(bundle, context) {
190
- require_schemaEnhancements.assertBundlePersistenceConstraints(bundle);
191
- await runWithMutationPlugin(async (plugin) => {
192
- await plugin.appendBundle(bundle, context);
193
- await plugin.commitBundle(context);
194
- });
195
- },
196
- async updateBundleById(bundleId, newBundle, context) {
197
- await runWithMutationPlugin(async (plugin) => {
198
- const current = await plugin.getBundleById(bundleId, context);
199
- if (!current) throw new Error("targetBundleId not found");
200
- require_schemaEnhancements.assertBundlePersistenceConstraints({
201
- ...current,
202
- ...newBundle
203
- });
204
- await plugin.updateBundle(bundleId, newBundle, context);
205
- await plugin.commitBundle(context);
160
+ }
161
+ const requestBundleSeeds = (0, _hot_updater_plugin_core.getRequestUpdateBundleSeeds)(context);
162
+ const requestBundles = require_requestBundleIdentityMap.createRequestBundleIdentityMap({
163
+ context,
164
+ loadBundleById: (bundleId, requestContext) => getPlugin().getBundleById(bundleId, requestContext),
165
+ seeds: requestBundleSeeds
166
+ });
167
+ const getCurrentBundle = () => {
168
+ if (args.bundleId === _hot_updater_core.NIL_UUID) return null;
169
+ const seededCurrentBundle = requestBundles.peek(args.bundleId);
170
+ if (seededCurrentBundle || requestBundleSeeds.length > 0) return seededCurrentBundle;
171
+ return requestBundles.get(args.bundleId);
172
+ };
173
+ const [fileUrl, targetBundle, currentBundle] = await Promise.all([
174
+ resolveFileUrl(storageUri ?? null, context),
175
+ requestBundles.get(info.id),
176
+ getCurrentBundle()
177
+ ]);
178
+ const baseResponse = {
179
+ ...rest,
180
+ fileUrl
181
+ };
182
+ const manifestArtifacts = await require_updateArtifacts.resolveManifestArtifacts({
183
+ currentBundle,
184
+ resolveFileUrl,
185
+ readStorageText,
186
+ targetBundle,
187
+ context
188
+ });
189
+ if (!manifestArtifacts) return baseResponse;
190
+ return {
191
+ ...baseResponse,
192
+ ...manifestArtifacts
193
+ };
194
+ },
195
+ async getChannels(context) {
196
+ return getPlugin().getChannels(context);
197
+ },
198
+ async getBundles(options, context) {
199
+ return getPlugin().getBundles(options, context);
200
+ },
201
+ async insertBundle(bundle, context) {
202
+ require_schemaEnhancements.assertBundlePersistenceConstraints(bundle);
203
+ await runWithMutationPlugin(async (plugin) => {
204
+ await plugin.appendBundle(bundle, context);
205
+ await plugin.commitBundle(context);
206
+ });
207
+ },
208
+ async updateBundleById(bundleId, newBundle, context) {
209
+ await runWithMutationPlugin(async (plugin) => {
210
+ const current = await plugin.getBundleById(bundleId, context);
211
+ if (!current) throw new Error("targetBundleId not found");
212
+ require_schemaEnhancements.assertBundlePersistenceConstraints({
213
+ ...current,
214
+ ...newBundle
206
215
  });
207
- },
208
- async deleteBundleById(bundleId, context) {
209
- await runWithMutationPlugin(async (plugin) => {
210
- const bundle = await plugin.getBundleById(bundleId, context);
211
- if (!bundle) return;
212
- await plugin.deleteBundle(bundle, context);
213
- await plugin.commitBundle(context);
216
+ await plugin.updateBundle(bundleId, newBundle, context);
217
+ await plugin.commitBundle(context);
218
+ });
219
+ },
220
+ async deleteBundleById(bundleId, context) {
221
+ await runWithMutationPlugin(async (plugin) => {
222
+ const bundle = await plugin.getBundleById(bundleId, context);
223
+ if (!bundle) return;
224
+ await plugin.deleteBundle(bundle, context);
225
+ await plugin.commitBundle(context);
226
+ });
227
+ }
228
+ };
229
+ Object.defineProperty(api, "diagnostics", {
230
+ configurable: true,
231
+ enumerable: true,
232
+ get() {
233
+ const diagnostics = getPlugin().diagnostics;
234
+ if (!diagnostics) {
235
+ Object.defineProperty(this, "diagnostics", {
236
+ configurable: true,
237
+ enumerable: true,
238
+ value: void 0
214
239
  });
240
+ return;
215
241
  }
216
- },
242
+ const wrappedDiagnostics = {};
243
+ if (diagnostics.bundleIndex) wrappedDiagnostics.bundleIndex = {
244
+ check: (context) => getPlugin().diagnostics.bundleIndex.check(context),
245
+ ...diagnostics.bundleIndex.repair ? { repair: (context) => getPlugin().diagnostics.bundleIndex.repair(context) } : {}
246
+ };
247
+ Object.defineProperty(this, "diagnostics", {
248
+ configurable: true,
249
+ enumerable: true,
250
+ value: wrappedDiagnostics
251
+ });
252
+ return wrappedDiagnostics;
253
+ }
254
+ });
255
+ return {
256
+ api,
217
257
  adapterName: getPlugin().name,
218
258
  createMigrator: () => {
219
259
  throw new Error("createMigrator is only available for Kysely/Prisma/Drizzle database adapters.");
@@ -1,6 +1,7 @@
1
1
  import { assertBundlePersistenceConstraints } from "./schemaEnhancements.mjs";
2
2
  import { resolveManifestArtifacts } from "./updateArtifacts.mjs";
3
- import { semverSatisfies } from "@hot-updater/plugin-core";
3
+ import { createRequestBundleIdentityMap } from "./requestBundleIdentityMap.mjs";
4
+ import { getRequestUpdateBundleSeeds, semverSatisfies } from "@hot-updater/plugin-core";
4
5
  import { NIL_UUID, isCohortEligibleForUpdate } from "@hot-updater/core";
5
6
  //#region src/db/pluginCore.ts
6
7
  const PAGE_SIZE = 100;
@@ -110,109 +111,148 @@ function createPluginDatabaseCore(getPlugin, resolveFileUrl, options) {
110
111
  enabled: true,
111
112
  id: { gte: minBundleId }
112
113
  });
113
- return {
114
- api: {
115
- async getBundleById(id, context) {
116
- return getPlugin().getBundleById(id, context);
117
- },
118
- async getUpdateInfo(args, context) {
119
- const directGetUpdateInfo = getPlugin().getUpdateInfo;
120
- if (directGetUpdateInfo) return context === void 0 ? await directGetUpdateInfo(args) : await directGetUpdateInfo(args, context);
121
- const channel = args.channel ?? "production";
122
- const minBundleId = args.minBundleId ?? NIL_UUID;
123
- const baseWhere = getBaseWhere({
124
- platform: args.platform,
125
- channel,
126
- minBundleId
127
- });
128
- if (args._updateStrategy === "fingerprint") return findUpdateInfoByScanning({
129
- args,
130
- queryWhere: {
131
- ...baseWhere,
132
- fingerprintHash: args.fingerprintHash
133
- },
134
- context,
135
- isCandidate: (bundle) => {
136
- return bundle.enabled && bundle.platform === args.platform && bundle.channel === channel && bundle.id.localeCompare(minBundleId) >= 0 && bundle.fingerprintHash === args.fingerprintHash;
137
- }
138
- });
139
- return findUpdateInfoByScanning({
140
- args,
141
- queryWhere: { ...baseWhere },
142
- context,
143
- isCandidate: (bundle) => {
144
- return bundle.enabled && bundle.platform === args.platform && bundle.channel === channel && bundle.id.localeCompare(minBundleId) >= 0 && !!bundle.targetAppVersion && semverSatisfies(bundle.targetAppVersion, args.appVersion);
145
- }
146
- });
147
- },
148
- async getAppUpdateInfo(args, context) {
149
- const info = await this.getUpdateInfo(args, context);
150
- if (!info) return null;
151
- const { storageUri, ...rest } = info;
152
- const readStorageText = options?.readStorageText;
153
- if (info.id === NIL_UUID || !readStorageText) {
154
- const fileUrl = await resolveFileUrl(storageUri ?? null, context);
155
- return {
156
- ...rest,
157
- fileUrl
158
- };
114
+ const api = {
115
+ async getBundleById(id, context) {
116
+ return getPlugin().getBundleById(id, context);
117
+ },
118
+ async getUpdateInfo(args, context) {
119
+ const directGetUpdateInfo = getPlugin().getUpdateInfo;
120
+ if (directGetUpdateInfo) return context === void 0 ? await directGetUpdateInfo(args) : await directGetUpdateInfo(args, context);
121
+ const channel = args.channel ?? "production";
122
+ const minBundleId = args.minBundleId ?? NIL_UUID;
123
+ const baseWhere = getBaseWhere({
124
+ platform: args.platform,
125
+ channel,
126
+ minBundleId
127
+ });
128
+ if (args._updateStrategy === "fingerprint") return findUpdateInfoByScanning({
129
+ args,
130
+ queryWhere: {
131
+ ...baseWhere,
132
+ fingerprintHash: args.fingerprintHash
133
+ },
134
+ context,
135
+ isCandidate: (bundle) => {
136
+ return bundle.enabled && bundle.platform === args.platform && bundle.channel === channel && bundle.id.localeCompare(minBundleId) >= 0 && bundle.fingerprintHash === args.fingerprintHash;
137
+ }
138
+ });
139
+ return findUpdateInfoByScanning({
140
+ args,
141
+ queryWhere: { ...baseWhere },
142
+ context,
143
+ isCandidate: (bundle) => {
144
+ return bundle.enabled && bundle.platform === args.platform && bundle.channel === channel && bundle.id.localeCompare(minBundleId) >= 0 && !!bundle.targetAppVersion && semverSatisfies(bundle.targetAppVersion, args.appVersion);
159
145
  }
160
- const [fileUrl, targetBundle, currentBundle] = await Promise.all([
161
- resolveFileUrl(storageUri ?? null, context),
162
- getPlugin().getBundleById(info.id, context),
163
- args.bundleId !== NIL_UUID ? getPlugin().getBundleById(args.bundleId, context) : null
164
- ]);
165
- const baseResponse = {
146
+ });
147
+ },
148
+ async getAppUpdateInfo(args, context) {
149
+ const info = await this.getUpdateInfo(args, context);
150
+ if (!info) return null;
151
+ const { storageUri, ...rest } = info;
152
+ const readStorageText = options?.readStorageText;
153
+ if (info.id === NIL_UUID || !readStorageText) {
154
+ const fileUrl = await resolveFileUrl(storageUri ?? null, context);
155
+ return {
166
156
  ...rest,
167
157
  fileUrl
168
158
  };
169
- const manifestArtifacts = await resolveManifestArtifacts({
170
- currentBundle,
171
- resolveFileUrl,
172
- readStorageText,
173
- targetBundle,
174
- context
175
- });
176
- if (!manifestArtifacts) return baseResponse;
177
- return {
178
- ...baseResponse,
179
- ...manifestArtifacts
180
- };
181
- },
182
- async getChannels(context) {
183
- return getPlugin().getChannels(context);
184
- },
185
- async getBundles(options, context) {
186
- return getPlugin().getBundles(options, context);
187
- },
188
- async insertBundle(bundle, context) {
189
- assertBundlePersistenceConstraints(bundle);
190
- await runWithMutationPlugin(async (plugin) => {
191
- await plugin.appendBundle(bundle, context);
192
- await plugin.commitBundle(context);
193
- });
194
- },
195
- async updateBundleById(bundleId, newBundle, context) {
196
- await runWithMutationPlugin(async (plugin) => {
197
- const current = await plugin.getBundleById(bundleId, context);
198
- if (!current) throw new Error("targetBundleId not found");
199
- assertBundlePersistenceConstraints({
200
- ...current,
201
- ...newBundle
202
- });
203
- await plugin.updateBundle(bundleId, newBundle, context);
204
- await plugin.commitBundle(context);
159
+ }
160
+ const requestBundleSeeds = getRequestUpdateBundleSeeds(context);
161
+ const requestBundles = createRequestBundleIdentityMap({
162
+ context,
163
+ loadBundleById: (bundleId, requestContext) => getPlugin().getBundleById(bundleId, requestContext),
164
+ seeds: requestBundleSeeds
165
+ });
166
+ const getCurrentBundle = () => {
167
+ if (args.bundleId === NIL_UUID) return null;
168
+ const seededCurrentBundle = requestBundles.peek(args.bundleId);
169
+ if (seededCurrentBundle || requestBundleSeeds.length > 0) return seededCurrentBundle;
170
+ return requestBundles.get(args.bundleId);
171
+ };
172
+ const [fileUrl, targetBundle, currentBundle] = await Promise.all([
173
+ resolveFileUrl(storageUri ?? null, context),
174
+ requestBundles.get(info.id),
175
+ getCurrentBundle()
176
+ ]);
177
+ const baseResponse = {
178
+ ...rest,
179
+ fileUrl
180
+ };
181
+ const manifestArtifacts = await resolveManifestArtifacts({
182
+ currentBundle,
183
+ resolveFileUrl,
184
+ readStorageText,
185
+ targetBundle,
186
+ context
187
+ });
188
+ if (!manifestArtifacts) return baseResponse;
189
+ return {
190
+ ...baseResponse,
191
+ ...manifestArtifacts
192
+ };
193
+ },
194
+ async getChannels(context) {
195
+ return getPlugin().getChannels(context);
196
+ },
197
+ async getBundles(options, context) {
198
+ return getPlugin().getBundles(options, context);
199
+ },
200
+ async insertBundle(bundle, context) {
201
+ assertBundlePersistenceConstraints(bundle);
202
+ await runWithMutationPlugin(async (plugin) => {
203
+ await plugin.appendBundle(bundle, context);
204
+ await plugin.commitBundle(context);
205
+ });
206
+ },
207
+ async updateBundleById(bundleId, newBundle, context) {
208
+ await runWithMutationPlugin(async (plugin) => {
209
+ const current = await plugin.getBundleById(bundleId, context);
210
+ if (!current) throw new Error("targetBundleId not found");
211
+ assertBundlePersistenceConstraints({
212
+ ...current,
213
+ ...newBundle
205
214
  });
206
- },
207
- async deleteBundleById(bundleId, context) {
208
- await runWithMutationPlugin(async (plugin) => {
209
- const bundle = await plugin.getBundleById(bundleId, context);
210
- if (!bundle) return;
211
- await plugin.deleteBundle(bundle, context);
212
- await plugin.commitBundle(context);
215
+ await plugin.updateBundle(bundleId, newBundle, context);
216
+ await plugin.commitBundle(context);
217
+ });
218
+ },
219
+ async deleteBundleById(bundleId, context) {
220
+ await runWithMutationPlugin(async (plugin) => {
221
+ const bundle = await plugin.getBundleById(bundleId, context);
222
+ if (!bundle) return;
223
+ await plugin.deleteBundle(bundle, context);
224
+ await plugin.commitBundle(context);
225
+ });
226
+ }
227
+ };
228
+ Object.defineProperty(api, "diagnostics", {
229
+ configurable: true,
230
+ enumerable: true,
231
+ get() {
232
+ const diagnostics = getPlugin().diagnostics;
233
+ if (!diagnostics) {
234
+ Object.defineProperty(this, "diagnostics", {
235
+ configurable: true,
236
+ enumerable: true,
237
+ value: void 0
213
238
  });
239
+ return;
214
240
  }
215
- },
241
+ const wrappedDiagnostics = {};
242
+ if (diagnostics.bundleIndex) wrappedDiagnostics.bundleIndex = {
243
+ check: (context) => getPlugin().diagnostics.bundleIndex.check(context),
244
+ ...diagnostics.bundleIndex.repair ? { repair: (context) => getPlugin().diagnostics.bundleIndex.repair(context) } : {}
245
+ };
246
+ Object.defineProperty(this, "diagnostics", {
247
+ configurable: true,
248
+ enumerable: true,
249
+ value: wrappedDiagnostics
250
+ });
251
+ return wrappedDiagnostics;
252
+ }
253
+ });
254
+ return {
255
+ api,
216
256
  adapterName: getPlugin().name,
217
257
  createMigrator: () => {
218
258
  throw new Error("createMigrator is only available for Kysely/Prisma/Drizzle database adapters.");
@@ -0,0 +1,29 @@
1
+ //#region src/db/requestBundleIdentityMap.ts
2
+ const createRequestBundleIdentityMap = ({ context, loadBundleById, seeds }) => {
3
+ const bundles = /* @__PURE__ */ new Map();
4
+ const pendingBundles = /* @__PURE__ */ new Map();
5
+ for (const seed of seeds) if (seed) bundles.set(seed.id, seed);
6
+ const get = async (bundleId) => {
7
+ const cachedBundle = bundles.get(bundleId);
8
+ if (cachedBundle) return cachedBundle;
9
+ const pendingBundle = pendingBundles.get(bundleId);
10
+ if (pendingBundle) return pendingBundle;
11
+ const lookup = loadBundleById(bundleId, context).then((bundle) => {
12
+ pendingBundles.delete(bundleId);
13
+ if (bundle) bundles.set(bundle.id, bundle);
14
+ return bundle;
15
+ }, (error) => {
16
+ pendingBundles.delete(bundleId);
17
+ throw error;
18
+ });
19
+ pendingBundles.set(bundleId, lookup);
20
+ return lookup;
21
+ };
22
+ const peek = (bundleId) => bundles.get(bundleId) ?? null;
23
+ return {
24
+ get,
25
+ peek
26
+ };
27
+ };
28
+ //#endregion
29
+ exports.createRequestBundleIdentityMap = createRequestBundleIdentityMap;
@@ -0,0 +1,29 @@
1
+ //#region src/db/requestBundleIdentityMap.ts
2
+ const createRequestBundleIdentityMap = ({ context, loadBundleById, seeds }) => {
3
+ const bundles = /* @__PURE__ */ new Map();
4
+ const pendingBundles = /* @__PURE__ */ new Map();
5
+ for (const seed of seeds) if (seed) bundles.set(seed.id, seed);
6
+ const get = async (bundleId) => {
7
+ const cachedBundle = bundles.get(bundleId);
8
+ if (cachedBundle) return cachedBundle;
9
+ const pendingBundle = pendingBundles.get(bundleId);
10
+ if (pendingBundle) return pendingBundle;
11
+ const lookup = loadBundleById(bundleId, context).then((bundle) => {
12
+ pendingBundles.delete(bundleId);
13
+ if (bundle) bundles.set(bundle.id, bundle);
14
+ return bundle;
15
+ }, (error) => {
16
+ pendingBundles.delete(bundleId);
17
+ throw error;
18
+ });
19
+ pendingBundles.set(bundleId, lookup);
20
+ return lookup;
21
+ };
22
+ const peek = (bundleId) => bundles.get(bundleId) ?? null;
23
+ return {
24
+ get,
25
+ peek
26
+ };
27
+ };
28
+ //#endregion
29
+ export { createRequestBundleIdentityMap };
@@ -1,7 +1,7 @@
1
1
  import { Provider } from "../node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/index-CMqePMTF.cjs";
2
2
  import { PaginatedResult } from "../types/index.cjs";
3
3
  import { AppUpdateAvailableInfo, Bundle, GetBundlesArgs, UpdateInfo } from "@hot-updater/core";
4
- import { DatabaseBundleQueryOptions, DatabasePlugin, HotUpdaterContext, RuntimeStoragePlugin } from "@hot-updater/plugin-core";
4
+ import { DatabaseBundleQueryOptions, DatabaseDiagnostics, DatabasePlugin, HotUpdaterContext, RuntimeStoragePlugin } from "@hot-updater/plugin-core";
5
5
 
6
6
  //#region src/db/types.d.ts
7
7
  type DatabasePluginFactory<TContext = unknown> = () => DatabasePlugin<TContext>;
@@ -27,6 +27,7 @@ interface DatabaseAPI<TContext = unknown> {
27
27
  insertBundle(bundle: Bundle, context?: HotUpdaterContext<TContext>): Promise<void>;
28
28
  updateBundleById(bundleId: string, newBundle: Partial<Bundle>, context?: HotUpdaterContext<TContext>): Promise<void>;
29
29
  deleteBundleById(bundleId: string, context?: HotUpdaterContext<TContext>): Promise<void>;
30
+ diagnostics?: DatabaseDiagnostics<TContext>;
30
31
  }
31
32
  type StoragePluginFactory<TContext = unknown> = () => RuntimeStoragePlugin<TContext>;
32
33
  //#endregion
@@ -1,6 +1,6 @@
1
1
  import { Provider } from "../node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/index-CMqePMTF.mjs";
2
2
  import { PaginatedResult } from "../types/index.mjs";
3
- import { DatabaseBundleQueryOptions, DatabasePlugin, HotUpdaterContext, RuntimeStoragePlugin } from "@hot-updater/plugin-core";
3
+ import { DatabaseBundleQueryOptions, DatabaseDiagnostics, DatabasePlugin, HotUpdaterContext, RuntimeStoragePlugin } from "@hot-updater/plugin-core";
4
4
  import { AppUpdateAvailableInfo, Bundle as Bundle$1, GetBundlesArgs, UpdateInfo } from "@hot-updater/core";
5
5
 
6
6
  //#region src/db/types.d.ts
@@ -27,6 +27,7 @@ interface DatabaseAPI<TContext = unknown> {
27
27
  insertBundle(bundle: Bundle$1, context?: HotUpdaterContext<TContext>): Promise<void>;
28
28
  updateBundleById(bundleId: string, newBundle: Partial<Bundle$1>, context?: HotUpdaterContext<TContext>): Promise<void>;
29
29
  deleteBundleById(bundleId: string, context?: HotUpdaterContext<TContext>): Promise<void>;
30
+ diagnostics?: DatabaseDiagnostics<TContext>;
30
31
  }
31
32
  type StoragePluginFactory<TContext = unknown> = () => RuntimeStoragePlugin<TContext>;
32
33
  //#endregion