@hot-updater/supabase 0.30.11 → 0.31.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 +5 -4
- package/dist/iac/index.mjs +5 -4
- package/dist/index.cjs +55 -31
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +56 -32
- package/dist/supabaseEdgeFunctionStorage-BKU_mLzA.mjs +274 -0
- package/dist/{supabaseEdgeFunctionStorage-ByPGforO.d.mts → supabaseEdgeFunctionStorage-BRxGvt-r.d.mts} +2 -2
- package/dist/supabaseEdgeFunctionStorage-BZC0Z0XP.cjs +292 -0
- package/dist/{supabaseEdgeFunctionStorage-CSPi2UB8.d.cts → supabaseEdgeFunctionStorage-CU396KO3.d.cts} +2 -2
- package/package.json +9 -9
- package/supabase/edge-functions/runtime.docker.integration.spec.ts +194 -14
- package/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +20 -0
- package/dist/supabaseEdgeFunctionStorage-Dd8ytWP1.cjs +0 -545
- package/dist/supabaseEdgeFunctionStorage-DnnViEfo.mjs +0 -527
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-BZC0Z0XP.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-BKU_mLzA.mjs";
|
|
2
2
|
export { supabaseEdgeFunctionDatabase, supabaseEdgeFunctionStorage };
|
package/dist/iac/index.cjs
CHANGED
|
@@ -6258,7 +6258,8 @@ const supabaseApi = (supabaseUrl, supabaseAnonKey) => {
|
|
|
6258
6258
|
const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
|
|
6259
6259
|
const EDGE_VENDOR_DIR = "_hot-updater";
|
|
6260
6260
|
const WORKSPACE_PACKAGE_PREFIX = "@hot-updater/";
|
|
6261
|
-
const
|
|
6261
|
+
const STATIC_IMPORT_SPECIFIER_PATTERN = /^\s*(?:import|export)\s+(?:type\s+)?(?:[^"'`]+?\s+from\s+)?["']([^"']+)["'];?/gm;
|
|
6262
|
+
const DYNAMIC_IMPORT_SPECIFIER_PATTERN = /\bimport\s*\(\s*["']([^"']+)["']\s*\)/g;
|
|
6262
6263
|
const getConfigScaffold = (build) => {
|
|
6263
6264
|
return (0, _hot_updater_cli_tools.createHotUpdaterConfigScaffoldFromBuilder)(new _hot_updater_cli_tools.ConfigBuilder().setBuildType(build).setStorage({
|
|
6264
6265
|
imports: [{
|
|
@@ -6291,7 +6292,6 @@ function App() {
|
|
|
6291
6292
|
export default HotUpdater.wrap({
|
|
6292
6293
|
baseURL: "%%source%%",
|
|
6293
6294
|
updateStrategy: "appVersion", // or "fingerprint"
|
|
6294
|
-
updateMode: "auto",
|
|
6295
6295
|
})(App);`;
|
|
6296
6296
|
const SUPABASE_CONFIG_TEMPLATE = `
|
|
6297
6297
|
project_id = "%%projectId%%"
|
|
@@ -6339,8 +6339,9 @@ const collectBareImportSpecifiers = async (entryPath) => {
|
|
|
6339
6339
|
if (!currentFile || visitedFiles.has(currentFile)) continue;
|
|
6340
6340
|
visitedFiles.add(currentFile);
|
|
6341
6341
|
const source = await fs_promises.default.readFile(currentFile, "utf8");
|
|
6342
|
-
|
|
6343
|
-
|
|
6342
|
+
const matches = [...source.matchAll(STATIC_IMPORT_SPECIFIER_PATTERN), ...source.matchAll(DYNAMIC_IMPORT_SPECIFIER_PATTERN)];
|
|
6343
|
+
for (const match of matches) {
|
|
6344
|
+
const specifier = match[1];
|
|
6344
6345
|
if (!specifier) continue;
|
|
6345
6346
|
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
6346
6347
|
const resolvedPath = await resolveLocalModulePath(currentFile, specifier);
|
package/dist/iac/index.mjs
CHANGED
|
@@ -6253,7 +6253,8 @@ const supabaseApi = (supabaseUrl, supabaseAnonKey) => {
|
|
|
6253
6253
|
const require$1 = createRequire(import.meta.url);
|
|
6254
6254
|
const EDGE_VENDOR_DIR = "_hot-updater";
|
|
6255
6255
|
const WORKSPACE_PACKAGE_PREFIX = "@hot-updater/";
|
|
6256
|
-
const
|
|
6256
|
+
const STATIC_IMPORT_SPECIFIER_PATTERN = /^\s*(?:import|export)\s+(?:type\s+)?(?:[^"'`]+?\s+from\s+)?["']([^"']+)["'];?/gm;
|
|
6257
|
+
const DYNAMIC_IMPORT_SPECIFIER_PATTERN = /\bimport\s*\(\s*["']([^"']+)["']\s*\)/g;
|
|
6257
6258
|
const getConfigScaffold = (build) => {
|
|
6258
6259
|
return createHotUpdaterConfigScaffoldFromBuilder(new ConfigBuilder().setBuildType(build).setStorage({
|
|
6259
6260
|
imports: [{
|
|
@@ -6286,7 +6287,6 @@ function App() {
|
|
|
6286
6287
|
export default HotUpdater.wrap({
|
|
6287
6288
|
baseURL: "%%source%%",
|
|
6288
6289
|
updateStrategy: "appVersion", // or "fingerprint"
|
|
6289
|
-
updateMode: "auto",
|
|
6290
6290
|
})(App);`;
|
|
6291
6291
|
const SUPABASE_CONFIG_TEMPLATE = `
|
|
6292
6292
|
project_id = "%%projectId%%"
|
|
@@ -6334,8 +6334,9 @@ const collectBareImportSpecifiers = async (entryPath) => {
|
|
|
6334
6334
|
if (!currentFile || visitedFiles.has(currentFile)) continue;
|
|
6335
6335
|
visitedFiles.add(currentFile);
|
|
6336
6336
|
const source = await fs.readFile(currentFile, "utf8");
|
|
6337
|
-
|
|
6338
|
-
|
|
6337
|
+
const matches = [...source.matchAll(STATIC_IMPORT_SPECIFIER_PATTERN), ...source.matchAll(DYNAMIC_IMPORT_SPECIFIER_PATTERN)];
|
|
6338
|
+
for (const match of matches) {
|
|
6339
|
+
const specifier = match[1];
|
|
6339
6340
|
if (!specifier) continue;
|
|
6340
6341
|
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
6341
6342
|
const resolvedPath = await resolveLocalModulePath(currentFile, specifier);
|
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-
|
|
24
|
+
const require_supabaseEdgeFunctionStorage = require("./supabaseEdgeFunctionStorage-BZC0Z0XP.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");
|
|
@@ -29,44 +29,68 @@ fs_promises = __toESM(fs_promises);
|
|
|
29
29
|
let path = require("path");
|
|
30
30
|
path = __toESM(path);
|
|
31
31
|
//#region src/supabaseStorage.ts
|
|
32
|
-
const supabaseStorage = (0, _hot_updater_plugin_core.
|
|
32
|
+
const supabaseStorage = (0, _hot_updater_plugin_core.createUniversalStoragePlugin)({
|
|
33
33
|
name: "supabaseStorage",
|
|
34
34
|
supportedProtocol: "supabase-storage",
|
|
35
35
|
factory: (config) => {
|
|
36
36
|
const bucket = (0, _supabase_supabase_js.createClient)(config.supabaseUrl, config.supabaseAnonKey).storage.from(config.bucketName);
|
|
37
37
|
const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(config.basePath);
|
|
38
38
|
return {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (error
|
|
45
|
-
|
|
39
|
+
node: {
|
|
40
|
+
async delete(storageUri) {
|
|
41
|
+
const { key, bucket: bucketName } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "supabase-storage");
|
|
42
|
+
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
43
|
+
const { error } = await bucket.remove([key]);
|
|
44
|
+
if (error) {
|
|
45
|
+
if (error.message?.includes("not found")) throw new Error(`Bundle not found`);
|
|
46
|
+
throw new Error(`Failed to delete bundle: ${error.message}`);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
async upload(key, filePath) {
|
|
50
|
+
const Body = await fs_promises.default.readFile(filePath);
|
|
51
|
+
const ContentType = (0, _hot_updater_plugin_core.getContentType)(filePath);
|
|
52
|
+
const Key = getStorageKey(key, path.default.basename(filePath));
|
|
53
|
+
const upload = await bucket.upload(Key, Body, {
|
|
54
|
+
contentType: ContentType,
|
|
55
|
+
cacheControl: "max-age=31536000",
|
|
56
|
+
headers: {}
|
|
57
|
+
});
|
|
58
|
+
if (upload.error) throw upload.error;
|
|
59
|
+
return { storageUri: `supabase-storage://${upload.data.fullPath}` };
|
|
60
|
+
},
|
|
61
|
+
async downloadFile(storageUri, filePath) {
|
|
62
|
+
const { key, bucket: bucketName } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "supabase-storage");
|
|
63
|
+
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
64
|
+
const { data, error } = await bucket.download(key);
|
|
65
|
+
if (error) throw new Error(`Failed to download bundle: ${error.message}`);
|
|
66
|
+
if (!data) throw new Error("Failed to download bundle");
|
|
67
|
+
await fs_promises.default.mkdir(path.default.dirname(filePath), { recursive: true });
|
|
68
|
+
await fs_promises.default.writeFile(filePath, new Uint8Array(await data.arrayBuffer()));
|
|
46
69
|
}
|
|
47
70
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
runtime: {
|
|
72
|
+
async readText(storageUri) {
|
|
73
|
+
const { key, bucket: bucketName } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "supabase-storage");
|
|
74
|
+
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
75
|
+
const { data, error } = await bucket.download(key);
|
|
76
|
+
if (error) {
|
|
77
|
+
if (error.message?.includes("not found")) return null;
|
|
78
|
+
throw new Error(`Failed to read storage text: ${error.message}`);
|
|
79
|
+
}
|
|
80
|
+
if (!data) return null;
|
|
81
|
+
return data.text();
|
|
82
|
+
},
|
|
83
|
+
async getDownloadUrl(storageUri) {
|
|
84
|
+
const u = new URL(storageUri);
|
|
85
|
+
if (u.protocol.replace(":", "") !== "supabase-storage") throw new Error("Invalid Supabase storage URI protocol");
|
|
86
|
+
let key = `${u.host}${u.pathname}`.replace(/^\//, "");
|
|
87
|
+
if (!key) throw new Error("Invalid Supabase storage URI: missing key");
|
|
88
|
+
if (key.startsWith(`${config.bucketName}/`)) key = key.substring(`${config.bucketName}/`.length);
|
|
89
|
+
const { data, error } = await bucket.createSignedUrl(key, 3600);
|
|
90
|
+
if (error) throw new Error(`Failed to generate download URL: ${error.message}`);
|
|
91
|
+
if (!data?.signedUrl) throw new Error("Failed to generate download URL");
|
|
92
|
+
return { fileUrl: data.signedUrl };
|
|
93
|
+
}
|
|
70
94
|
}
|
|
71
95
|
};
|
|
72
96
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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
2
|
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
3
3
|
|
|
4
4
|
//#region src/supabaseDatabase.d.ts
|
|
@@ -18,6 +18,6 @@ interface SupabaseStorageConfig {
|
|
|
18
18
|
*/
|
|
19
19
|
basePath?: string;
|
|
20
20
|
}
|
|
21
|
-
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.
|
|
21
|
+
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
|
|
22
22
|
//#endregion
|
|
23
23
|
export { SupabaseDatabaseConfig, SupabaseEdgeFunctionDatabaseConfig, SupabaseEdgeFunctionStorageConfig, SupabaseStorageConfig, supabaseDatabase, supabaseEdgeFunctionDatabase, supabaseEdgeFunctionStorage, supabaseStorage };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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
2
|
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
3
3
|
|
|
4
4
|
//#region src/supabaseDatabase.d.ts
|
|
@@ -18,6 +18,6 @@ interface SupabaseStorageConfig {
|
|
|
18
18
|
*/
|
|
19
19
|
basePath?: string;
|
|
20
20
|
}
|
|
21
|
-
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.
|
|
21
|
+
declare const supabaseStorage: (config: SupabaseStorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
|
|
22
22
|
//#endregion
|
|
23
23
|
export { SupabaseDatabaseConfig, SupabaseEdgeFunctionDatabaseConfig, SupabaseEdgeFunctionStorageConfig, SupabaseStorageConfig, supabaseDatabase, supabaseEdgeFunctionDatabase, supabaseEdgeFunctionStorage, supabaseStorage };
|
package/dist/index.mjs
CHANGED
|
@@ -1,47 +1,71 @@
|
|
|
1
|
-
import { n as supabaseEdgeFunctionDatabase, r as supabaseDatabase, t as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-
|
|
2
|
-
import { createStorageKeyBuilder,
|
|
1
|
+
import { n as supabaseEdgeFunctionDatabase, r as supabaseDatabase, t as supabaseEdgeFunctionStorage } from "./supabaseEdgeFunctionStorage-BKU_mLzA.mjs";
|
|
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
|
-
const supabaseStorage =
|
|
7
|
+
const supabaseStorage = createUniversalStoragePlugin({
|
|
8
8
|
name: "supabaseStorage",
|
|
9
9
|
supportedProtocol: "supabase-storage",
|
|
10
10
|
factory: (config) => {
|
|
11
11
|
const bucket = createClient(config.supabaseUrl, config.supabaseAnonKey).storage.from(config.bucketName);
|
|
12
12
|
const getStorageKey = createStorageKeyBuilder(config.basePath);
|
|
13
13
|
return {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (error
|
|
20
|
-
|
|
14
|
+
node: {
|
|
15
|
+
async delete(storageUri) {
|
|
16
|
+
const { key, bucket: bucketName } = parseStorageUri(storageUri, "supabase-storage");
|
|
17
|
+
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
18
|
+
const { error } = await bucket.remove([key]);
|
|
19
|
+
if (error) {
|
|
20
|
+
if (error.message?.includes("not found")) throw new Error(`Bundle not found`);
|
|
21
|
+
throw new Error(`Failed to delete bundle: ${error.message}`);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
async upload(key, filePath) {
|
|
25
|
+
const Body = await fs.readFile(filePath);
|
|
26
|
+
const ContentType = getContentType(filePath);
|
|
27
|
+
const Key = getStorageKey(key, path.basename(filePath));
|
|
28
|
+
const upload = await bucket.upload(Key, Body, {
|
|
29
|
+
contentType: ContentType,
|
|
30
|
+
cacheControl: "max-age=31536000",
|
|
31
|
+
headers: {}
|
|
32
|
+
});
|
|
33
|
+
if (upload.error) throw upload.error;
|
|
34
|
+
return { storageUri: `supabase-storage://${upload.data.fullPath}` };
|
|
35
|
+
},
|
|
36
|
+
async downloadFile(storageUri, filePath) {
|
|
37
|
+
const { key, bucket: bucketName } = parseStorageUri(storageUri, "supabase-storage");
|
|
38
|
+
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
39
|
+
const { data, error } = await bucket.download(key);
|
|
40
|
+
if (error) throw new Error(`Failed to download bundle: ${error.message}`);
|
|
41
|
+
if (!data) throw new Error("Failed to download bundle");
|
|
42
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
43
|
+
await fs.writeFile(filePath, new Uint8Array(await data.arrayBuffer()));
|
|
21
44
|
}
|
|
22
45
|
},
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
runtime: {
|
|
47
|
+
async readText(storageUri) {
|
|
48
|
+
const { key, bucket: bucketName } = parseStorageUri(storageUri, "supabase-storage");
|
|
49
|
+
if (bucketName !== config.bucketName) throw new Error(`Bucket name mismatch: expected "${config.bucketName}", but found "${bucketName}".`);
|
|
50
|
+
const { data, error } = await bucket.download(key);
|
|
51
|
+
if (error) {
|
|
52
|
+
if (error.message?.includes("not found")) return null;
|
|
53
|
+
throw new Error(`Failed to read storage text: ${error.message}`);
|
|
54
|
+
}
|
|
55
|
+
if (!data) return null;
|
|
56
|
+
return data.text();
|
|
57
|
+
},
|
|
58
|
+
async getDownloadUrl(storageUri) {
|
|
59
|
+
const u = new URL(storageUri);
|
|
60
|
+
if (u.protocol.replace(":", "") !== "supabase-storage") throw new Error("Invalid Supabase storage URI protocol");
|
|
61
|
+
let key = `${u.host}${u.pathname}`.replace(/^\//, "");
|
|
62
|
+
if (!key) throw new Error("Invalid Supabase storage URI: missing key");
|
|
63
|
+
if (key.startsWith(`${config.bucketName}/`)) key = key.substring(`${config.bucketName}/`.length);
|
|
64
|
+
const { data, error } = await bucket.createSignedUrl(key, 3600);
|
|
65
|
+
if (error) throw new Error(`Failed to generate download URL: ${error.message}`);
|
|
66
|
+
if (!data?.signedUrl) throw new Error("Failed to generate download URL");
|
|
67
|
+
return { fileUrl: data.signedUrl };
|
|
68
|
+
}
|
|
45
69
|
}
|
|
46
70
|
};
|
|
47
71
|
}
|
|
@@ -0,0 +1,274 @@
|
|
|
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";
|
|
3
|
+
import { createClient } from "@supabase/supabase-js";
|
|
4
|
+
//#region src/supabaseDatabase.ts
|
|
5
|
+
const normalizeMetadata = (value) => {
|
|
6
|
+
if (!value) return {};
|
|
7
|
+
if (typeof value === "string") try {
|
|
8
|
+
return normalizeMetadata(JSON.parse(value));
|
|
9
|
+
} catch {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
if (typeof value === "object" && !Array.isArray(value)) return value;
|
|
13
|
+
return {};
|
|
14
|
+
};
|
|
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
|
+
const buildBundlePatchId = (bundleId, baseBundleId) => `${bundleId}:${baseBundleId}`;
|
|
37
|
+
const mapRowToBundle = (row, patchRows = []) => {
|
|
38
|
+
const rawMetadata = normalizeMetadata(row.metadata);
|
|
39
|
+
const patches = patchRows.slice().sort((left, right) => left.order_index - right.order_index || left.base_bundle_id.localeCompare(right.base_bundle_id)).map((patch) => ({
|
|
40
|
+
baseBundleId: patch.base_bundle_id,
|
|
41
|
+
baseFileHash: patch.base_file_hash,
|
|
42
|
+
patchFileHash: patch.patch_file_hash,
|
|
43
|
+
patchStorageUri: patch.patch_storage_uri
|
|
44
|
+
}));
|
|
45
|
+
const primaryPatch = patches[0] ?? null;
|
|
46
|
+
return {
|
|
47
|
+
channel: row.channel,
|
|
48
|
+
enabled: Boolean(row.enabled),
|
|
49
|
+
shouldForceUpdate: Boolean(row.should_force_update),
|
|
50
|
+
fileHash: row.file_hash,
|
|
51
|
+
gitCommitHash: row.git_commit_hash,
|
|
52
|
+
id: row.id,
|
|
53
|
+
message: row.message,
|
|
54
|
+
platform: row.platform,
|
|
55
|
+
targetAppVersion: row.target_app_version,
|
|
56
|
+
fingerprintHash: row.fingerprint_hash,
|
|
57
|
+
storageUri: row.storage_uri,
|
|
58
|
+
metadata: stripBundleArtifactMetadata(rawMetadata),
|
|
59
|
+
manifestStorageUri: row.manifest_storage_uri ?? null,
|
|
60
|
+
manifestFileHash: row.manifest_file_hash ?? null,
|
|
61
|
+
assetBaseStorageUri: row.asset_base_storage_uri ?? null,
|
|
62
|
+
patches,
|
|
63
|
+
patchBaseBundleId: primaryPatch?.baseBundleId ?? null,
|
|
64
|
+
patchBaseFileHash: primaryPatch?.baseFileHash ?? null,
|
|
65
|
+
patchFileHash: primaryPatch?.patchFileHash ?? null,
|
|
66
|
+
patchStorageUri: primaryPatch?.patchStorageUri ?? null,
|
|
67
|
+
rolloutCohortCount: row.rollout_cohort_count ?? DEFAULT_ROLLOUT_COHORT_COUNT,
|
|
68
|
+
targetCohorts: row.target_cohorts ?? null
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
const bundleToRow = (bundle) => ({
|
|
72
|
+
id: bundle.id,
|
|
73
|
+
channel: bundle.channel,
|
|
74
|
+
enabled: bundle.enabled,
|
|
75
|
+
should_force_update: bundle.shouldForceUpdate,
|
|
76
|
+
file_hash: bundle.fileHash,
|
|
77
|
+
git_commit_hash: bundle.gitCommitHash,
|
|
78
|
+
message: bundle.message,
|
|
79
|
+
platform: bundle.platform,
|
|
80
|
+
target_app_version: bundle.targetAppVersion,
|
|
81
|
+
fingerprint_hash: bundle.fingerprintHash,
|
|
82
|
+
storage_uri: bundle.storageUri,
|
|
83
|
+
metadata: stripBundleArtifactMetadata(bundle.metadata) ?? {},
|
|
84
|
+
manifest_storage_uri: getManifestStorageUri(bundle),
|
|
85
|
+
manifest_file_hash: getManifestFileHash(bundle),
|
|
86
|
+
asset_base_storage_uri: getAssetBaseStorageUri(bundle),
|
|
87
|
+
rollout_cohort_count: bundle.rolloutCohortCount ?? DEFAULT_ROLLOUT_COHORT_COUNT,
|
|
88
|
+
target_cohorts: bundle.targetCohorts ?? null
|
|
89
|
+
});
|
|
90
|
+
const bundleToPatchRows = (bundle) => getBundlePatches(bundle).map((patch, index) => ({
|
|
91
|
+
id: buildBundlePatchId(bundle.id, patch.baseBundleId),
|
|
92
|
+
bundle_id: bundle.id,
|
|
93
|
+
base_bundle_id: patch.baseBundleId,
|
|
94
|
+
base_file_hash: patch.baseFileHash,
|
|
95
|
+
patch_file_hash: patch.patchFileHash,
|
|
96
|
+
patch_storage_uri: patch.patchStorageUri,
|
|
97
|
+
order_index: index
|
|
98
|
+
}));
|
|
99
|
+
const supabaseDatabase = createDatabasePlugin({
|
|
100
|
+
name: "supabaseDatabase",
|
|
101
|
+
factory: (config) => {
|
|
102
|
+
const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey);
|
|
103
|
+
const fetchPatchMap = async (bundleIds) => {
|
|
104
|
+
const patchMap = /* @__PURE__ */ new Map();
|
|
105
|
+
if (bundleIds.length === 0) return patchMap;
|
|
106
|
+
const { data, error } = await supabase.from("bundle_patches").select("*").in("bundle_id", bundleIds).order("order_index", { ascending: true });
|
|
107
|
+
if (error) throw createSupabaseError(error);
|
|
108
|
+
for (const row of data ?? []) {
|
|
109
|
+
const current = patchMap.get(row.bundle_id) ?? [];
|
|
110
|
+
current.push(row);
|
|
111
|
+
patchMap.set(row.bundle_id, current);
|
|
112
|
+
}
|
|
113
|
+
return patchMap;
|
|
114
|
+
};
|
|
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
|
+
return {
|
|
120
|
+
getUpdateInfo: createDatabasePluginGetUpdateInfo({
|
|
121
|
+
async listTargetAppVersions({ platform, channel, minBundleId }) {
|
|
122
|
+
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);
|
|
123
|
+
if (error) throw createSupabaseError(error);
|
|
124
|
+
return Array.from(new Set((data ?? []).map((row) => row.target_app_version).filter((version) => Boolean(version))));
|
|
125
|
+
},
|
|
126
|
+
async getBundlesByTargetAppVersions({ platform, channel, minBundleId }, targetAppVersions) {
|
|
127
|
+
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);
|
|
128
|
+
if (error) throw createSupabaseError(error);
|
|
129
|
+
return mapRowsToBundles(data ?? []);
|
|
130
|
+
},
|
|
131
|
+
async getBundlesByFingerprint({ platform, channel, minBundleId, fingerprintHash }) {
|
|
132
|
+
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);
|
|
133
|
+
if (error) throw createSupabaseError(error);
|
|
134
|
+
return mapRowsToBundles(data ?? []);
|
|
135
|
+
}
|
|
136
|
+
}),
|
|
137
|
+
async getBundleById(bundleId) {
|
|
138
|
+
const [{ data, error }, patchMap] = await Promise.all([supabase.from("bundles").select(BUNDLE_SELECT_COLUMNS).eq("id", bundleId).single(), fetchPatchMap([bundleId])]);
|
|
139
|
+
if (!data || error) return null;
|
|
140
|
+
return mapRowToBundle(data, patchMap.get(bundleId) ?? []);
|
|
141
|
+
},
|
|
142
|
+
async getBundles(options) {
|
|
143
|
+
const { where, limit, orderBy } = options ?? {};
|
|
144
|
+
const offset = (options && "offset" in options ? options.offset : void 0) ?? 0;
|
|
145
|
+
if (where?.targetAppVersionIn && where.targetAppVersionIn.length === 0 || where?.id?.in && where.id.in.length === 0) return {
|
|
146
|
+
data: [],
|
|
147
|
+
pagination: calculatePagination(0, {
|
|
148
|
+
limit,
|
|
149
|
+
offset
|
|
150
|
+
})
|
|
151
|
+
};
|
|
152
|
+
let countQuery = supabase.from("bundles").select("*", {
|
|
153
|
+
count: "exact",
|
|
154
|
+
head: true
|
|
155
|
+
});
|
|
156
|
+
if (where?.channel) countQuery = countQuery.eq("channel", where.channel);
|
|
157
|
+
if (where?.platform) countQuery = countQuery.eq("platform", where.platform);
|
|
158
|
+
if (where?.enabled !== void 0) countQuery = countQuery.eq("enabled", where.enabled);
|
|
159
|
+
if (where?.fingerprintHash !== void 0) countQuery = where.fingerprintHash === null ? countQuery.is("fingerprint_hash", null) : countQuery.eq("fingerprint_hash", where.fingerprintHash);
|
|
160
|
+
if (where?.targetAppVersion !== void 0) countQuery = where.targetAppVersion === null ? countQuery.is("target_app_version", null) : countQuery.eq("target_app_version", where.targetAppVersion);
|
|
161
|
+
if (where?.targetAppVersionIn) countQuery = countQuery.in("target_app_version", where.targetAppVersionIn);
|
|
162
|
+
if (where?.targetAppVersionNotNull) countQuery = countQuery.not("target_app_version", "is", null);
|
|
163
|
+
if (where?.id?.eq) countQuery = countQuery.eq("id", where.id.eq);
|
|
164
|
+
if (where?.id?.gt) countQuery = countQuery.gt("id", where.id.gt);
|
|
165
|
+
if (where?.id?.gte) countQuery = countQuery.gte("id", where.id.gte);
|
|
166
|
+
if (where?.id?.lt) countQuery = countQuery.lt("id", where.id.lt);
|
|
167
|
+
if (where?.id?.lte) countQuery = countQuery.lte("id", where.id.lte);
|
|
168
|
+
if (where?.id?.in) countQuery = countQuery.in("id", where.id.in);
|
|
169
|
+
const { count: total = 0 } = await countQuery;
|
|
170
|
+
let query = supabase.from("bundles").select(BUNDLE_SELECT_COLUMNS).order("id", { ascending: orderBy?.direction === "asc" });
|
|
171
|
+
if (where?.channel) query = query.eq("channel", where.channel);
|
|
172
|
+
if (where?.platform) query = query.eq("platform", where.platform);
|
|
173
|
+
if (where?.enabled !== void 0) query = query.eq("enabled", where.enabled);
|
|
174
|
+
if (where?.fingerprintHash !== void 0) query = where.fingerprintHash === null ? query.is("fingerprint_hash", null) : query.eq("fingerprint_hash", where.fingerprintHash);
|
|
175
|
+
if (where?.targetAppVersion !== void 0) query = where.targetAppVersion === null ? query.is("target_app_version", null) : query.eq("target_app_version", where.targetAppVersion);
|
|
176
|
+
if (where?.targetAppVersionIn) query = query.in("target_app_version", where.targetAppVersionIn);
|
|
177
|
+
if (where?.targetAppVersionNotNull) query = query.not("target_app_version", "is", null);
|
|
178
|
+
if (where?.id?.eq) query = query.eq("id", where.id.eq);
|
|
179
|
+
if (where?.id?.gt) query = query.gt("id", where.id.gt);
|
|
180
|
+
if (where?.id?.gte) query = query.gte("id", where.id.gte);
|
|
181
|
+
if (where?.id?.lt) query = query.lt("id", where.id.lt);
|
|
182
|
+
if (where?.id?.lte) query = query.lte("id", where.id.lte);
|
|
183
|
+
if (where?.id?.in) query = query.in("id", where.id.in);
|
|
184
|
+
if (limit) query = query.limit(limit);
|
|
185
|
+
if (offset) query = query.range(offset, offset + (limit || 20) - 1);
|
|
186
|
+
const { data } = await query;
|
|
187
|
+
const patchMap = await fetchPatchMap((data ?? []).map((bundle) => bundle.id));
|
|
188
|
+
return {
|
|
189
|
+
data: (data ?? []).map((bundle) => mapRowToBundle(bundle, patchMap.get(bundle.id) ?? [])),
|
|
190
|
+
pagination: calculatePagination(total ?? 0, {
|
|
191
|
+
limit,
|
|
192
|
+
offset
|
|
193
|
+
})
|
|
194
|
+
};
|
|
195
|
+
},
|
|
196
|
+
async getChannels() {
|
|
197
|
+
const { data, error } = await supabase.rpc("get_channels");
|
|
198
|
+
if (error) throw error;
|
|
199
|
+
return data.map((bundle) => bundle.channel);
|
|
200
|
+
},
|
|
201
|
+
async commitBundle({ changedSets }) {
|
|
202
|
+
if (changedSets.length === 0) return;
|
|
203
|
+
for (const op of changedSets) if (op.operation === "delete") {
|
|
204
|
+
const { error: patchDeleteError } = await supabase.from("bundle_patches").delete().eq("bundle_id", op.data.id);
|
|
205
|
+
if (patchDeleteError) throw new Error(`Failed to delete bundle patches: ${patchDeleteError.message}`);
|
|
206
|
+
const { error: basePatchDeleteError } = await supabase.from("bundle_patches").delete().eq("base_bundle_id", op.data.id);
|
|
207
|
+
if (basePatchDeleteError) throw new Error(`Failed to delete base bundle patches: ${basePatchDeleteError.message}`);
|
|
208
|
+
const { error } = await supabase.from("bundles").delete().eq("id", op.data.id);
|
|
209
|
+
if (error) throw new Error(`Failed to delete bundle: ${error.message}`);
|
|
210
|
+
} else if (op.operation === "insert" || op.operation === "update") {
|
|
211
|
+
const bundle = op.data;
|
|
212
|
+
const patchRows = bundleToPatchRows(bundle);
|
|
213
|
+
const { error } = await supabase.from("bundles").upsert(bundleToRow(bundle), { onConflict: "id" });
|
|
214
|
+
if (error) throw error;
|
|
215
|
+
const { error: patchDeleteError } = await supabase.from("bundle_patches").delete().eq("bundle_id", bundle.id);
|
|
216
|
+
if (patchDeleteError) throw patchDeleteError;
|
|
217
|
+
if (patchRows.length > 0) {
|
|
218
|
+
const { error: patchInsertError } = await supabase.from("bundle_patches").upsert(patchRows, { onConflict: "id" });
|
|
219
|
+
if (patchInsertError) throw patchInsertError;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/supabaseEdgeFunctionDatabase.ts
|
|
228
|
+
const supabaseEdgeFunctionDatabase = (config, hooks) => {
|
|
229
|
+
return supabaseDatabase({
|
|
230
|
+
supabaseUrl: config.supabaseUrl,
|
|
231
|
+
supabaseAnonKey: config.supabaseServiceRoleKey
|
|
232
|
+
}, hooks);
|
|
233
|
+
};
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/supabaseEdgeFunctionStorage.ts
|
|
236
|
+
const parseSupabaseStorageUri = (storageUri) => {
|
|
237
|
+
const storageUrl = new URL(storageUri);
|
|
238
|
+
if (storageUrl.protocol !== "supabase-storage:") throw new Error("Invalid Supabase storage URI protocol");
|
|
239
|
+
const bucketName = storageUrl.host;
|
|
240
|
+
const key = storageUrl.pathname.replace(/^\/+/, "");
|
|
241
|
+
if (!bucketName || !key) throw new Error("Invalid Supabase storage URI");
|
|
242
|
+
return {
|
|
243
|
+
bucketName,
|
|
244
|
+
key
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
const supabaseEdgeFunctionStorage = createRuntimeStoragePlugin({
|
|
248
|
+
name: "supabaseEdgeFunctionStorage",
|
|
249
|
+
supportedProtocol: "supabase-storage",
|
|
250
|
+
factory: (config) => {
|
|
251
|
+
const supabase = createClient(config.supabaseUrl, config.supabaseServiceRoleKey);
|
|
252
|
+
return {
|
|
253
|
+
async readText(storageUri) {
|
|
254
|
+
const { bucketName, key } = parseSupabaseStorageUri(storageUri);
|
|
255
|
+
const { data, error } = await supabase.storage.from(bucketName).download(key);
|
|
256
|
+
if (error) {
|
|
257
|
+
if (error.message?.includes("not found")) return null;
|
|
258
|
+
throw new Error(`Failed to read storage text: ${error.message}`);
|
|
259
|
+
}
|
|
260
|
+
if (!data) return null;
|
|
261
|
+
return data.text();
|
|
262
|
+
},
|
|
263
|
+
async getDownloadUrl(storageUri) {
|
|
264
|
+
const { bucketName, key } = parseSupabaseStorageUri(storageUri);
|
|
265
|
+
const { data, error } = await supabase.storage.from(bucketName).createSignedUrl(key, config.signedUrlExpiresIn ?? 3600);
|
|
266
|
+
if (error) throw new Error(`Failed to generate download URL: ${error.message}`);
|
|
267
|
+
if (!data?.signedUrl) throw new Error("Failed to generate download URL");
|
|
268
|
+
return { fileUrl: data.signedUrl };
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
//#endregion
|
|
274
|
+
export { supabaseEdgeFunctionDatabase as n, supabaseDatabase as r, supabaseEdgeFunctionStorage as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
2
|
-
import { DatabasePluginHooks
|
|
2
|
+
import { DatabasePluginHooks } from "@hot-updater/plugin-core";
|
|
3
3
|
|
|
4
4
|
//#region src/supabaseEdgeFunctionDatabase.d.ts
|
|
5
5
|
interface SupabaseEdgeFunctionDatabaseConfig {
|
|
@@ -14,6 +14,6 @@ interface SupabaseEdgeFunctionStorageConfig {
|
|
|
14
14
|
supabaseServiceRoleKey: string;
|
|
15
15
|
signedUrlExpiresIn?: number;
|
|
16
16
|
}
|
|
17
|
-
declare const supabaseEdgeFunctionStorage: (config: SupabaseEdgeFunctionStorageConfig) => () =>
|
|
17
|
+
declare const supabaseEdgeFunctionStorage: (config: SupabaseEdgeFunctionStorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.RuntimeStoragePlugin<unknown>;
|
|
18
18
|
//#endregion
|
|
19
19
|
export { supabaseEdgeFunctionDatabase as i, supabaseEdgeFunctionStorage as n, SupabaseEdgeFunctionDatabaseConfig as r, SupabaseEdgeFunctionStorageConfig as t };
|