@opensaas/stack-auth 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +90 -0
  3. package/CLAUDE.md +98 -0
  4. package/README.md +33 -0
  5. package/dist/config/adopt-better-auth-tables.d.ts +107 -0
  6. package/dist/config/adopt-better-auth-tables.d.ts.map +1 -0
  7. package/dist/config/adopt-better-auth-tables.js +70 -0
  8. package/dist/config/adopt-better-auth-tables.js.map +1 -0
  9. package/dist/config/derive-auth-lists.d.ts +50 -0
  10. package/dist/config/derive-auth-lists.d.ts.map +1 -0
  11. package/dist/config/derive-auth-lists.js +274 -0
  12. package/dist/config/derive-auth-lists.js.map +1 -0
  13. package/dist/config/index.d.ts.map +1 -1
  14. package/dist/config/index.js +43 -0
  15. package/dist/config/index.js.map +1 -1
  16. package/dist/config/plugin.d.ts.map +1 -1
  17. package/dist/config/plugin.js +52 -9
  18. package/dist/config/plugin.js.map +1 -1
  19. package/dist/config/types.d.ts +130 -3
  20. package/dist/config/types.d.ts.map +1 -1
  21. package/dist/index.d.ts +4 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +6 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/lists/index.d.ts +17 -11
  26. package/dist/lists/index.d.ts.map +1 -1
  27. package/dist/lists/index.js +34 -208
  28. package/dist/lists/index.js.map +1 -1
  29. package/dist/server/index.d.ts.map +1 -1
  30. package/dist/server/index.js +28 -7
  31. package/dist/server/index.js.map +1 -1
  32. package/package.json +2 -2
  33. package/src/config/adopt-better-auth-tables.ts +146 -0
  34. package/src/config/derive-auth-lists.ts +323 -0
  35. package/src/config/index.ts +58 -0
  36. package/src/config/plugin.ts +66 -9
  37. package/src/config/types.ts +146 -3
  38. package/src/index.ts +13 -0
  39. package/src/lists/index.ts +42 -202
  40. package/src/server/index.ts +31 -9
  41. package/tests/adopt-better-auth-tables.test.ts +183 -0
  42. package/tests/derive-auth-lists.test.ts +232 -0
  43. package/tests/plugin-derived-keys.test.ts +138 -0
  44. package/tests/plugin-schema-placement.test.ts +121 -0
  45. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,4 @@
1
1
 
2
- > @opensaas/stack-auth@0.21.0 build /home/runner/work/stack/stack/packages/auth
2
+ > @opensaas/stack-auth@0.22.0 build /home/runner/work/stack/stack/packages/auth
3
3
  > tsc
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,95 @@
1
1
  # @opensaas/stack-auth
2
2
 
