@hot-updater/supabase 0.31.4 → 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/edge.cjs +1 -1
- package/dist/edge.d.cts +1 -1
- package/dist/edge.d.mts +1 -1
- package/dist/edge.mjs +1 -1
- package/dist/iac/index.cjs +33 -14
- package/dist/iac/index.d.cts +2 -1
- package/dist/iac/index.d.mts +2 -1
- package/dist/iac/index.mjs +28 -10
- package/dist/index.cjs +50 -8
- package/dist/index.d.cts +17 -10
- package/dist/index.d.mts +17 -10
- package/dist/index.mjs +48 -6
- package/dist/{supabaseEdgeFunctionStorage-CXmcRBZ2.d.mts → supabaseEdgeFunctionStorage-BRxGvt-r.d.mts} +3 -2
- package/dist/{supabaseEdgeFunctionStorage-BKU_mLzA.mjs → supabaseEdgeFunctionStorage-CO70azPw.mjs} +95 -50
- package/dist/{supabaseEdgeFunctionStorage-hkokSgXP.d.cts → supabaseEdgeFunctionStorage-CU396KO3.d.cts} +3 -2
- package/dist/{supabaseEdgeFunctionStorage-BZC0Z0XP.cjs → supabaseEdgeFunctionStorage-DRxwDPRy.cjs} +98 -47
- package/package.json +9 -9
- package/supabase/edge-functions/index.ts +0 -1
- package/supabase/migrations/20260520014100_hot-updater_rls.sql +48 -0
- package/supabase/migrations.spec.ts +67 -0
package/dist/edge.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_supabaseEdgeFunctionStorage = require("./supabaseEdgeFunctionStorage-
|
|
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.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as supabaseEdgeFunctionDatabase, n as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-
|
|
1
|
+
import { i as supabaseEdgeFunctionDatabase, n as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-CU396KO3.cjs";
|
|
2
2
|
export { supabaseEdgeFunctionDatabase, supabaseEdgeFunctionStorage };
|
package/dist/edge.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as supabaseEdgeFunctionDatabase, n as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-
|
|
1
|
+
import { i as supabaseEdgeFunctionDatabase, n as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-BRxGvt-r.mjs";
|
|
2
2
|
export { supabaseEdgeFunctionDatabase, supabaseEdgeFunctionStorage };
|
package/dist/edge.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as supabaseEdgeFunctionDatabase, t as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-
|
|
1
|
+
import { n as supabaseEdgeFunctionDatabase, t as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-CO70azPw.mjs";
|
|
2
2
|
export { supabaseEdgeFunctionDatabase, supabaseEdgeFunctionStorage };
|
package/dist/iac/index.cjs
CHANGED
|
@@ -6,7 +6,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __commonJSMin = (cb, mod) => () => (mod ||
|
|
9
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
10
10
|
var __copyProps = (to, from, except, desc) => {
|
|
11
11
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
12
|
key = keys[i];
|
|
@@ -23,21 +23,21 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
}) : target, mod));
|
|
24
24
|
//#endregion
|
|
25
25
|
let fs_promises = require("fs/promises");
|
|
26
|
-
fs_promises = __toESM(fs_promises
|
|
26
|
+
fs_promises = __toESM(fs_promises);
|
|
27
27
|
let node_module = require("node:module");
|
|
28
28
|
let path = require("path");
|
|
29
|
-
path = __toESM(path
|
|
29
|
+
path = __toESM(path);
|
|
30
30
|
let _hot_updater_cli_tools = require("@hot-updater/cli-tools");
|
|
31
31
|
let node_url = require("node:url");
|
|
32
32
|
let node_child_process = require("node:child_process");
|
|
33
33
|
let node_string_decoder = require("node:string_decoder");
|
|
34
34
|
let node_util = require("node:util");
|
|
35
35
|
let node_process = require("node:process");
|
|
36
|
-
node_process = __toESM(node_process
|
|
36
|
+
node_process = __toESM(node_process);
|
|
37
37
|
let node_tty = require("node:tty");
|
|
38
|
-
node_tty = __toESM(node_tty
|
|
38
|
+
node_tty = __toESM(node_tty);
|
|
39
39
|
let node_path = require("node:path");
|
|
40
|
-
node_path = __toESM(node_path
|
|
40
|
+
node_path = __toESM(node_path);
|
|
41
41
|
let node_timers_promises = require("node:timers/promises");
|
|
42
42
|
let node_os = require("node:os");
|
|
43
43
|
let node_events = require("node:events");
|
|
@@ -6233,8 +6233,8 @@ createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
|
|
|
6233
6233
|
const { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcExport();
|
|
6234
6234
|
//#endregion
|
|
6235
6235
|
//#region iac/supabaseApi.ts
|
|
6236
|
-
const supabaseApi = (supabaseUrl,
|
|
6237
|
-
const supabase = (0, _supabase_supabase_js.createClient)(supabaseUrl,
|
|
6236
|
+
const supabaseApi = (supabaseUrl, supabaseServiceRoleKey) => {
|
|
6237
|
+
const supabase = (0, _supabase_supabase_js.createClient)(supabaseUrl, supabaseServiceRoleKey);
|
|
6238
6238
|
return {
|
|
6239
6239
|
listBuckets: async () => {
|
|
6240
6240
|
const { data, error } = await supabase.storage.listBuckets();
|
|
@@ -6268,7 +6268,7 @@ const getConfigScaffold = (build) => {
|
|
|
6268
6268
|
}],
|
|
6269
6269
|
configString: `supabaseStorage({
|
|
6270
6270
|
supabaseUrl: process.env.HOT_UPDATER_SUPABASE_URL!,
|
|
6271
|
-
|
|
6271
|
+
supabaseServiceRoleKey: process.env.HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY!,
|
|
6272
6272
|
bucketName: process.env.HOT_UPDATER_SUPABASE_BUCKET_NAME!,
|
|
6273
6273
|
})`
|
|
6274
6274
|
}).setDatabase({
|
|
@@ -6278,10 +6278,27 @@ const getConfigScaffold = (build) => {
|
|
|
6278
6278
|
}],
|
|
6279
6279
|
configString: `supabaseDatabase({
|
|
6280
6280
|
supabaseUrl: process.env.HOT_UPDATER_SUPABASE_URL!,
|
|
6281
|
-
|
|
6281
|
+
supabaseServiceRoleKey: process.env.HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY!,
|
|
6282
6282
|
})`
|
|
6283
6283
|
}));
|
|
6284
6284
|
};
|
|
6285
|
+
const getLegacySupabaseConfigReference = (configText) => {
|
|
6286
|
+
if (configText.includes("HOT_UPDATER_SUPABASE_ANON_KEY")) return "HOT_UPDATER_SUPABASE_ANON_KEY";
|
|
6287
|
+
if (/\bsupabaseAnonKey\s*:/.test(configText)) return "supabaseAnonKey";
|
|
6288
|
+
return null;
|
|
6289
|
+
};
|
|
6290
|
+
const assertSkippedConfigDoesNotUseLegacySupabaseKey = async (configWriteResult) => {
|
|
6291
|
+
if (configWriteResult.status !== "skipped") return;
|
|
6292
|
+
const configText = await fs_promises.default.readFile(configWriteResult.path, "utf-8").catch((error) => {
|
|
6293
|
+
if (error.code === "ENOENT") return null;
|
|
6294
|
+
throw error;
|
|
6295
|
+
});
|
|
6296
|
+
const legacyReference = configText === null ? null : getLegacySupabaseConfigReference(configText);
|
|
6297
|
+
if (!legacyReference) return;
|
|
6298
|
+
_hot_updater_cli_tools.p.log.error(`Existing '${configWriteResult.path}' still references '${legacyReference}'.`);
|
|
6299
|
+
_hot_updater_cli_tools.p.log.message("Update it to use 'supabaseServiceRoleKey' with 'HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY', then run init again.");
|
|
6300
|
+
process.exit(1);
|
|
6301
|
+
};
|
|
6285
6302
|
const SOURCE_TEMPLATE = `// add this to your App.tsx
|
|
6286
6303
|
import { HotUpdater } from "@hot-updater/react-native";
|
|
6287
6304
|
|
|
@@ -6642,9 +6659,9 @@ const runInit = async ({ build }) => {
|
|
|
6642
6659
|
process.exit(1);
|
|
6643
6660
|
}
|
|
6644
6661
|
spinner.stop();
|
|
6645
|
-
const
|
|
6646
|
-
if (!
|
|
6647
|
-
const bucket = await selectBucket(supabaseApi(`https://${project.id}.supabase.co`,
|
|
6662
|
+
const serviceRoleApiKey = apiKeys.find((key) => key.name === "service_role");
|
|
6663
|
+
if (!serviceRoleApiKey) throw new Error("Service role key not found, is your project paused?");
|
|
6664
|
+
const bucket = await selectBucket(supabaseApi(`https://${project.id}.supabase.co`, serviceRoleApiKey.api_key));
|
|
6648
6665
|
const { tmpDir, removeTmpDir } = await (0, _hot_updater_cli_tools.copyDirToTmp)(path.default.dirname(path.default.resolve(require$1.resolve("@hot-updater/supabase/scaffold"))), "supabase");
|
|
6649
6666
|
const migrationPath = await path.default.join(tmpDir, "supabase", "migrations");
|
|
6650
6667
|
const migrationFiles = await fs_promises.default.readdir(migrationPath);
|
|
@@ -6663,8 +6680,9 @@ const runInit = async ({ build }) => {
|
|
|
6663
6680
|
await deployEdgeFunction(tmpDir, project.id);
|
|
6664
6681
|
await removeTmpDir();
|
|
6665
6682
|
const configWriteResult = await (0, _hot_updater_cli_tools.writeHotUpdaterConfig)(getConfigScaffold(build));
|
|
6683
|
+
await assertSkippedConfigDoesNotUseLegacySupabaseKey(configWriteResult);
|
|
6666
6684
|
await (0, _hot_updater_cli_tools.makeEnv)({
|
|
6667
|
-
|
|
6685
|
+
HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY: serviceRoleApiKey.api_key,
|
|
6668
6686
|
HOT_UPDATER_SUPABASE_BUCKET_NAME: bucket.name,
|
|
6669
6687
|
HOT_UPDATER_SUPABASE_URL: `https://${project.id}.supabase.co`
|
|
6670
6688
|
});
|
|
@@ -6677,6 +6695,7 @@ const runInit = async ({ build }) => {
|
|
|
6677
6695
|
_hot_updater_cli_tools.p.log.success("Done! 🎉");
|
|
6678
6696
|
};
|
|
6679
6697
|
//#endregion
|
|
6698
|
+
exports.getLegacySupabaseConfigReference = getLegacySupabaseConfigReference;
|
|
6680
6699
|
exports.resolveEdgeFunctionDenoConfig = resolveEdgeFunctionDenoConfig;
|
|
6681
6700
|
exports.runInit = runInit;
|
|
6682
6701
|
exports.selectBucket = selectBucket;
|
package/dist/iac/index.d.cts
CHANGED
|
@@ -16,6 +16,7 @@ interface SupabaseApi {
|
|
|
16
16
|
}
|
|
17
17
|
//#endregion
|
|
18
18
|
//#region iac/index.d.ts
|
|
19
|
+
declare const getLegacySupabaseConfigReference: (configText: string) => "HOT_UPDATER_SUPABASE_ANON_KEY" | "supabaseAnonKey" | null;
|
|
19
20
|
declare const resolveEdgeFunctionDenoConfig: (targetDir: string) => Promise<{
|
|
20
21
|
imports: Record<string, string>;
|
|
21
22
|
}>;
|
|
@@ -34,4 +35,4 @@ declare const runInit: ({
|
|
|
34
35
|
build: BuildType;
|
|
35
36
|
}) => Promise<void>;
|
|
36
37
|
//#endregion
|
|
37
|
-
export { resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject };
|
|
38
|
+
export { getLegacySupabaseConfigReference, resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject };
|
package/dist/iac/index.d.mts
CHANGED
|
@@ -16,6 +16,7 @@ interface SupabaseApi {
|
|
|
16
16
|
}
|
|
17
17
|
//#endregion
|
|
18
18
|
//#region iac/index.d.ts
|
|
19
|
+
declare const getLegacySupabaseConfigReference: (configText: string) => "HOT_UPDATER_SUPABASE_ANON_KEY" | "supabaseAnonKey" | null;
|
|
19
20
|
declare const resolveEdgeFunctionDenoConfig: (targetDir: string) => Promise<{
|
|
20
21
|
imports: Record<string, string>;
|
|
21
22
|
}>;
|
|
@@ -34,4 +35,4 @@ declare const runInit: ({
|
|
|
34
35
|
build: BuildType;
|
|
35
36
|
}) => Promise<void>;
|
|
36
37
|
//#endregion
|
|
37
|
-
export { resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject };
|
|
38
|
+
export { getLegacySupabaseConfigReference, resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject };
|
package/dist/iac/index.mjs
CHANGED
|
@@ -25,7 +25,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
25
25
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
26
26
|
var __getProtoOf = Object.getPrototypeOf;
|
|
27
27
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
28
|
-
var __commonJSMin = (cb, mod) => () => (mod ||
|
|
28
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
29
29
|
var __copyProps = (to, from, except, desc) => {
|
|
30
30
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
31
31
|
key = keys[i];
|
|
@@ -6228,8 +6228,8 @@ createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
|
|
|
6228
6228
|
const { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcExport();
|
|
6229
6229
|
//#endregion
|
|
6230
6230
|
//#region iac/supabaseApi.ts
|
|
6231
|
-
const supabaseApi = (supabaseUrl,
|
|
6232
|
-
const supabase = createClient(supabaseUrl,
|
|
6231
|
+
const supabaseApi = (supabaseUrl, supabaseServiceRoleKey) => {
|
|
6232
|
+
const supabase = createClient(supabaseUrl, supabaseServiceRoleKey);
|
|
6233
6233
|
return {
|
|
6234
6234
|
listBuckets: async () => {
|
|
6235
6235
|
const { data, error } = await supabase.storage.listBuckets();
|
|
@@ -6263,7 +6263,7 @@ const getConfigScaffold = (build) => {
|
|
|
6263
6263
|
}],
|
|
6264
6264
|
configString: `supabaseStorage({
|
|
6265
6265
|
supabaseUrl: process.env.HOT_UPDATER_SUPABASE_URL!,
|
|
6266
|
-
|
|
6266
|
+
supabaseServiceRoleKey: process.env.HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY!,
|
|
6267
6267
|
bucketName: process.env.HOT_UPDATER_SUPABASE_BUCKET_NAME!,
|
|
6268
6268
|
})`
|
|
6269
6269
|
}).setDatabase({
|
|
@@ -6273,10 +6273,27 @@ const getConfigScaffold = (build) => {
|
|
|
6273
6273
|
}],
|
|
6274
6274
|
configString: `supabaseDatabase({
|
|
6275
6275
|
supabaseUrl: process.env.HOT_UPDATER_SUPABASE_URL!,
|
|
6276
|
-
|
|
6276
|
+
supabaseServiceRoleKey: process.env.HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY!,
|
|
6277
6277
|
})`
|
|
6278
6278
|
}));
|
|
6279
6279
|
};
|
|
6280
|
+
const getLegacySupabaseConfigReference = (configText) => {
|
|
6281
|
+
if (configText.includes("HOT_UPDATER_SUPABASE_ANON_KEY")) return "HOT_UPDATER_SUPABASE_ANON_KEY";
|
|
6282
|
+
if (/\bsupabaseAnonKey\s*:/.test(configText)) return "supabaseAnonKey";
|
|
6283
|
+
return null;
|
|
6284
|
+
};
|
|
6285
|
+
const assertSkippedConfigDoesNotUseLegacySupabaseKey = async (configWriteResult) => {
|
|
6286
|
+
if (configWriteResult.status !== "skipped") return;
|
|
6287
|
+
const configText = await fs.readFile(configWriteResult.path, "utf-8").catch((error) => {
|
|
6288
|
+
if (error.code === "ENOENT") return null;
|
|
6289
|
+
throw error;
|
|
6290
|
+
});
|
|
6291
|
+
const legacyReference = configText === null ? null : getLegacySupabaseConfigReference(configText);
|
|
6292
|
+
if (!legacyReference) return;
|
|
6293
|
+
p.log.error(`Existing '${configWriteResult.path}' still references '${legacyReference}'.`);
|
|
6294
|
+
p.log.message("Update it to use 'supabaseServiceRoleKey' with 'HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY', then run init again.");
|
|
6295
|
+
process.exit(1);
|
|
6296
|
+
};
|
|
6280
6297
|
const SOURCE_TEMPLATE = `// add this to your App.tsx
|
|
6281
6298
|
import { HotUpdater } from "@hot-updater/react-native";
|
|
6282
6299
|
|
|
@@ -6637,9 +6654,9 @@ const runInit = async ({ build }) => {
|
|
|
6637
6654
|
process.exit(1);
|
|
6638
6655
|
}
|
|
6639
6656
|
spinner.stop();
|
|
6640
|
-
const
|
|
6641
|
-
if (!
|
|
6642
|
-
const bucket = await selectBucket(supabaseApi(`https://${project.id}.supabase.co`,
|
|
6657
|
+
const serviceRoleApiKey = apiKeys.find((key) => key.name === "service_role");
|
|
6658
|
+
if (!serviceRoleApiKey) throw new Error("Service role key not found, is your project paused?");
|
|
6659
|
+
const bucket = await selectBucket(supabaseApi(`https://${project.id}.supabase.co`, serviceRoleApiKey.api_key));
|
|
6643
6660
|
const { tmpDir, removeTmpDir } = await copyDirToTmp(path.dirname(path.resolve(require$1.resolve("@hot-updater/supabase/scaffold"))), "supabase");
|
|
6644
6661
|
const migrationPath = await path.join(tmpDir, "supabase", "migrations");
|
|
6645
6662
|
const migrationFiles = await fs.readdir(migrationPath);
|
|
@@ -6658,8 +6675,9 @@ const runInit = async ({ build }) => {
|
|
|
6658
6675
|
await deployEdgeFunction(tmpDir, project.id);
|
|
6659
6676
|
await removeTmpDir();
|
|
6660
6677
|
const configWriteResult = await writeHotUpdaterConfig(getConfigScaffold(build));
|
|
6678
|
+
await assertSkippedConfigDoesNotUseLegacySupabaseKey(configWriteResult);
|
|
6661
6679
|
await makeEnv({
|
|
6662
|
-
|
|
6680
|
+
HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY: serviceRoleApiKey.api_key,
|
|
6663
6681
|
HOT_UPDATER_SUPABASE_BUCKET_NAME: bucket.name,
|
|
6664
6682
|
HOT_UPDATER_SUPABASE_URL: `https://${project.id}.supabase.co`
|
|
6665
6683
|
});
|
|
@@ -6672,4 +6690,4 @@ const runInit = async ({ build }) => {
|
|
|
6672
6690
|
p.log.success("Done! 🎉");
|
|
6673
6691
|
};
|
|
6674
6692
|
//#endregion
|
|
6675
|
-
export { resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject };
|
|
6693
|
+
export { getLegacySupabaseConfigReference, resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject };
|
package/dist/index.cjs
CHANGED
|
@@ -21,19 +21,44 @@ 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-
|
|
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");
|
|
28
|
-
fs_promises = __toESM(fs_promises
|
|
28
|
+
fs_promises = __toESM(fs_promises);
|
|
29
29
|
let path = require("path");
|
|
30
|
-
path = __toESM(path
|
|
30
|
+
path = __toESM(path);
|
|
31
31
|
//#region src/supabaseStorage.ts
|
|
32
|
+
function getErrorMessage(error) {
|
|
33
|
+
if (error instanceof Error) return error.message;
|
|
34
|
+
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") return error.message;
|
|
35
|
+
return String(error);
|
|
36
|
+
}
|
|
37
|
+
async function createSignedUrlOrThrow({ bucket, key, expiresIn }) {
|
|
38
|
+
let data = null;
|
|
39
|
+
let error = null;
|
|
40
|
+
try {
|
|
41
|
+
const response = await bucket.createSignedUrl(key, expiresIn);
|
|
42
|
+
data = response.data;
|
|
43
|
+
error = response.error;
|
|
44
|
+
} catch (thrownError) {
|
|
45
|
+
error = thrownError;
|
|
46
|
+
}
|
|
47
|
+
if (!error && data?.signedUrl) return data.signedUrl;
|
|
48
|
+
throw new Error(`Failed to generate download URL for "${key}": ${getErrorMessage(error ?? /* @__PURE__ */ new Error("missing signed URL"))}`);
|
|
49
|
+
}
|
|
50
|
+
async function verifyObjectCanBeSignedForRuntime({ bucket, key }) {
|
|
51
|
+
await createSignedUrlOrThrow({
|
|
52
|
+
bucket,
|
|
53
|
+
key,
|
|
54
|
+
expiresIn: 3600
|
|
55
|
+
});
|
|
56
|
+
}
|
|
32
57
|
const supabaseStorage = (0, _hot_updater_plugin_core.createUniversalStoragePlugin)({
|
|
33
58
|
name: "supabaseStorage",
|
|
34
59
|
supportedProtocol: "supabase-storage",
|
|
35
60
|
factory: (config) => {
|
|
36
|
-
const bucket = (0, _supabase_supabase_js.createClient)(config.supabaseUrl, config
|
|
61
|
+
const bucket = (0, _supabase_supabase_js.createClient)(config.supabaseUrl, require_supabaseEdgeFunctionStorage.resolveSupabaseServiceRoleKey(config)).storage.from(config.bucketName);
|
|
37
62
|
const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(config.basePath);
|
|
38
63
|
return {
|
|
39
64
|
node: {
|
|
@@ -56,8 +81,24 @@ const supabaseStorage = (0, _hot_updater_plugin_core.createUniversalStoragePlugi
|
|
|
56
81
|
headers: {}
|
|
57
82
|
});
|
|
58
83
|
if (upload.error) throw upload.error;
|
|
84
|
+
await verifyObjectCanBeSignedForRuntime({
|
|
85
|
+
bucket,
|
|
86
|
+
key: Key
|
|
87
|
+
});
|
|
59
88
|
return { storageUri: `supabase-storage://${upload.data.fullPath}` };
|
|
60
89
|
},
|
|
90
|
+
async exists(storageUri) {
|
|
91
|
+
const { key, bucket: bucketName } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "supabase-storage");
|
|
92
|
+
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
93
|
+
const { data, error } = await bucket.exists(key);
|
|
94
|
+
if (data === false) return false;
|
|
95
|
+
if (error) throw error;
|
|
96
|
+
await verifyObjectCanBeSignedForRuntime({
|
|
97
|
+
bucket,
|
|
98
|
+
key
|
|
99
|
+
});
|
|
100
|
+
return data;
|
|
101
|
+
},
|
|
61
102
|
async downloadFile(storageUri, filePath) {
|
|
62
103
|
const { key, bucket: bucketName } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "supabase-storage");
|
|
63
104
|
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
@@ -86,10 +127,11 @@ const supabaseStorage = (0, _hot_updater_plugin_core.createUniversalStoragePlugi
|
|
|
86
127
|
let key = `${u.host}${u.pathname}`.replace(/^\//, "");
|
|
87
128
|
if (!key) throw new Error("Invalid Supabase storage URI: missing key");
|
|
88
129
|
if (key.startsWith(`${config.bucketName}/`)) key = key.substring(`${config.bucketName}/`.length);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
130
|
+
return { fileUrl: await createSignedUrlOrThrow({
|
|
131
|
+
bucket,
|
|
132
|
+
key,
|
|
133
|
+
expiresIn: 3600
|
|
134
|
+
}) };
|
|
93
135
|
}
|
|
94
136
|
}
|
|
95
137
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
|
-
import { i as supabaseEdgeFunctionDatabase, n as supabaseEdgeFunctionStorage, r as SupabaseEdgeFunctionDatabaseConfig, t as SupabaseEdgeFunctionStorageConfig } from "./supabaseEdgeFunctionStorage-
|
|
1
|
+
import { i as supabaseEdgeFunctionDatabase, n as supabaseEdgeFunctionStorage, r as SupabaseEdgeFunctionDatabaseConfig, t as SupabaseEdgeFunctionStorageConfig } from "./supabaseEdgeFunctionStorage-CU396KO3.cjs";
|
|
2
|
+
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
2
3
|
|
|
3
|
-
//#region src/
|
|
4
|
-
|
|
4
|
+
//#region src/supabaseConfig.d.ts
|
|
5
|
+
type SupabaseServiceRoleConfig = {
|
|
6
|
+
supabaseUrl: string;
|
|
7
|
+
supabaseServiceRoleKey: string;
|
|
8
|
+
supabaseAnonKey?: string;
|
|
9
|
+
} | {
|
|
5
10
|
supabaseUrl: string;
|
|
6
11
|
supabaseAnonKey: string;
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
supabaseServiceRoleKey?: string;
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/supabaseDatabase.d.ts
|
|
16
|
+
type SupabaseDatabaseConfig = SupabaseServiceRoleConfig;
|
|
17
|
+
declare const supabaseDatabase: (config: SupabaseServiceRoleConfig, hooks?: _$_hot_updater_plugin_core0.DatabasePluginHooks) => () => _$_hot_updater_plugin_core0.DatabasePlugin<unknown>;
|
|
9
18
|
//#endregion
|
|
10
19
|
//#region src/supabaseStorage.d.ts
|
|
11
|
-
|
|
12
|
-
supabaseUrl: string;
|
|
13
|
-
supabaseAnonKey: string;
|
|
20
|
+
type SupabaseStorageConfig = SupabaseServiceRoleConfig & {
|
|
14
21
|
bucketName: string;
|
|
15
22
|
/**
|
|
16
23
|
* Base path where bundles will be stored in the bucket
|
|
17
24
|
*/
|
|
18
25
|
basePath?: string;
|
|
19
|
-
}
|
|
20
|
-
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?:
|
|
26
|
+
};
|
|
27
|
+
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
|
|
21
28
|
//#endregion
|
|
22
29
|
export { SupabaseDatabaseConfig, SupabaseEdgeFunctionDatabaseConfig, SupabaseEdgeFunctionStorageConfig, SupabaseStorageConfig, supabaseDatabase, supabaseEdgeFunctionDatabase, supabaseEdgeFunctionStorage, supabaseStorage };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
|
-
import { i as supabaseEdgeFunctionDatabase, n as supabaseEdgeFunctionStorage, r as SupabaseEdgeFunctionDatabaseConfig, t as SupabaseEdgeFunctionStorageConfig } from "./supabaseEdgeFunctionStorage-
|
|
1
|
+
import { i as supabaseEdgeFunctionDatabase, n as supabaseEdgeFunctionStorage, r as SupabaseEdgeFunctionDatabaseConfig, t as SupabaseEdgeFunctionStorageConfig } from "./supabaseEdgeFunctionStorage-BRxGvt-r.mjs";
|
|
2
|
+
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
2
3
|
|
|
3
|
-
//#region src/
|
|
4
|
-
|
|
4
|
+
//#region src/supabaseConfig.d.ts
|
|
5
|
+
type SupabaseServiceRoleConfig = {
|
|
6
|
+
supabaseUrl: string;
|
|
7
|
+
supabaseServiceRoleKey: string;
|
|
8
|
+
supabaseAnonKey?: string;
|
|
9
|
+
} | {
|
|
5
10
|
supabaseUrl: string;
|
|
6
11
|
supabaseAnonKey: string;
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
supabaseServiceRoleKey?: string;
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/supabaseDatabase.d.ts
|
|
16
|
+
type SupabaseDatabaseConfig = SupabaseServiceRoleConfig;
|
|
17
|
+
declare const supabaseDatabase: (config: SupabaseServiceRoleConfig, hooks?: _$_hot_updater_plugin_core0.DatabasePluginHooks) => () => _$_hot_updater_plugin_core0.DatabasePlugin<unknown>;
|
|
9
18
|
//#endregion
|
|
10
19
|
//#region src/supabaseStorage.d.ts
|
|
11
|
-
|
|
12
|
-
supabaseUrl: string;
|
|
13
|
-
supabaseAnonKey: string;
|
|
20
|
+
type SupabaseStorageConfig = SupabaseServiceRoleConfig & {
|
|
14
21
|
bucketName: string;
|
|
15
22
|
/**
|
|
16
23
|
* Base path where bundles will be stored in the bucket
|
|
17
24
|
*/
|
|
18
25
|
basePath?: string;
|
|
19
|
-
}
|
|
20
|
-
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?:
|
|
26
|
+
};
|
|
27
|
+
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
|
|
21
28
|
//#endregion
|
|
22
29
|
export { SupabaseDatabaseConfig, SupabaseEdgeFunctionDatabaseConfig, SupabaseEdgeFunctionStorageConfig, SupabaseStorageConfig, supabaseDatabase, supabaseEdgeFunctionDatabase, supabaseEdgeFunctionStorage, supabaseStorage };
|
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,39 @@
|
|
|
1
|
-
import { n as supabaseEdgeFunctionDatabase, r as supabaseDatabase, t as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-
|
|
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";
|
|
5
5
|
import path from "path";
|
|
6
6
|
//#region src/supabaseStorage.ts
|
|
7
|
+
function getErrorMessage(error) {
|
|
8
|
+
if (error instanceof Error) return error.message;
|
|
9
|
+
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") return error.message;
|
|
10
|
+
return String(error);
|
|
11
|
+
}
|
|
12
|
+
async function createSignedUrlOrThrow({ bucket, key, expiresIn }) {
|
|
13
|
+
let data = null;
|
|
14
|
+
let error = null;
|
|
15
|
+
try {
|
|
16
|
+
const response = await bucket.createSignedUrl(key, expiresIn);
|
|
17
|
+
data = response.data;
|
|
18
|
+
error = response.error;
|
|
19
|
+
} catch (thrownError) {
|
|
20
|
+
error = thrownError;
|
|
21
|
+
}
|
|
22
|
+
if (!error && data?.signedUrl) return data.signedUrl;
|
|
23
|
+
throw new Error(`Failed to generate download URL for "${key}": ${getErrorMessage(error ?? /* @__PURE__ */ new Error("missing signed URL"))}`);
|
|
24
|
+
}
|
|
25
|
+
async function verifyObjectCanBeSignedForRuntime({ bucket, key }) {
|
|
26
|
+
await createSignedUrlOrThrow({
|
|
27
|
+
bucket,
|
|
28
|
+
key,
|
|
29
|
+
expiresIn: 3600
|
|
30
|
+
});
|
|
31
|
+
}
|
|
7
32
|
const supabaseStorage = createUniversalStoragePlugin({
|
|
8
33
|
name: "supabaseStorage",
|
|
9
34
|
supportedProtocol: "supabase-storage",
|
|
10
35
|
factory: (config) => {
|
|
11
|
-
const bucket = createClient(config.supabaseUrl, config
|
|
36
|
+
const bucket = createClient(config.supabaseUrl, resolveSupabaseServiceRoleKey(config)).storage.from(config.bucketName);
|
|
12
37
|
const getStorageKey = createStorageKeyBuilder(config.basePath);
|
|
13
38
|
return {
|
|
14
39
|
node: {
|
|
@@ -31,8 +56,24 @@ const supabaseStorage = createUniversalStoragePlugin({
|
|
|
31
56
|
headers: {}
|
|
32
57
|
});
|
|
33
58
|
if (upload.error) throw upload.error;
|
|
59
|
+
await verifyObjectCanBeSignedForRuntime({
|
|
60
|
+
bucket,
|
|
61
|
+
key: Key
|
|
62
|
+
});
|
|
34
63
|
return { storageUri: `supabase-storage://${upload.data.fullPath}` };
|
|
35
64
|
},
|
|
65
|
+
async exists(storageUri) {
|
|
66
|
+
const { key, bucket: bucketName } = parseStorageUri(storageUri, "supabase-storage");
|
|
67
|
+
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
68
|
+
const { data, error } = await bucket.exists(key);
|
|
69
|
+
if (data === false) return false;
|
|
70
|
+
if (error) throw error;
|
|
71
|
+
await verifyObjectCanBeSignedForRuntime({
|
|
72
|
+
bucket,
|
|
73
|
+
key
|
|
74
|
+
});
|
|
75
|
+
return data;
|
|
76
|
+
},
|
|
36
77
|
async downloadFile(storageUri, filePath) {
|
|
37
78
|
const { key, bucket: bucketName } = parseStorageUri(storageUri, "supabase-storage");
|
|
38
79
|
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
@@ -61,10 +102,11 @@ const supabaseStorage = createUniversalStoragePlugin({
|
|
|
61
102
|
let key = `${u.host}${u.pathname}`.replace(/^\//, "");
|
|
62
103
|
if (!key) throw new Error("Invalid Supabase storage URI: missing key");
|
|
63
104
|
if (key.startsWith(`${config.bucketName}/`)) key = key.substring(`${config.bucketName}/`.length);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
105
|
+
return { fileUrl: await createSignedUrlOrThrow({
|
|
106
|
+
bucket,
|
|
107
|
+
key,
|
|
108
|
+
expiresIn: 3600
|
|
109
|
+
}) };
|
|
68
110
|
}
|
|
69
111
|
}
|
|
70
112
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
1
2
|
import { DatabasePluginHooks } from "@hot-updater/plugin-core";
|
|
2
3
|
|
|
3
4
|
//#region src/supabaseEdgeFunctionDatabase.d.ts
|
|
@@ -5,7 +6,7 @@ interface SupabaseEdgeFunctionDatabaseConfig {
|
|
|
5
6
|
supabaseUrl: string;
|
|
6
7
|
supabaseServiceRoleKey: string;
|
|
7
8
|
}
|
|
8
|
-
declare const supabaseEdgeFunctionDatabase: (config: SupabaseEdgeFunctionDatabaseConfig, hooks?: DatabasePluginHooks) => () =>
|
|
9
|
+
declare const supabaseEdgeFunctionDatabase: (config: SupabaseEdgeFunctionDatabaseConfig, hooks?: DatabasePluginHooks) => () => _$_hot_updater_plugin_core0.DatabasePlugin<unknown>;
|
|
9
10
|
//#endregion
|
|
10
11
|
//#region src/supabaseEdgeFunctionStorage.d.ts
|
|
11
12
|
interface SupabaseEdgeFunctionStorageConfig {
|
|
@@ -13,6 +14,6 @@ interface SupabaseEdgeFunctionStorageConfig {
|
|
|
13
14
|
supabaseServiceRoleKey: string;
|
|
14
15
|
signedUrlExpiresIn?: number;
|
|
15
16
|
}
|
|
16
|
-
declare const supabaseEdgeFunctionStorage: (config: SupabaseEdgeFunctionStorageConfig, hooks?:
|
|
17
|
+
declare const supabaseEdgeFunctionStorage: (config: SupabaseEdgeFunctionStorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.RuntimeStoragePlugin<unknown>;
|
|
17
18
|
//#endregion
|
|
18
19
|
export { supabaseEdgeFunctionDatabase as i, supabaseEdgeFunctionStorage as n, SupabaseEdgeFunctionDatabaseConfig as r, SupabaseEdgeFunctionStorageConfig as t };
|
package/dist/{supabaseEdgeFunctionStorage-BKU_mLzA.mjs → supabaseEdgeFunctionStorage-CO70azPw.mjs}
RENAMED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { DEFAULT_ROLLOUT_COHORT_COUNT, getAssetBaseStorageUri, getBundlePatches, getManifestFileHash, getManifestStorageUri, stripBundleArtifactMetadata } from "@hot-updater/core";
|
|
2
|
-
import { calculatePagination, createDatabasePlugin,
|
|
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/
|
|
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";
|
|
5
6
|
const normalizeMetadata = (value) => {
|
|
6
7
|
if (!value) return {};
|
|
7
8
|
if (typeof value === "string") try {
|
|
@@ -12,27 +13,6 @@ const normalizeMetadata = (value) => {
|
|
|
12
13
|
if (typeof value === "object" && !Array.isArray(value)) return value;
|
|
13
14
|
return {};
|
|
14
15
|
};
|
|
15
|
-
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";
|
|
16
|
-
const createSupabaseError = (error) => {
|
|
17
|
-
if (error instanceof Error) return error;
|
|
18
|
-
if (error && typeof error === "object") {
|
|
19
|
-
const properties = {};
|
|
20
|
-
let target = error;
|
|
21
|
-
while (target && target !== Object.prototype) {
|
|
22
|
-
for (const key of Object.getOwnPropertyNames(target)) properties[key] = error[key];
|
|
23
|
-
target = Object.getPrototypeOf(target);
|
|
24
|
-
}
|
|
25
|
-
return new Error(JSON.stringify({
|
|
26
|
-
name: error.constructor.name,
|
|
27
|
-
...properties
|
|
28
|
-
}));
|
|
29
|
-
}
|
|
30
|
-
try {
|
|
31
|
-
return new Error(JSON.stringify(error));
|
|
32
|
-
} catch {
|
|
33
|
-
return new Error(String(error));
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
16
|
const buildBundlePatchId = (bundleId, baseBundleId) => `${bundleId}:${baseBundleId}`;
|
|
37
17
|
const mapRowToBundle = (row, patchRows = []) => {
|
|
38
18
|
const rawMetadata = normalizeMetadata(row.metadata);
|
|
@@ -96,10 +76,47 @@ const bundleToPatchRows = (bundle) => getBundlePatches(bundle).map((patch, index
|
|
|
96
76
|
patch_storage_uri: patch.patchStorageUri,
|
|
97
77
|
order_index: index
|
|
98
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
|
+
});
|
|
99
116
|
const supabaseDatabase = createDatabasePlugin({
|
|
100
117
|
name: "supabaseDatabase",
|
|
101
118
|
factory: (config) => {
|
|
102
|
-
const supabase = createClient(config.supabaseUrl, config
|
|
119
|
+
const supabase = createClient(config.supabaseUrl, resolveSupabaseServiceRoleKey(config));
|
|
103
120
|
const fetchPatchMap = async (bundleIds) => {
|
|
104
121
|
const patchMap = /* @__PURE__ */ new Map();
|
|
105
122
|
if (bundleIds.length === 0) return patchMap;
|
|
@@ -112,28 +129,42 @@ const supabaseDatabase = createDatabasePlugin({
|
|
|
112
129
|
}
|
|
113
130
|
return patchMap;
|
|
114
131
|
};
|
|
115
|
-
const mapRowsToBundles = async (rows) => {
|
|
116
|
-
const patchMap = await fetchPatchMap(rows.map((row) => row.id));
|
|
117
|
-
return rows.map((row) => mapRowToBundle(row, patchMap.get(row.id)));
|
|
118
|
-
};
|
|
119
132
|
return {
|
|
120
|
-
getUpdateInfo
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
+
});
|
|
133
152
|
if (error) throw createSupabaseError(error);
|
|
134
|
-
|
|
153
|
+
const updateInfo = data?.[0] ?? null;
|
|
154
|
+
return updateInfo ? mapUpdateInfoRow(updateInfo) : null;
|
|
135
155
|
}
|
|
136
|
-
|
|
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
|
+
},
|
|
137
168
|
async getBundleById(bundleId) {
|
|
138
169
|
const [{ data, error }, patchMap] = await Promise.all([supabase.from("bundles").select(BUNDLE_SELECT_COLUMNS).eq("id", bundleId).single(), fetchPatchMap([bundleId])]);
|
|
139
170
|
if (!data || error) return null;
|
|
@@ -228,11 +259,16 @@ const supabaseDatabase = createDatabasePlugin({
|
|
|
228
259
|
const supabaseEdgeFunctionDatabase = (config, hooks) => {
|
|
229
260
|
return supabaseDatabase({
|
|
230
261
|
supabaseUrl: config.supabaseUrl,
|
|
231
|
-
|
|
262
|
+
supabaseServiceRoleKey: config.supabaseServiceRoleKey
|
|
232
263
|
}, hooks);
|
|
233
264
|
};
|
|
234
265
|
//#endregion
|
|
235
266
|
//#region src/supabaseEdgeFunctionStorage.ts
|
|
267
|
+
function getErrorMessage(error) {
|
|
268
|
+
if (error instanceof Error) return error.message;
|
|
269
|
+
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") return error.message;
|
|
270
|
+
return String(error);
|
|
271
|
+
}
|
|
236
272
|
const parseSupabaseStorageUri = (storageUri) => {
|
|
237
273
|
const storageUrl = new URL(storageUri);
|
|
238
274
|
if (storageUrl.protocol !== "supabase-storage:") throw new Error("Invalid Supabase storage URI protocol");
|
|
@@ -262,13 +298,22 @@ const supabaseEdgeFunctionStorage = createRuntimeStoragePlugin({
|
|
|
262
298
|
},
|
|
263
299
|
async getDownloadUrl(storageUri) {
|
|
264
300
|
const { bucketName, key } = parseSupabaseStorageUri(storageUri);
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
301
|
+
const bucket = supabase.storage.from(bucketName);
|
|
302
|
+
const expiresIn = config.signedUrlExpiresIn ?? 3600;
|
|
303
|
+
let data = null;
|
|
304
|
+
let error = null;
|
|
305
|
+
try {
|
|
306
|
+
const response = await bucket.createSignedUrl(key, expiresIn);
|
|
307
|
+
data = response.data;
|
|
308
|
+
error = response.error;
|
|
309
|
+
} catch (thrownError) {
|
|
310
|
+
error = thrownError;
|
|
311
|
+
}
|
|
312
|
+
if (!error && data?.signedUrl) return { fileUrl: data.signedUrl };
|
|
313
|
+
throw new Error(`Failed to generate download URL for "${bucketName}/${key}": ${getErrorMessage(error ?? /* @__PURE__ */ new Error("missing signed URL"))}`);
|
|
269
314
|
}
|
|
270
315
|
};
|
|
271
316
|
}
|
|
272
317
|
});
|
|
273
318
|
//#endregion
|
|
274
|
-
export { supabaseEdgeFunctionDatabase as n, supabaseDatabase as r, supabaseEdgeFunctionStorage as t };
|
|
319
|
+
export { resolveSupabaseServiceRoleKey as i, supabaseEdgeFunctionDatabase as n, supabaseDatabase as r, supabaseEdgeFunctionStorage as t };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
1
2
|
import { DatabasePluginHooks } from "@hot-updater/plugin-core";
|
|
2
3
|
|
|
3
4
|
//#region src/supabaseEdgeFunctionDatabase.d.ts
|
|
@@ -5,7 +6,7 @@ interface SupabaseEdgeFunctionDatabaseConfig {
|
|
|
5
6
|
supabaseUrl: string;
|
|
6
7
|
supabaseServiceRoleKey: string;
|
|
7
8
|
}
|
|
8
|
-
declare const supabaseEdgeFunctionDatabase: (config: SupabaseEdgeFunctionDatabaseConfig, hooks?: DatabasePluginHooks) => () =>
|
|
9
|
+
declare const supabaseEdgeFunctionDatabase: (config: SupabaseEdgeFunctionDatabaseConfig, hooks?: DatabasePluginHooks) => () => _$_hot_updater_plugin_core0.DatabasePlugin<unknown>;
|
|
9
10
|
//#endregion
|
|
10
11
|
//#region src/supabaseEdgeFunctionStorage.d.ts
|
|
11
12
|
interface SupabaseEdgeFunctionStorageConfig {
|
|
@@ -13,6 +14,6 @@ interface SupabaseEdgeFunctionStorageConfig {
|
|
|
13
14
|
supabaseServiceRoleKey: string;
|
|
14
15
|
signedUrlExpiresIn?: number;
|
|
15
16
|
}
|
|
16
|
-
declare const supabaseEdgeFunctionStorage: (config: SupabaseEdgeFunctionStorageConfig, hooks?:
|
|
17
|
+
declare const supabaseEdgeFunctionStorage: (config: SupabaseEdgeFunctionStorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.RuntimeStoragePlugin<unknown>;
|
|
17
18
|
//#endregion
|
|
18
19
|
export { supabaseEdgeFunctionDatabase as i, supabaseEdgeFunctionStorage as n, SupabaseEdgeFunctionDatabaseConfig as r, SupabaseEdgeFunctionStorageConfig as t };
|
package/dist/{supabaseEdgeFunctionStorage-BZC0Z0XP.cjs → supabaseEdgeFunctionStorage-DRxwDPRy.cjs}
RENAMED
|
@@ -2,7 +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/
|
|
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";
|
|
6
7
|
const normalizeMetadata = (value) => {
|
|
7
8
|
if (!value) return {};
|
|
8
9
|
if (typeof value === "string") try {
|
|
@@ -13,27 +14,6 @@ const normalizeMetadata = (value) => {
|
|
|
13
14
|
if (typeof value === "object" && !Array.isArray(value)) return value;
|
|
14
15
|
return {};
|
|
15
16
|
};
|
|
16
|
-
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";
|
|
17
|
-
const createSupabaseError = (error) => {
|
|
18
|
-
if (error instanceof Error) return error;
|
|
19
|
-
if (error && typeof error === "object") {
|
|
20
|
-
const properties = {};
|
|
21
|
-
let target = error;
|
|
22
|
-
while (target && target !== Object.prototype) {
|
|
23
|
-
for (const key of Object.getOwnPropertyNames(target)) properties[key] = error[key];
|
|
24
|
-
target = Object.getPrototypeOf(target);
|
|
25
|
-
}
|
|
26
|
-
return new Error(JSON.stringify({
|
|
27
|
-
name: error.constructor.name,
|
|
28
|
-
...properties
|
|
29
|
-
}));
|
|
30
|
-
}
|
|
31
|
-
try {
|
|
32
|
-
return new Error(JSON.stringify(error));
|
|
33
|
-
} catch {
|
|
34
|
-
return new Error(String(error));
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
17
|
const buildBundlePatchId = (bundleId, baseBundleId) => `${bundleId}:${baseBundleId}`;
|
|
38
18
|
const mapRowToBundle = (row, patchRows = []) => {
|
|
39
19
|
const rawMetadata = normalizeMetadata(row.metadata);
|
|
@@ -97,10 +77,47 @@ const bundleToPatchRows = (bundle) => (0, _hot_updater_core.getBundlePatches)(bu
|
|
|
97
77
|
patch_storage_uri: patch.patchStorageUri,
|
|
98
78
|
order_index: index
|
|
99
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
|
+
});
|
|
100
117
|
const supabaseDatabase = (0, _hot_updater_plugin_core.createDatabasePlugin)({
|
|
101
118
|
name: "supabaseDatabase",
|
|
102
119
|
factory: (config) => {
|
|
103
|
-
const supabase = (0, _supabase_supabase_js.createClient)(config.supabaseUrl, config
|
|
120
|
+
const supabase = (0, _supabase_supabase_js.createClient)(config.supabaseUrl, resolveSupabaseServiceRoleKey(config));
|
|
104
121
|
const fetchPatchMap = async (bundleIds) => {
|
|
105
122
|
const patchMap = /* @__PURE__ */ new Map();
|
|
106
123
|
if (bundleIds.length === 0) return patchMap;
|
|
@@ -113,28 +130,42 @@ const supabaseDatabase = (0, _hot_updater_plugin_core.createDatabasePlugin)({
|
|
|
113
130
|
}
|
|
114
131
|
return patchMap;
|
|
115
132
|
};
|
|
116
|
-
const mapRowsToBundles = async (rows) => {
|
|
117
|
-
const patchMap = await fetchPatchMap(rows.map((row) => row.id));
|
|
118
|
-
return rows.map((row) => mapRowToBundle(row, patchMap.get(row.id)));
|
|
119
|
-
};
|
|
120
133
|
return {
|
|
121
|
-
getUpdateInfo
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
+
});
|
|
134
153
|
if (error) throw createSupabaseError(error);
|
|
135
|
-
|
|
154
|
+
const updateInfo = data?.[0] ?? null;
|
|
155
|
+
return updateInfo ? mapUpdateInfoRow(updateInfo) : null;
|
|
136
156
|
}
|
|
137
|
-
|
|
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
|
+
},
|
|
138
169
|
async getBundleById(bundleId) {
|
|
139
170
|
const [{ data, error }, patchMap] = await Promise.all([supabase.from("bundles").select(BUNDLE_SELECT_COLUMNS).eq("id", bundleId).single(), fetchPatchMap([bundleId])]);
|
|
140
171
|
if (!data || error) return null;
|
|
@@ -229,11 +260,16 @@ const supabaseDatabase = (0, _hot_updater_plugin_core.createDatabasePlugin)({
|
|
|
229
260
|
const supabaseEdgeFunctionDatabase = (config, hooks) => {
|
|
230
261
|
return supabaseDatabase({
|
|
231
262
|
supabaseUrl: config.supabaseUrl,
|
|
232
|
-
|
|
263
|
+
supabaseServiceRoleKey: config.supabaseServiceRoleKey
|
|
233
264
|
}, hooks);
|
|
234
265
|
};
|
|
235
266
|
//#endregion
|
|
236
267
|
//#region src/supabaseEdgeFunctionStorage.ts
|
|
268
|
+
function getErrorMessage(error) {
|
|
269
|
+
if (error instanceof Error) return error.message;
|
|
270
|
+
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") return error.message;
|
|
271
|
+
return String(error);
|
|
272
|
+
}
|
|
237
273
|
const parseSupabaseStorageUri = (storageUri) => {
|
|
238
274
|
const storageUrl = new URL(storageUri);
|
|
239
275
|
if (storageUrl.protocol !== "supabase-storage:") throw new Error("Invalid Supabase storage URI protocol");
|
|
@@ -263,15 +299,30 @@ const supabaseEdgeFunctionStorage = (0, _hot_updater_plugin_core.createRuntimeSt
|
|
|
263
299
|
},
|
|
264
300
|
async getDownloadUrl(storageUri) {
|
|
265
301
|
const { bucketName, key } = parseSupabaseStorageUri(storageUri);
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
302
|
+
const bucket = supabase.storage.from(bucketName);
|
|
303
|
+
const expiresIn = config.signedUrlExpiresIn ?? 3600;
|
|
304
|
+
let data = null;
|
|
305
|
+
let error = null;
|
|
306
|
+
try {
|
|
307
|
+
const response = await bucket.createSignedUrl(key, expiresIn);
|
|
308
|
+
data = response.data;
|
|
309
|
+
error = response.error;
|
|
310
|
+
} catch (thrownError) {
|
|
311
|
+
error = thrownError;
|
|
312
|
+
}
|
|
313
|
+
if (!error && data?.signedUrl) return { fileUrl: data.signedUrl };
|
|
314
|
+
throw new Error(`Failed to generate download URL for "${bucketName}/${key}": ${getErrorMessage(error ?? /* @__PURE__ */ new Error("missing signed URL"))}`);
|
|
270
315
|
}
|
|
271
316
|
};
|
|
272
317
|
}
|
|
273
318
|
});
|
|
274
319
|
//#endregion
|
|
320
|
+
Object.defineProperty(exports, "resolveSupabaseServiceRoleKey", {
|
|
321
|
+
enumerable: true,
|
|
322
|
+
get: function() {
|
|
323
|
+
return resolveSupabaseServiceRoleKey;
|
|
324
|
+
}
|
|
325
|
+
});
|
|
275
326
|
Object.defineProperty(exports, "supabaseDatabase", {
|
|
276
327
|
enumerable: true,
|
|
277
328
|
get: function() {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/supabase",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.33.0",
|
|
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.
|
|
51
|
-
"@hot-updater/plugin-core": "0.
|
|
52
|
-
"@hot-updater/server": "0.
|
|
53
|
-
"@hot-updater/cli-tools": "0.
|
|
50
|
+
"@hot-updater/core": "0.33.0",
|
|
51
|
+
"@hot-updater/plugin-core": "0.33.0",
|
|
52
|
+
"@hot-updater/server": "0.33.0",
|
|
53
|
+
"@hot-updater/cli-tools": "0.33.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@electric-sql/pglite": "0.2.17",
|
|
@@ -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.
|
|
64
|
-
"@hot-updater/mock": "0.
|
|
65
|
-
"@hot-updater/
|
|
66
|
-
"@hot-updater/
|
|
63
|
+
"@hot-updater/js": "0.33.0",
|
|
64
|
+
"@hot-updater/mock": "0.33.0",
|
|
65
|
+
"@hot-updater/postgres": "0.33.0",
|
|
66
|
+
"@hot-updater/test-utils": "0.33.0"
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
69
|
"build": "tsdown",
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
-- HotUpdater.supabase_rls
|
|
2
|
+
|
|
3
|
+
ALTER TABLE public.bundles ENABLE ROW LEVEL SECURITY;
|
|
4
|
+
ALTER TABLE public.bundle_patches ENABLE ROW LEVEL SECURITY;
|
|
5
|
+
|
|
6
|
+
ALTER FUNCTION public.get_target_app_version_list(public.platforms, uuid)
|
|
7
|
+
SET search_path = public, pg_catalog;
|
|
8
|
+
ALTER FUNCTION public.get_channels()
|
|
9
|
+
SET search_path = public, pg_catalog;
|
|
10
|
+
ALTER FUNCTION public.positive_mod(integer, integer)
|
|
11
|
+
SET search_path = public, pg_catalog;
|
|
12
|
+
ALTER FUNCTION public.hash_rollout_value(text)
|
|
13
|
+
SET search_path = public, pg_catalog;
|
|
14
|
+
ALTER FUNCTION public.normalize_cohort_value(text)
|
|
15
|
+
SET search_path = public, pg_catalog;
|
|
16
|
+
ALTER FUNCTION public.gcd_int(integer, integer)
|
|
17
|
+
SET search_path = public, pg_catalog;
|
|
18
|
+
ALTER FUNCTION public.get_rollout_multiplier(uuid)
|
|
19
|
+
SET search_path = public, pg_catalog;
|
|
20
|
+
ALTER FUNCTION public.get_rollout_offset(uuid)
|
|
21
|
+
SET search_path = public, pg_catalog;
|
|
22
|
+
ALTER FUNCTION public.get_modular_inverse(integer, integer)
|
|
23
|
+
SET search_path = public, pg_catalog;
|
|
24
|
+
ALTER FUNCTION public.is_numeric_cohort(text)
|
|
25
|
+
SET search_path = public, pg_catalog;
|
|
26
|
+
ALTER FUNCTION public.get_numeric_cohort_rollout_position(uuid, text)
|
|
27
|
+
SET search_path = public, pg_catalog;
|
|
28
|
+
ALTER FUNCTION public.is_cohort_eligible(uuid, text, integer, text[])
|
|
29
|
+
SET search_path = public, pg_catalog;
|
|
30
|
+
ALTER FUNCTION public.get_update_info_by_fingerprint_hash(
|
|
31
|
+
public.platforms,
|
|
32
|
+
uuid,
|
|
33
|
+
uuid,
|
|
34
|
+
text,
|
|
35
|
+
text,
|
|
36
|
+
text
|
|
37
|
+
)
|
|
38
|
+
SET search_path = public, pg_catalog;
|
|
39
|
+
ALTER FUNCTION public.get_update_info_by_app_version(
|
|
40
|
+
public.platforms,
|
|
41
|
+
text,
|
|
42
|
+
uuid,
|
|
43
|
+
uuid,
|
|
44
|
+
text,
|
|
45
|
+
text[],
|
|
46
|
+
text
|
|
47
|
+
)
|
|
48
|
+
SET search_path = public, pg_catalog;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
|
|
6
|
+
const rlsMigrationPath = path.resolve(
|
|
7
|
+
"plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql",
|
|
8
|
+
);
|
|
9
|
+
const migrationsDir = path.dirname(rlsMigrationPath);
|
|
10
|
+
|
|
11
|
+
describe("Supabase RLS migration", () => {
|
|
12
|
+
it("runs after Supabase function redefinition migrations", async () => {
|
|
13
|
+
const migrations = (await fs.readdir(migrationsDir))
|
|
14
|
+
.filter((file) => file.endsWith(".sql"))
|
|
15
|
+
.sort();
|
|
16
|
+
|
|
17
|
+
expect(migrations.at(-1)).toBe(path.basename(rlsMigrationPath));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("enables RLS on Hot Updater tables", async () => {
|
|
21
|
+
const sql = await fs.readFile(rlsMigrationPath, "utf8");
|
|
22
|
+
|
|
23
|
+
expect(sql).toContain(
|
|
24
|
+
"ALTER TABLE public.bundles ENABLE ROW LEVEL SECURITY;",
|
|
25
|
+
);
|
|
26
|
+
expect(sql).toContain(
|
|
27
|
+
"ALTER TABLE public.bundle_patches ENABLE ROW LEVEL SECURITY;",
|
|
28
|
+
);
|
|
29
|
+
expect(sql).not.toContain("REVOKE ALL ON TABLE");
|
|
30
|
+
expect(sql).not.toContain("GRANT SELECT, INSERT, UPDATE, DELETE");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("pins search_path for public Hot Updater functions", async () => {
|
|
34
|
+
const sql = await fs.readFile(rlsMigrationPath, "utf8");
|
|
35
|
+
const hotUpdaterFunctions = [
|
|
36
|
+
"get_target_app_version_list",
|
|
37
|
+
"get_channels",
|
|
38
|
+
"positive_mod",
|
|
39
|
+
"hash_rollout_value",
|
|
40
|
+
"normalize_cohort_value",
|
|
41
|
+
"gcd_int",
|
|
42
|
+
"get_rollout_multiplier",
|
|
43
|
+
"get_rollout_offset",
|
|
44
|
+
"get_modular_inverse",
|
|
45
|
+
"is_numeric_cohort",
|
|
46
|
+
"get_numeric_cohort_rollout_position",
|
|
47
|
+
"is_cohort_eligible",
|
|
48
|
+
"get_update_info_by_fingerprint_hash",
|
|
49
|
+
"get_update_info_by_app_version",
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
for (const functionName of hotUpdaterFunctions) {
|
|
53
|
+
expect(sql).toContain(`ALTER FUNCTION public.${functionName}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
expect(sql.match(/SET search_path = public, pg_catalog;/g)).toHaveLength(
|
|
57
|
+
hotUpdaterFunctions.length,
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("does not change function execution grants", async () => {
|
|
62
|
+
const sql = await fs.readFile(rlsMigrationPath, "utf8");
|
|
63
|
+
|
|
64
|
+
expect(sql).not.toContain("REVOKE EXECUTE");
|
|
65
|
+
expect(sql).not.toContain("GRANT EXECUTE");
|
|
66
|
+
});
|
|
67
|
+
});
|