@better-auth/kysely-adapter 1.7.2 → 1.7.4
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/index.d.mts +23 -2
- package/dist/index.mjs +105 -3
- package/package.json +5 -3
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Kysely } from "kysely";
|
|
1
|
+
import { Kysely, TableMetadata } from "kysely";
|
|
2
2
|
import { DBAdapter, DBAdapterDebugLogOption } from "@better-auth/core/db/adapter";
|
|
3
|
+
import { ExpectedSchema, IntrospectedTable } from "@better-auth/core/db/internal";
|
|
3
4
|
import { BetterAuthOptions } from "@better-auth/core";
|
|
4
5
|
|
|
5
6
|
//#region src/types.d.ts
|
|
@@ -85,4 +86,24 @@ interface KyselyAdapterConfig {
|
|
|
85
86
|
}
|
|
86
87
|
declare const kyselyAdapter: (db: Kysely<any>, config?: KyselyAdapterConfig | undefined) => (options: BetterAuthOptions) => DBAdapter<BetterAuthOptions>;
|
|
87
88
|
//#endregion
|
|
88
|
-
|
|
89
|
+
//#region src/schema-check.d.ts
|
|
90
|
+
/**
|
|
91
|
+
* Converts Kysely table metadata into the shape `diffSchema` compares.
|
|
92
|
+
*/
|
|
93
|
+
declare function toIntrospectedTables(tables: readonly TableMetadata[]): IntrospectedTable[];
|
|
94
|
+
/**
|
|
95
|
+
* The expected schema in the identifiers the connection sends. A plugin that
|
|
96
|
+
* renames identifiers, such as `CamelCasePlugin`, does so in `transformQuery`,
|
|
97
|
+
* so compiling one select per table through the connection yields the names
|
|
98
|
+
* the database is asked for. Without such a plugin the schema is unchanged.
|
|
99
|
+
*/
|
|
100
|
+
declare function toPhysicalSchema(db: Kysely<unknown>, expected: ExpectedSchema): ExpectedSchema;
|
|
101
|
+
/**
|
|
102
|
+
* The default schema for migration tooling. Let PostgreSQL resolve role
|
|
103
|
+
* names and privileges, retaining the legacy public fallback when none exists.
|
|
104
|
+
* Runtime validation uses the effective search path instead of this fallback.
|
|
105
|
+
*/
|
|
106
|
+
declare function getPostgresSchema(db: Kysely<unknown>): Promise<string>;
|
|
107
|
+
declare function getMssqlSchema(db: Kysely<unknown>): Promise<string>;
|
|
108
|
+
//#endregion
|
|
109
|
+
export { DatabaseIndexColumnMetadata, DatabaseIndexIntrospector, DatabaseIndexMetadata, KyselyDatabaseType, createKyselyAdapter, getKyselyDatabaseType, getMssqlSchema, getPostgresSchema, kyselyAdapter, toIntrospectedTables, toPhysicalSchema };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Kysely, MssqlDialect, MysqlDialect, PostgresDialect, SqliteDialect, sql } from "kysely";
|
|
1
|
+
import { ColumnNode, Kysely, MssqlDialect, MysqlDialect, PostgresDialect, ReferenceNode, SelectQueryNode, SqliteDialect, TableNode, sql } from "kysely";
|
|
2
2
|
import { createAdapterFactory } from "@better-auth/core/db/adapter";
|
|
3
|
+
import { checksSchema, createSchemaCheck, diffSchema, getExpectedSchema, registerSchemaCheck } from "@better-auth/core/db/internal";
|
|
3
4
|
import { logger } from "@better-auth/core/env";
|
|
4
5
|
import { capitalizeFirstLetter } from "@better-auth/core/utils/string";
|
|
5
6
|
//#region src/dialect.ts
|
|
@@ -141,6 +142,105 @@ function insensitiveNe(columnRef, value) {
|
|
|
141
142
|
};
|
|
142
143
|
}
|
|
143
144
|
//#endregion
|
|
145
|
+
//#region src/schema-check.ts
|
|
146
|
+
/**
|
|
147
|
+
* Converts Kysely table metadata into the shape `diffSchema` compares.
|
|
148
|
+
*/
|
|
149
|
+
function toIntrospectedTables(tables) {
|
|
150
|
+
return tables.map((table) => ({
|
|
151
|
+
name: table.name,
|
|
152
|
+
schema: table.schema,
|
|
153
|
+
columns: table.columns.map((column) => ({
|
|
154
|
+
name: column.name,
|
|
155
|
+
nullable: column.isNullable,
|
|
156
|
+
hasDefault: column.hasDefaultValue || column.isAutoIncrementing
|
|
157
|
+
}))
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The expected schema in the identifiers the connection sends. A plugin that
|
|
162
|
+
* renames identifiers, such as `CamelCasePlugin`, does so in `transformQuery`,
|
|
163
|
+
* so compiling one select per table through the connection yields the names
|
|
164
|
+
* the database is asked for. Without such a plugin the schema is unchanged.
|
|
165
|
+
*/
|
|
166
|
+
function toPhysicalSchema(db, expected) {
|
|
167
|
+
const physical = {};
|
|
168
|
+
for (const [table, definition] of Object.entries(expected)) {
|
|
169
|
+
const entries = Object.entries(definition.fields);
|
|
170
|
+
const sent = sentIdentifiers(db, table, [definition.idColumn ?? "id", ...entries.map(([column]) => column)]);
|
|
171
|
+
const fields = {};
|
|
172
|
+
entries.forEach(([column, attribute], index) => {
|
|
173
|
+
fields[sent.columns[index + 1] ?? column] = attribute;
|
|
174
|
+
});
|
|
175
|
+
physical[sent.table] = {
|
|
176
|
+
...definition,
|
|
177
|
+
fields,
|
|
178
|
+
schema: sent.schema ?? definition.schema,
|
|
179
|
+
...sent.columns[0] !== "id" && { idColumn: sent.columns[0] }
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return physical;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Compiles a select of `columns` from `table` and reads the identifiers back
|
|
186
|
+
* out of the transformed query, including the schema a plugin such as
|
|
187
|
+
* `WithSchemaPlugin` qualifies the table with. A node the query does not
|
|
188
|
+
* carry in the expected shape leaves its identifier undefined.
|
|
189
|
+
*/
|
|
190
|
+
function sentIdentifiers(db, table, columns) {
|
|
191
|
+
const { query } = db.selectFrom(table).select(columns).compile();
|
|
192
|
+
if (!SelectQueryNode.is(query)) return {
|
|
193
|
+
table,
|
|
194
|
+
columns: []
|
|
195
|
+
};
|
|
196
|
+
const from = query.from?.froms[0];
|
|
197
|
+
const sentTable = from && TableNode.is(from) ? from.table : void 0;
|
|
198
|
+
return {
|
|
199
|
+
schema: sentTable?.schema?.name,
|
|
200
|
+
table: sentTable?.identifier.name ?? table,
|
|
201
|
+
columns: (query.selections ?? []).map(({ selection }) => ReferenceNode.is(selection) && ColumnNode.is(selection.column) ? selection.column.column.name : void 0)
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The default schema for migration tooling. Let PostgreSQL resolve role
|
|
206
|
+
* names and privileges, retaining the legacy public fallback when none exists.
|
|
207
|
+
* Runtime validation uses the effective search path instead of this fallback.
|
|
208
|
+
*/
|
|
209
|
+
async function getPostgresSchema(db) {
|
|
210
|
+
return (await sql`
|
|
211
|
+
SELECT pg_catalog.current_schema() AS schema
|
|
212
|
+
`.execute(db.withoutPlugins())).rows[0]?.schema ?? "public";
|
|
213
|
+
}
|
|
214
|
+
async function getMssqlSchema(db) {
|
|
215
|
+
return (await sql`
|
|
216
|
+
SELECT SCHEMA_NAME() AS "schemaName"
|
|
217
|
+
`.execute(db.withoutPlugins())).rows[0]?.schemaName || "dbo";
|
|
218
|
+
}
|
|
219
|
+
async function schemaSearchPath(db, dbType) {
|
|
220
|
+
if (dbType === "postgres") return (await sql`
|
|
221
|
+
SELECT pg_catalog.current_schemas(true)::text[] AS schemas
|
|
222
|
+
`.execute(db.withoutPlugins())).rows[0]?.schemas ?? [];
|
|
223
|
+
if (dbType === "mssql") return [await getMssqlSchema(db), "dbo"];
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Compares the live database with the tables this configuration writes. Both
|
|
227
|
+
* sides are read in the identifiers the connection sends: a plugin that
|
|
228
|
+
* renames identifiers or qualifies them with a schema is applied to the
|
|
229
|
+
* expected side, and introspection reports what the database stores.
|
|
230
|
+
*/
|
|
231
|
+
async function findSchemaProblems(db, dbType, expected) {
|
|
232
|
+
const physical = toPhysicalSchema(db, expected);
|
|
233
|
+
return db.connection().execute(async (connection) => {
|
|
234
|
+
const searchPath = await schemaSearchPath(connection, dbType);
|
|
235
|
+
const tables = toIntrospectedTables(await connection.introspection.getTables());
|
|
236
|
+
for (const [name, table] of Object.entries(physical)) {
|
|
237
|
+
if (table.schema !== void 0 || !searchPath) continue;
|
|
238
|
+
table.schema = searchPath.find((schema) => tables.some((candidate) => candidate.name === name && candidate.schema === schema)) ?? "";
|
|
239
|
+
}
|
|
240
|
+
return diffSchema(physical, tables);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
//#endregion
|
|
144
244
|
//#region src/kysely-adapter.ts
|
|
145
245
|
const kyselyAdapter = (db, config) => {
|
|
146
246
|
let lazyOptions = null;
|
|
@@ -589,8 +689,10 @@ const kyselyAdapter = (db, config) => {
|
|
|
589
689
|
const adapter = createAdapterFactory(adapterOptions);
|
|
590
690
|
return (options) => {
|
|
591
691
|
lazyOptions = options;
|
|
592
|
-
|
|
692
|
+
const instance = adapter(options);
|
|
693
|
+
if (checksSchema(options)) registerSchemaCheck(instance, createSchemaCheck(() => findSchemaProblems(db, config?.type, getExpectedSchema(options, { usePlural: config?.usePlural })), "database", options.database));
|
|
694
|
+
return instance;
|
|
593
695
|
};
|
|
594
696
|
};
|
|
595
697
|
//#endregion
|
|
596
|
-
export { createKyselyAdapter, getKyselyDatabaseType, kyselyAdapter };
|
|
698
|
+
export { createKyselyAdapter, getKyselyDatabaseType, getMssqlSchema, getPostgresSchema, kyselyAdapter, toIntrospectedTables, toPhysicalSchema };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/kysely-adapter",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.4",
|
|
4
4
|
"description": "Kysely adapter for Better Auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@better-auth/utils": "0.4.2",
|
|
44
44
|
"kysely": "^0.28.17 || ^0.29.0",
|
|
45
|
-
"@better-auth/core": "^1.7.
|
|
45
|
+
"@better-auth/core": "^1.7.4"
|
|
46
46
|
},
|
|
47
47
|
"peerDependenciesMeta": {
|
|
48
48
|
"kysely": {
|
|
@@ -52,10 +52,12 @@
|
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@better-auth/utils": "0.4.2",
|
|
54
54
|
"@cloudflare/workers-types": "^4.20250121.0",
|
|
55
|
+
"@types/pg": "^8.20.0",
|
|
55
56
|
"kysely": "^0.28.17 || ^0.29.0",
|
|
57
|
+
"pg": "^8.0.0",
|
|
56
58
|
"tsdown": "0.21.10",
|
|
57
59
|
"typescript": "^6.0.3",
|
|
58
|
-
"@better-auth/core": "1.7.
|
|
60
|
+
"@better-auth/core": "1.7.4"
|
|
59
61
|
},
|
|
60
62
|
"scripts": {
|
|
61
63
|
"build": "tsdown",
|