3
+ ## 0.22.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#509](https://github.com/OpenSaasAU/stack/pull/509) [`fdc48f8`](https://github.com/OpenSaasAU/stack/commit/fdc48f86a5a7f161bef0b512963e1511a8c8e00e) Thanks [@list({](https://github.com/list({)! - Add `adoptBetterAuthTables()` recipe for adopting an existing better-auth installation
8
+
9
+ A migrating project that already runs better-auth (its `AuthUser`/`AuthSession`/`AuthAccount`/`AuthVerification` tables live in a separate `auth` Postgres schema, and its app `User` is a different model) can now adopt those live tables without rebuilding the auth config by hand. The recipe presets the plugin-level `schema` plus each model's `modelName` (and optional column `fields` maps) to the conventions of a standard separate-schema better-auth install, so the derived Auth lists diff clean (Schema parity) against the live database — no destructive auth migration. The app's own domain `User` is left untouched; linking it to the Auth identity is the application's concern.
10
+
11
+ ```typescript
12
+ import { config } from '@opensaas/stack-core'
13
+ import { authPlugin, adoptBetterAuthTables } from '@opensaas/stack-auth'
14
+
15
+ export default config({
16
+ db: { provider: 'postgresql', url: process.env.DATABASE_URL },
17
+ plugins: [
18
+ authPlugin({
19
+ // Defaults: AuthUser/AuthSession/AuthAccount/AuthVerification in the
20
+ // `auth` schema, pinned to your live table names (@@map) + schema (@@schema).
21
+ ...adoptBetterAuthTables(),
22
+ emailAndPassword: { enabled: true },
23
+ }),
24
+ ],
25
+ lists: {
26
+ // Your own domain User stays in `public` and is NOT touched by the plugin.
27
+ fields: { subjectId: text({ validation: { isRequired: true } }) } }),
28
+ },
29
+ })
30
+
31
+ // Customise when your live tables diverge from the defaults:
32
+ adoptBetterAuthTables({
33
+ schema: 'identity', // default: 'auth'
34
+ modelNamePrefix: 'BA', // default: 'Auth'
35
+ fields: { user: { name: 'full_name' }, session: { userId: 'user_id' } },
36
+ })
37
+ ```
38
+
39
+ - [#497](https://github.com/OpenSaasAU/stack/pull/497) [`be4181a`](https://github.com/OpenSaasAU/stack/commit/be4181ada3f2d6386052df4d4869ad150d360f89) Thanks [@{](https://github.com/{)! - Derive the auth plugin's Auth lists from the better-auth config
40
+
41
+ `authPlugin` now mirrors the better-auth config a developer writes instead of hardcoding the keys `User`/`Session`/`Account`/`Verification`. Per-model `modelName` becomes the OpenSaaS list key (and a table `@@map`), and the `fields` column map becomes per-field `@map`s. The plugin only ever adds/extends its own derived keys, so an app's separate domain `User` is never overwritten. The runtime `getUser`/`getCurrentUser` helpers now resolve the user list key from the configured user model instead of a hardcoded `'user'`.
42
+
43
+ Default behaviour (no overrides) is unchanged: the lists are still keyed `User`/`Session`/`Account`/`Verification` with the original field shapes and no `@@map`.
44
+
45
+ ```typescript
46
+ // Adopt existing better-auth tables without a destructive migration
47
+ authPlugin({
48
+ modelName: 'AuthUser', fields: { name: 'full_name' } },
49
+ session: { modelName: 'AuthSession', fields: { userId: 'user_id' } },
50
+ account: { modelName: 'AuthAccount' },
51
+ verification: { modelName: 'AuthVerification' },
52
+ })
53
+ // -> lists keyed AuthUser/AuthSession/AuthAccount/AuthVerification
54
+ // with @@map + column @map matching the live tables
55
+ ```
56
+
57
+ Lists also gain a model-level `db.map` option, which emits a `@@map("...")` on the generated Prisma model so a list key can differ from its physical table name.
58
+
59
+ - [#502](https://github.com/OpenSaasAU/stack/pull/502) [`593390c`](https://github.com/OpenSaasAU/stack/commit/593390c57d9844ca7ada8f45b340c849f1d8d647) Thanks [@{](https://github.com/{)! - Add `authPlugin` schema placement so Auth lists can adopt an existing non-`public` better-auth layout (clean-diff adoption)
60
+
61
+ The auth lists can now be placed in a non-`public` Postgres schema (e.g. `auth`) so they diff CLEAN against a separate-schema better-auth installation. A plugin-level `schema` option applies `@@schema(...)` to all generated Auth lists, with a per-list override.
62
+
63
+ ```typescript
64
+ authPlugin({
65
+ schema: 'auth', // all Auth lists get @@schema("auth")
66
+ modelName: 'AuthUser' },
67
+ session: { modelName: 'AuthSession' },
68
+ account: { modelName: 'AuthAccount' },
69
+ // per-model override: relocate one list to a different schema
70
+ verification: { modelName: 'AuthVerification', schema: 'auth_internal' },
71
+ })
72
+ ```
73
+
74
+ The plugin's `beforeGenerate` hook wires the datasource `schemas` array (always including `public`) and defaults any list without an explicit `db.schema` to `public`, producing a valid multi-schema Prisma schema. With no `schema` option the output is unchanged (greenfield default stays in `public`, no `@@schema`).
75
+
76
+ Core support added for this (mirroring the `db.map` → `@@map` work):
77
+ - List-level `db.schema` → the Prisma generator emits `@@schema("...")` on the model.
78
+ - Database-level `db.schemas` → the generator emits the datasource `schemas = [...]` array and enables the `multiSchema` preview feature.
79
+
80
+ ```typescript
81
+ // Core/generator building blocks
82
+ db: { provider: 'postgresql', schemas: ['public', 'auth'] }
83
+ AuthUser: list({ fields: { ... }, db: { map: 'AuthUser', schema: 'auth' } })
84
+ // Generates: model AuthUser { ... @@map("AuthUser") @@schema("auth") }
85
+ ```
86
+
87
+ ### Patch Changes
88
+
89
+ - [#501](https://github.com/OpenSaasAU/stack/pull/501) [`e30f6a1`](https://github.com/OpenSaasAU/stack/commit/e30f6a1ef69dc65ae68b37539fa74c3f97823cfd) Thanks [@borisno2](https://github.com/borisno2)! - Keep `createdAt`/`updatedAt` on the auth lists now that auto-timestamps are off by default
90
+
91
+ The derived auth lists (User/Session/Account/Verification) now opt into `db: { timestamps: true }`. better-auth's adapter writes those columns and the schema converter returns `null` for them assuming the generator injects them, so the opt-in keeps the generated auth models intact.
92
+
3
93
  ## 0.21.0
4
94
 
5
95
  ### Minor Changes
package/CLAUDE.md CHANGED
@@ -62,6 +62,104 @@ config({
62
62
  // Result: { lists: { User, Session, Account, Verification, Post } }
63
63
  ```
64
64
 
65
+ ### Deriving Auth lists from better-auth config
66
+
67
+ The four Auth lists are **derived** from the better-auth model config the
68
+ developer writes — not hardcoded. The pure derivation lives in
69
+ `src/config/derive-auth-lists.ts` (`deriveAuthLists`), which `getAuthLists`
70
+ and the plugin's add-vs-extend logic consume:
71
+
72
+ - per-model `modelName` → list key + table `@@map`
73
+ - per-model `fields` (better-auth field → column) → field-level `@map`
74
+ - the `userId` column override → the `user` relationship foreign-key `@map`
75
+ - relationship refs between the Auth lists follow the derived keys
76
+ (e.g. `Session.user → AuthUser.sessions`)
77
+
78
+ With no `modelName`/`fields` overrides the output is unchanged
79
+ (`User`/`Session`/`Account`/`Verification`, original field shapes, no `@@map`).
80
+
81
+ ```typescript
82
+ // Adopt an existing better-auth installation (Auth lists ≠ app User)
83
+ authPlugin({
84
+ user: { modelName: 'AuthUser', fields: { name: 'full_name' } },
85
+ session: { modelName: 'AuthSession', fields: { userId: 'user_id' } },
86
+ })
87
+ // Adds AuthUser/AuthSession/... and leaves an app's own `User` untouched.
88
+ ```
89
+
90
+ Because the plugin only ever adds/extends its **derived** keys, an app's own
91
+ domain `User` (a different model from the better-auth user) is never extended
92
+ or overwritten when the user model is renamed. The runtime `getUser`/
93
+ `getCurrentUser` helpers resolve the user list's `context.db` key from the
94
+ configured user `modelName`.
95
+
96
+ ### Schema placement (relocatable Auth lists)
97
+
98
+ A plugin-level `schema` option places all generated Auth lists in a non-`public`
99
+ Postgres schema via `@@schema(...)`, so they can adopt a separate-schema
100
+ better-auth layout (e.g. an `auth` schema) and reach **Schema parity** with the
101
+ live tables. Combined with the derived keys/`@@map`/field `@map`s above, the
102
+ generated lists diff CLEAN against an existing `auth`-schema install — they are
103
+ modelled for runtime/types without producing a migration.
104
+
105
+ ```typescript
106
+ authPlugin({
107
+ schema: 'auth', // all Auth lists get @@schema("auth")
108
+ user: { modelName: 'AuthUser' },
109
+ session: { modelName: 'AuthSession' },
110
+ account: { modelName: 'AuthAccount' },
111
+ verification: { modelName: 'AuthVerification' },
112
+ // per-model override: relocate one list to a different schema
113
+ // verification: { modelName: 'AuthVerification', schema: 'auth_internal' },
114
+ })
115
+ ```
116
+
117
+ How it wires up (Postgres multi-schema):
118
+
119
+ - Each Auth list gets a list-level `db.schema` → `@@schema(...)` (per-model
120
+ `schema` override, else the plugin-level `schema`).
121
+ - The plugin's `beforeGenerate` hook adds the auth schema(s) (always plus
122
+ `public`) to the datasource `db.schemas` array and defaults any list without
123
+ an explicit `db.schema` to `public`, so the generated multi-schema Prisma
124
+ schema is valid (the generator emits `previewFeatures = ["multiSchema"]` and
125
+ `schemas = [...]`).
126
+ - With no `schema` option the Auth lists stay in `public` and no `@@schema` /
127
+ `schemas` / preview feature is emitted (greenfield default unchanged).
128
+
129
+ ### Adopting an existing better-auth install (`adoptBetterAuthTables`)
130
+
131
+ `adoptBetterAuthTables()` (`src/config/adopt-better-auth-tables.ts`) is a thin
132
+ recipe that returns the `AuthConfig` adoption knobs — the plugin-level `schema`
133
+ plus a per-model `modelName` (and optional column `fields` maps) — preset to the
134
+ conventions of a standard separate-schema better-auth install. It ties together
135
+ the keys/field derivation and schema placement so a migrator doesn't rebuild the
136
+ config by hand. Spread it into `authPlugin`:
137
+
138
+ ```typescript
139
+ import { authPlugin, adoptBetterAuthTables } from '@opensaas/stack-auth'
140
+
141
+ authPlugin({
142
+ ...adoptBetterAuthTables(), // schema: 'auth', AuthUser/AuthSession/AuthAccount/AuthVerification
143
+ emailAndPassword: { enabled: true },
144
+ })
145
+ // Options: adoptBetterAuthTables({ schema, modelNamePrefix, fields })
146
+ ```
147
+
148
+ It is pure config (no side effects): everything it sets can also be written
149
+ directly on `authPlugin`, and spreading it before your own keys lets you
150
+ override per model. Because the derived user key is `AuthUser` (not `User`), an
151
+ app's own domain `User` is left untouched — the plugin only ever adds/extends
152
+ its derived keys. Combined with the derivation + schema placement above, the
153
+ generated Auth lists reach **Schema parity** (clean `schema:diff`) against a live
154
+ `auth`-schema install — they are modelled for runtime/types, not migrated.
155
+
156
+ **App User ≠ Auth identity.** The plugin models the **Auth identity** (the
157
+ better-auth user); it does not assume that list is the app's domain `User`.
158
+ Linking an app's `User` to the Auth identity (e.g. a `relationship({ ref:
159
+ 'AuthUser' })` the app declares) is the application's concern. See the
160
+ [Authentication guide](../../docs/content/guides/authentication.md) (“Adopting an
161
+ existing better-auth installation”) for the end-to-end migrator walkthrough.
162
+
65
163
  ### Session Provider
66
164
 
67
165
  Better-auth provides session to context via custom `prismaClientConstructor`:
package/README.md CHANGED
@@ -203,6 +203,39 @@ authPlugin({
203
203
  })
204
204
  ```
205
205
 
206
+ ## Adopting an Existing better-auth Installation
207
+
208
+ Migrating a project that **already runs better-auth**? The plugin can adopt your
209
+ live tables rather than recreating them, so there's no destructive auth
210
+ migration. Use the `adoptBetterAuthTables()` recipe to preset the model/schema
211
+ knobs that match a standard separate-schema better-auth install:
212
+
213
+ ```typescript
214
+ import { authPlugin, adoptBetterAuthTables } from '@opensaas/stack-auth'
215
+
216
+ authPlugin({
217
+ // Defaults: AuthUser/AuthSession/AuthAccount/AuthVerification in the `auth`
218
+ // Postgres schema — pinned to your live table names + schema (@@map/@@schema).
219
+ ...adoptBetterAuthTables(),
220
+ emailAndPassword: { enabled: true },
221
+ })
222
+
223
+ // Customise when your live tables diverge from the defaults:
224
+ adoptBetterAuthTables({
225
+ schema: 'identity', // default: 'auth'
226
+ modelNamePrefix: 'BA', // default: 'Auth' (→ AuthUser/AuthSession/…)
227
+ fields: { user: { name: 'full_name' }, session: { userId: 'user_id' } },
228
+ })
229
+ ```
230
+
231
+ **App `User` vs the Auth identity.** The plugin models the **Auth identity** (the
232
+ better-auth user, e.g. `AuthUser`). It does not assume that list is your app's
233
+ own domain `User` — when the keys differ, your `User` is never extended or
234
+ overwritten. **Linking your domain `User` to the Auth identity is your app's
235
+ concern** (declare a `relationship({ ref: 'AuthUser' })` on your `User`). See the
236
+ [Authentication guide](https://stack.opensaas.au/docs/guides/authentication) for
237
+ the full migrator walkthrough and a Schema-parity (clean-diff) check.
238
+
206
239
  ## UI Components
207
240
 
208
241
  ### SignInForm
@@ -0,0 +1,107 @@
1
+ /**
2
+ * "Adopt existing better-auth tables" recipe.
3
+ *
4
+ * A migrating project usually already has a working, hand-wired better-auth
5
+ * installation: its tables are `AuthUser`/`AuthSession`/`AuthAccount`/
6
+ * `AuthVerification`, mapped into a separate `auth` Postgres schema, and its
7
+ * application `User` (`public.User`) is a *different* model. Reconstructing the
8
+ * matching {@link AuthConfig} by hand — four `modelName`s plus a `schema` on each
9
+ * model — is repetitive and easy to get wrong.
10
+ *
11
+ * {@link adoptBetterAuthTables} produces that {@link AuthConfig} fragment from a
12
+ * couple of options so the migrator doesn't rebuild it from scratch. It only
13
+ * sets the *adoption* knobs (per-model `modelName` + the plugin-level `schema`);
14
+ * the developer composes it with the rest of their auth config (providers,
15
+ * session fields, `extendUserList`, etc.):
16
+ *
17
+ * ```typescript
18
+ * authPlugin({
19
+ * ...adoptBetterAuthTables(),
20
+ * emailAndPassword: { enabled: true },
21
+ * sessionFields: ['userId', 'email', 'name'],
22
+ * })
23
+ * ```
24
+ *
25
+ * Combined with the keys/field derivation (`deriveAuthLists`) and schema
26
+ * placement, the generated Auth lists reach **Schema parity** with the live
27
+ * tables — they are modelled for runtime/types without producing a destructive
28
+ * auth migration. The recipe never touches the application's own domain `User`:
29
+ * its model names are `Auth`-prefixed by default and the plugin only ever
30
+ * adds/extends its *derived* keys.
31
+ */
32
+ import type { AuthConfig } from './types.js';
33
+ /**
34
+ * Options for {@link adoptBetterAuthTables}.
35
+ *
36
+ * All options are optional and default to the conventions of a standard
37
+ * separate-schema better-auth install (an `auth` Postgres schema with
38
+ * `Auth`-prefixed model names).
39
+ */
40
+ export type AdoptBetterAuthTablesOptions = {
41
+ /**
42
+ * The Postgres schema the live better-auth tables live in.
43
+ *
44
+ * Applied as the plugin-level {@link AuthConfig.schema}, placing every Auth
45
+ * list in this schema via `@@schema(...)`. Pass `'public'` (or any single
46
+ * schema) for an install that is not on a separate schema; pass an explicit
47
+ * value to match your layout.
48
+ *
49
+ * @default 'auth'
50
+ */
51
+ schema?: string;
52
+ /**
53
+ * Prefix applied to each better-auth model name to derive the list key /
54
+ * table name (e.g. prefix `'Auth'` → `AuthUser`/`AuthSession`/...).
55
+ *
56
+ * A live better-auth install with `modelName: 'AuthUser'` etc. is the common
57
+ * case; override this if your tables use a different prefix. Set to `''` to
58
+ * keep the default better-auth names (`User`/`Session`/...) — but note that an
59
+ * unprefixed `User` will share the app's `User` key, so prefer a prefix when
60
+ * your domain `User` is a separate model (see {@link AuthConfig.user}).
61
+ *
62
+ * @default 'Auth'
63
+ */
64
+ modelNamePrefix?: string;
65
+ /**
66
+ * Per-model better-auth field → column maps, keyed by model.
67
+ *
68
+ * Only needed when your live tables renamed columns away from the better-auth
69
+ * defaults (e.g. `name → full_name`). Merged into the matching model config so
70
+ * the derived field-level `@map`s match your live columns.
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * adoptBetterAuthTables({
75
+ * fields: {
76
+ * user: { name: 'full_name' },
77
+ * session: { userId: 'user_id' },
78
+ * },
79
+ * })
80
+ * ```
81
+ */
82
+ fields?: {
83
+ user?: Record<string, string>;
84
+ session?: Record<string, string>;
85
+ account?: Record<string, string>;
86
+ verification?: Record<string, string>;
87
+ };
88
+ };
89
+ /**
90
+ * The adoption-relevant slice of {@link AuthConfig}: the plugin-level `schema`
91
+ * and the per-model `modelName`/`fields`. Returned (not the full `AuthConfig`)
92
+ * so it spreads cleanly into the developer's own `authPlugin` config.
93
+ */
94
+ export type AdoptBetterAuthTablesConfig = Pick<AuthConfig, 'schema' | 'user' | 'session' | 'account' | 'verification'>;
95
+ /**
96
+ * Build the adoption {@link AuthConfig} fragment for a pre-existing better-auth
97
+ * installation.
98
+ *
99
+ * Returns only the model/schema knobs needed to adopt the live tables; spread it
100
+ * into {@link authPlugin} alongside your own auth config.
101
+ *
102
+ * @param options - Adoption conventions (schema, model-name prefix, column maps)
103
+ * @returns An {@link AuthConfig} fragment with `schema` + per-model `modelName`
104
+ * (and any field column maps) set to match the live tables
105
+ */
106
+ export declare function adoptBetterAuthTables(options?: AdoptBetterAuthTablesOptions): AdoptBetterAuthTablesConfig;
107
+ //# sourceMappingURL=adopt-better-auth-tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adopt-better-auth-tables.d.ts","sourceRoot":"","sources":["../../src/config/adopt-better-auth-tables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,YAAY,CAAA;AAE7D;;;;;;GAMG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IAExB;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAChC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KACtC,CAAA;CACF,CAAA;AAUD;;;;GAIG;AACH,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAC5C,UAAU,EACV,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,cAAc,CAC3D,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,4BAAiC,GACzC,2BAA2B,CAqB7B"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * "Adopt existing better-auth tables" recipe.
3
+ *
4
+ * A migrating project usually already has a working, hand-wired better-auth
5
+ * installation: its tables are `AuthUser`/`AuthSession`/`AuthAccount`/
6
+ * `AuthVerification`, mapped into a separate `auth` Postgres schema, and its
7
+ * application `User` (`public.User`) is a *different* model. Reconstructing the
8
+ * matching {@link AuthConfig} by hand — four `modelName`s plus a `schema` on each
9
+ * model — is repetitive and easy to get wrong.
10
+ *
11
+ * {@link adoptBetterAuthTables} produces that {@link AuthConfig} fragment from a
12
+ * couple of options so the migrator doesn't rebuild it from scratch. It only
13
+ * sets the *adoption* knobs (per-model `modelName` + the plugin-level `schema`);
14
+ * the developer composes it with the rest of their auth config (providers,
15
+ * session fields, `extendUserList`, etc.):
16
+ *
17
+ * ```typescript
18
+ * authPlugin({
19
+ * ...adoptBetterAuthTables(),
20
+ * emailAndPassword: { enabled: true },
21
+ * sessionFields: ['userId', 'email', 'name'],
22
+ * })
23
+ * ```
24
+ *
25
+ * Combined with the keys/field derivation (`deriveAuthLists`) and schema
26
+ * placement, the generated Auth lists reach **Schema parity** with the live
27
+ * tables — they are modelled for runtime/types without producing a destructive
28
+ * auth migration. The recipe never touches the application's own domain `User`:
29
+ * its model names are `Auth`-prefixed by default and the plugin only ever
30
+ * adds/extends its *derived* keys.
31
+ */
32
+ /** The four better-auth models and their default (unprefixed) model names. */
33
+ const MODEL_DEFAULT_NAMES = {
34
+ user: 'User',
35
+ session: 'Session',
36
+ account: 'Account',
37
+ verification: 'Verification',
38
+ };
39
+ /**
40
+ * Build the adoption {@link AuthConfig} fragment for a pre-existing better-auth
41
+ * installation.
42
+ *
43
+ * Returns only the model/schema knobs needed to adopt the live tables; spread it
44
+ * into {@link authPlugin} alongside your own auth config.
45
+ *
46
+ * @param options - Adoption conventions (schema, model-name prefix, column maps)
47
+ * @returns An {@link AuthConfig} fragment with `schema` + per-model `modelName`
48
+ * (and any field column maps) set to match the live tables
49
+ */
50
+ export function adoptBetterAuthTables(options = {}) {
51
+ const { schema = 'auth', modelNamePrefix = 'Auth', fields = {} } = options;
52
+ const buildModel = (model) => {
53
+ const config = {
54
+ modelName: `${modelNamePrefix}${MODEL_DEFAULT_NAMES[model]}`,
55
+ };
56
+ const fieldMap = fields[model];
57
+ if (fieldMap && Object.keys(fieldMap).length > 0) {
58
+ config.fields = fieldMap;
59
+ }
60
+ return config;
61
+ };
62
+ return {
63
+ schema,
64
+ user: buildModel('user'),
65
+ session: buildModel('session'),
66
+ account: buildModel('account'),
67
+ verification: buildModel('verification'),
68
+ };
69
+ }
70
+ //# sourceMappingURL=adopt-better-auth-tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adopt-better-auth-tables.js","sourceRoot":"","sources":["../../src/config/adopt-better-auth-tables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AA+DH,8EAA8E;AAC9E,MAAM,mBAAmB,GAAG;IAC1B,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,YAAY,EAAE,cAAc;CACpB,CAAA;AAYV;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAwC,EAAE;IAE1C,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,eAAe,GAAG,MAAM,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,OAAO,CAAA;IAE1E,MAAM,UAAU,GAAG,CAAC,KAAuC,EAAmB,EAAE;QAC9E,MAAM,MAAM,GAAoB;YAC9B,SAAS,EAAE,GAAG,eAAe,GAAG,mBAAmB,CAAC,KAAK,CAAC,EAAE;SAC7D,CAAA;QACD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;QAC9B,IAAI,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjD,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAA;QAC1B,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;IAED,OAAO;QACL,MAAM;QACN,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC;QACxB,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC;QAC9B,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC;QAC9B,YAAY,EAAE,UAAU,CAAC,cAAc,CAAC;KACzC,CAAA;AACH,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Pure `better-auth config → Auth lists` derivation.
3
+ *
4
+ * This module is intentionally free of side effects and plugin/runtime
5
+ * concerns: given the resolved better-auth model config (per-model `modelName`
6
+ * and `fields` column maps) plus any custom User fields, it produces the four
7
+ * OpenSaaS Auth lists (user/session/account/verification) with:
8
+ *
9
+ * - list keys taken from each model's `modelName`
10
+ * - a table `@@map` (list-level `db.map`) when the key differs from the
11
+ * default better-auth model name
12
+ * - field-level `@map` (`db.map`) for any better-auth field → column override
13
+ * - relationship refs between the auth lists wired to the *derived* keys
14
+ * (e.g. `Session.user → AuthUser.sessions`)
15
+ *
16
+ * When the developer supplies no `modelName`/`fields` overrides, the output is
17
+ * byte-for-byte the historical default set keyed `User`/`Session`/`Account`/
18
+ * `Verification` with the original field shapes — see the unit tests.
19
+ *
20
+ * `getAuthLists`/`convertBetterAuthSchema` (and the runtime user-key
21
+ * resolution) consume this module so derivation lives in exactly one place.
22
+ */
23
+ import type { ListConfig } from '@opensaas/stack-core';
24
+ import type { ExtendUserListConfig } from '../lists/index.js';
25
+ import type { NormalizedAuthModels } from './types.js';
26
+ /**
27
+ * The derived Auth list set together with the keys each list was placed under.
28
+ * Keys are surfaced separately so callers (plugin add-vs-extend logic, runtime
29
+ * user-key resolution) don't have to re-derive them.
30
+ */
31
+ export type DerivedAuthLists = {
32
+ /** Derived list keys, one per better-auth model. */
33
+ keys: {
34
+ user: string;
35
+ session: string;
36
+ account: string;
37
+ verification: string;
38
+ };
39
+ /** The derived list configs, keyed by their derived list keys. */
40
+ lists: Record<string, ListConfig<any>>;
41
+ };
42
+ /**
43
+ * Derive the OpenSaaS Auth lists from the resolved better-auth model config.
44
+ *
45
+ * @param models - Resolved better-auth per-model config (modelName + field column maps)
46
+ * @param userConfig - Extra User-list fields/access/hooks supplied via `extendUserList`
47
+ * @returns The derived list keys and the four Auth list configs keyed by those keys
48
+ */
49
+ export declare function deriveAuthLists(models: NormalizedAuthModels, userConfig?: ExtendUserListConfig): DerivedAuthLists;
50
+ //# sourceMappingURL=derive-auth-lists.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive-auth-lists.d.ts","sourceRoot":"","sources":["../../src/config/derive-auth-lists.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,KAAK,EAA6B,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAajF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,oDAAoD;IACpD,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,CAAA;QACf,YAAY,EAAE,MAAM,CAAA;KACrB,CAAA;IACD,kEAAkE;IAElE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;CACvC,CAAA;AA+OD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,oBAAoB,EAC5B,UAAU,GAAE,oBAAyB,GACpC,gBAAgB,CAiBlB"}