@hot-updater/supabase 1.0.0-rc.16 → 1.0.0-rc.18

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.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as plugins, n as supabaseDatabase, t as SupabaseDatabaseConfig } from "./supabaseDatabase-CGDLVEvG.cjs";
1
+ import { i as plugins, n as supabaseDatabase, t as SupabaseDatabaseConfig } from "./supabaseDatabase-C0-IshLi.cjs";
2
2
  import { StoragePluginWith } from "@hot-updater/plugin-core";
3
3
  //#region src/supabaseEdgeFunctionStorage.d.ts
4
4
  interface SupabaseEdgeFunctionStorageConfig {
package/dist/edge.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as plugins, n as supabaseDatabase, t as SupabaseDatabaseConfig } from "./supabaseDatabase-CGDLVEvG.mjs";
1
+ import { i as plugins, n as supabaseDatabase, t as SupabaseDatabaseConfig } from "./supabaseDatabase-C0-IshLi.mjs";
2
2
  import { StoragePluginWith } from "@hot-updater/plugin-core";
3
3
  //#region src/supabaseEdgeFunctionStorage.d.ts
4
4
  interface SupabaseEdgeFunctionStorageConfig {
@@ -7085,35 +7085,12 @@ const assertSkippedConfigDoesNotUseLegacySupabaseKey = async (configWriteResult)
7085
7085
  _hot_updater_cli_tools.p.log.message("Update it to use 'supabaseServiceRoleKey' with 'HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY', then run init again.");
7086
7086
  process.exit(1);
7087
7087
  };
7088
- const SOURCE_TEMPLATE = `// add this to your App.tsx
7089
- import { HotUpdater } from "@hot-updater/react-native";
7090
-
7091
- function App() {
7092
- return null; // Replace with your app root
7093
- }
7094
-
7095
- HotUpdater.init({
7096
- baseURL: "%%source%%",
7097
- requestHeaders: {
7098
- "x-api-key": %%apiKey%%,
7099
- },
7100
- });
7101
-
7102
- // Call HotUpdater.checkForUpdate({ updateStrategy: "appVersion" })
7103
- // when your app is ready to check.
7104
- export default App;`;
7105
- const getSupabaseReactNativeSource = ({ apiKey, functionName, projectId }) => (0, _hot_updater_cli_tools.transformTemplate)(SOURCE_TEMPLATE, {
7106
- apiKey: JSON.stringify(apiKey),
7107
- source: `https://${projectId}.supabase.co/functions/v1/${functionName}`
7108
- });
7088
+ /** The Edge Function URL an app sets as its baseURL. */
7089
+ const getSupabaseFunctionUrl = ({ functionName, projectId }) => `https://${projectId}.supabase.co/functions/v1/${functionName}`;
7109
7090
  const reportSupabaseOriginCatalogReady = () => {
7110
7091
  _hot_updater_cli_tools.p.log.success("Release catalog endpoint is ready in origin-only mode.");
7111
7092
  _hot_updater_cli_tools.p.log.info("Catalog checks still invoke the Supabase Edge Function.");
7112
7093
  };
7113
- const reportSupabaseApiKey = (apiKey) => {
7114
- _hot_updater_cli_tools.p.note(apiKey, "API Key");
7115
- _hot_updater_cli_tools.p.log.message("Store this API key separately in a secure place.");
7116
- };
7117
7094
  const resolvePackageExportPath = async (packageName, exportName) => {
7118
7095
  const packageJsonPath = require$1.resolve(`${packageName}/package.json`);
7119
7096
  const exportTarget = JSON.parse(await fs_promises.default.readFile(packageJsonPath, "utf-8")).exports?.[exportName];
@@ -7656,13 +7633,13 @@ const runInitWithoutCliMetadata = async ({ build, envFile }) => {
7656
7633
  supabaseServiceRoleKey: projectAccess.serviceRoleApiKey,
7657
7634
  supabaseUrl: `https://${project.id}.supabase.co`
7658
7635
  });
7659
- let apiKey;
7636
+ let credential;
7660
7637
  try {
7661
- apiKey = (await (0, _hot_updater_server_db.createDatabasePluginApis)(databasePlugin, plugins).apiKeys.provision({
7662
- existingApiKey: initInputEnv.HOT_UPDATER_API_KEY,
7638
+ credential = await (0, _hot_updater_server_db.provisionClientCredential)(databasePlugin, plugins, {
7639
+ env: initInputEnv,
7663
7640
  name: "Supabase init"
7664
- })).apiKey;
7665
- await (0, _hot_updater_cli_tools.makeEnv)({ HOT_UPDATER_API_KEY: apiKey });
7641
+ });
7642
+ if (credential !== void 0) await (0, _hot_updater_cli_tools.makeEnv)({ [credential.env]: credential.value });
7666
7643
  } finally {
7667
7644
  await databasePlugin.dispose?.();
7668
7645
  }
