@hot-updater/plugin-core 0.21.10 → 0.21.12

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.
@@ -43,269 +43,269 @@ function getSemverNormalizedVersions(version) {
43
43
  return Array.from(versions);
44
44
  }
45
45
  /**
46
+ * Creates a blob storage-based database plugin with lazy initialization.
46
47
  *
47
48
  * @param name - The name of the database plugin
48
- * @param listObjects - Function to list objects in the storage
49
- * @param loadObject - Function to load an JSON object from the storage
50
- * @param uploadObject - Function to upload an JSON object to the storage
51
- * @param deleteObject - Function to delete an object from the storage
52
- * @param invalidatePaths - Function to invalidate paths in the CDN
53
- * @param hooks - Optional hooks for additional functionality - see createDatabasePlugin
54
- * @returns
49
+ * @param factory - Function that creates blob storage operations from config
50
+ * @returns A double-curried function that lazily initializes the database plugin
55
51
  */
56
- const createBlobDatabasePlugin = ({ name, getContext, listObjects, loadObject, uploadObject, deleteObject, invalidatePaths, hooks, apiBasePath }) => {
57
- const bundlesMap = /* @__PURE__ */ new Map();
58
- const pendingBundlesMap = /* @__PURE__ */ new Map();
59
- const PLATFORMS = ["ios", "android"];
60
- async function reloadBundles(context) {
61
- bundlesMap.clear();
62
- const platformPromises = PLATFORMS.map(async (platform) => {
63
- const filePromises = (await listUpdateJsonKeys(context, platform)).map(async (key) => {
64
- return (await loadObject(context, key) ?? []).map((bundle) => ({
65
- ...bundle,
66
- _updateJsonKey: key
67
- }));
52
+ const createBlobDatabasePlugin = ({ name, factory }) => {
53
+ return (config, hooks) => {
54
+ const { listObjects, loadObject, uploadObject, deleteObject, invalidatePaths, apiBasePath } = factory(config);
55
+ const bundlesMap = /* @__PURE__ */ new Map();
56
+ const pendingBundlesMap = /* @__PURE__ */ new Map();
57
+ const PLATFORMS = ["ios", "android"];
58
+ async function reloadBundles() {
59
+ bundlesMap.clear();
60
+ const platformPromises = PLATFORMS.map(async (platform) => {
61
+ const filePromises = (await listUpdateJsonKeys(platform)).map(async (key) => {
62
+ return (await loadObject(key) ?? []).map((bundle) => ({
63
+ ...bundle,
64
+ _updateJsonKey: key
65
+ }));
66
+ });
67
+ return (await Promise.all(filePromises)).flat();
68
68
  });
69
- return (await Promise.all(filePromises)).flat();
70
- });
71
- const allBundles = (await Promise.all(platformPromises)).flat();
72
- for (const bundle of allBundles) bundlesMap.set(bundle.id, bundle);
73
- for (const [id, bundle] of pendingBundlesMap.entries()) bundlesMap.set(id, bundle);
74
- return (0, es_toolkit.orderBy)(allBundles, [(v) => v.id], ["desc"]);
75
- }
76
- /**
77
- * Updates target-app-versions.json for each channel on the given platform.
78
- * Returns true if the file was updated, false if no changes were made.
79
- */
80
- async function updateTargetVersionsForPlatform(context, platform) {
81
- const pattern = /* @__PURE__ */ new RegExp(`^[^/]+/${platform}/[^/]+/update\\.json$`);
82
- const keysByChannel = (await listObjects(context, "")).filter((key) => pattern.test(key)).reduce((acc, key) => {
83
- const channel = key.split("/")[0];
84
- acc[channel] = acc[channel] || [];
85
- acc[channel].push(key);
86
- return acc;
87
- }, {});
88
- const updatedTargetFiles = /* @__PURE__ */ new Set();
89
- for (const channel of Object.keys(keysByChannel)) {
90
- const updateKeys = keysByChannel[channel];
91
- const targetKey = `${channel}/${platform}/target-app-versions.json`;
92
- const currentVersions = updateKeys.map((key) => key.split("/")[2]);
93
- const oldTargetVersions = await loadObject(context, targetKey) ?? [];
94
- const newTargetVersions = oldTargetVersions.filter((v) => currentVersions.includes(v));
95
- for (const v of currentVersions) if (!newTargetVersions.includes(v)) newTargetVersions.push(v);
96
- if (JSON.stringify(oldTargetVersions) !== JSON.stringify(newTargetVersions)) {
97
- await uploadObject(context, targetKey, newTargetVersions);
98
- updatedTargetFiles.add(`/${targetKey}`);
69
+ const allBundles = (await Promise.all(platformPromises)).flat();
70
+ for (const bundle of allBundles) bundlesMap.set(bundle.id, bundle);
71
+ for (const [id, bundle] of pendingBundlesMap.entries()) bundlesMap.set(id, bundle);
72
+ return (0, es_toolkit.orderBy)(allBundles, [(v) => v.id], ["desc"]);
73
+ }
74
+ /**
75
+ * Updates target-app-versions.json for each channel on the given platform.
76
+ * Returns true if the file was updated, false if no changes were made.
77
+ */
78
+ async function updateTargetVersionsForPlatform(platform) {
79
+ const pattern = /* @__PURE__ */ new RegExp(`^[^/]+/${platform}/[^/]+/update\\.json$`);
80
+ const keysByChannel = (await listObjects("")).filter((key) => pattern.test(key)).reduce((acc, key) => {
81
+ const channel = key.split("/")[0];
82
+ acc[channel] = acc[channel] || [];
83
+ acc[channel].push(key);
84
+ return acc;
85
+ }, {});
86
+ const updatedTargetFiles = /* @__PURE__ */ new Set();
87
+ for (const channel of Object.keys(keysByChannel)) {
88
+ const updateKeys = keysByChannel[channel];
89
+ const targetKey = `${channel}/${platform}/target-app-versions.json`;
90
+ const currentVersions = updateKeys.map((key) => key.split("/")[2]);
91
+ const oldTargetVersions = await loadObject(targetKey) ?? [];
92
+ const newTargetVersions = oldTargetVersions.filter((v) => currentVersions.includes(v));
93
+ for (const v of currentVersions) if (!newTargetVersions.includes(v)) newTargetVersions.push(v);
94
+ if (JSON.stringify(oldTargetVersions) !== JSON.stringify(newTargetVersions)) {
95
+ await uploadObject(targetKey, newTargetVersions);
96
+ updatedTargetFiles.add(`/${targetKey}`);
97
+ }
99
98
  }
99
+ return updatedTargetFiles;
100
100
  }
101
- return updatedTargetFiles;
102
- }
103
- /**
104
- * Lists update.json keys for a given platform.
105
- *
106
- * - If a channel is provided, only that channel's update.json files are listed.
107
- * - Otherwise, all channels for the given platform are returned.
108
- */
109
- async function listUpdateJsonKeys(context, platform, channel) {
110
- const prefix = channel ? platform ? `${channel}/${platform}/` : `${channel}/` : "";
111
- const pattern = channel ? platform ? /* @__PURE__ */ new RegExp(`^${channel}/${platform}/[^/]+/update\\.json$`) : /* @__PURE__ */ new RegExp(`^${channel}/[^/]+/[^/]+/update\\.json$`) : platform ? /* @__PURE__ */ new RegExp(`^[^/]+/${platform}/[^/]+/update\\.json$`) : /^[^/]+\/[^/]+\/[^/]+\/update\.json$/;
112
- return listObjects(context, prefix).then((keys) => keys.filter((key) => pattern.test(key)));
113
- }
114
- return require_createDatabasePlugin.createDatabasePlugin(name, {
115
- getContext,
116
- async getBundleById(context, bundleId) {
117
- const pendingBundle = pendingBundlesMap.get(bundleId);
118
- if (pendingBundle) return removeBundleInternalKeys(pendingBundle);
119
- const bundle = bundlesMap.get(bundleId);
120
- if (bundle) return removeBundleInternalKeys(bundle);
121
- return (await reloadBundles(context)).find((bundle$1) => bundle$1.id === bundleId) ?? null;
122
- },
123
- async getBundles(context, options) {
124
- let allBundles = await reloadBundles(context);
125
- const { where, limit, offset } = options;
126
- if (where) allBundles = allBundles.filter((bundle) => {
127
- return Object.entries(where).every(([key, value]) => value === void 0 || value === null || bundle[key] === value);
128
- });
129
- const total = allBundles.length;
130
- let paginatedData = allBundles.map(removeBundleInternalKeys);
131
- if (offset > 0) paginatedData = paginatedData.slice(offset);
132
- if (limit) paginatedData = paginatedData.slice(0, limit);
133
- return {
134
- data: paginatedData,
135
- pagination: require_calculatePagination.calculatePagination(total, {
136
- limit,
137
- offset
138
- })
139
- };
140
- },
141
- async getChannels(context) {
142
- const total = (await reloadBundles(context)).length;
143
- const result = await this.getBundles(context, {
144
- limit: total,
145
- offset: 0
146
- });
147
- return [...new Set(result.data.map((bundle) => bundle.channel))];
148
- },
149
- async commitBundle(context, { changedSets }) {
150
- if (changedSets.length === 0) return;
151
- const changedBundlesByKey = {};
152
- const removalsByKey = {};
153
- const pathsToInvalidate = /* @__PURE__ */ new Set();
154
- let isTargetAppVersionChanged = false;
155
- for (const { operation, data } of changedSets) {
156
- if (data.targetAppVersion !== void 0) isTargetAppVersionChanged = true;
157
- if (operation === "insert") {
158
- const target = normalizeTargetAppVersion(data.targetAppVersion) ?? data.fingerprintHash;
159
- if (!target) throw new Error("target not found");
160
- const key = `${data.channel}/${data.platform}/${target}/update.json`;
161
- const bundleWithKey = {
162
- ...data,
163
- _updateJsonKey: key
101
+ /**
102
+ * Lists update.json keys for a given platform.
103
+ *
104
+ * - If a channel is provided, only that channel's update.json files are listed.
105
+ * - Otherwise, all channels for the given platform are returned.
106
+ */
107
+ async function listUpdateJsonKeys(platform, channel) {
108
+ const prefix = channel ? platform ? `${channel}/${platform}/` : `${channel}/` : "";
109
+ const pattern = channel ? platform ? /* @__PURE__ */ new RegExp(`^${channel}/${platform}/[^/]+/update\\.json$`) : /* @__PURE__ */ new RegExp(`^${channel}/[^/]+/[^/]+/update\\.json$`) : platform ? /* @__PURE__ */ new RegExp(`^[^/]+/${platform}/[^/]+/update\\.json$`) : /^[^/]+\/[^/]+\/[^/]+\/update\.json$/;
110
+ return listObjects(prefix).then((keys) => keys.filter((key) => pattern.test(key)));
111
+ }
112
+ return require_createDatabasePlugin.createDatabasePlugin({
113
+ name,
114
+ factory: () => ({
115
+ async getBundleById(bundleId) {
116
+ const pendingBundle = pendingBundlesMap.get(bundleId);
117
+ if (pendingBundle) return removeBundleInternalKeys(pendingBundle);
118
+ const bundle = bundlesMap.get(bundleId);
119
+ if (bundle) return removeBundleInternalKeys(bundle);
120
+ return (await reloadBundles()).find((bundle$1) => bundle$1.id === bundleId) ?? null;
121
+ },
122
+ async getBundles(options) {
123
+ let allBundles = await reloadBundles();
124
+ const { where, limit, offset } = options;
125
+ if (where) allBundles = allBundles.filter((bundle) => {
126
+ return Object.entries(where).every(([key, value]) => value === void 0 || value === null || bundle[key] === value);
127
+ });
128
+ const total = allBundles.length;
129
+ let paginatedData = allBundles.map(removeBundleInternalKeys);
130
+ if (offset > 0) paginatedData = paginatedData.slice(offset);
131
+ if (limit) paginatedData = paginatedData.slice(0, limit);
132
+ return {
133
+ data: paginatedData,
134
+ pagination: require_calculatePagination.calculatePagination(total, {
135
+ limit,
136
+ offset
137
+ })
164
138
  };
165
- bundlesMap.set(data.id, bundleWithKey);
166
- pendingBundlesMap.set(data.id, bundleWithKey);
167
- changedBundlesByKey[key] = changedBundlesByKey[key] || [];
168
- changedBundlesByKey[key].push(removeBundleInternalKeys(bundleWithKey));
169
- pathsToInvalidate.add(`/${key}`);
170
- if (data.fingerprintHash) pathsToInvalidate.add(`${apiBasePath}/fingerprint/${data.platform}/${data.fingerprintHash}/${data.channel}/*`);
171
- else if (data.targetAppVersion) if (!isExactVersion(data.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${data.platform}/*`);
172
- else {
173
- const normalizedVersions = getSemverNormalizedVersions(data.targetAppVersion);
174
- for (const version of normalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${data.platform}/${version}/${data.channel}/*`);
175
- }
176
- continue;
177
- }
178
- if (operation === "delete") {
179
- let bundle$1 = pendingBundlesMap.get(data.id);
180
- if (!bundle$1) bundle$1 = bundlesMap.get(data.id);
181
- if (!bundle$1) throw new Error("Bundle to delete not found");
182
- bundlesMap.delete(data.id);
183
- pendingBundlesMap.delete(data.id);
184
- const key = bundle$1._updateJsonKey;
185
- removalsByKey[key] = removalsByKey[key] || [];
186
- removalsByKey[key].push(bundle$1.id);
187
- pathsToInvalidate.add(`/${key}`);
188
- if (bundle$1.fingerprintHash) pathsToInvalidate.add(`${apiBasePath}/fingerprint/${bundle$1.platform}/${bundle$1.fingerprintHash}/${bundle$1.channel}/*`);
189
- else if (bundle$1.targetAppVersion) if (!isExactVersion(bundle$1.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle$1.platform}/*`);
190
- else {
191
- const normalizedVersions = getSemverNormalizedVersions(bundle$1.targetAppVersion);
192
- for (const version of normalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle$1.platform}/${version}/${bundle$1.channel}/*`);
193
- }
194
- continue;
195
- }
196
- let bundle = pendingBundlesMap.get(data.id);
197
- if (!bundle) bundle = bundlesMap.get(data.id);
198
- if (!bundle) throw new Error("targetBundleId not found");
199
- if (operation === "update") {
200
- const newChannel = data.channel !== void 0 ? data.channel : bundle.channel;
201
- const newPlatform = data.platform !== void 0 ? data.platform : bundle.platform;
202
- const target = data.fingerprintHash ?? bundle.fingerprintHash ?? normalizeTargetAppVersion(data.targetAppVersion) ?? normalizeTargetAppVersion(bundle.targetAppVersion);
203
- if (!target) throw new Error("target not found");
204
- const newKey = `${newChannel}/${newPlatform}/${target}/update.json`;
205
- if (newKey !== bundle._updateJsonKey) {
206
- const oldKey = bundle._updateJsonKey;
207
- removalsByKey[oldKey] = removalsByKey[oldKey] || [];
208
- removalsByKey[oldKey].push(bundle.id);
209
- changedBundlesByKey[newKey] = changedBundlesByKey[newKey] || [];
210
- const updatedBundle$1 = {
211
- ...bundle,
212
- ...data
213
- };
214
- updatedBundle$1._oldUpdateJsonKey = oldKey;
215
- updatedBundle$1._updateJsonKey = newKey;
216
- bundlesMap.set(data.id, updatedBundle$1);
217
- pendingBundlesMap.set(data.id, updatedBundle$1);
218
- changedBundlesByKey[newKey].push(removeBundleInternalKeys(updatedBundle$1));
219
- pathsToInvalidate.add(`/${oldKey}`);
220
- pathsToInvalidate.add(`/${newKey}`);
221
- const oldChannel = bundle.channel;
222
- const newChannel$1 = data.channel;
223
- if (oldChannel !== newChannel$1) {
224
- pathsToInvalidate.add(`/${oldChannel}/${bundle.platform}/target-app-versions.json`);
225
- pathsToInvalidate.add(`/${newChannel$1}/${bundle.platform}/target-app-versions.json`);
226
- if (bundle.fingerprintHash) {
227
- pathsToInvalidate.add(`${apiBasePath}/fingerprint/${bundle.platform}/${bundle.fingerprintHash}/${oldChannel}/*`);
228
- pathsToInvalidate.add(`${apiBasePath}/fingerprint/${bundle.platform}/${bundle.fingerprintHash}/${newChannel$1}/*`);
229
- }
230
- if (bundle.targetAppVersion) if (!isExactVersion(bundle.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/*`);
139
+ },
140
+ async getChannels() {
141
+ const total = (await reloadBundles()).length;
142
+ const result = await this.getBundles({
143
+ limit: total,
144
+ offset: 0
145
+ });
146
+ return [...new Set(result.data.map((bundle) => bundle.channel))];
147
+ },
148
+ async commitBundle({ changedSets }) {
149
+ if (changedSets.length === 0) return;
150
+ const changedBundlesByKey = {};
151
+ const removalsByKey = {};
152
+ const pathsToInvalidate = /* @__PURE__ */ new Set();
153
+ let isTargetAppVersionChanged = false;
154
+ for (const { operation, data } of changedSets) {
155
+ if (data.targetAppVersion !== void 0) isTargetAppVersionChanged = true;
156
+ if (operation === "insert") {
157
+ const target = normalizeTargetAppVersion(data.targetAppVersion) ?? data.fingerprintHash;
158
+ if (!target) throw new Error("target not found");
159
+ const key = `${data.channel}/${data.platform}/${target}/update.json`;
160
+ const bundleWithKey = {
161
+ ...data,
162
+ _updateJsonKey: key
163
+ };
164
+ bundlesMap.set(data.id, bundleWithKey);
165
+ pendingBundlesMap.set(data.id, bundleWithKey);
166
+ changedBundlesByKey[key] = changedBundlesByKey[key] || [];
167
+ changedBundlesByKey[key].push(removeBundleInternalKeys(bundleWithKey));
168
+ pathsToInvalidate.add(`/${key}`);
169
+ if (data.fingerprintHash) pathsToInvalidate.add(`${apiBasePath}/fingerprint/${data.platform}/${data.fingerprintHash}/${data.channel}/*`);
170
+ else if (data.targetAppVersion) if (!isExactVersion(data.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${data.platform}/*`);
231
171
  else {
232
- const normalizedVersions = getSemverNormalizedVersions(bundle.targetAppVersion);
233
- for (const version of normalizedVersions) {
234
- pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/${version}/${oldChannel}/*`);
235
- pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/${version}/${newChannel$1}/*`);
236
- }
172
+ const normalizedVersions = getSemverNormalizedVersions(data.targetAppVersion);
173
+ for (const version of normalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${data.platform}/${version}/${data.channel}/*`);
237
174
  }
175
+ continue;
238
176
  }
239
- if (updatedBundle$1.fingerprintHash) pathsToInvalidate.add(`${apiBasePath}/fingerprint/${bundle.platform}/${updatedBundle$1.fingerprintHash}/${updatedBundle$1.channel}/*`);
240
- else if (updatedBundle$1.targetAppVersion) {
241
- if (!isExactVersion(updatedBundle$1.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${updatedBundle$1.platform}/*`);
177
+ if (operation === "delete") {
178
+ let bundle$1 = pendingBundlesMap.get(data.id);
179
+ if (!bundle$1) bundle$1 = bundlesMap.get(data.id);
180
+ if (!bundle$1) throw new Error("Bundle to delete not found");
181
+ bundlesMap.delete(data.id);
182
+ pendingBundlesMap.delete(data.id);
183
+ const key = bundle$1._updateJsonKey;
184
+ removalsByKey[key] = removalsByKey[key] || [];
185
+ removalsByKey[key].push(bundle$1.id);
186
+ pathsToInvalidate.add(`/${key}`);
187
+ if (bundle$1.fingerprintHash) pathsToInvalidate.add(`${apiBasePath}/fingerprint/${bundle$1.platform}/${bundle$1.fingerprintHash}/${bundle$1.channel}/*`);
188
+ else if (bundle$1.targetAppVersion) if (!isExactVersion(bundle$1.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle$1.platform}/*`);
242
189
  else {
243
- const normalizedVersions = getSemverNormalizedVersions(updatedBundle$1.targetAppVersion);
244
- for (const version of normalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${updatedBundle$1.platform}/${version}/${updatedBundle$1.channel}/*`);
190
+ const normalizedVersions = getSemverNormalizedVersions(bundle$1.targetAppVersion);
191
+ for (const version of normalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle$1.platform}/${version}/${bundle$1.channel}/*`);
245
192
  }
246
- if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle$1.targetAppVersion) if (!isExactVersion(bundle.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/*`);
247
- else {
248
- const oldNormalizedVersions = getSemverNormalizedVersions(bundle.targetAppVersion);
249
- for (const version of oldNormalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/${version}/${bundle.channel}/*`);
193
+ continue;
194
+ }
195
+ let bundle = pendingBundlesMap.get(data.id);
196
+ if (!bundle) bundle = bundlesMap.get(data.id);
197
+ if (!bundle) throw new Error("targetBundleId not found");
198
+ if (operation === "update") {
199
+ const newChannel = data.channel !== void 0 ? data.channel : bundle.channel;
200
+ const newPlatform = data.platform !== void 0 ? data.platform : bundle.platform;
201
+ const target = data.fingerprintHash ?? bundle.fingerprintHash ?? normalizeTargetAppVersion(data.targetAppVersion) ?? normalizeTargetAppVersion(bundle.targetAppVersion);
202
+ if (!target) throw new Error("target not found");
203
+ const newKey = `${newChannel}/${newPlatform}/${target}/update.json`;
204
+ if (newKey !== bundle._updateJsonKey) {
205
+ const oldKey = bundle._updateJsonKey;
206
+ removalsByKey[oldKey] = removalsByKey[oldKey] || [];
207
+ removalsByKey[oldKey].push(bundle.id);
208
+ changedBundlesByKey[newKey] = changedBundlesByKey[newKey] || [];
209
+ const updatedBundle$1 = {
210
+ ...bundle,
211
+ ...data
212
+ };
213
+ updatedBundle$1._oldUpdateJsonKey = oldKey;
214
+ updatedBundle$1._updateJsonKey = newKey;
215
+ bundlesMap.set(data.id, updatedBundle$1);
216
+ pendingBundlesMap.set(data.id, updatedBundle$1);
217
+ changedBundlesByKey[newKey].push(removeBundleInternalKeys(updatedBundle$1));
218
+ pathsToInvalidate.add(`/${oldKey}`);
219
+ pathsToInvalidate.add(`/${newKey}`);
220
+ const oldChannel = bundle.channel;
221
+ const newChannel$1 = data.channel;
222
+ if (oldChannel !== newChannel$1) {
223
+ pathsToInvalidate.add(`/${oldChannel}/${bundle.platform}/target-app-versions.json`);
224
+ pathsToInvalidate.add(`/${newChannel$1}/${bundle.platform}/target-app-versions.json`);
225
+ if (bundle.fingerprintHash) {
226
+ pathsToInvalidate.add(`${apiBasePath}/fingerprint/${bundle.platform}/${bundle.fingerprintHash}/${oldChannel}/*`);
227
+ pathsToInvalidate.add(`${apiBasePath}/fingerprint/${bundle.platform}/${bundle.fingerprintHash}/${newChannel$1}/*`);
228
+ }
229
+ if (bundle.targetAppVersion) if (!isExactVersion(bundle.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/*`);
230
+ else {
231
+ const normalizedVersions = getSemverNormalizedVersions(bundle.targetAppVersion);
232
+ for (const version of normalizedVersions) {
233
+ pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/${version}/${oldChannel}/*`);
234
+ pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/${version}/${newChannel$1}/*`);
235
+ }
236
+ }
237
+ }
238
+ if (updatedBundle$1.fingerprintHash) pathsToInvalidate.add(`${apiBasePath}/fingerprint/${bundle.platform}/${updatedBundle$1.fingerprintHash}/${updatedBundle$1.channel}/*`);
239
+ else if (updatedBundle$1.targetAppVersion) {
240
+ if (!isExactVersion(updatedBundle$1.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${updatedBundle$1.platform}/*`);
241
+ else {
242
+ const normalizedVersions = getSemverNormalizedVersions(updatedBundle$1.targetAppVersion);
243
+ for (const version of normalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${updatedBundle$1.platform}/${version}/${updatedBundle$1.channel}/*`);
244
+ }
245
+ if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle$1.targetAppVersion) if (!isExactVersion(bundle.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/*`);
246
+ else {
247
+ const oldNormalizedVersions = getSemverNormalizedVersions(bundle.targetAppVersion);
248
+ for (const version of oldNormalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/${version}/${bundle.channel}/*`);
249
+ }
250
+ }
251
+ continue;
252
+ }
253
+ const currentKey = bundle._updateJsonKey;
254
+ const updatedBundle = {
255
+ ...bundle,
256
+ ...data
257
+ };
258
+ bundlesMap.set(data.id, updatedBundle);
259
+ pendingBundlesMap.set(data.id, updatedBundle);
260
+ changedBundlesByKey[currentKey] = changedBundlesByKey[currentKey] || [];
261
+ changedBundlesByKey[currentKey].push(removeBundleInternalKeys(updatedBundle));
262
+ pathsToInvalidate.add(`/${currentKey}`);
263
+ if (updatedBundle.fingerprintHash) pathsToInvalidate.add(`${apiBasePath}/fingerprint/${updatedBundle.platform}/${updatedBundle.fingerprintHash}/${updatedBundle.channel}/*`);
264
+ else if (updatedBundle.targetAppVersion) {
265
+ if (!isExactVersion(updatedBundle.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${updatedBundle.platform}/*`);
266
+ else {
267
+ const normalizedVersions = getSemverNormalizedVersions(updatedBundle.targetAppVersion);
268
+ for (const version of normalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${updatedBundle.platform}/${version}/${updatedBundle.channel}/*`);
269
+ }
270
+ if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle.targetAppVersion) if (!isExactVersion(bundle.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/*`);
271
+ else {
272
+ const oldNormalizedVersions = getSemverNormalizedVersions(bundle.targetAppVersion);
273
+ for (const version of oldNormalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/${version}/${bundle.channel}/*`);
274
+ }
250
275
  }
251
276
  }
252
- continue;
253
277
  }
254
- const currentKey = bundle._updateJsonKey;
255
- const updatedBundle = {
256
- ...bundle,
257
- ...data
258
- };
259
- bundlesMap.set(data.id, updatedBundle);
260
- pendingBundlesMap.set(data.id, updatedBundle);
261
- changedBundlesByKey[currentKey] = changedBundlesByKey[currentKey] || [];
262
- changedBundlesByKey[currentKey].push(removeBundleInternalKeys(updatedBundle));
263
- pathsToInvalidate.add(`/${currentKey}`);
264
- if (updatedBundle.fingerprintHash) pathsToInvalidate.add(`${apiBasePath}/fingerprint/${updatedBundle.platform}/${updatedBundle.fingerprintHash}/${updatedBundle.channel}/*`);
265
- else if (updatedBundle.targetAppVersion) {
266
- if (!isExactVersion(updatedBundle.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${updatedBundle.platform}/*`);
267
- else {
268
- const normalizedVersions = getSemverNormalizedVersions(updatedBundle.targetAppVersion);
269
- for (const version of normalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${updatedBundle.platform}/${version}/${updatedBundle.channel}/*`);
270
- }
271
- if (bundle.targetAppVersion && bundle.targetAppVersion !== updatedBundle.targetAppVersion) if (!isExactVersion(bundle.targetAppVersion)) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/*`);
272
- else {
273
- const oldNormalizedVersions = getSemverNormalizedVersions(bundle.targetAppVersion);
274
- for (const version of oldNormalizedVersions) pathsToInvalidate.add(`${apiBasePath}/app-version/${bundle.platform}/${version}/${bundle.channel}/*`);
278
+ for (const oldKey of Object.keys(removalsByKey)) await (async () => {
279
+ const updatedBundles = (await loadObject(oldKey) ?? []).filter((b) => !removalsByKey[oldKey].includes(b.id));
280
+ updatedBundles.sort((a, b) => b.id.localeCompare(a.id));
281
+ if (updatedBundles.length === 0) await deleteObject(oldKey);
282
+ else await uploadObject(oldKey, updatedBundles);
283
+ })();
284
+ for (const key of Object.keys(changedBundlesByKey)) await (async () => {
285
+ const currentBundles = await loadObject(key) ?? [];
286
+ const pureBundles = changedBundlesByKey[key].map((bundle) => bundle);
287
+ for (const changedBundle of pureBundles) {
288
+ const index = currentBundles.findIndex((b) => b.id === changedBundle.id);
289
+ if (index >= 0) currentBundles[index] = changedBundle;
290
+ else currentBundles.push(changedBundle);
275
291
  }
292
+ currentBundles.sort((a, b) => b.id.localeCompare(a.id));
293
+ await uploadObject(key, currentBundles);
294
+ })();
295
+ const updatedTargetFilePaths = /* @__PURE__ */ new Set();
296
+ if (isTargetAppVersionChanged) for (const platform of PLATFORMS) {
297
+ const updatedPaths = await updateTargetVersionsForPlatform(platform);
298
+ for (const path of updatedPaths) updatedTargetFilePaths.add(path);
276
299
  }
300
+ for (const path of updatedTargetFilePaths) pathsToInvalidate.add(path);
301
+ const encondedPaths = /* @__PURE__ */ new Set();
302
+ for (const path of pathsToInvalidate) encondedPaths.add(encodeURI(path));
303
+ await invalidatePaths(Array.from(encondedPaths));
304
+ pendingBundlesMap.clear();
277
305
  }
278
- }
279
- for (const oldKey of Object.keys(removalsByKey)) await (async () => {
280
- const updatedBundles = (await loadObject(context, oldKey) ?? []).filter((b) => !removalsByKey[oldKey].includes(b.id));
281
- updatedBundles.sort((a, b) => b.id.localeCompare(a.id));
282
- if (updatedBundles.length === 0) await deleteObject(context, oldKey);
283
- else await uploadObject(context, oldKey, updatedBundles);
284
- })();
285
- for (const key of Object.keys(changedBundlesByKey)) await (async () => {
286
- const currentBundles = await loadObject(context, key) ?? [];
287
- const pureBundles = changedBundlesByKey[key].map((bundle) => bundle);
288
- for (const changedBundle of pureBundles) {
289
- const index = currentBundles.findIndex((b) => b.id === changedBundle.id);
290
- if (index >= 0) currentBundles[index] = changedBundle;
291
- else currentBundles.push(changedBundle);
292
- }
293
- currentBundles.sort((a, b) => b.id.localeCompare(a.id));
294
- await uploadObject(context, key, currentBundles);
295
- })();
296
- const updatedTargetFilePaths = /* @__PURE__ */ new Set();
297
- if (isTargetAppVersionChanged) for (const platform of PLATFORMS) {
298
- const updatedPaths = await updateTargetVersionsForPlatform(context, platform);
299
- for (const path of updatedPaths) updatedTargetFilePaths.add(path);
300
- }
301
- for (const path of updatedTargetFilePaths) pathsToInvalidate.add(path);
302
- const encondedPaths = /* @__PURE__ */ new Set();
303
- for (const path of pathsToInvalidate) encondedPaths.add(encodeURI(path));
304
- await invalidatePaths(context, Array.from(encondedPaths));
305
- pendingBundlesMap.clear();
306
- hooks?.onDatabaseUpdated?.();
307
- }
308
- }, hooks);
306
+ })
307
+ })({}, hooks)();
308
+ };
309
309
  };
310
310
 
311
311
  //#endregion
@@ -1,38 +1,27 @@
1
- import { BasePluginArgs, DatabasePlugin, DatabasePluginHooks } from "./types/index.cjs";
1
+ import { DatabasePlugin, DatabasePluginHooks } from "./types/index.cjs";
2
2
 
3
3
  //#region src/createBlobDatabasePlugin.d.ts
4
-
4
+ interface BlobOperations {
5
+ listObjects: (prefix: string) => Promise<string[]>;
6
+ loadObject: <T>(key: string) => Promise<T | null>;
7
+ uploadObject: <T>(key: string, data: T) => Promise<void>;
8
+ deleteObject: (key: string) => Promise<void>;
9
+ invalidatePaths: (paths: string[]) => Promise<void>;
10
+ apiBasePath: string;
11
+ }
5
12
  /**
13
+ * Creates a blob storage-based database plugin with lazy initialization.
6
14
  *
7
15
  * @param name - The name of the database plugin
8
- * @param listObjects - Function to list objects in the storage
9
- * @param loadObject - Function to load an JSON object from the storage
10
- * @param uploadObject - Function to upload an JSON object to the storage
11
- * @param deleteObject - Function to delete an object from the storage
12
- * @param invalidatePaths - Function to invalidate paths in the CDN
13
- * @param hooks - Optional hooks for additional functionality - see createDatabasePlugin
14
- * @returns
16
+ * @param factory - Function that creates blob storage operations from config
17
+ * @returns A double-curried function that lazily initializes the database plugin
15
18
  */
16
- declare const createBlobDatabasePlugin: <TContext = object>({
19
+ declare const createBlobDatabasePlugin: <TConfig>({
17
20
  name,
18
- getContext,
19
- listObjects,
20
- loadObject,
21
- uploadObject,
22
- deleteObject,
23
- invalidatePaths,
24
- hooks,
25
- apiBasePath
21
+ factory
26
22
  }: {
27
23
  name: string;
28
- getContext: () => TContext;
29
- listObjects: (context: TContext, prefix: string) => Promise<string[]>;
30
- loadObject: <T>(context: TContext, key: string) => Promise<T | null>;
31
- uploadObject: <T>(context: TContext, key: string, data: T) => Promise<void>;
32
- deleteObject: (context: TContext, key: string) => Promise<void>;
33
- invalidatePaths: (context: TContext, paths: string[]) => Promise<void>;
34
- hooks?: DatabasePluginHooks;
35
- apiBasePath: string;
36
- }) => (options: BasePluginArgs) => DatabasePlugin;
24
+ factory: (config: TConfig) => BlobOperations;
25
+ }) => (config: TConfig, hooks?: DatabasePluginHooks) => DatabasePlugin;
37
26
  //#endregion
38
- export { createBlobDatabasePlugin };
27
+ export { BlobOperations, createBlobDatabasePlugin };
@@ -1,38 +1,27 @@
1
- import { BasePluginArgs, DatabasePlugin, DatabasePluginHooks } from "./types/index.js";
1
+ import { DatabasePlugin, DatabasePluginHooks } from "./types/index.js";
2
2
 
3
3
  //#region src/createBlobDatabasePlugin.d.ts
4
-
4
+ interface BlobOperations {
5
+ listObjects: (prefix: string) => Promise<string[]>;
6
+ loadObject: <T>(key: string) => Promise<T | null>;
7
+ uploadObject: <T>(key: string, data: T) => Promise<void>;
8
+ deleteObject: (key: string) => Promise<void>;
9
+ invalidatePaths: (paths: string[]) => Promise<void>;
10
+ apiBasePath: string;
11
+ }
5
12
  /**
13
+ * Creates a blob storage-based database plugin with lazy initialization.
6
14
  *
7
15
  * @param name - The name of the database plugin
8
- * @param listObjects - Function to list objects in the storage
9
- * @param loadObject - Function to load an JSON object from the storage
10
- * @param uploadObject - Function to upload an JSON object to the storage
11
- * @param deleteObject - Function to delete an object from the storage
12
- * @param invalidatePaths - Function to invalidate paths in the CDN
13
- * @param hooks - Optional hooks for additional functionality - see createDatabasePlugin
14
- * @returns
16
+ * @param factory - Function that creates blob storage operations from config
17
+ * @returns A double-curried function that lazily initializes the database plugin
15
18
  */
16
- declare const createBlobDatabasePlugin: <TContext = object>({
19
+ declare const createBlobDatabasePlugin: <TConfig>({
17
20
  name,
18
- getContext,
19
- listObjects,
20
- loadObject,
21
- uploadObject,
22
- deleteObject,
23
- invalidatePaths,
24
- hooks,
25
- apiBasePath
21
+ factory
26
22
  }: {
27
23
  name: string;
28
- getContext: () => TContext;
29
- listObjects: (context: TContext, prefix: string) => Promise<string[]>;
30
- loadObject: <T>(context: TContext, key: string) => Promise<T | null>;
31
- uploadObject: <T>(context: TContext, key: string, data: T) => Promise<void>;
32
- deleteObject: (context: TContext, key: string) => Promise<void>;
33
- invalidatePaths: (context: TContext, paths: string[]) => Promise<void>;
34
- hooks?: DatabasePluginHooks;
35
- apiBasePath: string;
36
- }) => (options: BasePluginArgs) => DatabasePlugin;
24
+ factory: (config: TConfig) => BlobOperations;
25
+ }) => (config: TConfig, hooks?: DatabasePluginHooks) => DatabasePlugin;
37
26
  //#endregion
38
- export { createBlobDatabasePlugin };
27
+ export { BlobOperations, createBlobDatabasePlugin };