@hot-updater/supabase 0.32.0 → 0.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/edge.cjs CHANGED
@@ -1,4 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_supabaseEdgeFunctionStorage = require("./supabaseEdgeFunctionStorage-D933mGy9.cjs");
2
+ const require_supabaseEdgeFunctionStorage = require("./supabaseEdgeFunctionStorage-DRxwDPRy.cjs");
3
3
  exports.supabaseEdgeFunctionDatabase = require_supabaseEdgeFunctionStorage.supabaseEdgeFunctionDatabase;
4
4
  exports.supabaseEdgeFunctionStorage = require_supabaseEdgeFunctionStorage.supabaseEdgeFunctionStorage;
package/dist/edge.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { n as supabaseEdgeFunctionDatabase, t as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-B4KN0khj.mjs";
1
+ import { n as supabaseEdgeFunctionDatabase, t as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-CO70azPw.mjs";
2
2
  export { supabaseEdgeFunctionDatabase, supabaseEdgeFunctionStorage };
@@ -47,15 +47,58 @@ let node_stream_promises = require("node:stream/promises");
47
47
  let node_stream = require("node:stream");
48
48
  let node_buffer = require("node:buffer");
49
49
  let _supabase_supabase_js = require("@supabase/supabase-js");
50
- //#region ../../node_modules/.pnpm/es-toolkit@1.32.0/node_modules/es-toolkit/dist/error/AbortError.mjs
51
- var AbortError = class extends Error {
50
+ //#region ../../node_modules/.pnpm/es-toolkit@1.47.0/node_modules/es-toolkit/dist/_internal/globalThis.mjs
51
+ const globalThis_ = typeof globalThis === "object" && globalThis || typeof window === "object" && window || typeof self === "object" && self || typeof global === "object" && global || (function() {
52
+ return this;
53
+ })() || Function("return this")();
54
+ //#endregion
55
+ //#region ../../node_modules/.pnpm/es-toolkit@1.47.0/node_modules/es-toolkit/dist/_internal/DOMException.mjs
56
+ const DOMException$1 = typeof globalThis_.DOMException !== "undefined" ? globalThis_.DOMException : Error;
57
+ //#endregion
58
+ //#region ../../node_modules/.pnpm/es-toolkit@1.47.0/node_modules/es-toolkit/dist/error/AbortError.mjs
59
+ /**
60
+ * An error class representing an aborted operation.
61
+ * @augments DOMException
62
+ */
63
+ var AbortError = class extends DOMException$1 {
52
64
  constructor(message = "The operation was aborted") {
53
65
  super(message);
54
- this.name = "AbortError";
55
66
  }
56
67
  };
57
68
  //#endregion
58
- //#region ../../node_modules/.pnpm/es-toolkit@1.32.0/node_modules/es-toolkit/dist/promise/delay.mjs
69
+ //#region ../../node_modules/.pnpm/es-toolkit@1.47.0/node_modules/es-toolkit/dist/promise/delay.mjs
70
+ /**
71
+ * Delays the execution of code for a specified number of milliseconds.
72
+ *
73
+ * This function returns a Promise that resolves after the specified delay, allowing you to use it
74
+ * with async/await to pause execution.
75
+ *
76
+ * @param {number} ms - The number of milliseconds to delay.
77
+ * @param {DelayOptions} options - The options object.
78
+ * @param {AbortSignal} options.signal - An optional AbortSignal to cancel the delay.
79
+ * @returns {Promise<void>} A Promise that resolves after the specified delay.
80
+ *
81
+ * @example
82
+ * async function foo() {
83
+ * console.log('Start');
84
+ * await delay(1000); // Delays execution for 1 second
85
+ * console.log('End');
86
+ * }
87
+ *
88
+ * foo();
89
+ *
90
+ * // With AbortSignal
91
+ * const controller = new AbortController();
92
+ * const { signal } = controller;
93
+ *
94
+ * setTimeout(() => controller.abort(), 50); // Will cancel the delay after 50ms
95
+ * try {
96
+ * await delay(100, { signal });
97
+ * } catch (error) {
98
+ * console.error(error); // Will log 'AbortError'
99
+ * }
100
+ * }
101
+ */
59
102
  function delay(ms, { signal } = {}) {
60
103
  return new Promise((resolve, reject) => {
61
104
  const abortError = () => {
@@ -647,7 +690,7 @@ const fallbackSymbols = {
647
690
  const figures = isUnicodeSupported() ? mainSymbols : fallbackSymbols;
648
691
  Object.entries(specialMainSymbols);
649
692
  //#endregion
650
- //#region ../../node_modules/.pnpm/yoctocolors@2.1.1/node_modules/yoctocolors/base.js
693
+ //#region ../../node_modules/.pnpm/yoctocolors@2.1.2/node_modules/yoctocolors/base.js
651
694
  const hasColors = node_tty.default?.WriteStream?.prototype?.hasColors?.() ?? false;
652
695
  const format = (open, close) => {
653
696
  if (!hasColors) return (input) => input;
@@ -659,8 +702,9 @@ const format = (open, close) => {
659
702
  if (index === -1) return openCode + string + closeCode;
660
703
  let result = openCode;
661
704
  let lastIndex = 0;
705
+ const replaceCode = (close === 22 ? closeCode : "") + openCode;
662
706
  while (index !== -1) {
663
- result += string.slice(lastIndex, index) + openCode;
707
+ result += string.slice(lastIndex, index) + replaceCode;
664
708
  lastIndex = index + closeCode.length;
665
709
  index = string.indexOf(closeCode, lastIndex);
666
710
  }
@@ -3060,7 +3104,7 @@ function parseMilliseconds(milliseconds) {
3060
3104
  throw new TypeError("Expected a finite number or bigint");
3061
3105
  }
3062
3106
  //#endregion
3063
- //#region ../../node_modules/.pnpm/pretty-ms@9.2.0/node_modules/pretty-ms/index.js
3107
+ //#region ../../node_modules/.pnpm/pretty-ms@9.3.0/node_modules/pretty-ms/index.js
3064
3108
  const isZero = (value) => value === 0 || value === 0n;
3065
3109
  const pluralize = (word, count) => count === 1 || count === 1n ? word : `${word}s`;
3066
3110
  const SECOND_ROUNDING_EPSILON = 1e-7;
@@ -3109,7 +3153,7 @@ function prettyMilliseconds(milliseconds, options) {
3109
3153
  add(Number(parsed.hours), "hour", "h");
3110
3154
  }
3111
3155
  add(Number(parsed.minutes), "minute", "m");
3112
- if (!options.hideSeconds) if (options.separateMilliseconds || options.formatSubMilliseconds || !options.colonNotation && milliseconds < 1e3) {
3156
+ if (!options.hideSeconds) if (options.separateMilliseconds || options.formatSubMilliseconds || !options.colonNotation && milliseconds < 1e3 && !options.subSecondsAsDecimals) {
3113
3157
  const seconds = Number(parsed.seconds);
3114
3158
  const milliseconds = Number(parsed.milliseconds);
3115
3159
  const microseconds = Number(parsed.microseconds);
@@ -42,15 +42,58 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
42
42
  }) : target, mod));
43
43
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
44
44
  //#endregion
45
- //#region ../../node_modules/.pnpm/es-toolkit@1.32.0/node_modules/es-toolkit/dist/error/AbortError.mjs
46
- var AbortError = class extends Error {
45
+ //#region ../../node_modules/.pnpm/es-toolkit@1.47.0/node_modules/es-toolkit/dist/_internal/globalThis.mjs
46
+ const globalThis_ = typeof globalThis === "object" && globalThis || typeof window === "object" && window || typeof self === "object" && self || typeof global === "object" && global || (function() {
47
+ return this;
48
+ })() || Function("return this")();
49
+ //#endregion
50
+ //#region ../../node_modules/.pnpm/es-toolkit@1.47.0/node_modules/es-toolkit/dist/_internal/DOMException.mjs
51
+ const DOMException$1 = typeof globalThis_.DOMException !== "undefined" ? globalThis_.DOMException : Error;
52
+ //#endregion
53
+ //#region ../../node_modules/.pnpm/es-toolkit@1.47.0/node_modules/es-toolkit/dist/error/AbortError.mjs
54
+ /**
55
+ * An error class representing an aborted operation.
56
+ * @augments DOMException
57
+ */
58
+ var AbortError = class extends DOMException$1 {
47
59
  constructor(message = "The operation was aborted") {
48
60
  super(message);
49
- this.name = "AbortError";
50
61
  }
51
62
  };
52
63
  //#endregion
53
- //#region ../../node_modules/.pnpm/es-toolkit@1.32.0/node_modules/es-toolkit/dist/promise/delay.mjs
64
+ //#region ../../node_modules/.pnpm/es-toolkit@1.47.0/node_modules/es-toolkit/dist/promise/delay.mjs
65
+ /**
66
+ * Delays the execution of code for a specified number of milliseconds.
67
+ *
68
+ * This function returns a Promise that resolves after the specified delay, allowing you to use it
69
+ * with async/await to pause execution.
70
+ *
71
+ * @param {number} ms - The number of milliseconds to delay.
72
+ * @param {DelayOptions} options - The options object.
73
+ * @param {AbortSignal} options.signal - An optional AbortSignal to cancel the delay.
74
+ * @returns {Promise<void>} A Promise that resolves after the specified delay.
75
+ *
76
+ * @example
77
+ * async function foo() {
78
+ * console.log('Start');
79
+ * await delay(1000); // Delays execution for 1 second
80
+ * console.log('End');
81
+ * }
82
+ *
83
+ * foo();
84
+ *
85
+ * // With AbortSignal
86
+ * const controller = new AbortController();
87
+ * const { signal } = controller;
88
+ *
89
+ * setTimeout(() => controller.abort(), 50); // Will cancel the delay after 50ms
90
+ * try {
91
+ * await delay(100, { signal });
92
+ * } catch (error) {
93
+ * console.error(error); // Will log 'AbortError'
94
+ * }
95
+ * }
96
+ */
54
97
  function delay(ms, { signal } = {}) {
55
98
  return new Promise((resolve, reject) => {
56
99
  const abortError = () => {
@@ -642,7 +685,7 @@ const fallbackSymbols = {
642
685
  const figures = isUnicodeSupported() ? mainSymbols : fallbackSymbols;
643
686
  Object.entries(specialMainSymbols);
644
687
  //#endregion
645
- //#region ../../node_modules/.pnpm/yoctocolors@2.1.1/node_modules/yoctocolors/base.js
688
+ //#region ../../node_modules/.pnpm/yoctocolors@2.1.2/node_modules/yoctocolors/base.js
646
689
  const hasColors = tty?.WriteStream?.prototype?.hasColors?.() ?? false;
647
690
  const format = (open, close) => {
648
691
  if (!hasColors) return (input) => input;
@@ -654,8 +697,9 @@ const format = (open, close) => {
654
697
  if (index === -1) return openCode + string + closeCode;
655
698
  let result = openCode;
656
699
  let lastIndex = 0;
700
+ const replaceCode = (close === 22 ? closeCode : "") + openCode;
657
701
  while (index !== -1) {
658
- result += string.slice(lastIndex, index) + openCode;
702
+ result += string.slice(lastIndex, index) + replaceCode;
659
703
  lastIndex = index + closeCode.length;
660
704
  index = string.indexOf(closeCode, lastIndex);
661
705
  }
@@ -3055,7 +3099,7 @@ function parseMilliseconds(milliseconds) {
3055
3099
  throw new TypeError("Expected a finite number or bigint");
3056
3100
  }
3057
3101
  //#endregion
3058
- //#region ../../node_modules/.pnpm/pretty-ms@9.2.0/node_modules/pretty-ms/index.js
3102
+ //#region ../../node_modules/.pnpm/pretty-ms@9.3.0/node_modules/pretty-ms/index.js
3059
3103
  const isZero = (value) => value === 0 || value === 0n;
3060
3104
  const pluralize = (word, count) => count === 1 || count === 1n ? word : `${word}s`;
3061
3105
  const SECOND_ROUNDING_EPSILON = 1e-7;
@@ -3104,7 +3148,7 @@ function prettyMilliseconds(milliseconds, options) {
3104
3148
  add(Number(parsed.hours), "hour", "h");
3105
3149
  }
3106
3150
  add(Number(parsed.minutes), "minute", "m");
3107
- if (!options.hideSeconds) if (options.separateMilliseconds || options.formatSubMilliseconds || !options.colonNotation && milliseconds < 1e3) {
3151
+ if (!options.hideSeconds) if (options.separateMilliseconds || options.formatSubMilliseconds || !options.colonNotation && milliseconds < 1e3 && !options.subSecondsAsDecimals) {
3108
3152
  const seconds = Number(parsed.seconds);
3109
3153
  const milliseconds = Number(parsed.milliseconds);
3110
3154
  const microseconds = Number(parsed.microseconds);
package/dist/index.cjs CHANGED
@@ -21,7 +21,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
- const require_supabaseEdgeFunctionStorage = require("./supabaseEdgeFunctionStorage-D933mGy9.cjs");
24
+ const require_supabaseEdgeFunctionStorage = require("./supabaseEdgeFunctionStorage-DRxwDPRy.cjs");
25
25
  let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
26
26
  let _supabase_supabase_js = require("@supabase/supabase-js");
27
27
  let fs_promises = require("fs/promises");
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as resolveSupabaseServiceRoleKey, n as supabaseEdgeFunctionDatabase, r as supabaseDatabase, t as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-B4KN0khj.mjs";
1
+ import { i as resolveSupabaseServiceRoleKey, n as supabaseEdgeFunctionDatabase, r as supabaseDatabase, t as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-CO70azPw.mjs";
2
2
  import { createStorageKeyBuilder, createUniversalStoragePlugin, getContentType, parseStorageUri } from "@hot-updater/plugin-core";
3
3
  import { createClient } from "@supabase/supabase-js";
4
4
  import fs from "fs/promises";
@@ -1,14 +1,8 @@
1
- import { DEFAULT_ROLLOUT_COHORT_COUNT, getAssetBaseStorageUri, getBundlePatches, getManifestFileHash, getManifestStorageUri, stripBundleArtifactMetadata } from "@hot-updater/core";
2
- import { calculatePagination, createDatabasePlugin, createDatabasePluginGetUpdateInfo, createRuntimeStoragePlugin } from "@hot-updater/plugin-core";
1
+ import { DEFAULT_ROLLOUT_COHORT_COUNT, NIL_UUID, getAssetBaseStorageUri, getBundlePatches, getManifestFileHash, getManifestStorageUri, stripBundleArtifactMetadata } from "@hot-updater/core";
2
+ import { calculatePagination, createDatabasePlugin, createRuntimeStoragePlugin, filterCompatibleAppVersions } from "@hot-updater/plugin-core";
3
3
  import { createClient } from "@supabase/supabase-js";
4
- //#region src/supabaseConfig.ts
5
- const resolveSupabaseServiceRoleKey = (config) => {
6
- const key = config.supabaseServiceRoleKey ?? config.supabaseAnonKey;
7
- if (!key) throw new Error("Supabase service role key is required. Set supabaseServiceRoleKey.");
8
- return key;
9
- };
10
- //#endregion
11
- //#region src/supabaseDatabase.ts
4
+ //#region src/supabaseBundleMapper.ts
5
+ const BUNDLE_SELECT_COLUMNS = "id, channel, enabled, platform, should_force_update, file_hash, git_commit_hash, message, fingerprint_hash, target_app_version, storage_uri, metadata, manifest_storage_uri, manifest_file_hash, asset_base_storage_uri, rollout_cohort_count, target_cohorts";
12
6
  const normalizeMetadata = (value) => {
13
7
  if (!value) return {};
14
8
  if (typeof value === "string") try {
@@ -19,27 +13,6 @@ const normalizeMetadata = (value) => {
19
13
  if (typeof value === "object" && !Array.isArray(value)) return value;
20
14
  return {};
21
15
  };
22
- const BUNDLE_SELECT_COLUMNS = "id, channel, enabled, platform, should_force_update, file_hash, git_commit_hash, message, fingerprint_hash, target_app_version, storage_uri, metadata, manifest_storage_uri, manifest_file_hash, asset_base_storage_uri, rollout_cohort_count, target_cohorts";
23
- const createSupabaseError = (error) => {
24
- if (error instanceof Error) return error;
25
- if (error && typeof error === "object") {
26
- const properties = {};
27
- let target = error;
28
- while (target && target !== Object.prototype) {
29
- for (const key of Object.getOwnPropertyNames(target)) properties[key] = error[key];
30
- target = Object.getPrototypeOf(target);
31
- }
32
- return new Error(JSON.stringify({
33
- name: error.constructor.name,
34
- ...properties
35
- }));
36
- }
37
- try {
38
- return new Error(JSON.stringify(error));
39
- } catch {
40
- return new Error(String(error));
41
- }
42
- };
43
16
  const buildBundlePatchId = (bundleId, baseBundleId) => `${bundleId}:${baseBundleId}`;
44
17
  const mapRowToBundle = (row, patchRows = []) => {
45
18
  const rawMetadata = normalizeMetadata(row.metadata);
@@ -103,6 +76,43 @@ const bundleToPatchRows = (bundle) => getBundlePatches(bundle).map((patch, index
103
76
  patch_storage_uri: patch.patchStorageUri,
104
77
  order_index: index
105
78
  }));
79
+ //#endregion
80
+ //#region src/supabaseConfig.ts
81
+ const resolveSupabaseServiceRoleKey = (config) => {
82
+ const key = config.supabaseServiceRoleKey ?? config.supabaseAnonKey;
83
+ if (!key) throw new Error("Supabase service role key is required. Set supabaseServiceRoleKey.");
84
+ return key;
85
+ };
86
+ //#endregion
87
+ //#region src/supabaseDatabase.ts
88
+ const createSupabaseError = (error) => {
89
+ if (error instanceof Error) return error;
90
+ if (error && typeof error === "object") {
91
+ const properties = {};
92
+ let target = error;
93
+ while (target && target !== Object.prototype) {
94
+ for (const key of Object.getOwnPropertyNames(target)) properties[key] = error[key];
95
+ target = Object.getPrototypeOf(target);
96
+ }
97
+ return new Error(JSON.stringify({
98
+ name: error.constructor.name,
99
+ ...properties
100
+ }));
101
+ }
102
+ try {
103
+ return new Error(JSON.stringify(error));
104
+ } catch {
105
+ return new Error(String(error));
106
+ }
107
+ };
108
+ const mapUpdateInfoRow = (row) => ({
109
+ id: row.id,
110
+ shouldForceUpdate: row.should_force_update,
111
+ message: row.message,
112
+ status: row.status,
113
+ storageUri: row.storage_uri,
114
+ fileHash: row.file_hash
115
+ });
106
116
  const supabaseDatabase = createDatabasePlugin({
107
117
  name: "supabaseDatabase",
108
118
  factory: (config) => {
@@ -119,28 +129,42 @@ const supabaseDatabase = createDatabasePlugin({
119
129
  }
120
130
  return patchMap;
121
131
  };
122
- const mapRowsToBundles = async (rows) => {
123
- const patchMap = await fetchPatchMap(rows.map((row) => row.id));
124
- return rows.map((row) => mapRowToBundle(row, patchMap.get(row.id)));
125
- };
126
132
  return {
127
- getUpdateInfo: createDatabasePluginGetUpdateInfo({
128
- async listTargetAppVersions({ platform, channel, minBundleId }) {
129
- const { data, error } = await supabase.from("bundles").select("target_app_version").eq("platform", platform).eq("channel", channel).eq("enabled", true).gte("id", minBundleId).not("target_app_version", "is", null);
133
+ async getUpdateInfo(args) {
134
+ const channel = args.channel ?? "production";
135
+ const minBundleId = args.minBundleId ?? NIL_UUID;
136
+ if (args._updateStrategy === "appVersion") {
137
+ const { data: targetAppVersionRows, error: targetAppVersionError } = await supabase.rpc("get_target_app_version_list", {
138
+ app_platform: args.platform,
139
+ min_bundle_id: minBundleId
140
+ });
141
+ if (targetAppVersionError) throw createSupabaseError(targetAppVersionError);
142
+ const targetAppVersionList = filterCompatibleAppVersions((targetAppVersionRows ?? []).map((row) => row.target_app_version).filter((version) => Boolean(version)), args.appVersion);
143
+ const { data, error } = await supabase.rpc("get_update_info_by_app_version", {
144
+ app_platform: args.platform,
145
+ app_version: args.appVersion,
146
+ bundle_id: args.bundleId,
147
+ min_bundle_id: minBundleId,
148
+ target_channel: channel,
149
+ target_app_version_list: targetAppVersionList,
150
+ cohort: args.cohort ?? null
151
+ });
130
152
  if (error) throw createSupabaseError(error);
131
- return Array.from(new Set((data ?? []).map((row) => row.target_app_version).filter((version) => Boolean(version))));
132
- },
133
- async getBundlesByTargetAppVersions({ platform, channel, minBundleId }, targetAppVersions) {
134
- const { data, error } = await supabase.from("bundles").select(BUNDLE_SELECT_COLUMNS).eq("platform", platform).eq("channel", channel).eq("enabled", true).gte("id", minBundleId).in("target_app_version", targetAppVersions);
135
- if (error) throw createSupabaseError(error);
136
- return mapRowsToBundles(data ?? []);
137
- },
138
- async getBundlesByFingerprint({ platform, channel, minBundleId, fingerprintHash }) {
139
- const { data, error } = await supabase.from("bundles").select(BUNDLE_SELECT_COLUMNS).eq("platform", platform).eq("channel", channel).eq("enabled", true).gte("id", minBundleId).eq("fingerprint_hash", fingerprintHash);
140
- if (error) throw createSupabaseError(error);
141
- return mapRowsToBundles(data ?? []);
153
+ const updateInfo = data?.[0] ?? null;
154
+ return updateInfo ? mapUpdateInfoRow(updateInfo) : null;
142
155
  }
143
- }),
156
+ const { data, error } = await supabase.rpc("get_update_info_by_fingerprint_hash", {
157
+ app_platform: args.platform,
158
+ bundle_id: args.bundleId,
159
+ min_bundle_id: minBundleId,
160
+ target_channel: channel,
161
+ target_fingerprint_hash: args.fingerprintHash,
162
+ cohort: args.cohort ?? null
163
+ });
164
+ if (error) throw createSupabaseError(error);
165
+ const updateInfo = data?.[0] ?? null;
166
+ return updateInfo ? mapUpdateInfoRow(updateInfo) : null;
167
+ },
144
168
  async getBundleById(bundleId) {
145
169
  const [{ data, error }, patchMap] = await Promise.all([supabase.from("bundles").select(BUNDLE_SELECT_COLUMNS).eq("id", bundleId).single(), fetchPatchMap([bundleId])]);
146
170
  if (!data || error) return null;
@@ -2,14 +2,8 @@ require("./index.cjs");
2
2
  let _hot_updater_core = require("@hot-updater/core");
3
3
  let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
4
4
  let _supabase_supabase_js = require("@supabase/supabase-js");
5
- //#region src/supabaseConfig.ts
6
- const resolveSupabaseServiceRoleKey = (config) => {
7
- const key = config.supabaseServiceRoleKey ?? config.supabaseAnonKey;
8
- if (!key) throw new Error("Supabase service role key is required. Set supabaseServiceRoleKey.");
9
- return key;
10
- };
11
- //#endregion
12
- //#region src/supabaseDatabase.ts
5
+ //#region src/supabaseBundleMapper.ts
6
+ const BUNDLE_SELECT_COLUMNS = "id, channel, enabled, platform, should_force_update, file_hash, git_commit_hash, message, fingerprint_hash, target_app_version, storage_uri, metadata, manifest_storage_uri, manifest_file_hash, asset_base_storage_uri, rollout_cohort_count, target_cohorts";
13
7
  const normalizeMetadata = (value) => {
14
8
  if (!value) return {};
15
9
  if (typeof value === "string") try {
@@ -20,27 +14,6 @@ const normalizeMetadata = (value) => {
20
14
  if (typeof value === "object" && !Array.isArray(value)) return value;
21
15
  return {};
22
16
  };
23
- const BUNDLE_SELECT_COLUMNS = "id, channel, enabled, platform, should_force_update, file_hash, git_commit_hash, message, fingerprint_hash, target_app_version, storage_uri, metadata, manifest_storage_uri, manifest_file_hash, asset_base_storage_uri, rollout_cohort_count, target_cohorts";
24
- const createSupabaseError = (error) => {
25
- if (error instanceof Error) return error;
26
- if (error && typeof error === "object") {
27
- const properties = {};
28
- let target = error;
29
- while (target && target !== Object.prototype) {
30
- for (const key of Object.getOwnPropertyNames(target)) properties[key] = error[key];
31
- target = Object.getPrototypeOf(target);
32
- }
33
- return new Error(JSON.stringify({
34
- name: error.constructor.name,
35
- ...properties
36
- }));
37
- }
38
- try {
39
- return new Error(JSON.stringify(error));
40
- } catch {
41
- return new Error(String(error));
42
- }
43
- };
44
17
  const buildBundlePatchId = (bundleId, baseBundleId) => `${bundleId}:${baseBundleId}`;
45
18
  const mapRowToBundle = (row, patchRows = []) => {
46
19
  const rawMetadata = normalizeMetadata(row.metadata);
@@ -104,6 +77,43 @@ const bundleToPatchRows = (bundle) => (0, _hot_updater_core.getBundlePatches)(bu
104
77
  patch_storage_uri: patch.patchStorageUri,
105
78
  order_index: index
106
79
  }));
80
+ //#endregion
81
+ //#region src/supabaseConfig.ts
82
+ const resolveSupabaseServiceRoleKey = (config) => {
83
+ const key = config.supabaseServiceRoleKey ?? config.supabaseAnonKey;
84
+ if (!key) throw new Error("Supabase service role key is required. Set supabaseServiceRoleKey.");
85
+ return key;
86
+ };
87
+ //#endregion
88
+ //#region src/supabaseDatabase.ts
89
+ const createSupabaseError = (error) => {
90
+ if (error instanceof Error) return error;
91
+ if (error && typeof error === "object") {
92
+ const properties = {};
93
+ let target = error;
94
+ while (target && target !== Object.prototype) {
95
+ for (const key of Object.getOwnPropertyNames(target)) properties[key] = error[key];
96
+ target = Object.getPrototypeOf(target);
97
+ }
98
+ return new Error(JSON.stringify({
99
+ name: error.constructor.name,
100
+ ...properties
101
+ }));
102
+ }
103
+ try {
104
+ return new Error(JSON.stringify(error));
105
+ } catch {
106
+ return new Error(String(error));
107
+ }
108
+ };
109
+ const mapUpdateInfoRow = (row) => ({
110
+ id: row.id,
111
+ shouldForceUpdate: row.should_force_update,
112
+ message: row.message,
113
+ status: row.status,
114
+ storageUri: row.storage_uri,
115
+ fileHash: row.file_hash
116
+ });
107
117
  const supabaseDatabase = (0, _hot_updater_plugin_core.createDatabasePlugin)({
108
118
  name: "supabaseDatabase",
109
119
  factory: (config) => {
@@ -120,28 +130,42 @@ const supabaseDatabase = (0, _hot_updater_plugin_core.createDatabasePlugin)({
120
130
  }
121
131
  return patchMap;
122
132
  };
123
- const mapRowsToBundles = async (rows) => {
124
- const patchMap = await fetchPatchMap(rows.map((row) => row.id));
125
- return rows.map((row) => mapRowToBundle(row, patchMap.get(row.id)));
126
- };
127
133
  return {
128
- getUpdateInfo: (0, _hot_updater_plugin_core.createDatabasePluginGetUpdateInfo)({
129
- async listTargetAppVersions({ platform, channel, minBundleId }) {
130
- const { data, error } = await supabase.from("bundles").select("target_app_version").eq("platform", platform).eq("channel", channel).eq("enabled", true).gte("id", minBundleId).not("target_app_version", "is", null);
134
+ async getUpdateInfo(args) {
135
+ const channel = args.channel ?? "production";
136
+ const minBundleId = args.minBundleId ?? _hot_updater_core.NIL_UUID;
137
+ if (args._updateStrategy === "appVersion") {
138
+ const { data: targetAppVersionRows, error: targetAppVersionError } = await supabase.rpc("get_target_app_version_list", {
139
+ app_platform: args.platform,
140
+ min_bundle_id: minBundleId
141
+ });
142
+ if (targetAppVersionError) throw createSupabaseError(targetAppVersionError);
143
+ const targetAppVersionList = (0, _hot_updater_plugin_core.filterCompatibleAppVersions)((targetAppVersionRows ?? []).map((row) => row.target_app_version).filter((version) => Boolean(version)), args.appVersion);
144
+ const { data, error } = await supabase.rpc("get_update_info_by_app_version", {
145
+ app_platform: args.platform,
146
+ app_version: args.appVersion,
147
+ bundle_id: args.bundleId,
148
+ min_bundle_id: minBundleId,
149
+ target_channel: channel,
150
+ target_app_version_list: targetAppVersionList,
151
+ cohort: args.cohort ?? null
152
+ });
131
153
  if (error) throw createSupabaseError(error);
132
- return Array.from(new Set((data ?? []).map((row) => row.target_app_version).filter((version) => Boolean(version))));
133
- },
134
- async getBundlesByTargetAppVersions({ platform, channel, minBundleId }, targetAppVersions) {
135
- const { data, error } = await supabase.from("bundles").select(BUNDLE_SELECT_COLUMNS).eq("platform", platform).eq("channel", channel).eq("enabled", true).gte("id", minBundleId).in("target_app_version", targetAppVersions);
136
- if (error) throw createSupabaseError(error);
137
- return mapRowsToBundles(data ?? []);
138
- },
139
- async getBundlesByFingerprint({ platform, channel, minBundleId, fingerprintHash }) {
140
- const { data, error } = await supabase.from("bundles").select(BUNDLE_SELECT_COLUMNS).eq("platform", platform).eq("channel", channel).eq("enabled", true).gte("id", minBundleId).eq("fingerprint_hash", fingerprintHash);
141
- if (error) throw createSupabaseError(error);
142
- return mapRowsToBundles(data ?? []);
154
+ const updateInfo = data?.[0] ?? null;
155
+ return updateInfo ? mapUpdateInfoRow(updateInfo) : null;
143
156
  }
144
- }),
157
+ const { data, error } = await supabase.rpc("get_update_info_by_fingerprint_hash", {
158
+ app_platform: args.platform,
159
+ bundle_id: args.bundleId,
160
+ min_bundle_id: minBundleId,
161
+ target_channel: channel,
162
+ target_fingerprint_hash: args.fingerprintHash,
163
+ cohort: args.cohort ?? null
164
+ });
165
+ if (error) throw createSupabaseError(error);
166
+ const updateInfo = data?.[0] ?? null;
167
+ return updateInfo ? mapUpdateInfoRow(updateInfo) : null;
168
+ },
145
169
  async getBundleById(bundleId) {
146
170
  const [{ data, error }, patchMap] = await Promise.all([supabase.from("bundles").select(BUNDLE_SELECT_COLUMNS).eq("id", bundleId).single(), fetchPatchMap([bundleId])]);
147
171
  if (!data || error) return null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/supabase",
3
3
  "type": "module",
4
- "version": "0.32.0",
4
+ "version": "0.33.1",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.mjs",
@@ -27,7 +27,10 @@
27
27
  "./package.json": "./package.json"
28
28
  },
29
29
  "license": "MIT",
30
- "repository": "https://github.com/gronxb/hot-updater",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/gronxb/hot-updater"
33
+ },
31
34
  "author": "gronxb <gron1gh1@gmail.com> (https://github.com/gronxb)",
32
35
  "bugs": {
33
36
  "url": "https://github.com/gronxb/hot-updater/issues"
@@ -47,23 +50,23 @@
47
50
  "@supabase/supabase-js": "2.76.1",
48
51
  "hono": "4.12.9",
49
52
  "uuidv7": "^1.0.2",
50
- "@hot-updater/core": "0.32.0",
51
- "@hot-updater/plugin-core": "0.32.0",
52
- "@hot-updater/cli-tools": "0.32.0",
53
- "@hot-updater/server": "0.32.0"
53
+ "@hot-updater/core": "0.33.1",
54
+ "@hot-updater/plugin-core": "0.33.1",
55
+ "@hot-updater/cli-tools": "0.33.1",
56
+ "@hot-updater/server": "0.33.1"
54
57
  },
55
58
  "devDependencies": {
56
- "@electric-sql/pglite": "0.2.17",
59
+ "@electric-sql/pglite": "0.4.1",
57
60
  "camelcase-keys": "^9.1.3",
58
61
  "dayjs": "^1.11.13",
59
62
  "es-toolkit": "^1.32.0",
60
63
  "execa": "9.5.2",
61
64
  "@types/node": "^20",
62
65
  "mime": "^4.0.4",
63
- "@hot-updater/js": "0.32.0",
64
- "@hot-updater/mock": "0.32.0",
65
- "@hot-updater/test-utils": "0.32.0",
66
- "@hot-updater/postgres": "0.32.0"
66
+ "@hot-updater/js": "0.33.1",
67
+ "@hot-updater/postgres": "0.33.1",
68
+ "@hot-updater/test-utils": "0.33.1",
69
+ "@hot-updater/mock": "0.33.1"
67
70
  },
68
71
  "scripts": {
69
72
  "build": "tsdown",