@protobase/server 0.58.6 → 0.58.7

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.
@@ -8819,6 +8819,10 @@ export declare const buildAppAuth: (settings: AuthSettings, input: AppAuthInput)
8819
8819
  column: (name: UserColumn) => string;
8820
8820
  fieldNames: Record<string, string>;
8821
8821
  metadata: string | undefined;
8822
+ keyTable: {
8823
+ schema: string;
8824
+ table: string;
8825
+ } | undefined;
8822
8826
  };
8823
8827
  socialProviders: string[];
8824
8828
  appName: string;
@@ -1,6 +1,10 @@
1
1
  import { AdminAuth } from "./create-auth.js";
2
2
  //#region src/better-auth/auth-tables.d.ts
3
- /** The admin store's tables, which the assistant's queries and the app's resources must never read: the user table and `storeTableNames`. */
3
+ /**
4
+ * The admin store's tables, which the assistant's queries and the app's resources must never read: the user table and
5
+ * `storeTableNames`, and with `users.keyTable` the table under the user view too, such as Supabase's `auth.users`,
6
+ * whose password hashes the view leaves out but the table holds.
7
+ */
4
8
  export declare const authStoreTables: (auth: AdminAuth) => {
5
9
  schema: string | undefined;
6
10
  name: string;
@@ -1,13 +1,21 @@
1
1
  import { storeNames } from "./store/store-names.js";
2
2
  import { storeTableNames } from "./store/table-names.js";
3
3
  //#region src/better-auth/auth-tables.ts
4
- /** The admin store's tables, which the assistant's queries and the app's resources must never read: the user table and `storeTableNames`. */
4
+ /**
5
+ * The admin store's tables, which the assistant's queries and the app's resources must never read: the user table and
6
+ * `storeTableNames`, and with `users.keyTable` the table under the user view too, such as Supabase's `auth.users`,
7
+ * whose password hashes the view leaves out but the table holds.
8
+ */
5
9
  const authStoreTables = (auth) => {
6
10
  const { schema, users } = storeNames(auth);
7
- return [users.modelName, ...storeTableNames].map((name) => ({
11
+ const own = [users.modelName, ...storeTableNames].map((name) => ({
8
12
  schema,
9
13
  name
10
14
  }));
15
+ return users.keyTable ? [...own, {
16
+ schema: users.keyTable.schema,
17
+ name: users.keyTable.table
18
+ }] : own;
11
19
  };
12
20
  //#endregion
13
21
  export { authStoreTables };
@@ -8958,6 +8958,10 @@ export declare const createAuth: (options: CreateAuthOptions, plugins?: AuthPlug
8958
8958
  column: (name: UserColumn) => string;
8959
8959
  fieldNames: Record<string, string>;
8960
8960
  metadata: string | undefined;
8961
+ keyTable: {
8962
+ schema: string;
8963
+ table: string;
8964
+ } | undefined;
8961
8965
  };
8962
8966
  socialProviders: string[];
8963
8967
  /** The app's name it signs in and mails as: `PROTOBASE_APP_NAME`, else `appName`, else `Protobase`. */
@@ -1,5 +1,18 @@
1
+ import { storeNames } from "./store-names.js";
2
+ import { storeDb } from "./store-db.js";
3
+ import { sql } from "kysely";
4
+ import { getSchema } from "better-auth/db";
1
5
  import { getMigrations } from "better-auth/db/migration";
2
6
  //#region src/better-auth/store/drift.ts
7
+ /** Each column of Better Auth's user model the view lacks, or the view itself when there is none. */
8
+ const userViewDrift = async (auth) => {
9
+ const { schema, users } = storeNames(auth);
10
+ const { rows } = await sql`select column_name as "column" from information_schema.columns
11
+ where table_schema = ${schema ?? sql`current_schema()`} and table_name = ${users.modelName}`.execute(storeDb(auth));
12
+ if (rows.length === 0) return [`lacks the view ${users.modelName}`];
13
+ const found = new Set(rows.map(({ column }) => column));
14
+ return ["id", ...Object.keys(getSchema(auth.options)[users.modelName]?.fields ?? {})].filter((column) => !found.has(column)).map((column) => `lacks the column ${users.modelName}.${column}`);
15
+ };
3
16
  /**
4
17
  * What Better Auth, with the features the app turns on, finds the store lacks or has otherwise: tables, `table.column`s,
5
18
  * indexes and its other findings; none when the store matches. It compares the database's tables, so only
@@ -7,12 +20,14 @@ import { getMigrations } from "better-auth/db/migration";
7
20
  */
8
21
  const authSchemaDrift = async (auth) => {
9
22
  const { toBeCreated, toBeAdded, toBeAddedIndexes, schemaProblems } = await getMigrations(auth.options, { throwOnUnsafe: false });
10
- return [
11
- ...toBeCreated.map(({ table }) => `lacks the table ${table}`),
12
- ...toBeAdded.flatMap(({ table, fields }) => Object.keys(fields).map((field) => `lacks the column ${table}.${field}`)),
13
- ...toBeAddedIndexes.map(({ name }) => `lacks the index ${name}`),
23
+ const view = auth.userTable.keyTable ? auth.userTable.modelName : void 0;
24
+ const tables = [
25
+ ...toBeCreated.filter(({ table }) => table !== view).map(({ table }) => `lacks the table ${table}`),
26
+ ...toBeAdded.filter(({ table }) => table !== view).flatMap(({ table, fields }) => Object.keys(fields).map((field) => `lacks the column ${table}.${field}`)),
27
+ ...toBeAddedIndexes.filter(({ table }) => table !== view).map(({ name }) => `lacks the index ${name}`),
14
28
  ...schemaProblems
15
29
  ];
30
+ return view ? [...tables, ...await userViewDrift(auth)] : tables;
16
31
  };
17
32
  //#endregion
18
33
  export { authSchemaDrift };
@@ -11,9 +11,10 @@ const core = {
11
11
  "verification",
12
12
  "jwks"
13
13
  ],
14
- statements: ({ table, user, userColumn: column, metadata, id, idRef }) => [
15
- ddl`create table if not exists ${user} (id ${id} primary key)`,
16
- ddl`alter table ${user}
14
+ statements: ({ table, user, userDdl, userColumn: column, metadata, id, idRef }) => [
15
+ ...userDdl([
16
+ ddl`create table if not exists ${user} (id ${id} primary key)`,
17
+ ddl`alter table ${user}
17
18
  add column if not exists ${column("name")} text not null,
18
19
  add column if not exists ${column("email")} text not null unique,
19
20
  add column if not exists ${column("email_verified")} boolean not null,
@@ -26,7 +27,8 @@ const core = {
26
27
  add column if not exists ${column("ban_expires")} timestamptz,
27
28
  add column if not exists ${column("last_role")} text,
28
29
  add column if not exists ${column("email_confirmed_at")} timestamptz`,
29
- ...metadata ? [ddl`alter table ${user} add column if not exists ${metadata} jsonb`] : [],
30
+ ...metadata ? [ddl`alter table ${user} add column if not exists ${metadata} jsonb`] : []
31
+ ]),
30
32
  ddl`create table ${table("session")} (
31
33
  id ${id} primary key,
32
34
  expires_at timestamptz not null,
@@ -6,8 +6,8 @@ const twoFactor = {
6
6
  name: "two_factor",
7
7
  title: "The authenticator secrets and backup codes of two-step sign-in",
8
8
  tables: ["two_factor"],
9
- statements: ({ table, user, userColumn: column, id, idRef }) => [
10
- ddl`alter table ${user} add column if not exists ${column("two_factor_enabled")} boolean`,
9
+ statements: ({ table, user, userDdl, userColumn: column, id, idRef }) => [
10
+ ...userDdl([ddl`alter table ${user} add column if not exists ${column("two_factor_enabled")} boolean`]),
11
11
  ddl`create table ${table("two_factor")} (
12
12
  id ${id} primary key,
13
13
  secret text not null,
@@ -10,8 +10,8 @@ const organizations = {
10
10
  "member",
11
11
  "invitation"
12
12
  ],
13
- statements: ({ table, user, userColumn: column, id, idRef }) => [
14
- ddl`alter table ${user} add column if not exists ${column("last_organization_id")} text`,
13
+ statements: ({ table, user, userDdl, userColumn: column, id, idRef }) => [
14
+ ...userDdl([ddl`alter table ${user} add column if not exists ${column("last_organization_id")} text`]),
15
15
  ddl`alter table ${table("session")} add column active_organization_id text`,
16
16
  ddl`create table ${table("organization")} (
17
17
  id ${id} primary key,
@@ -4,7 +4,8 @@ import { sql } from "kysely";
4
4
  /** The `ddl` parts of the app's names. */
5
5
  const migrationNames = (names) => ({
6
6
  table: (name) => storeTable(names, name),
7
- user: storeTable(names, names.users.modelName),
7
+ user: names.users.keyTable ? sql.id(names.users.keyTable.schema, names.users.keyTable.table) : storeTable(names, names.users.modelName),
8
+ userDdl: (statements) => names.users.keyTable ? [] : statements,
8
9
  userColumn: names.users.column,
9
10
  metadata: names.users.metadata,
10
11
  id: sql.raw(names.uuidIds ? "uuid default pg_catalog.gen_random_uuid()" : "text"),
@@ -28,5 +28,14 @@ export type UserTableOptions = {
28
28
  columns?: Partial<Record<UserColumn, string>>;
29
29
  /** A JSON column that holds each user's metadata, such as `raw_user_meta_data`; none by default. */
30
30
  metadata?: string;
31
+ /**
32
+ * The table that holds the user ids, when `table` is a view over another system's user table, such as
33
+ * `{ schema: 'auth', table: 'users' }` under a view over Supabase's `auth.users`: the store's migrations then make
34
+ * nothing of the user table, which the app's own migration makes, and every foreign key points at this table.
35
+ */
36
+ keyTable?: {
37
+ schema: string;
38
+ table: string;
39
+ };
31
40
  };
32
41
  //#endregion
@@ -17,7 +17,7 @@ const userFields = {
17
17
  last_organization_id: "lastOrganizationId"
18
18
  };
19
19
  /** The user table's names, from `auth.users` option; unknown columns are an error. */
20
- const userTable = ({ table = "user", columns = {}, metadata } = {}) => {
20
+ const userTable = ({ table = "user", columns = {}, metadata, keyTable } = {}) => {
21
21
  const unknown = Object.keys(columns).find((name) => !(name in userFields));
22
22
  if (unknown !== void 0) throw new Error(`users.columns names "${unknown}", which is not a column of the user table: ${Object.keys(userFields).join(", ")}`);
23
23
  const column = (name) => columns[name] ?? name;
@@ -25,7 +25,8 @@ const userTable = ({ table = "user", columns = {}, metadata } = {}) => {
25
25
  modelName: table,
26
26
  column,
27
27
  fieldNames: Object.fromEntries(Object.entries(userFields).map(([name, field]) => [field, column(name)])),
28
- metadata
28
+ metadata,
29
+ keyTable
29
30
  };
30
31
  };
31
32
  /** Better Auth's `user` option: the table and its core columns. */
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.58.6";
2
+ var version = "0.58.7";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.58.6",
3
+ "version": "0.58.7",
4
4
  "description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -42,9 +42,9 @@
42
42
  "@better-auth/sso": "^1.7.12",
43
43
  "@livekit/protocol": "1.53.1",
44
44
  "@modelcontextprotocol/server": "^2.3.1",
45
- "@protobase/layout": "0.58.6",
46
- "@protobase/query": "0.58.6",
47
- "@protobase/schema": "0.58.6",
45
+ "@protobase/layout": "0.58.7",
46
+ "@protobase/query": "0.58.7",
47
+ "@protobase/schema": "0.58.7",
48
48
  "@react-email/render": "^2.1.0",
49
49
  "@scalar/hono-api-reference": "^0.12.10",
50
50
  "@sentry/core": "^11.7.0",