@better-media/adapter-db 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 abenezeratnafu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
20
+ ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # @better-media/adapter-db
2
+
3
+ Database adapters for SQL (Kysely) and metadata persistence.
4
+
5
+ ## Features
6
+
7
+ - **Kysely Support**: Use with PostgreSQL, MySQL, SQLite, or LibSQL.
8
+ - **Drizzle/Prisma Friendly**: Designed to work with your existing schema through migrations.
9
+ - **Media Metadata**: Stores and retrieves file metadata, plugin results, and process logs.
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { KyselyDatabaseAdapter } from "@better-media/adapter-db";
15
+ import { Kysely, PostgresDialect } from "kysely";
16
+
17
+ const db = new Kysely({ dialect: new PostgresDialect({ pool }) });
18
+ const database = new KyselyDatabaseAdapter(db);
19
+ ```
20
+
21
+ For more, visit [better-media.dev/docs/adapters/db](https://better-media.dev/docs/adapters/db).
@@ -0,0 +1,8 @@
1
+ import { D as DbSchema, K as KyselyDbOptions } from '../../kysely-db.adapter-CHgcv4TU.mjs';
2
+ export { a as KyselyDbAdapter, b as KyselyDbConfig } from '../../kysely-db.adapter-CHgcv4TU.mjs';
3
+ import { DatabaseAdapter } from '@better-media/core';
4
+ import { Kysely } from 'kysely';
5
+
6
+ declare function kyselyAdapter(db: Kysely<DbSchema> | unknown, options: KyselyDbOptions): DatabaseAdapter;
7
+
8
+ export { DbSchema, KyselyDbOptions, kyselyAdapter };
@@ -0,0 +1,8 @@
1
+ import { D as DbSchema, K as KyselyDbOptions } from '../../kysely-db.adapter-CHgcv4TU.js';
2
+ export { a as KyselyDbAdapter, b as KyselyDbConfig } from '../../kysely-db.adapter-CHgcv4TU.js';
3
+ import { DatabaseAdapter } from '@better-media/core';
4
+ import { Kysely } from 'kysely';
5
+
6
+ declare function kyselyAdapter(db: Kysely<DbSchema> | unknown, options: KyselyDbOptions): DatabaseAdapter;
7
+
8
+ export { DbSchema, KyselyDbOptions, kyselyAdapter };
@@ -0,0 +1,399 @@
1
+ 'use strict';
2
+
3
+ var kysely = require('kysely');
4
+ var core = require('@better-media/core');
5
+
6
+ // src/adapters/kysely/kysely-db.adapter.ts
7
+ var KyselyDbAdapter = class _KyselyDbAdapter {
8
+ db;
9
+ config;
10
+ schema;
11
+ hooks;
12
+ constructor(db, options) {
13
+ this.db = db;
14
+ this.config = options.config;
15
+ this.schema = options.schema;
16
+ this.hooks = options.hooks;
17
+ }
18
+ getModelFields(model) {
19
+ return this.schema[model]?.fields ?? {};
20
+ }
21
+ getModelDefinition(model) {
22
+ return this.schema[model];
23
+ }
24
+ getHookContext(model, trx) {
25
+ return {
26
+ model,
27
+ adapter: this,
28
+ transaction: trx
29
+ };
30
+ }
31
+ applyWhere(qb, where, model, options) {
32
+ let currentQb = qb;
33
+ const definition = model ? this.getModelDefinition(model) : void 0;
34
+ if (definition?.softDelete && !options?.withDeleted) {
35
+ currentQb = currentQb.where("deletedAt", "is", null);
36
+ }
37
+ if (!where || where.length === 0) return currentQb;
38
+ for (let i = 0; i < where.length; i++) {
39
+ const condition = where[i];
40
+ if (!condition) continue;
41
+ const connector = i > 0 ? where[i - 1]?.connector ?? "AND" : "AND";
42
+ const field = condition.field;
43
+ let operator = condition.operator ?? "=";
44
+ let value = condition.value;
45
+ if (operator === "contains") {
46
+ operator = "like";
47
+ value = `%${value}%`;
48
+ } else if (operator === "starts_with") {
49
+ operator = "like";
50
+ value = `${value}%`;
51
+ } else if (operator === "ends_with") {
52
+ operator = "like";
53
+ value = `%${value}`;
54
+ } else if (operator === "not_in") {
55
+ operator = "not in";
56
+ }
57
+ const method = connector === "OR" ? "orWhere" : "where";
58
+ currentQb = currentQb[method](field, operator, value);
59
+ }
60
+ return currentQb;
61
+ }
62
+ async create(options) {
63
+ const fields = this.getModelFields(options.model);
64
+ const context = this.getHookContext(options.model);
65
+ let dataToInsert = options.data;
66
+ dataToInsert = await core.runHooks.beforeCreate(this.hooks, dataToInsert, context);
67
+ const serializedData = core.serializeData(fields, dataToInsert);
68
+ let resultRecord;
69
+ if (this.config.provider === "sqlite") {
70
+ await this.db.insertInto(options.model).values(serializedData).execute();
71
+ const refetched = await this.db.selectFrom(options.model).selectAll().where("id", "=", serializedData.id).executeTakeFirst();
72
+ resultRecord = core.deserializeData(
73
+ fields,
74
+ refetched || serializedData
75
+ );
76
+ } else {
77
+ const result = await this.db.insertInto(options.model).values(serializedData).returningAll().executeTakeFirst() || serializedData;
78
+ resultRecord = core.deserializeData(fields, result);
79
+ }
80
+ await core.runHooks.afterCreate(this.hooks, resultRecord, context);
81
+ return resultRecord;
82
+ }
83
+ async findOne(options) {
84
+ const fields = this.getModelFields(options.model);
85
+ let qb = this.db.selectFrom(options.model);
86
+ if (options.select) {
87
+ qb = qb.select(options.select);
88
+ } else {
89
+ qb = qb.selectAll();
90
+ }
91
+ qb = this.applyWhere(qb, options.where, options.model, { withDeleted: options.withDeleted });
92
+ if (options.populate) {
93
+ for (const relation of options.populate) {
94
+ const fieldDef = this.schema[options.model]?.fields[relation];
95
+ if (fieldDef?.references) {
96
+ qb = qb.leftJoin(
97
+ fieldDef.references.model,
98
+ `${options.model}.${relation}`,
99
+ `${fieldDef.references.model}.${fieldDef.references.field}`
100
+ );
101
+ }
102
+ }
103
+ }
104
+ const result = await qb.executeTakeFirst();
105
+ if (!result) return null;
106
+ return core.deserializeData(fields, result);
107
+ }
108
+ async findMany(options) {
109
+ const fields = this.getModelFields(options.model);
110
+ let qb = this.db.selectFrom(options.model);
111
+ if (options.select) {
112
+ qb = qb.select(options.select);
113
+ } else {
114
+ qb = qb.selectAll();
115
+ }
116
+ qb = this.applyWhere(qb, options.where, options.model, { withDeleted: options.withDeleted });
117
+ if (options.sortBy) {
118
+ qb = qb.orderBy(options.sortBy.field, options.sortBy.direction);
119
+ }
120
+ if (options.limit !== void 0) qb = qb.limit(options.limit);
121
+ if (options.offset !== void 0) qb = qb.offset(options.offset);
122
+ const results = await qb.execute();
123
+ return results.map((row) => core.deserializeData(fields, row));
124
+ }
125
+ async update(options) {
126
+ const fields = this.getModelFields(options.model);
127
+ const context = this.getHookContext(options.model);
128
+ const target = await this.findOne({ model: options.model, where: options.where });
129
+ if (!target) return null;
130
+ let updatedData = { ...target, ...options.update };
131
+ updatedData = await core.runHooks.beforeUpdate(this.hooks, updatedData, context);
132
+ const updatePayload = core.serializeData(fields, options.update);
133
+ let qb = this.db.updateTable(options.model).set(updatePayload);
134
+ qb = this.applyWhere(qb, options.where, options.model);
135
+ let resultRecord;
136
+ if (this.config.provider === "sqlite") {
137
+ await qb.execute();
138
+ resultRecord = core.deserializeData(fields, core.serializeData(fields, updatedData));
139
+ } else {
140
+ const result = await qb.returningAll().executeTakeFirst();
141
+ resultRecord = core.deserializeData(
142
+ fields,
143
+ result || core.serializeData(fields, updatedData)
144
+ );
145
+ }
146
+ await core.runHooks.afterUpdate(this.hooks, resultRecord, context);
147
+ return resultRecord;
148
+ }
149
+ async updateMany(options) {
150
+ const fields = this.getModelFields(options.model);
151
+ const updatePayload = core.serializeData(fields, options.update);
152
+ let qb = this.db.updateTable(options.model).set(updatePayload);
153
+ qb = this.applyWhere(
154
+ qb,
155
+ options.where,
156
+ options.model
157
+ );
158
+ const results = await qb.execute();
159
+ return Number(results[0]?.numUpdatedRows || 0);
160
+ }
161
+ async delete(options) {
162
+ const context = this.getHookContext(options.model);
163
+ const definition = this.getModelDefinition(options.model);
164
+ await core.runHooks.beforeDelete(this.hooks, options.where, context);
165
+ if (definition?.softDelete) {
166
+ await this.updateMany({
167
+ model: options.model,
168
+ where: options.where,
169
+ update: { deletedAt: /* @__PURE__ */ new Date() }
170
+ });
171
+ } else {
172
+ let qb = this.db.deleteFrom(options.model);
173
+ qb = this.applyWhere(qb, options.where, options.model);
174
+ await qb.execute();
175
+ }
176
+ await core.runHooks.afterDelete(this.hooks, options.where, context);
177
+ }
178
+ async deleteMany(options) {
179
+ const definition = this.getModelDefinition(options.model);
180
+ if (definition?.softDelete) {
181
+ return await this.updateMany({
182
+ model: options.model,
183
+ where: options.where,
184
+ update: { deletedAt: /* @__PURE__ */ new Date() }
185
+ });
186
+ }
187
+ let qb = this.db.deleteFrom(options.model);
188
+ qb = this.applyWhere(qb, options.where, options.model);
189
+ const results = await qb.execute();
190
+ return Number(results[0]?.numDeletedRows || 0);
191
+ }
192
+ async count(options) {
193
+ let qb = this.db.selectFrom(options.model);
194
+ qb = this.applyWhere(qb, options.where, options.model);
195
+ const { count } = this.db.fn;
196
+ qb = qb.select(count("id").as("c"));
197
+ const result = await qb.executeTakeFirst();
198
+ return Number(result?.c || 0);
199
+ }
200
+ async raw(query, params) {
201
+ const result = await kysely.sql.raw(query, params).execute(this.db);
202
+ return result.rows;
203
+ }
204
+ async transaction(callback) {
205
+ return await this.db.transaction().execute(async (trx) => {
206
+ const trxAdapter = new _KyselyDbAdapter(trx, {
207
+ config: this.config,
208
+ schema: this.schema,
209
+ hooks: this.hooks
210
+ });
211
+ return await callback(trxAdapter);
212
+ });
213
+ }
214
+ /** @internal Used by runMigrations — not part of the public DatabaseAdapter contract. */
215
+ async __getMetadata() {
216
+ const tables = await this.db.introspection.getTables();
217
+ const dialect = this.__getDialect();
218
+ let filtered = tables;
219
+ if (dialect === "postgres") {
220
+ const currentSchema = await this.getPostgresSchema();
221
+ filtered = tables.filter((table) => {
222
+ const schema = table.schema;
223
+ return !schema || schema === currentSchema;
224
+ });
225
+ }
226
+ return filtered.map((table) => ({
227
+ name: table.name,
228
+ columns: table.columns.map((col) => ({
229
+ name: col.name,
230
+ dataType: col.dataType,
231
+ isNullable: col.isNullable ?? true
232
+ }))
233
+ }));
234
+ }
235
+ async getPostgresSchema() {
236
+ try {
237
+ const result = await kysely.sql`SHOW search_path`.execute(this.db);
238
+ const searchPath = result.rows[0]?.search_path ?? result.rows[0]?.searchPath;
239
+ if (!searchPath) return "public";
240
+ const schemas = searchPath.split(",").map((s) => s.trim()).map((s) => s.replace(/^["']|["']$/g, "")).filter((s) => !s.startsWith("$") && !s.startsWith("\\$"));
241
+ return schemas[0] || "public";
242
+ } catch {
243
+ return "public";
244
+ }
245
+ }
246
+ /** @internal Used by runMigrations to auto-detect the SQL dialect. */
247
+ __getDialect() {
248
+ switch (this.config.provider) {
249
+ case "pg":
250
+ return "postgres";
251
+ case "mysql":
252
+ return "mysql";
253
+ case "sqlite":
254
+ return "sqlite";
255
+ default:
256
+ return "postgres";
257
+ }
258
+ }
259
+ /** @internal Used by runMigrations — not part of the public DatabaseAdapter contract. */
260
+ async __executeMigration(operation) {
261
+ const dialect = this.__getDialect();
262
+ if (operation.type === "createTable") {
263
+ let builder = this.db.schema.createTable(operation.table).ifNotExists();
264
+ for (const [name, field] of Object.entries(operation.definition.fields)) {
265
+ const type = core.getColumnType(field, dialect);
266
+ builder = builder.addColumn(name, type, (col) => {
267
+ if (field.primaryKey) col = col.primaryKey();
268
+ if (field.required) col = col.notNull();
269
+ if (field.unique) col = col.unique();
270
+ if (field.references) {
271
+ col = col.references(`${field.references.model}.${field.references.field}`).onDelete(field.references.onDelete || "cascade");
272
+ }
273
+ return col;
274
+ });
275
+ }
276
+ await builder.execute();
277
+ if (operation.definition.indexes) {
278
+ for (const index of operation.definition.indexes) {
279
+ const indexName = `idx_${operation.table}_${index.fields.join("_")}`;
280
+ let indexBuilder = this.db.schema.createIndex(indexName).on(operation.table).columns(index.fields);
281
+ if (index.unique) {
282
+ indexBuilder = indexBuilder.unique();
283
+ }
284
+ await indexBuilder.execute();
285
+ }
286
+ }
287
+ } else if (operation.type === "addColumn") {
288
+ const type = core.getColumnType(operation.definition, dialect);
289
+ await this.db.schema.alterTable(operation.table).addColumn(operation.field, type, (col) => {
290
+ if (operation.definition.required) col = col.notNull();
291
+ if (operation.definition.unique) col = col.unique();
292
+ if (operation.definition.references) {
293
+ col = col.references(
294
+ `${operation.definition.references.model}.${operation.definition.references.field}`
295
+ ).onDelete(operation.definition.references.onDelete || "cascade");
296
+ }
297
+ return col;
298
+ }).execute();
299
+ } else if (operation.type === "createIndex") {
300
+ let builder = this.db.schema.createIndex(operation.name).on(operation.table).columns(operation.fields);
301
+ if (operation.unique) {
302
+ builder = builder.unique();
303
+ }
304
+ await builder.execute();
305
+ }
306
+ }
307
+ };
308
+ function isKyselyInstance(obj) {
309
+ if (!obj || typeof obj !== "object") return false;
310
+ const db = obj;
311
+ return typeof db.selectFrom === "function" && typeof db.transaction === "function";
312
+ }
313
+ function ensureKyselyInstance(db, options) {
314
+ if (isKyselyInstance(db)) {
315
+ if (!options.config.provider) {
316
+ throw new Error(
317
+ "When providing a Kysely instance, 'options.config.provider' must be specified ('pg', 'mysql', or 'sqlite')."
318
+ );
319
+ }
320
+ return { db, provider: options.config.provider };
321
+ }
322
+ const dbConnection = db;
323
+ if (dbConnection && typeof dbConnection.connect === "function" && typeof dbConnection.query === "function") {
324
+ return {
325
+ db: new kysely.Kysely({
326
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
327
+ dialect: new kysely.PostgresDialect({ pool: dbConnection })
328
+ }),
329
+ provider: "pg"
330
+ };
331
+ }
332
+ if (dbConnection && typeof dbConnection.getConnection === "function" && typeof dbConnection.query === "function") {
333
+ return {
334
+ db: new kysely.Kysely({
335
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
336
+ dialect: new kysely.MysqlDialect({ pool: dbConnection })
337
+ }),
338
+ provider: "mysql"
339
+ };
340
+ }
341
+ if (dbConnection && typeof dbConnection.prepare === "function" && typeof dbConnection.exec === "function") {
342
+ return {
343
+ db: new kysely.Kysely({
344
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
345
+ dialect: new kysely.SqliteDialect({ database: dbConnection })
346
+ }),
347
+ provider: "sqlite"
348
+ };
349
+ }
350
+ if (dbConnection && typeof dbConnection.createAdapter === "function" && typeof dbConnection.createQueryCompiler === "function") {
351
+ if (!options.config.provider) {
352
+ throw new Error(
353
+ "When providing a Kysely Dialect, 'options.config.provider' must be specified for adapter behavior."
354
+ );
355
+ }
356
+ return {
357
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
358
+ db: new kysely.Kysely({ dialect: dbConnection }),
359
+ provider: options.config.provider
360
+ };
361
+ }
362
+ throw new Error(
363
+ "Unsupported database connection type. Please provide a Kysely instance, a pg/mysql2 Pool, or a better-sqlite3 Database."
364
+ );
365
+ }
366
+ function kyselyAdapter(db, options) {
367
+ const { db: kyselyDb, provider } = ensureKyselyInstance(db, options);
368
+ options.config.provider = provider;
369
+ const adapter = new KyselyDbAdapter(kyselyDb, options);
370
+ return new Proxy(adapter, {
371
+ get(target, prop, receiver) {
372
+ if (prop === "get") {
373
+ return async (key) => {
374
+ const res = await adapter.findOne({
375
+ model: "legacy",
376
+ where: [{ field: "id", value: key }]
377
+ });
378
+ return res;
379
+ };
380
+ }
381
+ if (prop === "put") {
382
+ return async (key, data) => {
383
+ await adapter.create({ model: "legacy", data: { id: key, ...data } });
384
+ };
385
+ }
386
+ if (prop === "delete") {
387
+ return async (key) => {
388
+ await adapter.delete({ model: "legacy", where: [{ field: "id", value: key }] });
389
+ };
390
+ }
391
+ return Reflect.get(target, prop, receiver);
392
+ }
393
+ });
394
+ }
395
+
396
+ exports.KyselyDbAdapter = KyselyDbAdapter;
397
+ exports.kyselyAdapter = kyselyAdapter;
398
+ //# sourceMappingURL=index.js.map
399
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/adapters/kysely/kysely-db.adapter.ts","../../../src/adapters/kysely/index.ts"],"names":["runHooks","serializeData","deserializeData","sql","getColumnType","Kysely","PostgresDialect","MysqlDialect","SqliteDialect"],"mappings":"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","file":"index.js","sourcesContent":["import { Kysely, sql } from \"kysely\";\nimport type {\n DatabaseAdapter,\n DatabaseTransactionAdapter,\n WhereClause,\n CreateOptions,\n FindOptions,\n UpdateOptions,\n DeleteOptions,\n CountOptions,\n} from \"@better-media/core\";\nimport type {\n FieldType,\n FieldDefinition,\n BmSchema,\n DbHooks,\n DatabaseHookContext,\n ModelDefinition,\n MigrationOperation,\n SqlDialect,\n TableMetadata,\n} from \"@better-media/core\";\nimport { serializeData, deserializeData, runHooks, getColumnType } from \"@better-media/core\";\nimport type { KyselyDbConfig } from \"./kysely-db-config.interface\";\n\nexport interface KyselyDbOptions {\n config: KyselyDbConfig;\n schema: BmSchema;\n hooks?: DbHooks;\n}\n\nexport type DbSchema = Record<string, Record<string, unknown>>;\n\ntype AnyFunction = (...args: unknown[]) => unknown;\n\ntype KyselyBuilder = {\n where: (field: string, op: string, value: unknown) => KyselyBuilder;\n orWhere: (field: string, op: string, value: unknown) => KyselyBuilder;\n select: (fields: unknown) => KyselyBuilder;\n selectAll: () => KyselyBuilder;\n leftJoin: (table: string, left: string, right: string) => KyselyBuilder;\n orderBy: (field: string, direction: string) => KyselyBuilder;\n limit: (n: number) => KyselyBuilder;\n offset: (n: number) => KyselyBuilder;\n set: (data: Record<string, unknown>) => KyselyBuilder;\n values: (data: Record<string, unknown>) => KyselyBuilder;\n returningAll: () => KyselyBuilder;\n execute: () => Promise<unknown[] & { numUpdatedRows?: number; numDeletedRows?: number }>;\n executeTakeFirst: () => Promise<unknown>;\n};\n\n/**\n * SQL database adapter using Kysely.\n */\nexport class KyselyDbAdapter implements DatabaseAdapter {\n private readonly db: Kysely<DbSchema>;\n private readonly config: KyselyDbConfig;\n private readonly schema: BmSchema;\n private readonly hooks?: DbHooks;\n\n constructor(db: Kysely<DbSchema>, options: KyselyDbOptions) {\n this.db = db;\n this.config = options.config;\n this.schema = options.schema;\n this.hooks = options.hooks;\n }\n\n private getModelFields(model: string): Record<string, { type: FieldType }> {\n return this.schema[model]?.fields ?? {};\n }\n\n private getModelDefinition(model: string): ModelDefinition | undefined {\n return this.schema[model];\n }\n\n private getHookContext(model: string, trx?: DatabaseTransactionAdapter): DatabaseHookContext {\n return {\n model,\n adapter: this,\n transaction: trx,\n };\n }\n\n private applyWhere(\n qb: KyselyBuilder,\n where?: WhereClause,\n model?: string,\n options?: { withDeleted?: boolean }\n ): KyselyBuilder {\n let currentQb = qb;\n const definition = model ? this.getModelDefinition(model) : undefined;\n\n // Soft delete filtering\n if (definition?.softDelete && !options?.withDeleted) {\n currentQb = currentQb.where(\"deletedAt\", \"is\", null);\n }\n\n if (!where || where.length === 0) return currentQb;\n\n for (let i = 0; i < where.length; i++) {\n const condition = where[i];\n if (!condition) continue;\n\n const connector = i > 0 ? (where[i - 1]?.connector ?? \"AND\") : \"AND\";\n const field = condition.field;\n let operator = condition.operator ?? \"=\";\n let value = condition.value;\n\n if (operator === \"contains\") {\n operator = \"like\";\n value = `%${value}%`;\n } else if (operator === \"starts_with\") {\n operator = \"like\";\n value = `${value}%`;\n } else if (operator === \"ends_with\") {\n operator = \"like\";\n value = `%${value}`;\n } else if (operator === \"not_in\") {\n operator = \"not in\" as \"in\";\n }\n\n const method = (connector === \"OR\" ? \"orWhere\" : \"where\") as keyof KyselyBuilder;\n currentQb = (currentQb[method] as AnyFunction)(field, operator, value) as KyselyBuilder;\n }\n\n return currentQb;\n }\n\n async create<T extends Record<string, unknown>>(options: CreateOptions<T>): Promise<T> {\n const fields = this.getModelFields(options.model);\n const context = this.getHookContext(options.model);\n\n let dataToInsert = options.data as Record<string, unknown>;\n dataToInsert = await runHooks.beforeCreate(this.hooks, dataToInsert, context);\n\n const serializedData = serializeData(fields, dataToInsert);\n let resultRecord: T;\n\n if (this.config.provider === \"sqlite\") {\n await (this.db.insertInto(options.model) as unknown as KyselyBuilder)\n .values(serializedData)\n .execute();\n\n const refetched = await (this.db.selectFrom(options.model) as unknown as KyselyBuilder)\n .selectAll()\n .where(\"id\", \"=\", serializedData.id)\n .executeTakeFirst();\n\n resultRecord = deserializeData(\n fields,\n (refetched as Record<string, unknown>) || serializedData\n ) as T;\n } else {\n const result =\n (await (this.db.insertInto(options.model) as unknown as KyselyBuilder)\n .values(serializedData)\n .returningAll()\n .executeTakeFirst()) || serializedData;\n\n resultRecord = deserializeData(fields, result as Record<string, unknown>) as T;\n }\n\n await runHooks.afterCreate(this.hooks, resultRecord as Record<string, unknown>, context);\n return resultRecord;\n }\n\n async findOne<T extends Record<string, unknown>>(options: FindOptions<T>): Promise<T | null> {\n const fields = this.getModelFields(options.model);\n let qb = this.db.selectFrom(options.model) as unknown as KyselyBuilder;\n\n if (options.select) {\n qb = qb.select(options.select);\n } else {\n qb = qb.selectAll();\n }\n\n qb = this.applyWhere(qb, options.where, options.model, { withDeleted: options.withDeleted });\n\n if (options.populate) {\n // Basic population via left joins if references exist\n for (const relation of options.populate) {\n const fieldDef = this.schema[options.model]?.fields[relation];\n if (fieldDef?.references) {\n qb = qb.leftJoin(\n fieldDef.references.model,\n `${options.model}.${relation}`,\n `${fieldDef.references.model}.${fieldDef.references.field}`\n );\n }\n }\n }\n\n const result = await qb.executeTakeFirst();\n if (!result) return null;\n\n return deserializeData(fields, result as Record<string, unknown>) as T;\n }\n\n async findMany<T extends Record<string, unknown>>(options: FindOptions<T>): Promise<T[]> {\n const fields = this.getModelFields(options.model);\n let qb = this.db.selectFrom(options.model) as unknown as KyselyBuilder;\n\n if (options.select) {\n qb = qb.select(options.select);\n } else {\n qb = qb.selectAll();\n }\n\n qb = this.applyWhere(qb, options.where, options.model, { withDeleted: options.withDeleted });\n\n if (options.sortBy) {\n qb = qb.orderBy(options.sortBy.field, options.sortBy.direction);\n }\n\n if (options.limit !== undefined) qb = qb.limit(options.limit);\n if (options.offset !== undefined) qb = qb.offset(options.offset);\n\n const results = await (qb as { execute: AnyFunction }).execute();\n return (results as Record<string, unknown>[]).map((row) => deserializeData(fields, row)) as T[];\n }\n\n async update<T extends Record<string, unknown>>(options: UpdateOptions<T>): Promise<T | null> {\n const fields = this.getModelFields(options.model);\n const context = this.getHookContext(options.model);\n\n // We still need the current record for hooks (merging data)\n const target = await this.findOne({ model: options.model, where: options.where });\n if (!target) return null;\n\n let updatedData = { ...target, ...(options.update as Record<string, unknown>) };\n updatedData = await runHooks.beforeUpdate(this.hooks, updatedData, context);\n\n const updatePayload = serializeData(fields, options.update as Record<string, unknown>);\n\n let qb = (this.db.updateTable(options.model) as unknown as KyselyBuilder).set(updatePayload);\n qb = this.applyWhere(qb, options.where, options.model);\n\n let resultRecord: T;\n if (this.config.provider === \"sqlite\") {\n await qb.execute();\n resultRecord = deserializeData(fields, serializeData(fields, updatedData)) as T;\n } else {\n const result = await (qb as unknown as KyselyBuilder).returningAll().executeTakeFirst();\n resultRecord = deserializeData(\n fields,\n (result as Record<string, unknown>) || serializeData(fields, updatedData)\n ) as T;\n }\n\n await runHooks.afterUpdate(this.hooks, resultRecord as Record<string, unknown>, context);\n return resultRecord;\n }\n\n async updateMany<T extends Record<string, unknown>>(options: UpdateOptions<T>): Promise<number> {\n const fields = this.getModelFields(options.model);\n const updatePayload = serializeData(fields, options.update as Record<string, unknown>);\n\n let qb = (this.db.updateTable(options.model) as unknown as KyselyBuilder).set(updatePayload);\n qb = this.applyWhere(\n qb as unknown as KyselyBuilder,\n options.where,\n options.model\n ) as KyselyBuilder;\n\n const results = (await qb.execute()) as unknown as { numUpdatedRows: bigint | number }[];\n return Number(results[0]?.numUpdatedRows || 0);\n }\n\n async delete(options: DeleteOptions): Promise<void> {\n const context = this.getHookContext(options.model);\n const definition = this.getModelDefinition(options.model);\n\n await runHooks.beforeDelete(this.hooks, options.where, context);\n\n if (definition?.softDelete) {\n await this.updateMany({\n model: options.model,\n where: options.where,\n update: { deletedAt: new Date() } as unknown as Record<string, unknown>,\n });\n } else {\n let qb = this.db.deleteFrom(options.model) as unknown as KyselyBuilder;\n qb = this.applyWhere(qb, options.where, options.model);\n await qb.execute();\n }\n\n await runHooks.afterDelete(this.hooks, options.where, context);\n }\n\n async deleteMany(options: DeleteOptions): Promise<number> {\n const definition = this.getModelDefinition(options.model);\n\n if (definition?.softDelete) {\n return await this.updateMany({\n model: options.model,\n where: options.where,\n update: { deletedAt: new Date() } as unknown as Record<string, unknown>,\n });\n }\n\n let qb = this.db.deleteFrom(options.model) as unknown as KyselyBuilder;\n qb = this.applyWhere(qb, options.where, options.model);\n\n const results = (await qb.execute()) as unknown as { numDeletedRows: bigint | number }[];\n return Number(results[0]?.numDeletedRows || 0);\n }\n\n async count(options: CountOptions): Promise<number> {\n let qb = this.db.selectFrom(options.model) as unknown as KyselyBuilder;\n qb = this.applyWhere(qb, options.where, options.model);\n\n const { count } = this.db.fn as unknown as {\n count: (f: string) => { as: (a: string) => unknown };\n };\n qb = qb.select(count(\"id\").as(\"c\"));\n\n const result = (await qb.executeTakeFirst()) as { c: string | number } | undefined;\n return Number(result?.c || 0);\n }\n\n async raw<T = unknown>(query: string, params?: unknown[]): Promise<T> {\n const result = await (\n sql as unknown as {\n raw: (q: string, p: unknown) => { execute: (db: unknown) => Promise<{ rows: T }> };\n }\n )\n .raw(query, params)\n .execute(this.db);\n return result.rows;\n }\n\n async transaction<R>(callback: (trx: DatabaseTransactionAdapter) => Promise<R>): Promise<R> {\n return (await (\n this.db.transaction() as unknown as {\n execute: (cb: (trx: Kysely<DbSchema>) => Promise<R>) => Promise<R>;\n }\n ).execute(async (trx) => {\n const trxAdapter = new KyselyDbAdapter(trx, {\n config: this.config,\n schema: this.schema,\n hooks: this.hooks,\n });\n return await callback(trxAdapter);\n })) as R;\n }\n\n /** @internal Used by runMigrations — not part of the public DatabaseAdapter contract. */\n async __getMetadata(): Promise<TableMetadata[]> {\n const tables = await this.db.introspection.getTables();\n const dialect = this.__getDialect();\n\n let filtered = tables;\n if (dialect === \"postgres\") {\n const currentSchema = await this.getPostgresSchema();\n filtered = tables.filter((table) => {\n const schema = (table as unknown as { schema?: string }).schema;\n return !schema || schema === currentSchema;\n });\n }\n\n return filtered.map((table) => ({\n name: table.name,\n columns: table.columns.map((col) => ({\n name: col.name,\n dataType: col.dataType,\n isNullable: col.isNullable ?? true,\n })),\n }));\n }\n\n private async getPostgresSchema(): Promise<string> {\n try {\n const result = await sql<{\n search_path?: string;\n searchPath?: string;\n }>`SHOW search_path`.execute(this.db);\n const searchPath = result.rows[0]?.search_path ?? result.rows[0]?.searchPath;\n if (!searchPath) return \"public\";\n const schemas = searchPath\n .split(\",\")\n .map((s) => s.trim())\n .map((s) => s.replace(/^[\"']|[\"']$/g, \"\"))\n .filter((s) => !s.startsWith(\"$\") && !s.startsWith(\"\\\\$\"));\n return schemas[0] || \"public\";\n } catch {\n return \"public\";\n }\n }\n\n /** @internal Used by runMigrations to auto-detect the SQL dialect. */\n __getDialect(): SqlDialect {\n switch (this.config.provider) {\n case \"pg\":\n return \"postgres\";\n case \"mysql\":\n return \"mysql\";\n case \"sqlite\":\n return \"sqlite\";\n default:\n return \"postgres\";\n }\n }\n\n /** @internal Used by runMigrations — not part of the public DatabaseAdapter contract. */\n async __executeMigration(operation: MigrationOperation): Promise<void> {\n const dialect = this.__getDialect();\n\n if (operation.type === \"createTable\") {\n let builder = this.db.schema.createTable(operation.table).ifNotExists();\n\n for (const [name, field] of Object.entries(operation.definition.fields) as [\n string,\n FieldDefinition,\n ][]) {\n const type = getColumnType(field, dialect);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n builder = builder.addColumn(name, type as any, (col: any) => {\n if (field.primaryKey) col = col.primaryKey();\n if (field.required) col = col.notNull();\n if (field.unique) col = col.unique();\n if (field.references) {\n col = col\n .references(`${field.references.model}.${field.references.field}`)\n .onDelete(field.references.onDelete || \"cascade\");\n }\n return col;\n });\n }\n await builder.execute();\n\n // Create indexes\n if (operation.definition.indexes) {\n for (const index of operation.definition.indexes) {\n const indexName = `idx_${operation.table}_${index.fields.join(\"_\")}`;\n let indexBuilder = this.db.schema\n .createIndex(indexName)\n .on(operation.table)\n .columns(index.fields);\n if (index.unique) {\n indexBuilder = indexBuilder.unique();\n }\n await indexBuilder.execute();\n }\n }\n } else if (operation.type === \"addColumn\") {\n const type = getColumnType(operation.definition, dialect);\n await this.db.schema\n .alterTable(operation.table)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n .addColumn(operation.field, type as any, (col: any) => {\n if (operation.definition.required) col = col.notNull();\n if (operation.definition.unique) col = col.unique();\n if (operation.definition.references) {\n col = col\n .references(\n `${operation.definition.references.model}.${operation.definition.references.field}`\n )\n .onDelete(operation.definition.references.onDelete || \"cascade\");\n }\n return col;\n })\n .execute();\n } else if (operation.type === \"createIndex\") {\n let builder = this.db.schema\n .createIndex(operation.name)\n .on(operation.table)\n .columns(operation.fields);\n if (operation.unique) {\n builder = builder.unique();\n }\n await builder.execute();\n }\n }\n}\n","import { KyselyDbAdapter, type KyselyDbOptions, type DbSchema } from \"./kysely-db.adapter\";\nimport type { DatabaseAdapter } from \"@better-media/core\";\nimport { Kysely, PostgresDialect, MysqlDialect, SqliteDialect } from \"kysely\";\n\nexport * from \"./kysely-db-config.interface\";\nexport * from \"./kysely-db.adapter\";\n\nfunction isKyselyInstance(obj: unknown): obj is Kysely<DbSchema> {\n if (!obj || typeof obj !== \"object\") return false;\n const db = obj as Record<string, unknown>;\n return typeof db.selectFrom === \"function\" && typeof db.transaction === \"function\";\n}\n\n/**\n * Detects and wraps a raw database connection into a Kysely instance.\n */\nfunction ensureKyselyInstance(\n db: unknown,\n options: KyselyDbOptions\n): { db: Kysely<DbSchema>; provider: \"pg\" | \"mysql\" | \"sqlite\" } {\n if (isKyselyInstance(db)) {\n if (!options.config.provider) {\n throw new Error(\n \"When providing a Kysely instance, 'options.config.provider' must be specified ('pg', 'mysql', or 'sqlite').\"\n );\n }\n return { db, provider: options.config.provider };\n }\n\n const dbConnection = db as Record<string, unknown>;\n\n // Detect PostgreSQL (pg Pool)\n if (\n dbConnection &&\n typeof dbConnection.connect === \"function\" &&\n typeof dbConnection.query === \"function\"\n ) {\n return {\n db: new Kysely<DbSchema>({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n dialect: new PostgresDialect({ pool: dbConnection as any }),\n }),\n provider: \"pg\",\n };\n }\n\n // Detect MySQL (mysql2 Pool)\n if (\n dbConnection &&\n typeof dbConnection.getConnection === \"function\" &&\n typeof dbConnection.query === \"function\"\n ) {\n return {\n db: new Kysely<DbSchema>({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n dialect: new MysqlDialect({ pool: dbConnection as any }),\n }),\n provider: \"mysql\",\n };\n }\n\n // Detect SQLite (better-sqlite3)\n if (\n dbConnection &&\n typeof dbConnection.prepare === \"function\" &&\n typeof dbConnection.exec === \"function\"\n ) {\n return {\n db: new Kysely<DbSchema>({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n dialect: new SqliteDialect({ database: dbConnection as any }),\n }),\n provider: \"sqlite\",\n };\n }\n\n // Handle Kysely Dialect directly if passed (as better-auth supports other relational databases)\n if (\n dbConnection &&\n typeof dbConnection.createAdapter === \"function\" &&\n typeof dbConnection.createQueryCompiler === \"function\"\n ) {\n if (!options.config.provider) {\n throw new Error(\n \"When providing a Kysely Dialect, 'options.config.provider' must be specified for adapter behavior.\"\n );\n }\n return {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n db: new Kysely<DbSchema>({ dialect: dbConnection as any }),\n provider: options.config.provider,\n };\n }\n\n throw new Error(\n \"Unsupported database connection type. Please provide a Kysely instance, a pg/mysql2 Pool, or a better-sqlite3 Database.\"\n );\n}\n\nexport function kyselyAdapter(\n db: Kysely<DbSchema> | unknown,\n options: KyselyDbOptions\n): DatabaseAdapter {\n const { db: kyselyDb, provider } = ensureKyselyInstance(db, options);\n\n // Ensure provider is set in config for the adapter's internal logic\n options.config.provider = provider;\n\n const adapter = new KyselyDbAdapter(kyselyDb, options);\n return new Proxy(adapter, {\n get(target, prop, receiver) {\n if (prop === \"get\") {\n return async (key: string) => {\n const res = await adapter.findOne({\n model: \"legacy\",\n where: [{ field: \"id\", value: key }],\n });\n return res;\n };\n }\n if (prop === \"put\") {\n return async (key: string, data: Record<string, unknown>) => {\n await adapter.create({ model: \"legacy\", data: { id: key, ...data } });\n };\n }\n if (prop === \"delete\") {\n return async (key: string) => {\n await adapter.delete({ model: \"legacy\", where: [{ field: \"id\", value: key }] });\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n"]}