@@ -7675,12 +7652,14 @@ const runInitWithoutCliMetadata = async ({ build, envFile }) => {
7675
7652
  else if (configWriteResult.status === "merged") _hot_updater_cli_tools.p.log.success("Updated 'hot-updater.config.ts' file with Supabase settings.");
7676
7653
  else _hot_updater_cli_tools.p.log.warn(`Kept existing 'hot-updater.config.ts' unchanged: ${configWriteResult.reason}`);
7677
7654
  await (0, _hot_updater_cli_tools.generateHotUpdaterPlugins)("@hot-updater/supabase");
7678
- _hot_updater_cli_tools.p.note(getSupabaseReactNativeSource({
7679
- apiKey,
7680
- functionName,
7681
- projectId: project.id
7682
- }));
7683
- reportSupabaseApiKey(apiKey);
7655
+ (0, _hot_updater_cli_tools.printAppSetup)({
7656
+ baseURL: getSupabaseFunctionUrl({
7657
+ functionName,
7658
+ projectId: project.id
7659
+ }),
7660
+ ...credential === void 0 ? {} : { credential },
7661
+ clientPlugins: (0, _hot_updater_server_db.clientPluginsOf)(plugins)
7662
+ });
7684
7663
  reportSupabaseOriginCatalogReady();
7685
7664
  _hot_updater_cli_tools.p.log.message(`Next step: ${(0, _hot_updater_cli_tools.link)("https://hot-updater.dev/docs/managed/supabase#step-4-add-hotupdater-to-your-project")}`);
7686
7665
  _hot_updater_cli_tools.p.log.success("Done! 🎉");
@@ -7689,9 +7668,8 @@ const runInit = (options) => withSupabaseCliMetadataCleanup(process.cwd(), () =>
7689
7668
  //#endregion
7690
7669
  exports.createSelectedBucket = createSelectedBucket;
7691
7670
  exports.getLegacySupabaseConfigReference = getLegacySupabaseConfigReference;
7671
+ exports.getSupabaseFunctionUrl = getSupabaseFunctionUrl;
7692
7672
  exports.getSupabaseProjectAccess = getSupabaseProjectAccess;
7693
- exports.getSupabaseReactNativeSource = getSupabaseReactNativeSource;
7694
- exports.reportSupabaseApiKey = reportSupabaseApiKey;
7695
7673
  exports.reportSupabaseOriginCatalogReady = reportSupabaseOriginCatalogReady;
7696
7674
  exports.resolveEdgeFunctionDenoConfig = resolveEdgeFunctionDenoConfig;
7697
7675
  exports.runInit = runInit;
@@ -45,13 +45,12 @@ interface SupabaseManagementApi {
45
45
  //#endregion
46
46
  //#region iac/index.d.ts
47
47
  declare const getLegacySupabaseConfigReference: (configText: string) => "HOT_UPDATER_SUPABASE_ANON_KEY" | "supabaseAnonKey" | null;
48
- declare const getSupabaseReactNativeSource: ({ apiKey, functionName, projectId }: {
49
- readonly apiKey: string;
48
+ /** The Edge Function URL an app sets as its baseURL. */
49
+ declare const getSupabaseFunctionUrl: ({ functionName, projectId }: {
50
50
  readonly functionName: string;
51
51
  readonly projectId: string;
52
52
  }) => string;
53
53
  declare const reportSupabaseOriginCatalogReady: () => void;
54
- declare const reportSupabaseApiKey: (apiKey: string) => void;
55
54
  declare const resolveEdgeFunctionDenoConfig: (targetDir: string) => Promise<{
56
55
  imports: Record<string, string>;
57
56
  }>;
@@ -99,4 +98,4 @@ declare const getSupabaseProjectAccess: ({ accessToken, managementApi, project,
99
98
  declare const withSupabaseCliMetadataCleanup: <Result>(cwd: string, operation: () => Promise<Result>) => Promise<Result>;
100
99
  declare const runInit: (options: RunInitOptions) => Promise<void>;
101
100
  //#endregion
102
- export { SupabaseBucketSelection, SupabaseProjectSelection, createSelectedBucket, getLegacySupabaseConfigReference, getSupabaseProjectAccess, getSupabaseReactNativeSource, reportSupabaseApiKey, reportSupabaseOriginCatalogReady, resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject, waitForSupabaseProjectReady, waitForSupabaseSchemaReady, withSupabaseCliMetadataCleanup };
101
+ export { SupabaseBucketSelection, SupabaseProjectSelection, createSelectedBucket, getLegacySupabaseConfigReference, getSupabaseFunctionUrl, getSupabaseProjectAccess, reportSupabaseOriginCatalogReady, resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject, waitForSupabaseProjectReady, waitForSupabaseSchemaReady, withSupabaseCliMetadataCleanup };
@@ -45,13 +45,12 @@ interface SupabaseManagementApi {
45
45
  //#endregion
46
46
  //#region iac/index.d.ts
47
47
  declare const getLegacySupabaseConfigReference: (configText: string) => "HOT_UPDATER_SUPABASE_ANON_KEY" | "supabaseAnonKey" | null;
48
- declare const getSupabaseReactNativeSource: ({ apiKey, functionName, projectId }: {
49
- readonly apiKey: string;
48
+ /** The Edge Function URL an app sets as its baseURL. */
49
+ declare const getSupabaseFunctionUrl: ({ functionName, projectId }: {
50
50
  readonly functionName: string;
51
51
  readonly projectId: string;
52
52
  }) => string;
53
53
  declare const reportSupabaseOriginCatalogReady: () => void;
54
- declare const reportSupabaseApiKey: (apiKey: string) => void;
55
54
  declare const resolveEdgeFunctionDenoConfig: (targetDir: string) => Promise<{
56
55
  imports: Record<string, string>;
57
56
  }>;
@@ -99,4 +98,4 @@ declare const getSupabaseProjectAccess: ({ accessToken, managementApi, project,
99
98
  declare const withSupabaseCliMetadataCleanup: <Result>(cwd: string, operation: () => Promise<Result>) => Promise<Result>;
100
99
  declare const runInit: (options: RunInitOptions) => Promise<void>;
101
100
  //#endregion
102
- export { SupabaseBucketSelection, SupabaseProjectSelection, createSelectedBucket, getLegacySupabaseConfigReference, getSupabaseProjectAccess, getSupabaseReactNativeSource, reportSupabaseApiKey, reportSupabaseOriginCatalogReady, resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject, waitForSupabaseProjectReady, waitForSupabaseSchemaReady, withSupabaseCliMetadataCleanup };
101
+ export { SupabaseBucketSelection, SupabaseProjectSelection, createSelectedBucket, getLegacySupabaseConfigReference, getSupabaseFunctionUrl, getSupabaseProjectAccess, reportSupabaseOriginCatalogReady, resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject, waitForSupabaseProjectReady, waitForSupabaseSchemaReady, withSupabaseCliMetadataCleanup };
@@ -1,8 +1,8 @@
1
1
  import { createRequire } from "node:module";
2
2
  import fs from "fs/promises";
3
3
  import path from "path";
4
- import { ConfigBuilder, InitError, MissingInitInputsError, assertInfrastructureGenerationAtUrl, assertInitInputs, assertInitProviderInputs, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, generateHotUpdaterPlugins, getHotUpdaterEnvValue, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, readHotUpdaterInitEnv, resolveInitProviderInput, resolvePackageVersion, transformEnv, transformTemplate, writeHotUpdaterConfig } from "@hot-updater/cli-tools";
5
- import { createDatabasePluginApis } from "@hot-updater/server/db";
4
+ import { ConfigBuilder, InitError, MissingInitInputsError, assertInfrastructureGenerationAtUrl, assertInitInputs, assertInitProviderInputs, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, generateHotUpdaterPlugins, getHotUpdaterEnvValue, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, printAppSetup, readHotUpdaterInitEnv, resolveInitProviderInput, resolvePackageVersion, transformEnv, transformTemplate, writeHotUpdaterConfig } from "@hot-updater/cli-tools";
5
+ import { clientPluginsOf, provisionClientCredential } from "@hot-updater/server/db";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
8
8
  import { StringDecoder } from "node:string_decoder";
@@ -7080,35 +7080,12 @@ const assertSkippedConfigDoesNotUseLegacySupabaseKey = async (configWriteResult)
7080
7080
  p.log.message("Update it to use 'supabaseServiceRoleKey' with 'HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY', then run init again.");
7081
7081
  process.exit(1);
7082
7082
  };
7083
- const SOURCE_TEMPLATE = `// add this to your App.tsx
7084
- import { HotUpdater } from "@hot-updater/react-native";
7085
-
7086
- function App() {
7087
- return null; // Replace with your app root
7088
- }
7089
-
7090
- HotUpdater.init({
7091
- baseURL: "%%source%%",
7092
- requestHeaders: {
7093
- "x-api-key": %%apiKey%%,
7094
- },
7095
- });
7096
-
7097
- // Call HotUpdater.checkForUpdate({ updateStrategy: "appVersion" })
7098
- // when your app is ready to check.
7099
- export default App;`;
7100
- const getSupabaseReactNativeSource = ({ apiKey, functionName, projectId }) => transformTemplate(SOURCE_TEMPLATE, {
7101
- apiKey: JSON.stringify(apiKey),
7102
- source: `https://${projectId}.supabase.co/functions/v1/${functionName}`
7103
- });
7083
+ /** The Edge Function URL an app sets as its baseURL. */
7084
+ const getSupabaseFunctionUrl = ({ functionName, projectId }) => `https://${projectId}.supabase.co/functions/v1/${functionName}`;
7104
7085
  const reportSupabaseOriginCatalogReady = () => {
7105
7086
  p.log.success("Release catalog endpoint is ready in origin-only mode.");
7106
7087
  p.log.info("Catalog checks still invoke the Supabase Edge Function.");
7107
7088
  };
7108
- const reportSupabaseApiKey = (apiKey) => {
7109
- p.note(apiKey, "API Key");
7110
- p.log.message("Store this API key separately in a secure place.");
7111
- };
7112
7089
  const resolvePackageExportPath = async (packageName, exportName) => {
7113
7090
  const packageJsonPath = require$1.resolve(`${packageName}/package.json`);
7114
7091
  const exportTarget = JSON.parse(await fs.readFile(packageJsonPath, "utf-8")).exports?.[exportName];
@@ -7651,13 +7628,13 @@ const runInitWithoutCliMetadata = async ({ build, envFile }) => {
7651
7628
  supabaseServiceRoleKey: projectAccess.serviceRoleApiKey,
7652
7629
  supabaseUrl: `https://${project.id}.supabase.co`
7653
7630
  });
7654
- let apiKey;
7631
+ let credential;
7655
7632
  try {
7656
- apiKey = (await createDatabasePluginApis(databasePlugin, plugins).apiKeys.provision({
7657
- existingApiKey: initInputEnv.HOT_UPDATER_API_KEY,
7633
+ credential = await provisionClientCredential(databasePlugin, plugins, {
7634
+ env: initInputEnv,
7658
7635
  name: "Supabase init"
7659
- })).apiKey;
7660
- await makeEnv({ HOT_UPDATER_API_KEY: apiKey });
7636
+ });
7637
+ if (credential !== void 0) await makeEnv({ [credential.env]: credential.value });
7661
7638
  } finally {
7662
7639
  await databasePlugin.dispose?.();
7663
7640
  }
@@ -7670,16 +7647,18 @@ const runInitWithoutCliMetadata = async ({ build, envFile }) => {
7670
7647
  else if (configWriteResult.status === "merged") p.log.success("Updated 'hot-updater.config.ts' file with Supabase settings.");
7671
7648
  else p.log.warn(`Kept existing 'hot-updater.config.ts' unchanged: ${configWriteResult.reason}`);
7672
7649
  await generateHotUpdaterPlugins("@hot-updater/supabase");
7673
- p.note(getSupabaseReactNativeSource({
7674
- apiKey,
7675
- functionName,
7676
- projectId: project.id
7677
- }));
7678
- reportSupabaseApiKey(apiKey);
7650
+ printAppSetup({
7651
+ baseURL: getSupabaseFunctionUrl({
7652
+ functionName,
7653
+ projectId: project.id
7654
+ }),
7655
+ ...credential === void 0 ? {} : { credential },
7656
+ clientPlugins: clientPluginsOf(plugins)
7657
+ });
7679
7658
  reportSupabaseOriginCatalogReady();
7680
7659
  p.log.message(`Next step: ${link("https://hot-updater.dev/docs/managed/supabase#step-4-add-hotupdater-to-your-project")}`);
7681
7660
  p.log.success("Done! 🎉");
7682
7661
  };
7683
7662
  const runInit = (options) => withSupabaseCliMetadataCleanup(process.cwd(), () => runInitWithoutCliMetadata(options));
7684
7663
  //#endregion
7685
- export { createSelectedBucket, getLegacySupabaseConfigReference, getSupabaseProjectAccess, getSupabaseReactNativeSource, reportSupabaseApiKey, reportSupabaseOriginCatalogReady, resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject, waitForSupabaseProjectReady, waitForSupabaseSchemaReady, withSupabaseCliMetadataCleanup };
7664
+ export { createSelectedBucket, getLegacySupabaseConfigReference, getSupabaseFunctionUrl, getSupabaseProjectAccess, reportSupabaseOriginCatalogReady, resolveEdgeFunctionDenoConfig, runInit, selectBucket, selectProject, waitForSupabaseProjectReady, waitForSupabaseSchemaReady, withSupabaseCliMetadataCleanup };
package/dist/index.cjs CHANGED
@@ -4,7 +4,7 @@ let _hot_updater_server_database = require("@hot-updater/server/database");
4
4
  let _hot_updater_server_db = require("@hot-updater/server/db");
5
5
  //#region src/supabaseSchema.ts
6
6
  /** Every table the apply RPC may name: models, index tables, settings, and the write guard. */
7
- const supabaseTableNames = (schema = _hot_updater_server_database.builtInTarget.schema) => [
7
+ const supabaseTableNames = (schema = _hot_updater_server_database.coreTarget.schema) => [
8
8
  ...schema.tables.flatMap((table) => [table.name, ...table.indexes.filter((index) => (0, _hot_updater_server_database.isMultiIndex)(table, index)).map((index) => `${table.name}__${index.name}`)]),
9
9
  _hot_updater_server_database.SETTINGS_TABLE.name,
10
10
  _hot_updater_server_database.WRITE_GUARD_TABLE.name
@@ -102,9 +102,9 @@ $apply$`;
102
102
  * write guard, row-level security on every table (no policy, so only the
103
103
  * service role reads or writes), the apply RPC for the service role alone,
104
104
  * and the settings rows last. Every statement can run again, so a migration
105
- * for a server's plugin tables repeats the built-in ones.
105
+ * for a server's plugin tables repeats core's.
106
106
  */
107
- const supabaseSchemaStatements = ({ schema, settings: expected } = _hot_updater_server_database.builtInTarget) => {
107
+ const supabaseSchemaStatements = ({ schema, settings: expected } = _hot_updater_server_database.coreTarget) => {
108
108
  const engine = (0, _hot_updater_server_db.generateEngineSql)("postgresql", schema, expected, { tablePrefix: require_plugins.SUPABASE_TABLE_PREFIX });
109
109
  const settings = engine.slice(-Object.keys(expected).length);
110
110
  const tables = supabaseTableNames(schema);
@@ -124,14 +124,14 @@ const supabaseSchemaSql = (target) => `-- HotUpdater.schema\n\n${supabaseSchemaS
124
124
  //#region src/supabaseMigration.ts
125
125
  /**
126
126
  * Hot Updater's database on Supabase, with the migration `hot-updater db
127
- * generate` writes to `supabase/migrations`: the built-in tables, the
128
- * server's plugin tables, the apply RPC that may name them, and their
129
- * settings rows. `supabase db push` applies it.
127
+ * generate` writes to `supabase/migrations`: core's tables, the server's
128
+ * plugin tables, the apply RPC that may name them, and their settings rows.
129
+ * `supabase db push` applies it.
130
130
  */
131
131
  const supabaseDatabase = (config) => ({
132
132
  ...require_plugins.supabaseDatabase(config),
133
- generateSchema: ((version, _name, target = _hot_updater_server_database.builtInTarget) => {
134
- if (version !== "latest" && version !== _hot_updater_server_database.builtInSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
133
+ generateSchema: ((version, _name, target = _hot_updater_server_database.coreTarget) => {
134
+ if (version !== "latest" && version !== _hot_updater_server_database.coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
135
135
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
136
136
  return {
137
137
  code: supabaseSchemaSql(target),
package/dist/index.d.cts CHANGED
@@ -1,12 +1,12 @@
1
- import { i as plugins, r as SupabaseServiceRoleConfig, t as SupabaseDatabaseConfig } from "./supabaseDatabase-CGDLVEvG.cjs";
1
+ import { i as plugins, r as SupabaseServiceRoleConfig, t as SupabaseDatabaseConfig } from "./supabaseDatabase-C0-IshLi.cjs";
2
2
  import { StoragePluginWith } from "@hot-updater/plugin-core";
3
3
  import { ToolingDatabase } from "@hot-updater/server/db";
4
4
  //#region src/supabaseMigration.d.ts
5
5
  /**
6
6
  * Hot Updater's database on Supabase, with the migration `hot-updater db
7
- * generate` writes to `supabase/migrations`: the built-in tables, the
8
- * server's plugin tables, the apply RPC that may name them, and their
9
- * settings rows. `supabase db push` applies it.
7
+ * generate` writes to `supabase/migrations`: core's tables, the server's
8
+ * plugin tables, the apply RPC that may name them, and their settings rows.
9
+ * `supabase db push` applies it.
10
10
  */
11
11
  declare const supabaseDatabase: (config: SupabaseDatabaseConfig) => ToolingDatabase;
12
12
  //#endregion
package/dist/index.d.mts CHANGED
@@ -1,12 +1,12 @@
1
- import { i as plugins, r as SupabaseServiceRoleConfig, t as SupabaseDatabaseConfig } from "./supabaseDatabase-CGDLVEvG.mjs";
1
+ import { i as plugins, r as SupabaseServiceRoleConfig, t as SupabaseDatabaseConfig } from "./supabaseDatabase-C0-IshLi.mjs";
2
2
  import { ToolingDatabase } from "@hot-updater/server/db";
3
3
  import { StoragePluginWith } from "@hot-updater/plugin-core";
4
4
  //#region src/supabaseMigration.d.ts
5
5
  /**
6
6
  * Hot Updater's database on Supabase, with the migration `hot-updater db
7
- * generate` writes to `supabase/migrations`: the built-in tables, the
8
- * server's plugin tables, the apply RPC that may name them, and their
9
- * settings rows. `supabase db push` applies it.
7
+ * generate` writes to `supabase/migrations`: core's tables, the server's
8
+ * plugin tables, the apply RPC that may name them, and their settings rows.
9
+ * `supabase db push` applies it.
10
10
  */
11
11
  declare const supabaseDatabase: (config: SupabaseDatabaseConfig) => ToolingDatabase;
12
12
  //#endregion
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import { a as SUPABASE_TABLE_PREFIX, i as SUPABASE_APPLY_FUNCTION, n as supabaseStorage, r as supabaseDatabase$1, t as plugins } from "./plugins--10vxSpf.mjs";
2
- import { SETTINGS_TABLE, WRITE_GUARD_TABLE, builtInSettings, builtInTarget, createTableStatements, isMultiIndex } from "@hot-updater/server/database";
2
+ import { SETTINGS_TABLE, WRITE_GUARD_TABLE, coreSettings, coreTarget, createTableStatements, isMultiIndex } from "@hot-updater/server/database";
3
3
  import { generateEngineSql } from "@hot-updater/server/db";
4
4
  //#region src/supabaseSchema.ts
5
5
  /** Every table the apply RPC may name: models, index tables, settings, and the write guard. */
6
- const supabaseTableNames = (schema = builtInTarget.schema) => [
6
+ const supabaseTableNames = (schema = coreTarget.schema) => [
7
7
  ...schema.tables.flatMap((table) => [table.name, ...table.indexes.filter((index) => isMultiIndex(table, index)).map((index) => `${table.name}__${index.name}`)]),
8
8
  SETTINGS_TABLE.name,
9
9
  WRITE_GUARD_TABLE.name
@@ -101,9 +101,9 @@ $apply$`;
101
101
  * write guard, row-level security on every table (no policy, so only the
102
102
  * service role reads or writes), the apply RPC for the service role alone,
103
103
  * and the settings rows last. Every statement can run again, so a migration
104
- * for a server's plugin tables repeats the built-in ones.
104
+ * for a server's plugin tables repeats core's.
105
105
  */
106
- const supabaseSchemaStatements = ({ schema, settings: expected } = builtInTarget) => {
106
+ const supabaseSchemaStatements = ({ schema, settings: expected } = coreTarget) => {
107
107
  const engine = generateEngineSql("postgresql", schema, expected, { tablePrefix: SUPABASE_TABLE_PREFIX });
108
108
  const settings = engine.slice(-Object.keys(expected).length);
109
109
  const tables = supabaseTableNames(schema);
@@ -123,14 +123,14 @@ const supabaseSchemaSql = (target) => `-- HotUpdater.schema\n\n${supabaseSchemaS
123
123
  //#region src/supabaseMigration.ts
124
124
  /**
125
125
  * Hot Updater's database on Supabase, with the migration `hot-updater db
126
- * generate` writes to `supabase/migrations`: the built-in tables, the
127
- * server's plugin tables, the apply RPC that may name them, and their
128
- * settings rows. `supabase db push` applies it.
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
129
  */
130
130
  const supabaseDatabase = (config) => ({
131
131
  ...supabaseDatabase$1(config),
132
- generateSchema: ((version, _name, target = builtInTarget) => {
133
- if (version !== "latest" && version !== builtInSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
132
+ generateSchema: ((version, _name, target = coreTarget) => {
133
+ if (version !== "latest" && version !== coreSettings["schema.core"]) throw new Error(`Invalid version ${version}`);
134
134
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
135
135
  return {
136
136
  code: supabaseSchemaSql(target),
@@ -133,12 +133,7 @@ declare const plugins: readonly [import("@hot-updater/server/plugins").HotUpdate
133
133
  readonly received_at_ms: {
134
134
  readonly type: "integer";
135
135
  };
136
- readonly current_release_id: {
137
- readonly type: "string";
138
- readonly maxLength: 36;
139
- readonly required: false;
140
- };
141
- }, {}, {
136
+ }, Readonly<Record<string, import("@hot-updater/server/database").FieldType>>, {
142
137
  readonly byUser: {
143
138
  readonly eq: readonly ["user_id"];
144
139
  readonly sort: readonly ["install_id"];
@@ -152,7 +147,7 @@ declare const plugins: readonly [import("@hot-updater/server/plugins").HotUpdate
152
147
  readonly bucket_start_ms: {
153
148
  readonly type: "integer";
154
149
  };
155
- }, "downloads" | "launches" | "failed_launches", {
150
+ }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
156
151
  readonly window: {
157
152
  readonly eq: readonly ["identity"];
158
153
  readonly sort: readonly ["bucket_start_ms"];
@@ -171,7 +166,63 @@ declare const plugins: readonly [import("@hot-updater/server/plugins").HotUpdate
171
166
  readonly eq: readonly ["identity"];
172
167
  readonly sort: readonly ["bucket_start_ms"];
173
168
  };
174
- }, "launch_users" | "activity_users", readonly ["identity", "bucket_start_ms"]>;
169
+ }, "launch_users" | "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
170
+ readonly insights_overview_daily: import("@hot-updater/server/database").AggregateDefinition<{
171
+ readonly identity: {
172
+ readonly type: "string";
173
+ readonly maxLength: 32;
174
+ };
175
+ readonly bucket_start_ms: {
176
+ readonly type: "integer";
177
+ };
178
+ }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
179
+ readonly window: {
180
+ readonly eq: readonly ["identity"];
181
+ readonly sort: readonly ["bucket_start_ms"];
182
+ };
183
+ }, never, readonly ["identity", "bucket_start_ms"]>;
184
+ readonly insights_sketches_daily: import("@hot-updater/server/database").AggregateDefinition<{
185
+ readonly identity: {
186
+ readonly type: "string";
187
+ readonly maxLength: 32;
188
+ };
189
+ readonly bucket_start_ms: {
190
+ readonly type: "integer";
191
+ };
192
+ }, never, {
193
+ readonly window: {
194
+ readonly eq: readonly ["identity"];
195
+ readonly sort: readonly ["bucket_start_ms"];
196
+ };
197
+ }, "launch_users" | "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
198
+ readonly insights_overview_lifetime: import("@hot-updater/server/database").AggregateDefinition<{
199
+ readonly identity: {
200
+ readonly type: "string";
201
+ readonly maxLength: 32;
202
+ };
203
+ readonly bucket_start_ms: {
204
+ readonly type: "integer";
205
+ };
206
+ }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
207
+ readonly window: {
208
+ readonly eq: readonly ["identity"];
209
+ readonly sort: readonly ["bucket_start_ms"];
210
+ };
211
+ }, never, readonly ["identity", "bucket_start_ms"]>;
212
+ readonly insights_sketches_lifetime: import("@hot-updater/server/database").AggregateDefinition<{
213
+ readonly identity: {
214
+ readonly type: "string";
215
+ readonly maxLength: 32;
216
+ };
217
+ readonly bucket_start_ms: {
218
+ readonly type: "integer";
219
+ };
220
+ }, never, {
221
+ readonly window: {
222
+ readonly eq: readonly ["identity"];
223
+ readonly sort: readonly ["bucket_start_ms"];
224
+ };
225
+ }, "failed_users", readonly ["identity", "bucket_start_ms"]>;
175
226
  readonly insights_distribution: import("@hot-updater/server/database").AggregateDefinition<{
176
227
  readonly channel: {
177
228
  readonly type: "string";
@@ -254,22 +305,52 @@ declare const plugins: readonly [import("@hot-updater/server/plugins").HotUpdate
254
305
  readonly sort: readonly ["bucket_start_ms"];
255
306
  };
256
307
  }, never, readonly ["platform", "channel", "type", "bundle_ref", "bucket_start_ms"]>;
308
+ readonly insights_failures: import("@hot-updater/server/database").AggregateDefinition<{
309
+ readonly platform: {
310
+ readonly type: "string";
311
+ readonly maxLength: 16;
312
+ };
313
+ readonly channel: {
314
+ readonly type: "string";
315
+ };
316
+ readonly bucket_start_ms: {
317
+ readonly type: "integer";
318
+ };
319
+ readonly release_id: {
320
+ readonly type: "string";
321
+ readonly maxLength: 36;
322
+ };
323
+ readonly stage: {
324
+ readonly type: "string";
325
+ readonly maxLength: 16;
326
+ };
327
+ readonly reason: {
328
+ readonly type: "string";
329
+ readonly maxLength: 64;
330
+ };
331
+ readonly detail: {
332
+ readonly type: "string";
333
+ readonly maxLength: 160;
334
+ };
335
+ }, "events", {
336
+ readonly byScope: {
337
+ readonly eq: readonly ["platform", "channel"];
338
+ readonly sort: readonly ["bucket_start_ms"];
339
+ };
340
+ }, never, readonly ["platform", "channel", "bucket_start_ms", "release_id", "stage", "reason", "detail"]>;
257
341
  }, {
258
342
  api: {
259
- recordEvent: (event: import("@hot-updater/plugin-core").BundleEventRow) => Promise<void>;
260
- listEvents: (input: import("@hot-updater/plugin-core").InsightsListEventsInput) => Promise<readonly import("@hot-updater/plugin-core").BundleEventRow[]>;
261
- findLatestEvents: (input: import("@hot-updater/plugin-core").InsightsFindLatestEventsInput) => Promise<readonly import("@hot-updater/plugin-core").BundleEventRow[]>;
262
- countLatestEvents: (input: import("@hot-updater/plugin-core").InsightsCountLatestEventsInput) => Promise<number>;
263
- countEvents: (input: import("@hot-updater/plugin-core").InsightsCountEventsInput) => Promise<number>;
264
- getReleaseActivity: (input: import("@hot-updater/plugin-core").InsightsGetReleaseActivityInput) => Promise<import("@hot-updater/plugin-core").InsightsGetReleaseActivityResult>;
265
- getAppUsage: (input: import("@hot-updater/plugin-core").InsightsGetAppUsageInput) => Promise<import("@hot-updater/plugin-core").InsightsGetAppUsageResult>;
343
+ retention: import("@hot-updater/server/plugins/insights").InsightsRetention;
344
+ recordEvent: (event: import("@hot-updater/server/plugins/insights").BundleEventRow) => Promise<void>;
345
+ listEvents: (input: import("@hot-updater/server/plugins/insights").InsightsListEventsInput) => Promise<readonly import("@hot-updater/server/plugins/insights").BundleEventRow[]>;
346
+ findLatestEvents: (input: import("@hot-updater/server/plugins/insights").InsightsFindLatestEventsInput) => Promise<readonly import("@hot-updater/server/plugins/insights").BundleEventRow[]>;
347
+ countLatestEvents: (input: import("@hot-updater/server/plugins/insights").InsightsCountLatestEventsInput) => Promise<number>;
348
+ countEvents: (input: import("@hot-updater/server/plugins/insights").InsightsCountEventsInput) => Promise<number>;
349
+ getReleaseActivity: (input: import("@hot-updater/server/plugins/insights").InsightsGetReleaseActivityInput) => Promise<import("@hot-updater/server/plugins/insights").InsightsGetReleaseActivityResult>;
350
+ getAppUsage: (input: import("@hot-updater/server/plugins/insights").InsightsGetAppUsageInput) => Promise<import("@hot-updater/server/plugins/insights").InsightsGetAppUsageResult>;
351
+ getUpdateFailures: (input: import("@hot-updater/server/plugins/insights").InsightsUpdateFailuresInput) => Promise<import("@hot-updater/server/plugins/insights").InsightsUpdateFailures>;
266
352
  };
267
- endpoints: {
268
- access: "client" | "admin";
269
- method: "POST" | "GET";
270
- path: "/events" | "/overview" | "/installations" | "/installations/:installId/events" | "/installations/:installId";
271
- handler: (request: Request, params: Readonly<Record<string, string>>) => Promise<Response>;
272
- }[];
353
+ endpoints: import("@hot-updater/server/plugins").PluginEndpoint[];
273
354
  }, {}>, import("@hot-updater/server/plugins").HotUpdaterPlugin<"apiKeys", {
274
355
  readonly api_keys: import("@hot-updater/server/database").TableDefinition<{
275
356
  readonly id: {
@@ -312,21 +393,21 @@ declare const plugins: readonly [import("@hot-updater/server/plugins").HotUpdate
312
393
  readonly apiKey: string;
313
394
  readonly name: string;
314
395
  readonly createdAtMs?: number;
315
- }) => Promise<import("@hot-updater/server").CreatedApiKey>;
396
+ }) => Promise<import("@hot-updater/server/plugins/api-keys").CreatedApiKey>;
316
397
  provision: (input: {
317
398
  readonly existingApiKey?: string;
318
399
  readonly name: string;
319
- }) => Promise<import("@hot-updater/server").CreatedApiKey>;
400
+ }) => Promise<import("@hot-updater/server/plugins/api-keys").CreatedApiKey>;
320
401
  create(input: {
321
402
  readonly name: string;
322
403
  }): Promise<{
323
404
  readonly apiKey: string;
324
- readonly record: import("@hot-updater/server").ApiKeyMetadata;
405
+ readonly record: import("@hot-updater/server/plugins/api-keys").ApiKeyMetadata;
325
406
  }>;
326
- list(): Promise<readonly import("@hot-updater/server").ApiKeyMetadata[]>;
407
+ list(): Promise<readonly import("@hot-updater/server/plugins/api-keys").ApiKeyMetadata[]>;
327
408
  revoke(input: {
328
409
  readonly id: string;
329
- }): Promise<import("@hot-updater/server").ApiKeyMetadata | null>;
410
+ }): Promise<import("@hot-updater/server/plugins/api-keys").ApiKeyMetadata | null>;
330
411
  };
331
412
  clientAuth: {
332
413
  varyHeaders: string[];
@@ -133,12 +133,7 @@ declare const plugins: readonly [import("@hot-updater/server/plugins").HotUpdate
133
133
  readonly received_at_ms: {
134
134
  readonly type: "integer";
135
135
  };
136
- readonly current_release_id: {
137
- readonly type: "string";
138
- readonly maxLength: 36;
139
- readonly required: false;
140
- };
141
- }, {}, {
136
+ }, Readonly<Record<string, import("@hot-updater/server/database").FieldType>>, {
142
137
  readonly byUser: {
143
138
  readonly eq: readonly ["user_id"];
144
139
  readonly sort: readonly ["install_id"];
@@ -152,7 +147,7 @@ declare const plugins: readonly [import("@hot-updater/server/plugins").HotUpdate
152
147
  readonly bucket_start_ms: {
153
148
  readonly type: "integer";
154
149
  };
155
- }, "downloads" | "launches" | "failed_launches", {
150
+ }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
156
151
  readonly window: {
157
152
  readonly eq: readonly ["identity"];
158
153
  readonly sort: readonly ["bucket_start_ms"];
@@ -171,7 +166,63 @@ declare const plugins: readonly [import("@hot-updater/server/plugins").HotUpdate
171
166
  readonly eq: readonly ["identity"];
172
167
  readonly sort: readonly ["bucket_start_ms"];
173
168
  };
174
- }, "launch_users" | "activity_users", readonly ["identity", "bucket_start_ms"]>;
169
+ }, "launch_users" | "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
170
+ readonly insights_overview_daily: import("@hot-updater/server/database").AggregateDefinition<{
171
+ readonly identity: {
172
+ readonly type: "string";
173
+ readonly maxLength: 32;
174
+ };
175
+ readonly bucket_start_ms: {
176
+ readonly type: "integer";
177
+ };
178
+ }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
179
+ readonly window: {
180
+ readonly eq: readonly ["identity"];
181
+ readonly sort: readonly ["bucket_start_ms"];
182
+ };
183
+ }, never, readonly ["identity", "bucket_start_ms"]>;
184
+ readonly insights_sketches_daily: import("@hot-updater/server/database").AggregateDefinition<{
185
+ readonly identity: {
186
+ readonly type: "string";
187
+ readonly maxLength: 32;
188
+ };
189
+ readonly bucket_start_ms: {
190
+ readonly type: "integer";
191
+ };
192
+ }, never, {
193
+ readonly window: {
194
+ readonly eq: readonly ["identity"];
195
+ readonly sort: readonly ["bucket_start_ms"];
196
+ };
197
+ }, "launch_users" | "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
198
+ readonly insights_overview_lifetime: import("@hot-updater/server/database").AggregateDefinition<{
199
+ readonly identity: {
200
+ readonly type: "string";
201
+ readonly maxLength: 32;
202
+ };
203
+ readonly bucket_start_ms: {
204
+ readonly type: "integer";
205
+ };
206
+ }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
207
+ readonly window: {
208
+ readonly eq: readonly ["identity"];
209
+ readonly sort: readonly ["bucket_start_ms"];
210
+ };
211
+ }, never, readonly ["identity", "bucket_start_ms"]>;
212
+ readonly insights_sketches_lifetime: import("@hot-updater/server/database").AggregateDefinition<{
213
+ readonly identity: {
214
+ readonly type: "string";
215
+ readonly maxLength: 32;
216
+ };
217
+ readonly bucket_start_ms: {
218
+ readonly type: "integer";
219
+ };
220
+ }, never, {
221
+ readonly window: {
222
+ readonly eq: readonly ["identity"];
223
+ readonly sort: readonly ["bucket_start_ms"];
224
+ };
225
+ }, "failed_users", readonly ["identity", "bucket_start_ms"]>;
175
226
  readonly insights_distribution: import("@hot-updater/server/database").AggregateDefinition<{
176
227
  readonly channel: {
177
228
  readonly type: "string";
@@ -254,22 +305,52 @@ declare const plugins: readonly [import("@hot-updater/server/plugins").HotUpdate
254
305
  readonly sort: readonly ["bucket_start_ms"];
255
306
  };
256
307
  }, never, readonly ["platform", "channel", "type", "bundle_ref", "bucket_start_ms"]>;
308
+ readonly insights_failures: import("@hot-updater/server/database").AggregateDefinition<{
309
+ readonly platform: {
310
+ readonly type: "string";
311
+ readonly maxLength: 16;
312
+ };
313
+ readonly channel: {
314
+ readonly type: "string";
315
+ };
316
+ readonly bucket_start_ms: {
317
+ readonly type: "integer";
318
+ };
319
+ readonly release_id: {
320
+ readonly type: "string";
321
+ readonly maxLength: 36;
322
+ };
323
+ readonly stage: {
324
+ readonly type: "string";
325
+ readonly maxLength: 16;
326
+ };
327
+ readonly reason: {
328
+ readonly type: "string";
329
+ readonly maxLength: 64;
330
+ };
331
+ readonly detail: {
332
+ readonly type: "string";
333
+ readonly maxLength: 160;
334
+ };
335
+ }, "events", {
336
+ readonly byScope: {
337
+ readonly eq: readonly ["platform", "channel"];
338
+ readonly sort: readonly ["bucket_start_ms"];
339
+ };
340
+ }, never, readonly ["platform", "channel", "bucket_start_ms", "release_id", "stage", "reason", "detail"]>;
257
341
  }, {
258
342
  api: {
259
- recordEvent: (event: import("@hot-updater/plugin-core").BundleEventRow) => Promise<void>;
260
- listEvents: (input: import("@hot-updater/plugin-core").InsightsListEventsInput) => Promise<readonly import("@hot-updater/plugin-core").BundleEventRow[]>;
261
- findLatestEvents: (input: import("@hot-updater/plugin-core").InsightsFindLatestEventsInput) => Promise<readonly import("@hot-updater/plugin-core").BundleEventRow[]>;
262
- countLatestEvents: (input: import("@hot-updater/plugin-core").InsightsCountLatestEventsInput) => Promise<number>;
263
- countEvents: (input: import("@hot-updater/plugin-core").InsightsCountEventsInput) => Promise<number>;
264
- getReleaseActivity: (input: import("@hot-updater/plugin-core").InsightsGetReleaseActivityInput) => Promise<import("@hot-updater/plugin-core").InsightsGetReleaseActivityResult>;
265
- getAppUsage: (input: import("@hot-updater/plugin-core").InsightsGetAppUsageInput) => Promise<import("@hot-updater/plugin-core").InsightsGetAppUsageResult>;
343
+ retention: import("@hot-updater/server/plugins/insights").InsightsRetention;
344
+ recordEvent: (event: import("@hot-updater/server/plugins/insights").BundleEventRow) => Promise<void>;
345
+ listEvents: (input: import("@hot-updater/server/plugins/insights").InsightsListEventsInput) => Promise<readonly import("@hot-updater/server/plugins/insights").BundleEventRow[]>;
346
+ findLatestEvents: (input: import("@hot-updater/server/plugins/insights").InsightsFindLatestEventsInput) => Promise<readonly import("@hot-updater/server/plugins/insights").BundleEventRow[]>;
347
+ countLatestEvents: (input: import("@hot-updater/server/plugins/insights").InsightsCountLatestEventsInput) => Promise<number>;
348
+ countEvents: (input: import("@hot-updater/server/plugins/insights").InsightsCountEventsInput) => Promise<number>;
349
+ getReleaseActivity: (input: import("@hot-updater/server/plugins/insights").InsightsGetReleaseActivityInput) => Promise<import("@hot-updater/server/plugins/insights").InsightsGetReleaseActivityResult>;
350
+ getAppUsage: (input: import("@hot-updater/server/plugins/insights").InsightsGetAppUsageInput) => Promise<import("@hot-updater/server/plugins/insights").InsightsGetAppUsageResult>;
351
+ getUpdateFailures: (input: import("@hot-updater/server/plugins/insights").InsightsUpdateFailuresInput) => Promise<import("@hot-updater/server/plugins/insights").InsightsUpdateFailures>;
266
352
  };
267
- endpoints: {
268
- access: "client" | "admin";
269
- method: "POST" | "GET";
270
- path: "/events" | "/overview" | "/installations" | "/installations/:installId/events" | "/installations/:installId";
271
- handler: (request: Request, params: Readonly<Record<string, string>>) => Promise<Response>;
272
- }[];
353
+ endpoints: import("@hot-updater/server/plugins").PluginEndpoint[];
273
354
  }, {}>, import("@hot-updater/server/plugins").HotUpdaterPlugin<"apiKeys", {
274
355
  readonly api_keys: import("@hot-updater/server/database").TableDefinition<{
275
356
  readonly id: {
@@ -312,21 +393,21 @@ declare const plugins: readonly [import("@hot-updater/server/plugins").HotUpdate
312
393
  readonly apiKey: string;
313
394
  readonly name: string;
314
395
  readonly createdAtMs?: number;
315
- }) => Promise<import("@hot-updater/server").CreatedApiKey>;
396
+ }) => Promise<import("@hot-updater/server/plugins/api-keys").CreatedApiKey>;
316
397
  provision: (input: {
317
398
  readonly existingApiKey?: string;
318
399
  readonly name: string;
319
- }) => Promise<import("@hot-updater/server").CreatedApiKey>;
400
+ }) => Promise<import("@hot-updater/server/plugins/api-keys").CreatedApiKey>;
320
401
  create(input: {
321
402
  readonly name: string;
322
403
  }): Promise<{
323
404
  readonly apiKey: string;
324
- readonly record: import("@hot-updater/server").ApiKeyMetadata;
405
+ readonly record: import("@hot-updater/server/plugins/api-keys").ApiKeyMetadata;
325
406
  }>;
326
- list(): Promise<readonly import("@hot-updater/server").ApiKeyMetadata[]>;
407
+ list(): Promise<readonly import("@hot-updater/server/plugins/api-keys").ApiKeyMetadata[]>;
327
408
  revoke(input: {
328
409
  readonly id: string;
329
- }): Promise<import("@hot-updater/server").ApiKeyMetadata | null>;
410
+ }): Promise<import("@hot-updater/server/plugins/api-keys").ApiKeyMetadata | null>;
330
411
  };
331
412
  clientAuth: {
332
413
  varyHeaders: string[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/supabase",
3
3
  "type": "module",
4
- "version": "1.0.0-rc.16",
4
+ "version": "1.0.0-rc.18",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.mjs",
@@ -55,10 +55,10 @@
55
55
  "@supabase/supabase-js": "2.76.1",
56
56
  "hono": "4.12.34",
57
57
  "uuidv7": "^1.0.2",
58
- "@hot-updater/plugin-core": "1.0.0-rc.16",
59
- "@hot-updater/cli-tools": "1.0.0-rc.16",
60
- "@hot-updater/server": "1.0.0-rc.16",
61
- "@hot-updater/core": "1.0.0-rc.15"
58
+ "@hot-updater/core": "1.0.0-rc.15",
59
+ "@hot-updater/plugin-core": "1.0.0-rc.17",
60
+ "@hot-updater/server": "1.0.0-rc.18",
61
+ "@hot-updater/cli-tools": "1.0.0-rc.17"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@electric-sql/pglite": "0.4.1",
@@ -68,9 +68,9 @@
68
68
  "execa": "9.5.2",
69
69
  "@types/node": "^20",
70
70
  "mime": "^4.0.4",
71
- "@hot-updater/mock": "1.0.0-rc.16",
72
- "@hot-updater/postgres": "1.0.0-rc.16",
73
- "@hot-updater/test-utils": "1.0.0-rc.16"
71
+ "@hot-updater/mock": "1.0.0-rc.17",
72
+ "@hot-updater/postgres": "1.0.0-rc.18",
73
+ "@hot-updater/test-utils": "1.0.0-rc.17"
74
74
  },
75
75
  "scripts": {
76
76
  "build": "tsdown",
@@ -15,17 +15,13 @@ import { fileURLToPath, pathToFileURL } from "node:url";
15
15
 
16
16
  import { resolvePackageVersion, transformEnv } from "@hot-updater/cli-tools";
17
17
  import type { Bundle } from "@hot-updater/core";
18
- import {
19
- type BundleEventRow,
20
- type HotUpdaterCoreApi,
21
- rowToBundle,
22
- } from "@hot-updater/plugin-core";
18
+ import { type HotUpdaterCoreApi, rowToBundle } from "@hot-updater/plugin-core";
23
19
  import { createHotUpdater } from "@hot-updater/server";
24
20
  import {
25
- builtInSchema,
26
21
  createDatabaseEngine,
27
22
  createSqlAdapter,
28
23
  type RetryOptions,
24
+ toolingTargetOf,
29
25
  } from "@hot-updater/server/database";
30
26
  import {
31
27
  createDatabaseCoreApi,
@@ -37,7 +33,9 @@ import {
37
33
  createInsightsModel,
38
34
  insights,
39
35
  insightsSchema,
36
+ type BundleEventRow,
40
37
  } from "@hot-updater/server/plugins/insights";
38
+ import { insightsTestSuite } from "@hot-updater/server/plugins/insights/testing";
41
39
  import {
42
40
  runContentionHarness,
43
41
  setupDatabaseTestSuite,
@@ -55,6 +53,7 @@ import {
55
53
  stopRuntime,
56
54
  waitForHttpOk,
57
55
  } from "../../../../packages/test-utils/src/runtimeProcess";
56
+ import { plugins } from "../../src/plugins";
58
57
  import { supabaseDatabase } from "../../src/supabaseDatabase";
59
58
  import { supabaseExecutor } from "../../src/supabaseExecutor";
60
59
  import {
@@ -174,7 +173,6 @@ const rolloutMove = (
174
173
  app_version: "1.0.0",
175
174
  channel: "production",
176
175
  metadata: {
177
- username: null,
178
176
  cohort: "1",
179
177
  update_strategy: "appVersion",
180
178
  fingerprint_hash: null,
@@ -251,9 +249,9 @@ describe.sequential("supabase edge runtime acceptance", () => {
251
249
  });
252
250
  };
253
251
 
254
- /** Empties every data table but `keep`; the settings rows stay. */
252
+ /** Empties every data table but `keep`, the managed plugins' included; the settings rows stay. */
255
253
  const truncateDataTables = (keep: readonly string[] = []) => {
256
- const tables = supabaseTableNames()
254
+ const tables = supabaseTableNames(toolingTargetOf(plugins).schema)
257
255
  .filter(
258
256
  (table) => table !== SUPABASE_SETTINGS_TABLE && !keep.includes(table),
259
257
  )
@@ -456,10 +454,14 @@ describe.sequential("supabase edge runtime acceptance", () => {
456
454
  clientAccess: "public",
457
455
  }).handlers,
458
456
  ),
459
- createInsightsModel: (database) =>
460
- createInsightsModel(
461
- createDatabasePluginApis(database, [insights()]).insights,
462
- ),
457
+ plugins: [
458
+ insightsTestSuite({
459
+ createModel: (database) =>
460
+ createInsightsModel(
461
+ createDatabasePluginApis(database, [insights()]).insights,
462
+ ),
463
+ }),
464
+ ],
463
465
  });
464
466
 
465
467
  it("returns one canonical Channel row under concurrent inserts", async () => {
@@ -728,7 +730,7 @@ describe.sequential("supabase edge runtime acceptance", () => {
728
730
  db: createDatabaseEngine({
729
731
  adapter:
730
732
  latencyMs > 0 ? withAdapterLatency(adapter, latencyMs) : adapter,
731
- schema: builtInSchema,
733
+ schema: toolingTargetOf([insights()]).schema,
732
734
  ...(retry === undefined ? {} : { retry }),
733
735
  }).database(module),
734
736
  // Insights never reads core.
@@ -46,13 +46,31 @@ CREATE TABLE IF NOT EXISTS "hot_updater_v1_bundle_events__byBundle" ("platform"
46
46
 
47
47
  CREATE INDEX IF NOT EXISTS "hot_updater_v1_bundle_events_byDay" ON "hot_updater_v1_bundle_events" ("day", "received_at_ms", "id");
48
48
 
49
- CREATE TABLE IF NOT EXISTS "hot_updater_v1_bundle_event_heads" ("install_id" varchar(255) COLLATE "C" NOT NULL, "id" varchar(36) COLLATE "C" NOT NULL, "type" varchar(32) COLLATE "C" NOT NULL, "user_id" varchar(255) COLLATE "C", "from_release_id" varchar(36) COLLATE "C", "from_bundle_id" varchar(36) COLLATE "C", "to_release_id" varchar(36) COLLATE "C", "to_bundle_id" varchar(36) COLLATE "C" NOT NULL, "platform" varchar(16) COLLATE "C" NOT NULL, "app_version" text COLLATE "C" NOT NULL, "channel" text COLLATE "C" NOT NULL, "metadata" jsonb NOT NULL, "received_at_ms" bigint NOT NULL, "current_release_id" varchar(36) COLLATE "C", "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("install_id"));
49
+ CREATE TABLE IF NOT EXISTS "hot_updater_v1_bundle_event_heads" ("install_id" varchar(255) COLLATE "C" NOT NULL, "id" varchar(36) COLLATE "C" NOT NULL, "type" varchar(32) COLLATE "C" NOT NULL, "user_id" varchar(255) COLLATE "C", "from_release_id" varchar(36) COLLATE "C", "from_bundle_id" varchar(36) COLLATE "C", "to_release_id" varchar(36) COLLATE "C", "to_bundle_id" varchar(36) COLLATE "C" NOT NULL, "platform" varchar(16) COLLATE "C" NOT NULL, "app_version" text COLLATE "C" NOT NULL, "channel" text COLLATE "C" NOT NULL, "metadata" jsonb NOT NULL, "received_at_ms" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("install_id"));
50
50
 
51
51
  CREATE INDEX IF NOT EXISTS "hot_updater_v1_bundle_event_heads_byUser" ON "hot_updater_v1_bundle_event_heads" ("user_id", "install_id");
52
52
 
53
- CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_overview" ("identity" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "downloads" bigint NOT NULL, "launches" bigint NOT NULL, "failed_launches" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard"));
53
+ CREATE INDEX IF NOT EXISTS "hot_updater_v1_bundle_event_heads__retention" ON "hot_updater_v1_bundle_event_heads" ("received_at_ms", "install_id");
54
54
 
55
- CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_sketches" ("identity" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "launch_users" text COLLATE "C", "activity_users" text COLLATE "C", "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard"));
55
+ CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_overview" ("identity" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "downloads" bigint NOT NULL, "launches" bigint NOT NULL, "failed_launches" bigint NOT NULL, "failed_updates" bigint NOT NULL, "patch_downloads" bigint NOT NULL, "patch_fallbacks" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard"));
56
+
57
+ CREATE INDEX IF NOT EXISTS "hot_updater_v1_insights_overview__retention" ON "hot_updater_v1_insights_overview" ("bucket_start_ms", "identity", "_shard");
58
+
59
+ CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_sketches" ("identity" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "launch_users" text COLLATE "C", "activity_users" text COLLATE "C", "failed_users" text COLLATE "C", "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard"));
60
+
61
+ CREATE INDEX IF NOT EXISTS "hot_updater_v1_insights_sketches__retention" ON "hot_updater_v1_insights_sketches" ("bucket_start_ms", "identity", "_shard");
62
+
63
+ CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_overview_daily" ("identity" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "downloads" bigint NOT NULL, "launches" bigint NOT NULL, "failed_launches" bigint NOT NULL, "failed_updates" bigint NOT NULL, "patch_downloads" bigint NOT NULL, "patch_fallbacks" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard"));
64
+
65
+ CREATE INDEX IF NOT EXISTS "hot_updater_v1_insights_overview_daily__retention" ON "hot_updater_v1_insights_overview_daily" ("bucket_start_ms", "identity", "_shard");
66
+
67
+ CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_sketches_daily" ("identity" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "launch_users" text COLLATE "C", "activity_users" text COLLATE "C", "failed_users" text COLLATE "C", "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard"));
68
+
69
+ CREATE INDEX IF NOT EXISTS "hot_updater_v1_insights_sketches_daily__retention" ON "hot_updater_v1_insights_sketches_daily" ("bucket_start_ms", "identity", "_shard");
70
+
71
+ CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_overview_lifetime" ("identity" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "downloads" bigint NOT NULL, "launches" bigint NOT NULL, "failed_launches" bigint NOT NULL, "failed_updates" bigint NOT NULL, "patch_downloads" bigint NOT NULL, "patch_fallbacks" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard"));
72
+
73
+ CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_sketches_lifetime" ("identity" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "failed_users" text COLLATE "C", "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard"));
56
74
 
57
75
  CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_distribution" ("channel" text COLLATE "C" NOT NULL, "platform" varchar(16) COLLATE "C" NOT NULL, "app_version" text COLLATE "C" NOT NULL, "release_id" varchar(36) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "latest_installations" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("channel", "platform", "app_version", "release_id", "bucket_start_ms", "_shard"));
58
76
 
@@ -60,10 +78,20 @@ CREATE INDEX IF NOT EXISTS "hot_updater_v1_insights_distribution_byScope" ON "ho
60
78
 
61
79
  CREATE INDEX IF NOT EXISTS "hot_updater_v1_insights_distribution_byVersion" ON "hot_updater_v1_insights_distribution" ("channel", "platform", "app_version", "bucket_start_ms", "release_id", "_shard");
62
80
 
81
+ CREATE INDEX IF NOT EXISTS "hot_updater_v1_insights_distribution__retention" ON "hot_updater_v1_insights_distribution" ("bucket_start_ms", "channel", "platform", "app_version", "release_id", "_shard");
82
+
63
83
  CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_latest_by_bundle" ("platform" varchar(16) COLLATE "C" NOT NULL, "channel" text COLLATE "C" NOT NULL, "bundle_field" varchar(16) COLLATE "C" NOT NULL, "bundle_id" varchar(36) COLLATE "C" NOT NULL, "type" varchar(32) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "installations" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("platform", "channel", "bundle_field", "bundle_id", "type", "bucket_start_ms", "_shard"));
64
84
 
85
+ CREATE INDEX IF NOT EXISTS "hot_updater_v1_insights_latest_by_bundle__retention" ON "hot_updater_v1_insights_latest_by_bundle" ("bucket_start_ms", "platform", "channel", "bundle_field", "bundle_id", "type", "_shard");
86
+
65
87
  CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_outcomes" ("platform" varchar(16) COLLATE "C" NOT NULL, "channel" text COLLATE "C" NOT NULL, "type" varchar(32) COLLATE "C" NOT NULL, "bundle_ref" varchar(41) COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "_shard" bigint NOT NULL, "events" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("platform", "channel", "type", "bundle_ref", "bucket_start_ms", "_shard"));
66
88
 
89
+ CREATE INDEX IF NOT EXISTS "hot_updater_v1_insights_outcomes__retention" ON "hot_updater_v1_insights_outcomes" ("bucket_start_ms", "platform", "channel", "type", "bundle_ref", "_shard");
90
+
91
+ CREATE TABLE IF NOT EXISTS "hot_updater_v1_insights_failures" ("platform" varchar(16) COLLATE "C" NOT NULL, "channel" text COLLATE "C" NOT NULL, "bucket_start_ms" bigint NOT NULL, "release_id" varchar(36) COLLATE "C" NOT NULL, "stage" varchar(16) COLLATE "C" NOT NULL, "reason" varchar(64) COLLATE "C" NOT NULL, "detail" varchar(160) COLLATE "C" NOT NULL, "_shard" bigint NOT NULL, "events" bigint NOT NULL, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("platform", "channel", "bucket_start_ms", "release_id", "stage", "reason", "detail", "_shard"));
92
+
93
+ CREATE INDEX IF NOT EXISTS "hot_updater_v1_insights_failures__retention" ON "hot_updater_v1_insights_failures" ("bucket_start_ms", "platform", "channel", "release_id", "stage", "reason", "detail", "_shard");
94
+
67
95
  CREATE TABLE IF NOT EXISTS "hot_updater_v1_api_keys" ("id" varchar(255) COLLATE "C" NOT NULL, "hash" varchar(64) COLLATE "C" NOT NULL, "name" varchar(64) COLLATE "C" NOT NULL, "prefix" varchar(16) COLLATE "C" NOT NULL, "role" varchar(16) COLLATE "C" NOT NULL, "created_at_ms" bigint NOT NULL, "revoked_at_ms" bigint, "_v" bigint NOT NULL DEFAULT 0, PRIMARY KEY ("id"));
68
96
 
69
97
  CREATE INDEX IF NOT EXISTS "hot_updater_v1_api_keys_byCreated" ON "hot_updater_v1_api_keys" ("created_at_ms", "id");
@@ -94,12 +122,22 @@ ALTER TABLE "hot_updater_v1_insights_overview" ENABLE ROW LEVEL SECURITY;
94
122
 
95
123
  ALTER TABLE "hot_updater_v1_insights_sketches" ENABLE ROW LEVEL SECURITY;
96
124
 
125
+ ALTER TABLE "hot_updater_v1_insights_overview_daily" ENABLE ROW LEVEL SECURITY;
126
+
127
+ ALTER TABLE "hot_updater_v1_insights_sketches_daily" ENABLE ROW LEVEL SECURITY;
128
+
129
+ ALTER TABLE "hot_updater_v1_insights_overview_lifetime" ENABLE ROW LEVEL SECURITY;
130
+
131
+ ALTER TABLE "hot_updater_v1_insights_sketches_lifetime" ENABLE ROW LEVEL SECURITY;
132
+
97
133
  ALTER TABLE "hot_updater_v1_insights_distribution" ENABLE ROW LEVEL SECURITY;
98
134
 
99
135
  ALTER TABLE "hot_updater_v1_insights_latest_by_bundle" ENABLE ROW LEVEL SECURITY;
100
136
 
101
137
  ALTER TABLE "hot_updater_v1_insights_outcomes" ENABLE ROW LEVEL SECURITY;
102
138
 
139
+ ALTER TABLE "hot_updater_v1_insights_failures" ENABLE ROW LEVEL SECURITY;
140
+
103
141
  ALTER TABLE "hot_updater_v1_api_keys" ENABLE ROW LEVEL SECURITY;
104
142
 
105
143
  ALTER TABLE "hot_updater_v1_private_hot_updater_settings" ENABLE ROW LEVEL SECURITY;
@@ -125,7 +163,7 @@ BEGIN
125
163
  FOR item IN SELECT value FROM jsonb_array_elements(p_statements) LOOP
126
164
  statement := item->>'sql';
127
165
  FOR target IN SELECT (regexp_matches(statement, '(?:FROM|INTO|UPDATE|JOIN) "([^"]+)"', 'g'))[1] LOOP
128
- IF NOT target = ANY (ARRAY['hot_updater_v1_bundles', 'hot_updater_v1_bundle_patches', 'hot_updater_v1_releases', 'hot_updater_v1_release_catalogs', 'hot_updater_v1_channels', 'hot_updater_v1_bundle_totals', 'hot_updater_v1_bundle_events', 'hot_updater_v1_bundle_events__byBundle', 'hot_updater_v1_bundle_event_heads', 'hot_updater_v1_insights_overview', 'hot_updater_v1_insights_sketches', 'hot_updater_v1_insights_distribution', 'hot_updater_v1_insights_latest_by_bundle', 'hot_updater_v1_insights_outcomes', 'hot_updater_v1_api_keys', 'hot_updater_v1_private_hot_updater_settings', 'hot_updater_v1__hu_write']) THEN
166
+ IF NOT target = ANY (ARRAY['hot_updater_v1_bundles', 'hot_updater_v1_bundle_patches', 'hot_updater_v1_releases', 'hot_updater_v1_release_catalogs', 'hot_updater_v1_channels', 'hot_updater_v1_bundle_totals', 'hot_updater_v1_bundle_events', 'hot_updater_v1_bundle_events__byBundle', 'hot_updater_v1_bundle_event_heads', 'hot_updater_v1_insights_overview', 'hot_updater_v1_insights_sketches', 'hot_updater_v1_insights_overview_daily', 'hot_updater_v1_insights_sketches_daily', 'hot_updater_v1_insights_overview_lifetime', 'hot_updater_v1_insights_sketches_lifetime', 'hot_updater_v1_insights_distribution', 'hot_updater_v1_insights_latest_by_bundle', 'hot_updater_v1_insights_outcomes', 'hot_updater_v1_insights_failures', 'hot_updater_v1_api_keys', 'hot_updater_v1_private_hot_updater_settings', 'hot_updater_v1__hu_write']) THEN
129
167
  RAISE EXCEPTION 'hot_updater_v1_apply: % is not a Hot Updater table', target USING ERRCODE = '42501';
130
168
  END IF;
131
169
  END LOOP;
@@ -160,6 +198,6 @@ INSERT INTO "hot_updater_v1_private_hot_updater_settings" ("key", "value", "_v")
160
198
 
161
199
  INSERT INTO "hot_updater_v1_private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.core', '1.0.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value";
162
200
 
163
- INSERT INTO "hot_updater_v1_private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.insights', '1.0.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value";
201
+ INSERT INTO "hot_updater_v1_private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.insights', '1.2.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value";
164
202
 
165
203
  INSERT INTO "hot_updater_v1_private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.apiKeys', '1.0.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value";
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
 
4
4
  import { PGlite } from "@electric-sql/pglite";
5
5
  import { createHotUpdater } from "@hot-updater/server";
6
+ import { toolingTargetOf } from "@hot-updater/server/database";
6
7
  import {
7
8
  createDatabasePluginApis,
8
9
  createMigrator,
@@ -13,12 +14,14 @@ import {
13
14
  createInsightsModel,
14
15
  insights,
15
16
  } from "@hot-updater/server/plugins/insights";
17
+ import { insightsTestSuite } from "@hot-updater/server/plugins/insights/testing";
16
18
  import {
17
19
  setupDatabaseTestSuite,
18
20
  startHttpTestServer,
19
21
  } from "@hot-updater/test-utils";
20
22
  import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
21
23
 
24
+ import { plugins } from "../src/plugins";
22
25
  import { supabaseDatabase } from "../src/supabaseDatabase";
23
26
  import { toApplyStatement } from "../src/supabaseExecutor";
24
27
  import { supabaseDatabase as supabaseToolingDatabase } from "../src/supabaseMigration";
@@ -31,6 +34,8 @@ import {
31
34
 
32
35
  const MIGRATIONS = path.resolve("plugins/supabase/supabase/migrations");
33
36
  const MIGRATION = path.join(MIGRATIONS, "20260818000000_hot-updater_1.0.0.sql");
37
+ /** The managed server's tables: core's, and its plugins' (Insights and API keys). */
38
+ const managed = toolingTargetOf(plugins);
34
39
 
35
40
  const state = vi.hoisted(() => ({ db: undefined as PGlite | undefined }));
36
41
 
@@ -74,14 +79,16 @@ const apply = (db: PGlite, sql: string) =>
74
79
  describe("Supabase schema", () => {
75
80
  it("checks in exactly the generated migration as the only one", async () => {
76
81
  if (process.env.HOT_UPDATER_UPDATE_SQL === "1") {
77
- await fs.writeFile(MIGRATION, supabaseSchemaSql());
82
+ await fs.writeFile(MIGRATION, supabaseSchemaSql(managed));
78
83
  }
79
84
  const files = (await fs.readdir(MIGRATIONS)).filter((file) =>
80
85
  file.endsWith(".sql"),
81
86
  );
82
87
  expect(files).toEqual([path.basename(MIGRATION)]);
83
88
  // Regenerate with HOT_UPDATER_UPDATE_SQL=1 after the schema changes.
84
- expect(await fs.readFile(MIGRATION, "utf8")).toBe(supabaseSchemaSql());
89
+ expect(await fs.readFile(MIGRATION, "utf8")).toBe(
90
+ supabaseSchemaSql(managed),
91
+ );
85
92
  });
86
93
 
87
94
  it("creates namespaced tables with row-level security beside a v0 schema it leaves alone", async () => {
@@ -96,7 +103,7 @@ describe("Supabase schema", () => {
96
103
  name.startsWith("hot_updater_v1_"),
97
104
  );
98
105
  expect(ours.map(({ name }) => name)).toEqual(
99
- supabaseTableNames().toSorted(),
106
+ supabaseTableNames(managed.schema).toSorted(),
100
107
  );
101
108
  expect(ours.every(({ secured }) => secured)).toBe(true);
102
109
  await expect(
@@ -254,10 +261,14 @@ describe("supabaseDatabase over the apply RPC", () => {
254
261
  clientAccess: "public",
255
262
  }).handlers,
256
263
  ),
257
- createInsightsModel: (database) =>
258
- createInsightsModel(
259
- createDatabasePluginApis(database, [insights()]).insights,
260
- ),
264
+ plugins: [
265
+ insightsTestSuite({
266
+ createModel: (database) =>
267
+ createInsightsModel(
268
+ createDatabasePluginApis(database, [insights()]).insights,
269
+ ),
270
+ }),
271
+ ],
261
272
  name: "supabaseDatabase (PGlite, apply RPC)",
262
273
  migrate: () => undefined,
263
274
  createDatabase: () =>
@@ -266,7 +277,7 @@ describe("supabaseDatabase over the apply RPC", () => {
266
277
  supabaseServiceRoleKey: "service-role-key",
267
278
  }),
268
279
  reset: async () => {
269
- const tables = supabaseTableNames()
280
+ const tables = supabaseTableNames(managed.schema)
270
281
  .filter((table) => table !== SUPABASE_SETTINGS_TABLE)
271
282
  .map((table) => `public."${table}"`);
272
283
  await state.db!.exec(`TRUNCATE ${tables.join(", ")} CASCADE`);