@hot-updater/supabase 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.
@@ -0,0 +1,527 @@
1
+ import { DEFAULT_ROLLOUT_COHORT_COUNT, NIL_UUID } from "@hot-updater/core";
2
+ import { calculatePagination, createDatabasePlugin, filterCompatibleAppVersions } from "@hot-updater/plugin-core";
3
+ import { createClient } from "@supabase/supabase-js";
4
+ //#region ../../node_modules/.pnpm/map-obj@5.0.0/node_modules/map-obj/index.js
5
+ const isObject$1 = (value) => typeof value === "object" && value !== null;
6
+ const isObjectCustom = (value) => isObject$1(value) && !(value instanceof RegExp) && !(value instanceof Error) && !(value instanceof Date);
7
+ const mapObjectSkip = Symbol("mapObjectSkip");
8
+ const _mapObject = (object, mapper, options, isSeen = /* @__PURE__ */ new WeakMap()) => {
9
+ options = {
10
+ deep: false,
11
+ target: {},
12
+ ...options
13
+ };
14
+ if (isSeen.has(object)) return isSeen.get(object);
15
+ isSeen.set(object, options.target);
16
+ const { target } = options;
17
+ delete options.target;
18
+ const mapArray = (array) => array.map((element) => isObjectCustom(element) ? _mapObject(element, mapper, options, isSeen) : element);
19
+ if (Array.isArray(object)) return mapArray(object);
20
+ for (const [key, value] of Object.entries(object)) {
21
+ const mapResult = mapper(key, value, object);
22
+ if (mapResult === mapObjectSkip) continue;
23
+ let [newKey, newValue, { shouldRecurse = true } = {}] = mapResult;
24
+ if (newKey === "__proto__") continue;
25
+ if (options.deep && shouldRecurse && isObjectCustom(newValue)) newValue = Array.isArray(newValue) ? mapArray(newValue) : _mapObject(newValue, mapper, options, isSeen);
26
+ target[newKey] = newValue;
27
+ }
28
+ return target;
29
+ };
30
+ function mapObject(object, mapper, options) {
31
+ if (!isObject$1(object)) throw new TypeError(`Expected an object, got \`${object}\` (${typeof object})`);
32
+ return _mapObject(object, mapper, options);
33
+ }
34
+ //#endregion
35
+ //#region ../../node_modules/.pnpm/camelcase@8.0.0/node_modules/camelcase/index.js
36
+ const UPPERCASE = /[\p{Lu}]/u;
37
+ const LOWERCASE = /[\p{Ll}]/u;
38
+ const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu;
39
+ const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
40
+ const SEPARATORS = /[_.\- ]+/;
41
+ const LEADING_SEPARATORS = new RegExp("^" + SEPARATORS.source);
42
+ const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, "gu");
43
+ const NUMBERS_AND_IDENTIFIER = new RegExp("\\d+" + IDENTIFIER.source, "gu");
44
+ const preserveCamelCase = (string, toLowerCase, toUpperCase, preserveConsecutiveUppercase) => {
45
+ let isLastCharLower = false;
46
+ let isLastCharUpper = false;
47
+ let isLastLastCharUpper = false;
48
+ let isLastLastCharPreserved = false;
49
+ for (let index = 0; index < string.length; index++) {
50
+ const character = string[index];
51
+ isLastLastCharPreserved = index > 2 ? string[index - 3] === "-" : true;
52
+ if (isLastCharLower && UPPERCASE.test(character)) {
53
+ string = string.slice(0, index) + "-" + string.slice(index);
54
+ isLastCharLower = false;
55
+ isLastLastCharUpper = isLastCharUpper;
56
+ isLastCharUpper = true;
57
+ index++;
58
+ } else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character) && (!isLastLastCharPreserved || preserveConsecutiveUppercase)) {
59
+ string = string.slice(0, index - 1) + "-" + string.slice(index - 1);
60
+ isLastLastCharUpper = isLastCharUpper;
61
+ isLastCharUpper = false;
62
+ isLastCharLower = true;
63
+ } else {
64
+ isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character;
65
+ isLastLastCharUpper = isLastCharUpper;
66
+ isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
67
+ }
68
+ }
69
+ return string;
70
+ };
71
+ const preserveConsecutiveUppercase = (input, toLowerCase) => {
72
+ LEADING_CAPITAL.lastIndex = 0;
73
+ return input.replaceAll(LEADING_CAPITAL, (match) => toLowerCase(match));
74
+ };
75
+ const postProcess = (input, toUpperCase) => {
76
+ SEPARATORS_AND_IDENTIFIER.lastIndex = 0;
77
+ NUMBERS_AND_IDENTIFIER.lastIndex = 0;
78
+ return input.replaceAll(NUMBERS_AND_IDENTIFIER, (match, pattern, offset) => ["_", "-"].includes(input.charAt(offset + match.length)) ? match : toUpperCase(match)).replaceAll(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier));
79
+ };
80
+ function camelCase(input, options) {
81
+ if (!(typeof input === "string" || Array.isArray(input))) throw new TypeError("Expected the input to be `string | string[]`");
82
+ options = {
83
+ pascalCase: false,
84
+ preserveConsecutiveUppercase: false,
85
+ ...options
86
+ };
87
+ if (Array.isArray(input)) input = input.map((x) => x.trim()).filter((x) => x.length).join("-");
88
+ else input = input.trim();
89
+ if (input.length === 0) return "";
90
+ const toLowerCase = options.locale === false ? (string) => string.toLowerCase() : (string) => string.toLocaleLowerCase(options.locale);
91
+ const toUpperCase = options.locale === false ? (string) => string.toUpperCase() : (string) => string.toLocaleUpperCase(options.locale);
92
+ if (input.length === 1) {
93
+ if (SEPARATORS.test(input)) return "";
94
+ return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
95
+ }
96
+ if (input !== toLowerCase(input)) input = preserveCamelCase(input, toLowerCase, toUpperCase, options.preserveConsecutiveUppercase);
97
+ input = input.replace(LEADING_SEPARATORS, "");
98
+ input = options.preserveConsecutiveUppercase ? preserveConsecutiveUppercase(input, toLowerCase) : toLowerCase(input);
99
+ if (options.pascalCase) input = toUpperCase(input.charAt(0)) + input.slice(1);
100
+ return postProcess(input, toUpperCase);
101
+ }
102
+ //#endregion
103
+ //#region ../../node_modules/.pnpm/quick-lru@6.1.2/node_modules/quick-lru/index.js
104
+ var QuickLRU = class extends Map {
105
+ constructor(options = {}) {
106
+ super();
107
+ if (!(options.maxSize && options.maxSize > 0)) throw new TypeError("`maxSize` must be a number greater than 0");
108
+ if (typeof options.maxAge === "number" && options.maxAge === 0) throw new TypeError("`maxAge` must be a number greater than 0");
109
+ this.maxSize = options.maxSize;
110
+ this.maxAge = options.maxAge || Number.POSITIVE_INFINITY;
111
+ this.onEviction = options.onEviction;
112
+ this.cache = /* @__PURE__ */ new Map();
113
+ this.oldCache = /* @__PURE__ */ new Map();
114
+ this._size = 0;
115
+ }
116
+ _emitEvictions(cache) {
117
+ if (typeof this.onEviction !== "function") return;
118
+ for (const [key, item] of cache) this.onEviction(key, item.value);
119
+ }
120
+ _deleteIfExpired(key, item) {
121
+ if (typeof item.expiry === "number" && item.expiry <= Date.now()) {
122
+ if (typeof this.onEviction === "function") this.onEviction(key, item.value);
123
+ return this.delete(key);
124
+ }
125
+ return false;
126
+ }
127
+ _getOrDeleteIfExpired(key, item) {
128
+ if (this._deleteIfExpired(key, item) === false) return item.value;
129
+ }
130
+ _getItemValue(key, item) {
131
+ return item.expiry ? this._getOrDeleteIfExpired(key, item) : item.value;
132
+ }
133
+ _peek(key, cache) {
134
+ const item = cache.get(key);
135
+ return this._getItemValue(key, item);
136
+ }
137
+ _set(key, value) {
138
+ this.cache.set(key, value);
139
+ this._size++;
140
+ if (this._size >= this.maxSize) {
141
+ this._size = 0;
142
+ this._emitEvictions(this.oldCache);
143
+ this.oldCache = this.cache;
144
+ this.cache = /* @__PURE__ */ new Map();
145
+ }
146
+ }
147
+ _moveToRecent(key, item) {
148
+ this.oldCache.delete(key);
149
+ this._set(key, item);
150
+ }
151
+ *_entriesAscending() {
152
+ for (const item of this.oldCache) {
153
+ const [key, value] = item;
154
+ if (!this.cache.has(key)) {
155
+ if (this._deleteIfExpired(key, value) === false) yield item;
156
+ }
157
+ }
158
+ for (const item of this.cache) {
159
+ const [key, value] = item;
160
+ if (this._deleteIfExpired(key, value) === false) yield item;
161
+ }
162
+ }
163
+ get(key) {
164
+ if (this.cache.has(key)) {
165
+ const item = this.cache.get(key);
166
+ return this._getItemValue(key, item);
167
+ }
168
+ if (this.oldCache.has(key)) {
169
+ const item = this.oldCache.get(key);
170
+ if (this._deleteIfExpired(key, item) === false) {
171
+ this._moveToRecent(key, item);
172
+ return item.value;
173
+ }
174
+ }
175
+ }
176
+ set(key, value, { maxAge = this.maxAge } = {}) {
177
+ const expiry = typeof maxAge === "number" && maxAge !== Number.POSITIVE_INFINITY ? Date.now() + maxAge : void 0;
178
+ if (this.cache.has(key)) this.cache.set(key, {
179
+ value,
180
+ expiry
181
+ });
182
+ else this._set(key, {
183
+ value,
184
+ expiry
185
+ });
186
+ return this;
187
+ }
188
+ has(key) {
189
+ if (this.cache.has(key)) return !this._deleteIfExpired(key, this.cache.get(key));
190
+ if (this.oldCache.has(key)) return !this._deleteIfExpired(key, this.oldCache.get(key));
191
+ return false;
192
+ }
193
+ peek(key) {
194
+ if (this.cache.has(key)) return this._peek(key, this.cache);
195
+ if (this.oldCache.has(key)) return this._peek(key, this.oldCache);
196
+ }
197
+ delete(key) {
198
+ const deleted = this.cache.delete(key);
199
+ if (deleted) this._size--;
200
+ return this.oldCache.delete(key) || deleted;
201
+ }
202
+ clear() {
203
+ this.cache.clear();
204
+ this.oldCache.clear();
205
+ this._size = 0;
206
+ }
207
+ resize(newSize) {
208
+ if (!(newSize && newSize > 0)) throw new TypeError("`maxSize` must be a number greater than 0");
209
+ const items = [...this._entriesAscending()];
210
+ const removeCount = items.length - newSize;
211
+ if (removeCount < 0) {
212
+ this.cache = new Map(items);
213
+ this.oldCache = /* @__PURE__ */ new Map();
214
+ this._size = items.length;
215
+ } else {
216
+ if (removeCount > 0) this._emitEvictions(items.slice(0, removeCount));
217
+ this.oldCache = new Map(items.slice(removeCount));
218
+ this.cache = /* @__PURE__ */ new Map();
219
+ this._size = 0;
220
+ }
221
+ this.maxSize = newSize;
222
+ }
223
+ *keys() {
224
+ for (const [key] of this) yield key;
225
+ }
226
+ *values() {
227
+ for (const [, value] of this) yield value;
228
+ }
229
+ *[Symbol.iterator]() {
230
+ for (const item of this.cache) {
231
+ const [key, value] = item;
232
+ if (this._deleteIfExpired(key, value) === false) yield [key, value.value];
233
+ }
234
+ for (const item of this.oldCache) {
235
+ const [key, value] = item;
236
+ if (!this.cache.has(key)) {
237
+ if (this._deleteIfExpired(key, value) === false) yield [key, value.value];
238
+ }
239
+ }
240
+ }
241
+ *entriesDescending() {
242
+ let items = [...this.cache];
243
+ for (let i = items.length - 1; i >= 0; --i) {
244
+ const [key, value] = items[i];
245
+ if (this._deleteIfExpired(key, value) === false) yield [key, value.value];
246
+ }
247
+ items = [...this.oldCache];
248
+ for (let i = items.length - 1; i >= 0; --i) {
249
+ const [key, value] = items[i];
250
+ if (!this.cache.has(key)) {
251
+ if (this._deleteIfExpired(key, value) === false) yield [key, value.value];
252
+ }
253
+ }
254
+ }
255
+ *entriesAscending() {
256
+ for (const [key, value] of this._entriesAscending()) yield [key, value.value];
257
+ }
258
+ get size() {
259
+ if (!this._size) return this.oldCache.size;
260
+ let oldCacheSize = 0;
261
+ for (const key of this.oldCache.keys()) if (!this.cache.has(key)) oldCacheSize++;
262
+ return Math.min(this._size + oldCacheSize, this.maxSize);
263
+ }
264
+ entries() {
265
+ return this.entriesAscending();
266
+ }
267
+ forEach(callbackFunction, thisArgument = this) {
268
+ for (const [key, value] of this.entriesAscending()) callbackFunction.call(thisArgument, value, key, this);
269
+ }
270
+ get [Symbol.toStringTag]() {
271
+ return JSON.stringify([...this.entriesAscending()]);
272
+ }
273
+ };
274
+ //#endregion
275
+ //#region ../../node_modules/.pnpm/camelcase-keys@9.1.3/node_modules/camelcase-keys/index.js
276
+ const has = (array, key) => array.some((element) => {
277
+ if (typeof element === "string") return element === key;
278
+ element.lastIndex = 0;
279
+ return element.test(key);
280
+ });
281
+ const cache = new QuickLRU({ maxSize: 1e5 });
282
+ const isObject = (value) => typeof value === "object" && value !== null && !(value instanceof RegExp) && !(value instanceof Error) && !(value instanceof Date);
283
+ const transform = (input, options = {}) => {
284
+ if (!isObject(input)) return input;
285
+ const { exclude, pascalCase = false, stopPaths, deep = false, preserveConsecutiveUppercase = false } = options;
286
+ const stopPathsSet = new Set(stopPaths);
287
+ const makeMapper = (parentPath) => (key, value) => {
288
+ if (deep && isObject(value)) {
289
+ const path = parentPath === void 0 ? key : `${parentPath}.${key}`;
290
+ if (!stopPathsSet.has(path)) value = mapObject(value, makeMapper(path));
291
+ }
292
+ if (!(exclude && has(exclude, key))) {
293
+ const cacheKey = pascalCase ? `${key}_` : key;
294
+ if (cache.has(cacheKey)) key = cache.get(cacheKey);
295
+ else {
296
+ const returnValue = camelCase(key, {
297
+ pascalCase,
298
+ locale: false,
299
+ preserveConsecutiveUppercase
300
+ });
301
+ if (key.length < 100) cache.set(cacheKey, returnValue);
302
+ key = returnValue;
303
+ }
304
+ }
305
+ return [key, value];
306
+ };
307
+ return mapObject(input, makeMapper(void 0));
308
+ };
309
+ function camelcaseKeys(input, options) {
310
+ if (Array.isArray(input)) return Object.keys(input).map((key) => transform(input[key], options));
311
+ return transform(input, options);
312
+ }
313
+ //#endregion
314
+ //#region src/getUpdateInfo.ts
315
+ const appVersionStrategy = async (supabase, { platform, appVersion, bundleId, minBundleId = NIL_UUID, channel = "production", cohort }) => {
316
+ const { data: appVersionList, error: appVersionListError } = await supabase.rpc("get_target_app_version_list", {
317
+ app_platform: platform,
318
+ min_bundle_id: minBundleId
319
+ });
320
+ if (appVersionListError) throw appVersionListError;
321
+ const targetAppVersionList = filterCompatibleAppVersions((appVersionList ?? []).map((group) => group.target_app_version), appVersion);
322
+ const { data, error } = await supabase.rpc("get_update_info_by_app_version", {
323
+ app_platform: platform,
324
+ app_version: appVersion,
325
+ bundle_id: bundleId,
326
+ min_bundle_id: minBundleId,
327
+ target_channel: channel,
328
+ target_app_version_list: targetAppVersionList,
329
+ cohort: cohort ?? null
330
+ });
331
+ if (error) throw error;
332
+ const row = data?.[0];
333
+ return row ? camelcaseKeys(row) : null;
334
+ };
335
+ const fingerprintStrategy = async (supabase, { platform, fingerprintHash, bundleId, minBundleId = NIL_UUID, channel = "production", cohort }) => {
336
+ const { data, error } = await supabase.rpc("get_update_info_by_fingerprint_hash", {
337
+ app_platform: platform,
338
+ bundle_id: bundleId,
339
+ min_bundle_id: minBundleId,
340
+ target_channel: channel,
341
+ target_fingerprint_hash: fingerprintHash,
342
+ cohort: cohort ?? null
343
+ });
344
+ if (error) throw error;
345
+ const row = data?.[0];
346
+ return row ? camelcaseKeys(row) : null;
347
+ };
348
+ const getUpdateInfo = (supabase, args) => {
349
+ if (args._updateStrategy === "appVersion") return appVersionStrategy(supabase, args);
350
+ return fingerprintStrategy(supabase, args);
351
+ };
352
+ //#endregion
353
+ //#region src/supabaseDatabase.ts
354
+ const supabaseDatabase = createDatabasePlugin({
355
+ name: "supabaseDatabase",
356
+ factory: (config) => {
357
+ const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey);
358
+ return {
359
+ async getUpdateInfo(args) {
360
+ return getUpdateInfo(supabase, args);
361
+ },
362
+ async getBundleById(bundleId) {
363
+ const { data, error } = await supabase.from("bundles").select("channel, enabled, should_force_update, file_hash, git_commit_hash, id, message, platform, target_app_version, fingerprint_hash, storage_uri, metadata, rollout_cohort_count, target_cohorts").eq("id", bundleId).single();
364
+ if (!data || error) return null;
365
+ return {
366
+ channel: data.channel,
367
+ enabled: data.enabled,
368
+ shouldForceUpdate: data.should_force_update,
369
+ fileHash: data.file_hash,
370
+ gitCommitHash: data.git_commit_hash,
371
+ id: data.id,
372
+ message: data.message,
373
+ platform: data.platform,
374
+ targetAppVersion: data.target_app_version,
375
+ fingerprintHash: data.fingerprint_hash,
376
+ storageUri: data.storage_uri,
377
+ metadata: data.metadata ?? {},
378
+ rolloutCohortCount: data.rollout_cohort_count ?? DEFAULT_ROLLOUT_COHORT_COUNT,
379
+ targetCohorts: data.target_cohorts ?? null
380
+ };
381
+ },
382
+ async getBundles(options) {
383
+ const { where, limit, orderBy } = options ?? {};
384
+ const offset = (options && "offset" in options ? options.offset : void 0) ?? 0;
385
+ if (where?.targetAppVersionIn && where.targetAppVersionIn.length === 0 || where?.id?.in && where.id.in.length === 0) return {
386
+ data: [],
387
+ pagination: calculatePagination(0, {
388
+ limit,
389
+ offset
390
+ })
391
+ };
392
+ let countQuery = supabase.from("bundles").select("*", {
393
+ count: "exact",
394
+ head: true
395
+ });
396
+ if (where?.channel) countQuery = countQuery.eq("channel", where.channel);
397
+ if (where?.platform) countQuery = countQuery.eq("platform", where.platform);
398
+ if (where?.enabled !== void 0) countQuery = countQuery.eq("enabled", where.enabled);
399
+ if (where?.fingerprintHash !== void 0) countQuery = where.fingerprintHash === null ? countQuery.is("fingerprint_hash", null) : countQuery.eq("fingerprint_hash", where.fingerprintHash);
400
+ if (where?.targetAppVersion !== void 0) countQuery = where.targetAppVersion === null ? countQuery.is("target_app_version", null) : countQuery.eq("target_app_version", where.targetAppVersion);
401
+ if (where?.targetAppVersionIn) countQuery = countQuery.in("target_app_version", where.targetAppVersionIn);
402
+ if (where?.targetAppVersionNotNull) countQuery = countQuery.not("target_app_version", "is", null);
403
+ if (where?.id?.eq) countQuery = countQuery.eq("id", where.id.eq);
404
+ if (where?.id?.gt) countQuery = countQuery.gt("id", where.id.gt);
405
+ if (where?.id?.gte) countQuery = countQuery.gte("id", where.id.gte);
406
+ if (where?.id?.lt) countQuery = countQuery.lt("id", where.id.lt);
407
+ if (where?.id?.lte) countQuery = countQuery.lte("id", where.id.lte);
408
+ if (where?.id?.in) countQuery = countQuery.in("id", where.id.in);
409
+ const { count: total = 0 } = await countQuery;
410
+ let query = supabase.from("bundles").select("id, channel, enabled, platform, should_force_update, file_hash, git_commit_hash, message, fingerprint_hash, target_app_version, storage_uri, metadata, rollout_cohort_count, target_cohorts").order("id", { ascending: orderBy?.direction === "asc" });
411
+ if (where?.channel) query = query.eq("channel", where.channel);
412
+ if (where?.platform) query = query.eq("platform", where.platform);
413
+ if (where?.enabled !== void 0) query = query.eq("enabled", where.enabled);
414
+ if (where?.fingerprintHash !== void 0) query = where.fingerprintHash === null ? query.is("fingerprint_hash", null) : query.eq("fingerprint_hash", where.fingerprintHash);
415
+ if (where?.targetAppVersion !== void 0) query = where.targetAppVersion === null ? query.is("target_app_version", null) : query.eq("target_app_version", where.targetAppVersion);
416
+ if (where?.targetAppVersionIn) query = query.in("target_app_version", where.targetAppVersionIn);
417
+ if (where?.targetAppVersionNotNull) query = query.not("target_app_version", "is", null);
418
+ if (where?.id?.eq) query = query.eq("id", where.id.eq);
419
+ if (where?.id?.gt) query = query.gt("id", where.id.gt);
420
+ if (where?.id?.gte) query = query.gte("id", where.id.gte);
421
+ if (where?.id?.lt) query = query.lt("id", where.id.lt);
422
+ if (where?.id?.lte) query = query.lte("id", where.id.lte);
423
+ if (where?.id?.in) query = query.in("id", where.id.in);
424
+ if (limit) query = query.limit(limit);
425
+ if (offset) query = query.range(offset, offset + (limit || 20) - 1);
426
+ const { data } = await query;
427
+ return {
428
+ data: data ? data.map((bundle) => ({
429
+ channel: bundle.channel,
430
+ enabled: bundle.enabled,
431
+ shouldForceUpdate: bundle.should_force_update,
432
+ fileHash: bundle.file_hash,
433
+ gitCommitHash: bundle.git_commit_hash,
434
+ id: bundle.id,
435
+ message: bundle.message,
436
+ platform: bundle.platform,
437
+ targetAppVersion: bundle.target_app_version,
438
+ fingerprintHash: bundle.fingerprint_hash,
439
+ storageUri: bundle.storage_uri,
440
+ metadata: bundle.metadata ?? {},
441
+ rolloutCohortCount: bundle.rollout_cohort_count ?? DEFAULT_ROLLOUT_COHORT_COUNT,
442
+ targetCohorts: bundle.target_cohorts ?? null
443
+ })) : [],
444
+ pagination: calculatePagination(total ?? 0, {
445
+ limit,
446
+ offset
447
+ })
448
+ };
449
+ },
450
+ async getChannels() {
451
+ const { data, error } = await supabase.rpc("get_channels");
452
+ if (error) throw error;
453
+ return data.map((bundle) => bundle.channel);
454
+ },
455
+ async commitBundle({ changedSets }) {
456
+ if (changedSets.length === 0) return;
457
+ for (const op of changedSets) if (op.operation === "delete") {
458
+ const { error } = await supabase.from("bundles").delete().eq("id", op.data.id);
459
+ if (error) throw new Error(`Failed to delete bundle: ${error.message}`);
460
+ } else if (op.operation === "insert" || op.operation === "update") {
461
+ const bundle = op.data;
462
+ const { error } = await supabase.from("bundles").upsert({
463
+ id: bundle.id,
464
+ channel: bundle.channel,
465
+ enabled: bundle.enabled,
466
+ should_force_update: bundle.shouldForceUpdate,
467
+ file_hash: bundle.fileHash,
468
+ git_commit_hash: bundle.gitCommitHash,
469
+ message: bundle.message,
470
+ platform: bundle.platform,
471
+ target_app_version: bundle.targetAppVersion,
472
+ fingerprint_hash: bundle.fingerprintHash,
473
+ storage_uri: bundle.storageUri,
474
+ metadata: bundle.metadata,
475
+ rollout_cohort_count: bundle.rolloutCohortCount ?? DEFAULT_ROLLOUT_COHORT_COUNT,
476
+ target_cohorts: bundle.targetCohorts ?? null
477
+ }, { onConflict: "id" });
478
+ if (error) throw error;
479
+ }
480
+ }
481
+ };
482
+ }
483
+ });
484
+ //#endregion
485
+ //#region src/supabaseEdgeFunctionDatabase.ts
486
+ const supabaseEdgeFunctionDatabase = (config, hooks) => {
487
+ return supabaseDatabase({
488
+ supabaseUrl: config.supabaseUrl,
489
+ supabaseAnonKey: config.supabaseServiceRoleKey
490
+ }, hooks);
491
+ };
492
+ //#endregion
493
+ //#region src/supabaseEdgeFunctionStorage.ts
494
+ const supabaseEdgeFunctionStorage = (config) => {
495
+ const supabase = createClient(config.supabaseUrl, config.supabaseServiceRoleKey);
496
+ return () => {
497
+ return {
498
+ name: "supabaseEdgeFunctionStorage",
499
+ supportedProtocol: "supabase-storage",
500
+ async upload() {
501
+ throw new Error("supabaseEdgeFunctionStorage does not support upload() in the edge runtime.");
502
+ },
503
+ async delete(storageUri) {
504
+ const storageUrl = new URL(storageUri);
505
+ if (storageUrl.protocol !== "supabase-storage:") throw new Error("Invalid Supabase storage URI protocol");
506
+ const bucketName = storageUrl.host;
507
+ const key = storageUrl.pathname.replace(/^\/+/, "");
508
+ if (!bucketName || !key) throw new Error("Invalid Supabase storage URI");
509
+ const { error } = await supabase.storage.from(bucketName).remove([key]);
510
+ if (error) throw new Error(`Failed to delete bundle: ${error.message}`);
511
+ },
512
+ async getDownloadUrl(storageUri) {
513
+ const storageUrl = new URL(storageUri);
514
+ if (storageUrl.protocol !== "supabase-storage:") throw new Error("Invalid Supabase storage URI protocol");
515
+ const bucketName = storageUrl.host;
516
+ const key = storageUrl.pathname.replace(/^\/+/, "");
517
+ if (!bucketName || !key) throw new Error("Invalid Supabase storage URI");
518
+ const { data, error } = await supabase.storage.from(bucketName).createSignedUrl(key, config.signedUrlExpiresIn ?? 3600);
519
+ if (error) throw new Error(`Failed to generate download URL: ${error.message}`);
520
+ if (!data?.signedUrl) throw new Error("Failed to generate download URL");
521
+ return { fileUrl: data.signedUrl };
522
+ }
523
+ };
524
+ };
525
+ };
526
+ //#endregion
527
+ export { supabaseEdgeFunctionDatabase as n, supabaseDatabase as r, supabaseEdgeFunctionStorage as t };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/supabase",
3
3
  "type": "module",
