@hot-updater/supabase 1.0.0-rc.2 → 1.0.0-rc.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/edge.cjs CHANGED
@@ -1,7 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_supabaseStorage = require("./supabaseStorage-CbzHzqCa.cjs");
2
+ const require_plugins = require("./plugins-jpIkI_T2.cjs");
3
3
  //#region src/supabaseEdgeFunctionStorage.ts
4
- const supabaseEdgeFunctionStorage = (config) => require_supabaseStorage.supabaseStorage(config);
4
+ const supabaseEdgeFunctionStorage = (config) => require_plugins.supabaseStorage(config);
5
5
  //#endregion
6
- exports.supabaseDatabase = require_supabaseStorage.supabaseDatabase;
6
+ exports.plugins = require_plugins.plugins;
7
+ exports.supabaseDatabase = require_plugins.supabaseDatabase;
7
8
  exports.supabaseStorage = supabaseEdgeFunctionStorage;
package/dist/edge.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { n as supabaseDatabase, t as SupabaseDatabaseConfig } from "./supabaseDatabase-3d90EtUD.cjs";
2
- import { StoragePluginWith } from "@hot-updater/plugin-core";
1
+ import { i as plugins, n as supabaseDatabase, t as SupabaseDatabaseConfig } from "./supabaseDatabase-DVb3-y2x.cjs";
2
+ import { StorageAdapterWith } from "@hot-updater/plugin-core";
3
3
  //#region src/supabaseEdgeFunctionStorage.d.ts