4
- "version": "0.29.4",
4
+ "version": "0.29.6",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.mjs",
@@ -47,10 +47,10 @@
47
47
  "@supabase/supabase-js": "^2.76.1",
48
48
  "hono": "4.12.9",
49
49
  "uuidv7": "^1.0.2",
50
- "@hot-updater/core": "0.29.4",
51
- "@hot-updater/cli-tools": "0.29.4",
52
- "@hot-updater/plugin-core": "0.29.4",
53
- "@hot-updater/server": "0.29.4"
50
+ "@hot-updater/core": "0.29.6",
51
+ "@hot-updater/cli-tools": "0.29.6",
52
+ "@hot-updater/plugin-core": "0.29.6",
53
+ "@hot-updater/server": "0.29.6"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@electric-sql/pglite": "^0.2.15",
@@ -60,10 +60,10 @@
60
60
  "execa": "9.5.2",
61
61
  "@types/node": "^20",
62
62
  "mime": "^4.0.4",
63
- "@hot-updater/js": "0.29.4",
64
- "@hot-updater/mock": "0.29.4",
65
- "@hot-updater/test-utils": "0.29.4",
66
- "@hot-updater/postgres": "0.29.4"
63
+ "@hot-updater/mock": "0.29.6",
64
+ "@hot-updater/postgres": "0.29.6",
65
+ "@hot-updater/test-utils": "0.29.6",
66
+ "@hot-updater/js": "0.29.6"
67
67
  },
68
68
  "scripts": {
69
69
  "build": "tsdown",