4
4
  interface SupabaseEdgeFunctionStorageConfig {
5
5
  supabaseUrl: string;
@@ -8,6 +8,6 @@ interface SupabaseEdgeFunctionStorageConfig {
8
8
  basePath?: string;
9
9
  signedUrlExpiresIn?: number;
10
10
  }
11
- declare const supabaseEdgeFunctionStorage: (config: SupabaseEdgeFunctionStorageConfig) => StoragePluginWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete">;
11
+ declare const supabaseEdgeFunctionStorage: (config: SupabaseEdgeFunctionStorageConfig) => StorageAdapterWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete">;
12
12
  //#endregion
13
- export { type SupabaseDatabaseConfig, type SupabaseEdgeFunctionStorageConfig as SupabaseStorageConfig, supabaseDatabase, supabaseEdgeFunctionStorage as supabaseStorage };
13
+ export { type SupabaseDatabaseConfig, type SupabaseEdgeFunctionStorageConfig as SupabaseStorageConfig, plugins, supabaseDatabase, supabaseEdgeFunctionStorage as supabaseStorage };
package/dist/edge.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { n as supabaseDatabase, t as SupabaseDatabaseConfig } from "./supabaseDatabase-3d90EtUD.mjs";
2
- import { StoragePluginWith } from "@hot-updater/plugin-core";
1
+ import { i as plugins, n as supabaseDatabase, t as SupabaseDatabaseConfig } from "./supabaseDatabase-DVb3-y2x.mjs";
2
+ import { StorageAdapterWith } from "@hot-updater/plugin-core";
3
3
  //#region src/supabaseEdgeFunctionStorage.d.ts
4
4
  interface SupabaseEdgeFunctionStorageConfig {
5
5
  supabaseUrl: string;
@@ -8,6 +8,6 @@ interface SupabaseEdgeFunctionStorageConfig {
8
8
  basePath?: string;
9
9
  signedUrlExpiresIn?: number;
10
10
  }
11
- declare const supabaseEdgeFunctionStorage: (config: SupabaseEdgeFunctionStorageConfig) => StoragePluginWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete">;
11
+ declare const supabaseEdgeFunctionStorage: (config: SupabaseEdgeFunctionStorageConfig) => StorageAdapterWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete">;
12
12
  //#endregion
13
- export { type SupabaseDatabaseConfig, type SupabaseEdgeFunctionStorageConfig as SupabaseStorageConfig, supabaseDatabase, supabaseEdgeFunctionStorage as supabaseStorage };
13
+ export { type SupabaseDatabaseConfig, type SupabaseEdgeFunctionStorageConfig as SupabaseStorageConfig, plugins, supabaseDatabase, supabaseEdgeFunctionStorage as supabaseStorage };
package/dist/edge.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { n as supabaseDatabase, t as supabaseStorage } from "./supabaseStorage-CjteZ2zX.mjs";
1
+ import { n as supabaseStorage, r as supabaseDatabase, t as plugins } from "./plugins-Dj-4Pk3j.mjs";
2
2
  //#region src/supabaseEdgeFunctionStorage.ts
3
3
  const supabaseEdgeFunctionStorage = (config) => supabaseStorage(config);
4
4
  //#endregion
5
- export { supabaseDatabase, supabaseEdgeFunctionStorage as supabaseStorage };
5
+ export { plugins, supabaseDatabase, supabaseEdgeFunctionStorage as supabaseStorage };
package/dist/index.cjs CHANGED
@@ -1,4 +1,144 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_supabaseStorage = require("./supabaseStorage-CbzHzqCa.cjs");
3
- exports.supabaseDatabase = require_supabaseStorage.supabaseDatabase;
4
- exports.supabaseStorage = require_supabaseStorage.supabaseStorage;
2
+ const require_plugins = require("./plugins-jpIkI_T2.cjs");
3
+ let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
4
+ //#region src/supabaseSchema.ts
5
+ /** Every table the apply RPC may name: models, index tables, settings, and the write guard. */
6
+ const supabaseTableNames = (schema = _hot_updater_plugin_core.coreTarget.schema) => [
7
+ ...schema.tables.flatMap((table) => [table.name, ...table.indexes.filter((index) => (0, _hot_updater_plugin_core.isMultiIndex)(table, index)).map((index) => `${table.name}__${index.name}`)]),
8
+ _hot_updater_plugin_core.SETTINGS_TABLE.name,
9
+ _hot_updater_plugin_core.WRITE_GUARD_TABLE.name
10
+ ].map((name) => require_plugins.SUPABASE_TABLE_PREFIX + name);
11
+ /** The words the SQL core's statements use besides quoted names, numbers, and operators. */
12
+ const KEYWORDS = [
13
+ "SELECT",
14
+ "FROM",
15
+ "WHERE",
16
+ "AND",
17
+ "OR",
18
+ "NOT",
19
+ "EXISTS",
20
+ "INSERT",
21
+ "INTO",
22
+ "VALUES",
23
+ "UPDATE",
24
+ "SET",
25
+ "DELETE",
26
+ "ORDER",
27
+ "BY",
28
+ "ASC",
29
+ "DESC",
30
+ "LIMIT",
31
+ "IS",
32
+ "NULL",
33
+ "ON",
34
+ "CONFLICT",
35
+ "DO",
36
+ "JOIN",
37
+ "FOR",
38
+ "bigint",
39
+ "double",
40
+ "precision",
41
+ "boolean",
42
+ "jsonb",
43
+ "i",
44
+ "b"
45
+ ];
46
+ const literal = (value) => `'${value.replaceAll("'", "''")}'`;
47
+ /**
48
+ * Runs a batch of the SQL core's statements in the caller's transaction and
49
+ * privileges (service role only), with every parameter read from one jsonb
50
+ * array. Only the core's statement shapes on Hot Updater's tables pass: one
51
+ * statement each, no literal, comment, or function call, and no word the
52
+ * core does not write.
53
+ */
54
+ const applyFunction = (tables) => `CREATE OR REPLACE FUNCTION public.${require_plugins.SUPABASE_APPLY_FUNCTION}(p_statements jsonb)
55
+ RETURNS jsonb
56
+ LANGUAGE plpgsql
57
+ SECURITY INVOKER
58
+ SET search_path = public, pg_temp
59
+ AS $apply$
60
+ DECLARE
61
+ item jsonb;
62
+ statement text;
63
+ shape text;
64
+ target text;
65
+ found record;
66
+ rows jsonb;
67
+ changed bigint;
68
+ results jsonb := '[]'::jsonb;
69
+ BEGIN
70
+ FOR item IN SELECT value FROM jsonb_array_elements(p_statements) LOOP
71
+ statement := item->>'sql';
72
+ FOR target IN SELECT (regexp_matches(statement, '(?:FROM|INTO|UPDATE|JOIN) "([^"]+)"', 'g'))[1] LOOP
73
+ IF NOT target = ANY (ARRAY[${tables.map(literal).join(", ")}]) THEN
74
+ RAISE EXCEPTION '${require_plugins.SUPABASE_APPLY_FUNCTION}: % is not a Hot Updater table', target USING ERRCODE = '42501';
75
+ END IF;
76
+ END LOOP;
77
+ shape := regexp_replace(regexp_replace(statement, '^INSERT INTO "[^"]+" \\(', 'INSERT INTO ('), '"[^"]*"', '"', 'g');
78
+ IF shape !~ '^(SELECT|INSERT INTO|UPDATE|DELETE FROM) '
79
+ OR shape ~ '"[[:space:]]*\\('
80
+ OR shape ~ '[;''\\\\]|--|/\\*'
81
+ OR regexp_replace(shape, '\\m(${KEYWORDS.join("|")})\\M', '', 'gi') !~ '^[[:space:]"(),.*=<>+0-9$:-]*$' THEN
82
+ RAISE EXCEPTION '${require_plugins.SUPABASE_APPLY_FUNCTION}: statement not allowed' USING ERRCODE = '42501';
83
+ END IF;
84
+ IF statement ~ '^SELECT ' THEN
85
+ rows := '[]'::jsonb;
86
+ FOR found IN EXECUTE statement USING item->'params' LOOP
87
+ rows := rows || jsonb_build_array(to_jsonb(found));
88
+ END LOOP;
89
+ results := results || jsonb_build_array(jsonb_build_object('rows', rows, 'changes', 0));
90
+ ELSE
91
+ EXECUTE statement USING item->'params';
92
+ GET DIAGNOSTICS changed = ROW_COUNT;
93
+ results := results || jsonb_build_array(jsonb_build_object('rows', '[]'::jsonb, 'changes', changed));
94
+ END IF;
95
+ END LOOP;
96
+ RETURN results;
97
+ END;
98
+ $apply$`;
99
+ /**
100
+ * Supabase's migration: the shared SQL schema under the table prefix, the
101
+ * write guard, row-level security on every table (no policy, so only the
102
+ * service role reads or writes), the apply RPC for the service role alone,
103
+ * and the settings rows last. Every statement can run again, so a migration
104
+ * for a server's plugin tables repeats core's.
105
+ */
106
+ const supabaseSchemaStatements = ({ schema, settings: expected } = _hot_updater_plugin_core.coreTarget) => {
107
+ const engine = (0, _hot_updater_plugin_core.generateEngineSql)("postgresql", schema, expected, { tablePrefix: require_plugins.SUPABASE_TABLE_PREFIX });
108
+ const settings = engine.slice(-Object.keys(expected).length);
109
+ const tables = supabaseTableNames(schema);
110
+ const apply = `public.${require_plugins.SUPABASE_APPLY_FUNCTION}(jsonb)`;
111
+ return [
112
+ ...(0, _hot_updater_plugin_core.createTableStatements)("postgresql", [_hot_updater_plugin_core.WRITE_GUARD_TABLE], require_plugins.SUPABASE_TABLE_PREFIX),
113
+ ...engine.slice(0, -settings.length),
114
+ ...tables.map((table) => `ALTER TABLE "${table}" ENABLE ROW LEVEL SECURITY`),
115
+ applyFunction(tables),
116
+ `REVOKE EXECUTE ON FUNCTION ${apply} FROM PUBLIC, anon, authenticated`,
117
+ `GRANT EXECUTE ON FUNCTION ${apply} TO service_role`,
118
+ ...settings
119
+ ];
120
+ };
121
+ const supabaseSchemaSql = (target) => `-- HotUpdater.schema\n\n${supabaseSchemaStatements(target).map((statement) => `${statement};`).join("\n\n")}\n`;
122
+ //#endregion
123
+ //#region src/supabaseMigration.ts
124
+ /**
125
+ * Hot Updater's database on Supabase, with the migration `hot-updater db
126
+ * generate` writes to `supabase/migrations`: core's tables, the server's
127
+ * plugin tables, the apply RPC that may name them, and their settings rows.
128
+ * `supabase db push` applies it.
129
+ */
130
+ const supabaseDatabase = (config) => ({
131
+ ...require_plugins.supabaseDatabase(config),
132
+ generateSchema: ((version, _name, target = _hot_updater_plugin_core.coreTarget) => {
133
+ if (version !== "latest" && version !== _hot_updater_plugin_core.coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
134
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
135
+ return {
136
+ code: supabaseSchemaSql(target),
137
+ path: `supabase/migrations/${timestamp}_hot-updater.sql`
138
+ };
139
+ })
140
+ });
141
+ //#endregion
142
+ exports.plugins = require_plugins.plugins;
143
+ exports.supabaseDatabase = supabaseDatabase;
144
+ exports.supabaseStorage = require_plugins.supabaseStorage;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,14 @@
1
- import { n as supabaseDatabase, r as SupabaseServiceRoleConfig, t as SupabaseDatabaseConfig } from "./supabaseDatabase-3d90EtUD.cjs";
2
- import { StoragePluginWith } from "@hot-updater/plugin-core";
1
+ import { i as plugins, r as SupabaseServiceRoleConfig, t as SupabaseDatabaseConfig } from "./supabaseDatabase-DVb3-y2x.cjs";
2
+ import { StorageAdapterWith, ToolingDatabase } from "@hot-updater/plugin-core";
3
+ //#region src/supabaseMigration.d.ts
4
+ /**
5
+ * Hot Updater's database on Supabase, with the migration `hot-updater db
6
+ * generate` writes to `supabase/migrations`: core's tables, the server's
7
+ * plugin tables, the apply RPC that may name them, and their settings rows.
8
+ * `supabase db push` applies it.
9
+ */
10
+ declare const supabaseDatabase: (config: SupabaseDatabaseConfig) => ToolingDatabase;
11
+ //#endregion
3
12
  //#region src/supabaseStorage.d.ts
4
13
  type SupabaseStorageConfig = SupabaseServiceRoleConfig & {
5
14
  bucketName: string;
@@ -8,6 +17,6 @@ type SupabaseStorageConfig = SupabaseServiceRoleConfig & {
8
17
  /** Signed download URL lifetime in seconds. @default 3600 */
9
18
  signedUrlExpiresIn?: number;
10
19
  };
11
- declare const supabaseStorage: (config: SupabaseStorageConfig) => StoragePluginWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete">;
20
+ declare const supabaseStorage: (config: SupabaseStorageConfig) => StorageAdapterWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete">;
12
21
  //#endregion
13
- export { SupabaseDatabaseConfig, SupabaseStorageConfig, supabaseDatabase, supabaseStorage };
22
+ export { type SupabaseDatabaseConfig, SupabaseStorageConfig, plugins, supabaseDatabase, supabaseStorage };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,14 @@
1
- import { n as supabaseDatabase, r as SupabaseServiceRoleConfig, t as SupabaseDatabaseConfig } from "./supabaseDatabase-3d90EtUD.mjs";
2
- import { StoragePluginWith } from "@hot-updater/plugin-core";
1
+ import { i as plugins, r as SupabaseServiceRoleConfig, t as SupabaseDatabaseConfig } from "./supabaseDatabase-DVb3-y2x.mjs";
2
+ import { StorageAdapterWith, ToolingDatabase } from "@hot-updater/plugin-core";
3
+ //#region src/supabaseMigration.d.ts
4
+ /**
5
+ * Hot Updater's database on Supabase, with the migration `hot-updater db
6
+ * generate` writes to `supabase/migrations`: core's tables, the server's
7
+ * plugin tables, the apply RPC that may name them, and their settings rows.
8
+ * `supabase db push` applies it.
9
+ */
10
+ declare const supabaseDatabase: (config: SupabaseDatabaseConfig) => ToolingDatabase;
11
+ //#endregion
3
12
  //#region src/supabaseStorage.d.ts
4
13
  type SupabaseStorageConfig = SupabaseServiceRoleConfig & {
5
14
  bucketName: string;
@@ -8,6 +17,6 @@ type SupabaseStorageConfig = SupabaseServiceRoleConfig & {
8
17
  /** Signed download URL lifetime in seconds. @default 3600 */
9
18
  signedUrlExpiresIn?: number;
10
19
  };
11
- declare const supabaseStorage: (config: SupabaseStorageConfig) => StoragePluginWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete">;
20
+ declare const supabaseStorage: (config: SupabaseStorageConfig) => StorageAdapterWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete">;
12
21
  //#endregion
13
- export { SupabaseDatabaseConfig, SupabaseStorageConfig, supabaseDatabase, supabaseStorage };
22
+ export { type SupabaseDatabaseConfig, SupabaseStorageConfig, plugins, supabaseDatabase, supabaseStorage };
package/dist/index.mjs CHANGED
@@ -1,2 +1,141 @@
1
- import { n as supabaseDatabase, t as supabaseStorage } from "./supabaseStorage-CjteZ2zX.mjs";
2
- export { supabaseDatabase, supabaseStorage };
1
+ import { a as SUPABASE_TABLE_PREFIX, i as SUPABASE_APPLY_FUNCTION, n as supabaseStorage, r as supabaseDatabase$1, t as plugins } from "./plugins-Dj-4Pk3j.mjs";
2
+ import { SETTINGS_TABLE, WRITE_GUARD_TABLE, coreSettings, coreTarget, createTableStatements, generateEngineSql, isMultiIndex } from "@hot-updater/plugin-core";
3
+ //#region src/supabaseSchema.ts
4
+ /** Every table the apply RPC may name: models, index tables, settings, and the write guard. */
5
+ const supabaseTableNames = (schema = coreTarget.schema) => [
6
+ ...schema.tables.flatMap((table) => [table.name, ...table.indexes.filter((index) => isMultiIndex(table, index)).map((index) => `${table.name}__${index.name}`)]),
7
+ SETTINGS_TABLE.name,
8
+ WRITE_GUARD_TABLE.name
9
+ ].map((name) => SUPABASE_TABLE_PREFIX + name);
10
+ /** The words the SQL core's statements use besides quoted names, numbers, and operators. */
11
+ const KEYWORDS = [
12
+ "SELECT",
13
+ "FROM",
14
+ "WHERE",
15
+ "AND",
16
+ "OR",
17
+ "NOT",
18
+ "EXISTS",
19
+ "INSERT",
20
+ "INTO",
21
+ "VALUES",
22
+ "UPDATE",
23
+ "SET",
24
+ "DELETE",
25
+ "ORDER",
26
+ "BY",
27
+ "ASC",
28
+ "DESC",
29
+ "LIMIT",
30
+ "IS",
31
+ "NULL",
32
+ "ON",
33
+ "CONFLICT",
34
+ "DO",
35
+ "JOIN",
36
+ "FOR",
37
+ "bigint",
38
+ "double",
39
+ "precision",
40
+ "boolean",
41
+ "jsonb",
42
+ "i",
43
+ "b"
44
+ ];
45
+ const literal = (value) => `'${value.replaceAll("'", "''")}'`;
46
+ /**
47
+ * Runs a batch of the SQL core's statements in the caller's transaction and
48
+ * privileges (service role only), with every parameter read from one jsonb
49
+ * array. Only the core's statement shapes on Hot Updater's tables pass: one
50
+ * statement each, no literal, comment, or function call, and no word the
51
+ * core does not write.
52
+ */
53
+ const applyFunction = (tables) => `CREATE OR REPLACE FUNCTION public.${SUPABASE_APPLY_FUNCTION}(p_statements jsonb)
54
+ RETURNS jsonb
55
+ LANGUAGE plpgsql
56
+ SECURITY INVOKER
57
+ SET search_path = public, pg_temp
58
+ AS $apply$
59
+ DECLARE
60
+ item jsonb;
61
+ statement text;
62
+ shape text;
63
+ target text;
64
+ found record;
65
+ rows jsonb;
66
+ changed bigint;
67
+ results jsonb := '[]'::jsonb;
68
+ BEGIN
69
+ FOR item IN SELECT value FROM jsonb_array_elements(p_statements) LOOP
70
+ statement := item->>'sql';
71
+ FOR target IN SELECT (regexp_matches(statement, '(?:FROM|INTO|UPDATE|JOIN) "([^"]+)"', 'g'))[1] LOOP
72
+ IF NOT target = ANY (ARRAY[${tables.map(literal).join(", ")}]) THEN
73
+ RAISE EXCEPTION '${SUPABASE_APPLY_FUNCTION}: % is not a Hot Updater table', target USING ERRCODE = '42501';
74
+ END IF;
75
+ END LOOP;
76
+ shape := regexp_replace(regexp_replace(statement, '^INSERT INTO "[^"]+" \\(', 'INSERT INTO ('), '"[^"]*"', '"', 'g');
77
+ IF shape !~ '^(SELECT|INSERT INTO|UPDATE|DELETE FROM) '
78
+ OR shape ~ '"[[:space:]]*\\('
79
+ OR shape ~ '[;''\\\\]|--|/\\*'
80
+ OR regexp_replace(shape, '\\m(${KEYWORDS.join("|")})\\M', '', 'gi') !~ '^[[:space:]"(),.*=<>+0-9$:-]*$' THEN
81
+ RAISE EXCEPTION '${SUPABASE_APPLY_FUNCTION}: statement not allowed' USING ERRCODE = '42501';
82
+ END IF;
83
+ IF statement ~ '^SELECT ' THEN
84
+ rows := '[]'::jsonb;
85
+ FOR found IN EXECUTE statement USING item->'params' LOOP
86
+ rows := rows || jsonb_build_array(to_jsonb(found));
87
+ END LOOP;
88
+ results := results || jsonb_build_array(jsonb_build_object('rows', rows, 'changes', 0));
89
+ ELSE
90
+ EXECUTE statement USING item->'params';
91
+ GET DIAGNOSTICS changed = ROW_COUNT;
92
+ results := results || jsonb_build_array(jsonb_build_object('rows', '[]'::jsonb, 'changes', changed));
93
+ END IF;
94
+ END LOOP;
95
+ RETURN results;
96
+ END;
97
+ $apply$`;
98
+ /**
99
+ * Supabase's migration: the shared SQL schema under the table prefix, the
100
+ * write guard, row-level security on every table (no policy, so only the
101
+ * service role reads or writes), the apply RPC for the service role alone,
102
+ * and the settings rows last. Every statement can run again, so a migration
103
+ * for a server's plugin tables repeats core's.
104
+ */
105
+ const supabaseSchemaStatements = ({ schema, settings: expected } = coreTarget) => {
106
+ const engine = generateEngineSql("postgresql", schema, expected, { tablePrefix: SUPABASE_TABLE_PREFIX });
107
+ const settings = engine.slice(-Object.keys(expected).length);
108
+ const tables = supabaseTableNames(schema);
109
+ const apply = `public.${SUPABASE_APPLY_FUNCTION}(jsonb)`;
110
+ return [
111
+ ...createTableStatements("postgresql", [WRITE_GUARD_TABLE], SUPABASE_TABLE_PREFIX),
112
+ ...engine.slice(0, -settings.length),
113
+ ...tables.map((table) => `ALTER TABLE "${table}" ENABLE ROW LEVEL SECURITY`),
114
+ applyFunction(tables),
115
+ `REVOKE EXECUTE ON FUNCTION ${apply} FROM PUBLIC, anon, authenticated`,
116
+ `GRANT EXECUTE ON FUNCTION ${apply} TO service_role`,
117
+ ...settings
118
+ ];
119
+ };
120
+ const supabaseSchemaSql = (target) => `-- HotUpdater.schema\n\n${supabaseSchemaStatements(target).map((statement) => `${statement};`).join("\n\n")}\n`;
121
+ //#endregion
122
+ //#region src/supabaseMigration.ts
123
+ /**
124
+ * Hot Updater's database on Supabase, with the migration `hot-updater db
125
+ * generate` writes to `supabase/migrations`: core's tables, the server's
126
+ * plugin tables, the apply RPC that may name them, and their settings rows.
127
+ * `supabase db push` applies it.
128
+ */
129
+ const supabaseDatabase = (config) => ({
130
+ ...supabaseDatabase$1(config),
131
+ generateSchema: ((version, _name, target = coreTarget) => {
132
+ if (version !== "latest" && version !== coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
133
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
134
+ return {
135
+ code: supabaseSchemaSql(target),
136
+ path: `supabase/migrations/${timestamp}_hot-updater.sql`
137
+ };
138
+ })
139
+ });
140
+ //#endregion
141
+ export { plugins, supabaseDatabase, supabaseStorage };