@xcelsior/auth-adapter-knex 1.1.9 → 1.1.11
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 +6 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +146 -81
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +134 -80
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -2
- package/src/index.ts +1 -1
- package/src/knex.ts +164 -93
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Knex } from 'knex';
|
|
2
2
|
import { UserMeta, SessionMeta, IStorageProvider, CreateUserInput, User, UserId, UserFilter, FindUsersOptions, FindUsersResult, CreateSessionInput, Session, SessionId } from '@xcelsior/auth';
|
|
3
3
|
|
|
4
|
+
declare const runInTransaction: <T>(knex: Knex, fnt: () => Promise<T>) => Promise<T>;
|
|
4
5
|
interface KnexConfig {
|
|
5
6
|
/** Pre-configured Knex instance */
|
|
6
7
|
knex: Knex;
|
|
@@ -17,8 +18,8 @@ declare class KnexStorageProvider<Meta extends UserMeta = Record<string, any>, S
|
|
|
17
18
|
private sessionsTableName;
|
|
18
19
|
private schema?;
|
|
19
20
|
constructor(config: KnexConfig);
|
|
20
|
-
private
|
|
21
|
-
private
|
|
21
|
+
private table;
|
|
22
|
+
private sessionsTable;
|
|
22
23
|
createUser(user: CreateUserInput<Meta>): Promise<User<Meta>>;
|
|
23
24
|
getUserById(id: UserId): Promise<User<Meta> | null>;
|
|
24
25
|
getUserByEmail(email: string): Promise<User<Meta> | null>;
|
|
@@ -68,6 +69,8 @@ declare class KnexStorageProvider<Meta extends UserMeta = Record<string, any>, S
|
|
|
68
69
|
* Meta fields in updates are flattened as individual snake_case columns.
|
|
69
70
|
*/
|
|
70
71
|
private toDbSessionUpdates;
|
|
72
|
+
private isTransactionClient;
|
|
73
|
+
protected withClient<T>(fnt: (client: Knex) => Promise<T>, transactional?: boolean): Promise<T>;
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
/**
|
|
@@ -137,4 +140,4 @@ declare function createUsersTableMigration(knex: Knex, tableName: string, schema
|
|
|
137
140
|
*/
|
|
138
141
|
declare function createSessionsTableMigration(knex: Knex, tableName: string, schemaOrCustomColumns?: string | ((table: Knex.CreateTableBuilder) => void), customColumns?: (table: Knex.CreateTableBuilder) => void): Promise<void>;
|
|
139
142
|
|
|
140
|
-
export { type KnexConfig, KnexStorageProvider, createSessionsTableMigration, createUsersTableMigration };
|
|
143
|
+
export { type KnexConfig, KnexStorageProvider, createSessionsTableMigration, createUsersTableMigration, runInTransaction };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Knex } from 'knex';
|
|
2
2
|
import { UserMeta, SessionMeta, IStorageProvider, CreateUserInput, User, UserId, UserFilter, FindUsersOptions, FindUsersResult, CreateSessionInput, Session, SessionId } from '@xcelsior/auth';
|
|
3
3
|
|
|
4
|
+
declare const runInTransaction: <T>(knex: Knex, fnt: () => Promise<T>) => Promise<T>;
|
|
4
5
|
interface KnexConfig {
|
|
5
6
|
/** Pre-configured Knex instance */
|
|
6
7
|
knex: Knex;
|
|
@@ -17,8 +18,8 @@ declare class KnexStorageProvider<Meta extends UserMeta = Record<string, any>, S
|
|
|
17
18
|
private sessionsTableName;
|
|
18
19
|
private schema?;
|
|
19
20
|
constructor(config: KnexConfig);
|
|
20
|
-
private
|
|
21
|
-
private
|
|
21
|
+
private table;
|
|
22
|
+
private sessionsTable;
|
|
22
23
|
createUser(user: CreateUserInput<Meta>): Promise<User<Meta>>;
|
|
23
24
|
getUserById(id: UserId): Promise<User<Meta> | null>;
|
|
24
25
|
getUserByEmail(email: string): Promise<User<Meta> | null>;
|
|
@@ -68,6 +69,8 @@ declare class KnexStorageProvider<Meta extends UserMeta = Record<string, any>, S
|
|
|
68
69
|
* Meta fields in updates are flattened as individual snake_case columns.
|
|
69
70
|
*/
|
|
70
71
|
private toDbSessionUpdates;
|
|
72
|
+
private isTransactionClient;
|
|
73
|
+
protected withClient<T>(fnt: (client: Knex) => Promise<T>, transactional?: boolean): Promise<T>;
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
/**
|
|
@@ -137,4 +140,4 @@ declare function createUsersTableMigration(knex: Knex, tableName: string, schema
|
|
|
137
140
|
*/
|
|
138
141
|
declare function createSessionsTableMigration(knex: Knex, tableName: string, schemaOrCustomColumns?: string | ((table: Knex.CreateTableBuilder) => void), customColumns?: (table: Knex.CreateTableBuilder) => void): Promise<void>;
|
|
139
142
|
|
|
140
|
-
export { type KnexConfig, KnexStorageProvider, createSessionsTableMigration, createUsersTableMigration };
|
|
143
|
+
export { type KnexConfig, KnexStorageProvider, createSessionsTableMigration, createUsersTableMigration, runInTransaction };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -22,11 +32,21 @@ var index_exports = {};
|
|
|
22
32
|
__export(index_exports, {
|
|
23
33
|
KnexStorageProvider: () => KnexStorageProvider,
|
|
24
34
|
createSessionsTableMigration: () => createSessionsTableMigration,
|
|
25
|
-
createUsersTableMigration: () => createUsersTableMigration
|
|
35
|
+
createUsersTableMigration: () => createUsersTableMigration,
|
|
36
|
+
runInTransaction: () => runInTransaction
|
|
26
37
|
});
|
|
27
38
|
module.exports = __toCommonJS(index_exports);
|
|
28
39
|
|
|
29
40
|
// src/knex.ts
|
|
41
|
+
var continuationLocalStorage = __toESM(require("continuation-local-storage"));
|
|
42
|
+
var CLS_KNEX_CLIENT_KEY = "KNEX_CLIENT";
|
|
43
|
+
var knexClientStore = continuationLocalStorage.createNamespace("auth-adapter-knex");
|
|
44
|
+
var runInTransaction = (knex, fnt) => {
|
|
45
|
+
return knexClientStore.runAndReturn(async () => {
|
|
46
|
+
knexClientStore.set(CLS_KNEX_CLIENT_KEY, knex);
|
|
47
|
+
return await fnt();
|
|
48
|
+
});
|
|
49
|
+
};
|
|
30
50
|
function camelToSnake(str) {
|
|
31
51
|
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
32
52
|
}
|
|
@@ -60,20 +80,23 @@ var CORE_SESSION_DB_FIELDS = /* @__PURE__ */ new Set([
|
|
|
60
80
|
]);
|
|
61
81
|
var KnexStorageProvider = class {
|
|
62
82
|
constructor(config) {
|
|
83
|
+
this.isTransactionClient = (client) => {
|
|
84
|
+
return client && "commit" in client && "rollback" in client;
|
|
85
|
+
};
|
|
63
86
|
this.knex = config.knex;
|
|
64
87
|
this.tableName = config.tableName;
|
|
65
88
|
this.sessionsTableName = config.sessionsTableName;
|
|
66
89
|
this.schema = config.schema;
|
|
67
90
|
}
|
|
68
|
-
|
|
69
|
-
const query =
|
|
91
|
+
table(knex) {
|
|
92
|
+
const query = knex(this.tableName);
|
|
70
93
|
if (this.schema) {
|
|
71
94
|
return query.withSchema(this.schema);
|
|
72
95
|
}
|
|
73
96
|
return query;
|
|
74
97
|
}
|
|
75
|
-
|
|
76
|
-
const query =
|
|
98
|
+
sessionsTable(knex) {
|
|
99
|
+
const query = knex(this.sessionsTableName);
|
|
77
100
|
if (this.schema) {
|
|
78
101
|
return query.withSchema(this.schema);
|
|
79
102
|
}
|
|
@@ -81,9 +104,9 @@ var KnexStorageProvider = class {
|
|
|
81
104
|
}
|
|
82
105
|
// ==================== User Methods ====================
|
|
83
106
|
async createUser(user) {
|
|
84
|
-
return
|
|
107
|
+
return this.withClient(async (knex) => {
|
|
85
108
|
const dbUser = this.toDbUser(user);
|
|
86
|
-
const query =
|
|
109
|
+
const query = knex(this.tableName);
|
|
87
110
|
if (this.schema) {
|
|
88
111
|
query.withSchema(this.schema);
|
|
89
112
|
}
|
|
@@ -94,36 +117,46 @@ var KnexStorageProvider = class {
|
|
|
94
117
|
});
|
|
95
118
|
}
|
|
96
119
|
async getUserById(id) {
|
|
97
|
-
|
|
98
|
-
|
|
120
|
+
return this.withClient(async (knex) => {
|
|
121
|
+
const row = await this.table(knex).where({ id }).first();
|
|
122
|
+
return row ? this.fromDbUser(row) : null;
|
|
123
|
+
});
|
|
99
124
|
}
|
|
100
125
|
async getUserByEmail(email) {
|
|
101
|
-
|
|
102
|
-
|
|
126
|
+
return this.withClient(async (knex) => {
|
|
127
|
+
const row = await this.table(knex).where({ email }).first();
|
|
128
|
+
return row ? this.fromDbUser(row) : null;
|
|
129
|
+
});
|
|
103
130
|
}
|
|
104
131
|
async getUserByResetPasswordToken(resetPasswordToken) {
|
|
105
|
-
|
|
106
|
-
|
|
132
|
+
return this.withClient(async (knex) => {
|
|
133
|
+
const row = await this.table(knex).where({ reset_password_token: resetPasswordToken }).first();
|
|
134
|
+
return row ? this.fromDbUser(row) : null;
|
|
135
|
+
});
|
|
107
136
|
}
|
|
108
137
|
async getUserByVerifyEmailToken(verifyEmailToken) {
|
|
109
|
-
|
|
110
|
-
|
|
138
|
+
return this.withClient(async (knex) => {
|
|
139
|
+
const row = await this.table(knex).where({ verification_token: verifyEmailToken }).first();
|
|
140
|
+
return row ? this.fromDbUser(row) : null;
|
|
141
|
+
});
|
|
111
142
|
}
|
|
112
143
|
async updateUser(id, updates) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
144
|
+
return this.withClient(async (knex) => {
|
|
145
|
+
const dbUpdates = this.toDbUpdates(updates);
|
|
146
|
+
if (Object.keys(dbUpdates).length === 0) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
await this.table(knex).where({ id }).update(dbUpdates);
|
|
150
|
+
});
|
|
118
151
|
}
|
|
119
152
|
async deleteUser(id) {
|
|
120
|
-
|
|
121
|
-
const sessionsQuery =
|
|
153
|
+
return this.withClient(async (knex) => {
|
|
154
|
+
const sessionsQuery = knex(this.sessionsTableName);
|
|
122
155
|
if (this.schema) {
|
|
123
156
|
sessionsQuery.withSchema(this.schema);
|
|
124
157
|
}
|
|
125
158
|
await sessionsQuery.where({ user_id: id }).delete();
|
|
126
|
-
const userQuery =
|
|
159
|
+
const userQuery = knex(this.tableName);
|
|
127
160
|
if (this.schema) {
|
|
128
161
|
userQuery.withSchema(this.schema);
|
|
129
162
|
}
|
|
@@ -131,83 +164,101 @@ var KnexStorageProvider = class {
|
|
|
131
164
|
});
|
|
132
165
|
}
|
|
133
166
|
async findUsers(filter, options = {}) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (filter.emailContains) {
|
|
140
|
-
query = query.where("email", "like", `%${filter.emailContains}%`);
|
|
141
|
-
}
|
|
142
|
-
if (filter.isEmailVerified !== void 0) {
|
|
143
|
-
query = query.where("is_email_verified", filter.isEmailVerified);
|
|
144
|
-
}
|
|
145
|
-
if (filter.roles && filter.roles.length > 0) {
|
|
146
|
-
for (const role of filter.roles) {
|
|
147
|
-
query = query.whereRaw(
|
|
148
|
-
this.knex.client.config.client === "sqlite3" ? `roles LIKE ?` : `roles @> ?`,
|
|
149
|
-
this.knex.client.config.client === "sqlite3" ? [`%"${role}"%`] : [JSON.stringify([role])]
|
|
150
|
-
);
|
|
167
|
+
return this.withClient(async (knex) => {
|
|
168
|
+
const limit = options.limit ?? 50;
|
|
169
|
+
let query = this.table(knex).clone();
|
|
170
|
+
if (filter.email) {
|
|
171
|
+
query = query.where("email", filter.email);
|
|
151
172
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
173
|
+
if (filter.emailContains) {
|
|
174
|
+
query = query.where("email", "like", `%${filter.emailContains}%`);
|
|
175
|
+
}
|
|
176
|
+
if (filter.isEmailVerified !== void 0) {
|
|
177
|
+
query = query.where("is_email_verified", filter.isEmailVerified);
|
|
178
|
+
}
|
|
179
|
+
if (filter.roles && filter.roles.length > 0) {
|
|
180
|
+
for (const role of filter.roles) {
|
|
181
|
+
query = query.whereRaw(
|
|
157
182
|
this.knex.client.config.client === "sqlite3" ? `roles LIKE ?` : `roles @> ?`,
|
|
158
183
|
this.knex.client.config.client === "sqlite3" ? [`%"${role}"%`] : [JSON.stringify([role])]
|
|
159
184
|
);
|
|
160
185
|
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
186
|
+
}
|
|
187
|
+
if (filter.hasAnyRole && filter.hasAnyRole.length > 0) {
|
|
188
|
+
query = query.where((builder) => {
|
|
189
|
+
for (const role of filter.hasAnyRole) {
|
|
190
|
+
builder.orWhereRaw(
|
|
191
|
+
this.knex.client.config.client === "sqlite3" ? `roles LIKE ?` : `roles @> ?`,
|
|
192
|
+
this.knex.client.config.client === "sqlite3" ? [`%"${role}"%`] : [JSON.stringify([role])]
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (options.cursor) {
|
|
198
|
+
const cursorData = JSON.parse(Buffer.from(options.cursor, "base64").toString());
|
|
199
|
+
query = query.where("id", ">", cursorData.lastId);
|
|
200
|
+
}
|
|
201
|
+
query = query.orderBy("id", "asc").limit(limit + 1);
|
|
202
|
+
const rows = await query;
|
|
203
|
+
const hasMore = rows.length > limit;
|
|
204
|
+
const resultRows = hasMore ? rows.slice(0, limit) : rows;
|
|
205
|
+
const users = resultRows.map((row) => this.fromDbUser(row));
|
|
206
|
+
let nextCursor;
|
|
207
|
+
if (hasMore && resultRows.length > 0) {
|
|
208
|
+
const lastUser = resultRows[resultRows.length - 1];
|
|
209
|
+
nextCursor = Buffer.from(JSON.stringify({ lastId: lastUser.id })).toString(
|
|
210
|
+
"base64"
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return { users, nextCursor };
|
|
214
|
+
});
|
|
178
215
|
}
|
|
179
216
|
// ==================== Session Methods ====================
|
|
180
217
|
async createSession(session) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
218
|
+
return this.withClient(async (knex) => {
|
|
219
|
+
const dbSession = this.toDbSession(session);
|
|
220
|
+
const result = await this.sessionsTable(knex).insert(dbSession).returning("id");
|
|
221
|
+
const insertedId = result?.[0]?.id ?? result?.[0] ?? session.id;
|
|
222
|
+
const finalId = session.id ?? insertedId;
|
|
223
|
+
return { ...session, id: finalId };
|
|
224
|
+
});
|
|
186
225
|
}
|
|
187
226
|
async getSessionById(id) {
|
|
188
|
-
|
|
189
|
-
|
|
227
|
+
return this.withClient(async (knex) => {
|
|
228
|
+
const row = await this.sessionsTable(knex).where({ id }).first();
|
|
229
|
+
return row ? this.fromDbSession(row) : null;
|
|
230
|
+
});
|
|
190
231
|
}
|
|
191
232
|
async getSessionsByUserId(userId) {
|
|
192
|
-
|
|
193
|
-
|
|
233
|
+
return this.withClient(async (knex) => {
|
|
234
|
+
const rows = await this.sessionsTable(knex).where({ user_id: userId });
|
|
235
|
+
return rows.map((row) => this.fromDbSession(row));
|
|
236
|
+
});
|
|
194
237
|
}
|
|
195
238
|
async updateSession(id, updates) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
239
|
+
return this.withClient(async (knex) => {
|
|
240
|
+
const dbUpdates = this.toDbSessionUpdates(updates);
|
|
241
|
+
if (Object.keys(dbUpdates).length === 0) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
await this.sessionsTable(knex).where({ id }).update(dbUpdates);
|
|
245
|
+
});
|
|
201
246
|
}
|
|
202
247
|
async deleteSession(id) {
|
|
203
|
-
|
|
248
|
+
return this.withClient(async (knex) => {
|
|
249
|
+
await this.sessionsTable(knex).where({ id }).delete();
|
|
250
|
+
});
|
|
204
251
|
}
|
|
205
252
|
async deleteAllUserSessions(userId) {
|
|
206
|
-
|
|
253
|
+
return this.withClient(async (knex) => {
|
|
254
|
+
await this.sessionsTable(knex).where({ user_id: userId }).delete();
|
|
255
|
+
});
|
|
207
256
|
}
|
|
208
257
|
async deleteExpiredSessions() {
|
|
209
|
-
|
|
210
|
-
|
|
258
|
+
return this.withClient(async (knex) => {
|
|
259
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
260
|
+
await this.sessionsTable(knex).where("expires_at", "<", now).delete();
|
|
261
|
+
});
|
|
211
262
|
}
|
|
212
263
|
// ==================== User Mapping Helpers ====================
|
|
213
264
|
/**
|
|
@@ -397,6 +448,19 @@ var KnexStorageProvider = class {
|
|
|
397
448
|
}
|
|
398
449
|
return dbUpdates;
|
|
399
450
|
}
|
|
451
|
+
async withClient(fnt, transactional = false) {
|
|
452
|
+
const existingClient = knexClientStore.get(CLS_KNEX_CLIENT_KEY);
|
|
453
|
+
if (existingClient && (!transactional || this.isTransactionClient(existingClient))) {
|
|
454
|
+
return await fnt(existingClient);
|
|
455
|
+
}
|
|
456
|
+
const client = this.knex;
|
|
457
|
+
if (transactional) {
|
|
458
|
+
return await client.transaction(async (trx) => {
|
|
459
|
+
return await fnt(trx);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
return await fnt(client);
|
|
463
|
+
}
|
|
400
464
|
};
|
|
401
465
|
|
|
402
466
|
// src/migration.ts
|
|
@@ -450,6 +514,7 @@ async function createSessionsTableMigration(knex, tableName, schemaOrCustomColum
|
|
|
450
514
|
0 && (module.exports = {
|
|
451
515
|
KnexStorageProvider,
|
|
452
516
|
createSessionsTableMigration,
|
|
453
|
-
createUsersTableMigration
|
|
517
|
+
createUsersTableMigration,
|
|
518
|
+
runInTransaction
|
|
454
519
|
});
|
|
455
520
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/knex.ts","../src/migration.ts"],"sourcesContent":["export { KnexStorageProvider, type KnexConfig } from './knex';\nexport { createUsersTableMigration, createSessionsTableMigration } from './migration';\n","import type { Knex } from 'knex';\nimport type {\n CreateSessionInput,\n CreateUserInput,\n FindUsersOptions,\n FindUsersResult,\n IStorageProvider,\n Session,\n SessionId,\n User,\n UserFilter,\n UserId,\n UserMeta,\n SessionMeta,\n} from '@xcelsior/auth';\n\n/** Convert camelCase string to snake_case */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/** Convert snake_case string to camelCase */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/** Core user DB columns that are not part of meta */\nconst CORE_USER_DB_FIELDS = new Set([\n 'id',\n 'email',\n 'first_name',\n 'last_name',\n 'password_hash',\n 'roles',\n 'is_email_verified',\n 'verification_token',\n 'reset_password_token',\n 'reset_password_expires',\n 'created_at',\n 'updated_at',\n]);\n\n/** Core session DB columns that are not part of meta */\nconst CORE_SESSION_DB_FIELDS = new Set([\n 'id',\n 'user_id',\n 'refresh_token_hash',\n 'user_agent',\n 'ip_address',\n 'device_name',\n 'created_at',\n 'last_used_at',\n 'expires_at',\n]);\n\nexport interface KnexConfig {\n /** Pre-configured Knex instance */\n knex: Knex;\n /** Table name for users */\n tableName: string;\n /** Table name for sessions */\n sessionsTableName: string;\n /** Optional schema (for PostgreSQL) */\n schema?: string;\n}\n\nexport class KnexStorageProvider<\n Meta extends UserMeta = Record<string, any>,\n SMeta extends SessionMeta = Record<string, any>,\n> implements IStorageProvider<Meta, SMeta>\n{\n private knex: Knex;\n private tableName: string;\n private sessionsTableName: string;\n private schema?: string;\n\n constructor(config: KnexConfig) {\n this.knex = config.knex;\n this.tableName = config.tableName;\n this.sessionsTableName = config.sessionsTableName;\n this.schema = config.schema;\n }\n\n private get table() {\n const query = this.knex(this.tableName);\n if (this.schema) {\n return query.withSchema(this.schema);\n }\n return query;\n }\n\n private get sessionsTable() {\n const query = this.knex(this.sessionsTableName);\n if (this.schema) {\n return query.withSchema(this.schema);\n }\n return query;\n }\n\n // ==================== User Methods ====================\n\n async createUser(user: CreateUserInput<Meta>): Promise<User<Meta>> {\n return await this.knex.transaction(async (trx) => {\n const dbUser = this.toDbUser(user);\n const query = trx(this.tableName);\n if (this.schema) {\n query.withSchema(this.schema);\n }\n const result = await query.insert(dbUser).returning('id');\n const insertedId = result?.[0]?.id ?? result?.[0] ?? user.id;\n const finalId = user.id ?? insertedId;\n return { ...user, id: finalId } as User<Meta>;\n });\n }\n\n async getUserById(id: UserId): Promise<User<Meta> | null> {\n const row = await this.table.where({ id }).first();\n return row ? this.fromDbUser(row) : null;\n }\n\n async getUserByEmail(email: string): Promise<User<Meta> | null> {\n const row = await this.table.where({ email }).first();\n return row ? this.fromDbUser(row) : null;\n }\n\n async getUserByResetPasswordToken(resetPasswordToken: string): Promise<User<Meta> | null> {\n const row = await this.table.where({ reset_password_token: resetPasswordToken }).first();\n return row ? this.fromDbUser(row) : null;\n }\n\n async getUserByVerifyEmailToken(verifyEmailToken: string): Promise<User<Meta> | null> {\n const row = await this.table.where({ verification_token: verifyEmailToken }).first();\n return row ? this.fromDbUser(row) : null;\n }\n\n async updateUser(id: UserId, updates: Partial<User<Meta>>): Promise<void> {\n const dbUpdates = this.toDbUpdates(updates);\n\n if (Object.keys(dbUpdates).length === 0) {\n return;\n }\n\n await this.table.where({ id }).update(dbUpdates);\n }\n\n async deleteUser(id: UserId): Promise<void> {\n await this.knex.transaction(async (trx) => {\n // Delete all user sessions first\n const sessionsQuery = trx(this.sessionsTableName);\n if (this.schema) {\n sessionsQuery.withSchema(this.schema);\n }\n await sessionsQuery.where({ user_id: id }).delete();\n // Then delete the user\n const userQuery = trx(this.tableName);\n if (this.schema) {\n userQuery.withSchema(this.schema);\n }\n await userQuery.where({ id }).delete();\n });\n }\n\n async findUsers(\n filter: UserFilter,\n options: FindUsersOptions = {}\n ): Promise<FindUsersResult<Meta>> {\n const limit = options.limit ?? 50;\n\n let query = this.table.clone();\n\n // Email exact match\n if (filter.email) {\n query = query.where('email', filter.email);\n }\n\n // Email contains (partial match)\n if (filter.emailContains) {\n query = query.where('email', 'like', `%${filter.emailContains}%`);\n }\n\n // Email verification status\n if (filter.isEmailVerified !== undefined) {\n query = query.where('is_email_verified', filter.isEmailVerified);\n }\n\n // Roles filtering - user must have ALL specified roles\n if (filter.roles && filter.roles.length > 0) {\n for (const role of filter.roles) {\n // Use JSON contains - works for PostgreSQL (jsonb) and MySQL (json)\n // For SQLite, we fall back to LIKE on the JSON string\n query = query.whereRaw(\n this.knex.client.config.client === 'sqlite3' ? `roles LIKE ?` : `roles @> ?`,\n this.knex.client.config.client === 'sqlite3'\n ? [`%\"${role}\"%`]\n : [JSON.stringify([role])]\n );\n }\n }\n\n // HasAnyRole - user must have at least ONE of specified roles\n if (filter.hasAnyRole && filter.hasAnyRole.length > 0) {\n query = query.where((builder: Knex.QueryBuilder) => {\n for (const role of filter.hasAnyRole!) {\n builder.orWhereRaw(\n this.knex.client.config.client === 'sqlite3'\n ? `roles LIKE ?`\n : `roles @> ?`,\n this.knex.client.config.client === 'sqlite3'\n ? [`%\"${role}\"%`]\n : [JSON.stringify([role])]\n );\n }\n });\n }\n\n // Apply pagination\n if (options.cursor) {\n const cursorData = JSON.parse(Buffer.from(options.cursor, 'base64').toString());\n query = query.where('id', '>', cursorData.lastId);\n }\n\n // Order by id for consistent pagination\n query = query.orderBy('id', 'asc').limit(limit + 1);\n\n const rows = await query;\n const hasMore = rows.length > limit;\n const resultRows = hasMore ? rows.slice(0, limit) : rows;\n const users = resultRows.map((row: Record<string, unknown>) => this.fromDbUser(row));\n\n let nextCursor: string | undefined;\n if (hasMore && resultRows.length > 0) {\n const lastUser = resultRows[resultRows.length - 1];\n nextCursor = Buffer.from(JSON.stringify({ lastId: lastUser.id })).toString('base64');\n }\n\n return { users, nextCursor } as FindUsersResult<Meta>;\n }\n\n // ==================== Session Methods ====================\n\n async createSession(session: CreateSessionInput<SMeta>): Promise<Session<SMeta>> {\n const dbSession = this.toDbSession(session);\n const result = await this.sessionsTable.insert(dbSession).returning('id');\n // PostgreSQL returns [{ id }] from .returning(), SQLite returns [insertedId]\n const insertedId = result?.[0]?.id ?? result?.[0] ?? session.id;\n const finalId = session.id ?? insertedId;\n return { ...session, id: finalId } as Session<SMeta>;\n }\n\n async getSessionById(id: SessionId): Promise<Session<SMeta> | null> {\n const row = await this.sessionsTable.where({ id }).first();\n return row ? this.fromDbSession(row) : null;\n }\n\n async getSessionsByUserId(userId: UserId): Promise<Session<SMeta>[]> {\n const rows = await this.sessionsTable.where({ user_id: userId });\n return rows.map((row: Record<string, unknown>) => this.fromDbSession(row));\n }\n\n async updateSession(\n id: SessionId,\n updates: Partial<Session<SMeta>> & Record<string, unknown>\n ): Promise<void> {\n const dbUpdates = this.toDbSessionUpdates(updates);\n\n if (Object.keys(dbUpdates).length === 0) {\n return;\n }\n\n await this.sessionsTable.where({ id }).update(dbUpdates);\n }\n\n async deleteSession(id: SessionId): Promise<void> {\n await this.sessionsTable.where({ id }).delete();\n }\n\n async deleteAllUserSessions(userId: UserId): Promise<void> {\n await this.sessionsTable.where({ user_id: userId }).delete();\n }\n\n async deleteExpiredSessions(): Promise<void> {\n const now = new Date().toISOString();\n await this.sessionsTable.where('expires_at', '<', now).delete();\n }\n\n // ==================== User Mapping Helpers ====================\n\n /**\n * Safely convert a DB date value (Date object or string) to ISO 8601 string\n */\n private toDateString(value: unknown): string {\n if (value instanceof Date) return value.toISOString();\n return String(value);\n }\n\n /**\n * Convert User object to database row format (snake_case).\n * Meta fields are flattened as individual snake_case columns.\n */\n private toDbUser(user: CreateUserInput<Meta>): Record<string, unknown> {\n const now = new Date().toISOString();\n const row: Record<string, unknown> = {\n email: user.email,\n first_name: user.firstName ?? null,\n last_name: user.lastName ?? null,\n password_hash: user.passwordHash,\n roles: JSON.stringify(user.roles),\n is_email_verified: user.isEmailVerified ? 1 : 0,\n verification_token: user.verificationToken ?? null,\n reset_password_token: user.resetPasswordToken ?? null,\n reset_password_expires: user.resetPasswordExpires ?? null,\n created_at: user.createdAt ?? now,\n updated_at: user.updatedAt ?? now,\n };\n // Only include id if provided (for string UUIDs)\n // Omit for auto-increment DBs\n if (user.id !== undefined) {\n row.id = user.id;\n }\n // Flatten meta fields as individual snake_case columns\n if (user.meta) {\n for (const [key, value] of Object.entries(user.meta)) {\n row[camelToSnake(key)] = value;\n }\n }\n return row;\n }\n\n /**\n * Convert database row to User object (camelCase).\n * Non-core columns are gathered into the typed `meta` object.\n */\n private fromDbUser(row: Record<string, unknown>): User<Meta> {\n const meta: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(row)) {\n if (!CORE_USER_DB_FIELDS.has(key)) {\n meta[snakeToCamel(key)] = value;\n }\n }\n\n return {\n id: row.id as string | number,\n email: row.email as string,\n firstName: row.first_name as string | undefined,\n lastName: row.last_name as string | undefined,\n passwordHash: row.password_hash as string,\n roles: typeof row.roles === 'string' ? JSON.parse(row.roles) : row.roles,\n isEmailVerified: Boolean(row.is_email_verified),\n verificationToken: row.verification_token as string | undefined,\n resetPasswordToken: row.reset_password_token as string | undefined,\n resetPasswordExpires: row.reset_password_expires\n ? this.toDateString(row.reset_password_expires)\n : undefined,\n createdAt: this.toDateString(row.created_at),\n updatedAt: this.toDateString(row.updated_at),\n ...(Object.keys(meta).length > 0 ? { meta: meta as Meta } : {}),\n } as User<Meta>;\n }\n\n /**\n * Convert partial User updates to database format.\n * Meta fields in updates are flattened as individual snake_case columns.\n */\n private toDbUpdates(updates: Partial<User<Meta>>): Record<string, unknown> {\n const dbUpdates: Record<string, unknown> = {};\n\n if (updates.email !== undefined) {\n dbUpdates.email = updates.email;\n }\n if ('firstName' in updates) {\n dbUpdates.first_name = updates.firstName ?? null;\n }\n if ('lastName' in updates) {\n dbUpdates.last_name = updates.lastName ?? null;\n }\n if (updates.passwordHash !== undefined) {\n dbUpdates.password_hash = updates.passwordHash;\n }\n if (updates.roles !== undefined) {\n dbUpdates.roles = JSON.stringify(updates.roles);\n }\n if (updates.isEmailVerified !== undefined) {\n dbUpdates.is_email_verified = updates.isEmailVerified ? 1 : 0;\n }\n if ('verificationToken' in updates) {\n dbUpdates.verification_token = updates.verificationToken ?? null;\n }\n if ('resetPasswordToken' in updates) {\n dbUpdates.reset_password_token = updates.resetPasswordToken ?? null;\n }\n if ('resetPasswordExpires' in updates) {\n dbUpdates.reset_password_expires = updates.resetPasswordExpires ?? null;\n }\n if (updates.updatedAt !== undefined) {\n dbUpdates.updated_at = updates.updatedAt;\n }\n // Flatten meta fields as individual snake_case columns\n if (updates.meta) {\n for (const [key, value] of Object.entries(updates.meta)) {\n dbUpdates[camelToSnake(key)] = value;\n }\n }\n\n return dbUpdates;\n }\n\n // ==================== Session Mapping Helpers ====================\n\n /**\n * Convert Session object to database row format (snake_case).\n * Meta fields are flattened as individual snake_case columns.\n */\n private toDbSession(session: CreateSessionInput<SMeta>): Record<string, unknown> {\n const row: Record<string, unknown> = {\n user_id: session.userId,\n refresh_token_hash: session.refreshTokenHash,\n user_agent: session.userAgent ?? null,\n ip_address: session.ipAddress ?? null,\n device_name: session.deviceName ?? null,\n created_at: session.createdAt,\n last_used_at: session.lastUsedAt,\n expires_at: session.expiresAt,\n };\n // Only include id if provided (for string UUIDs)\n // Omit for auto-increment DBs\n if (session.id !== undefined) {\n row.id = session.id;\n }\n // Flatten meta fields as individual snake_case columns\n if (session.meta) {\n for (const [key, value] of Object.entries(session.meta)) {\n row[camelToSnake(key)] = value;\n }\n }\n return row;\n }\n\n /**\n * Convert database row to Session object (camelCase).\n * Non-core columns are gathered into the typed `meta` object.\n */\n private fromDbSession(row: Record<string, unknown>): Session<SMeta> {\n const meta: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(row)) {\n if (!CORE_SESSION_DB_FIELDS.has(key)) {\n meta[snakeToCamel(key)] = value;\n }\n }\n\n return {\n id: row.id as string | number,\n userId: row.user_id as string | number,\n refreshTokenHash: row.refresh_token_hash as string,\n userAgent: row.user_agent as string | undefined,\n ipAddress: row.ip_address as string | undefined,\n deviceName: row.device_name as string | undefined,\n createdAt: this.toDateString(row.created_at),\n lastUsedAt: this.toDateString(row.last_used_at),\n expiresAt: this.toDateString(row.expires_at),\n ...(Object.keys(meta).length > 0 ? { meta: meta as SMeta } : {}),\n } as Session<SMeta>;\n }\n\n /**\n * Convert partial Session updates to database format.\n * Meta fields in updates are flattened as individual snake_case columns.\n */\n private toDbSessionUpdates(\n updates: Partial<Session<SMeta>> & Record<string, unknown>\n ): Record<string, unknown> {\n const dbUpdates: Record<string, unknown> = {};\n\n if (updates.refreshTokenHash !== undefined) {\n dbUpdates.refresh_token_hash = updates.refreshTokenHash;\n }\n if ('userAgent' in updates) {\n dbUpdates.user_agent = updates.userAgent ?? null;\n }\n if ('ipAddress' in updates) {\n dbUpdates.ip_address = updates.ipAddress ?? null;\n }\n if ('deviceName' in updates) {\n dbUpdates.device_name = updates.deviceName ?? null;\n }\n if (updates.lastUsedAt !== undefined) {\n dbUpdates.last_used_at = updates.lastUsedAt;\n }\n if (updates.expiresAt !== undefined) {\n dbUpdates.expires_at = updates.expiresAt;\n }\n // Flatten meta fields as individual snake_case columns\n if (updates.meta) {\n for (const [key, value] of Object.entries(updates.meta)) {\n dbUpdates[camelToSnake(key)] = value;\n }\n }\n\n return dbUpdates;\n }\n}\n","import type { Knex } from 'knex';\n\n/**\n * SQL migration helper to create the users table\n * Can be used with Knex migrations\n *\n * @param knex - Knex instance\n * @param tableName - Table name for users\n * @param schemaOrCustomColumns - Optional schema name (for PostgreSQL) or callback to add custom columns\n * @param customColumns - Optional callback to add custom columns to the table (when schema is provided)\n *\n * @example\n * ```ts\n * // In your migration file\n * import { createUsersTableMigration, createSessionsTableMigration } from '@xcelsior/auth-adapter-knex';\n *\n * export async function up(knex: Knex): Promise<void> {\n * await createUsersTableMigration(knex, 'users');\n * await createSessionsTableMigration(knex, 'sessions');\n * }\n *\n * // With custom meta columns\n * export async function up(knex: Knex): Promise<void> {\n * await createUsersTableMigration(knex, 'users', (table) => {\n * table.string('phone').nullable();\n * table.string('company').nullable();\n * });\n * await createSessionsTableMigration(knex, 'sessions');\n * }\n *\n * // With schema and custom meta columns\n * export async function up(knex: Knex): Promise<void> {\n * await createUsersTableMigration(knex, 'users', 'my_schema', (table) => {\n * table.string('phone').nullable();\n * table.string('company').nullable();\n * });\n * }\n *\n * export async function down(knex: Knex): Promise<void> {\n * await knex.schema.dropTableIfExists('sessions');\n * await knex.schema.dropTableIfExists('users');\n * }\n * ```\n */\nexport async function createUsersTableMigration(\n knex: Knex,\n tableName: string,\n schemaOrCustomColumns?: string | ((table: Knex.CreateTableBuilder) => void),\n customColumns?: (table: Knex.CreateTableBuilder) => void\n): Promise<void> {\n const schema = typeof schemaOrCustomColumns === 'string' ? schemaOrCustomColumns : undefined;\n const addCustomColumns =\n typeof schemaOrCustomColumns === 'function' ? schemaOrCustomColumns : customColumns;\n const schemaBuilder = schema ? knex.schema.withSchema(schema) : knex.schema;\n\n await schemaBuilder.createTable(tableName, (table) => {\n table.string('id').primary();\n table.string('email').notNullable().unique();\n table.string('first_name').nullable();\n table.string('last_name').nullable();\n table.string('password_hash').notNullable();\n table.jsonb('roles').notNullable();\n table.boolean('is_email_verified').notNullable().defaultTo(false);\n table.string('verification_token').nullable();\n table.string('reset_password_token').nullable();\n table.timestamp('reset_password_expires', { useTz: true }).nullable();\n table.timestamp('created_at', { useTz: true }).notNullable();\n table.timestamp('updated_at', { useTz: true }).notNullable();\n\n // Indexes for common lookups\n table.index('email');\n table.index('verification_token');\n table.index('reset_password_token');\n\n // Add custom meta columns if provided\n if (addCustomColumns) {\n addCustomColumns(table);\n }\n });\n}\n\n/**\n * SQL migration helper to create the sessions table\n * Can be used with Knex migrations\n *\n * @param knex - Knex instance\n * @param tableName - Table name for sessions\n * @param schemaOrCustomColumns - Optional schema name (for PostgreSQL) or callback to add custom columns\n * @param customColumns - Optional callback to add custom columns to the table (when schema is provided)\n *\n * @example\n * ```ts\n * // With custom meta columns\n * await createSessionsTableMigration(knex, 'sessions', (table) => {\n * table.string('organization_id').nullable();\n * });\n *\n * // With schema and custom meta columns\n * await createSessionsTableMigration(knex, 'sessions', 'my_schema', (table) => {\n * table.string('organization_id').nullable();\n * });\n * ```\n */\nexport async function createSessionsTableMigration(\n knex: Knex,\n tableName: string,\n schemaOrCustomColumns?: string | ((table: Knex.CreateTableBuilder) => void),\n customColumns?: (table: Knex.CreateTableBuilder) => void\n): Promise<void> {\n const schema = typeof schemaOrCustomColumns === 'string' ? schemaOrCustomColumns : undefined;\n const addCustomColumns =\n typeof schemaOrCustomColumns === 'function' ? schemaOrCustomColumns : customColumns;\n const schemaBuilder = schema ? knex.schema.withSchema(schema) : knex.schema;\n\n await schemaBuilder.createTable(tableName, (table) => {\n table.string('id').primary();\n table.string('user_id').notNullable();\n table.string('refresh_token_hash').notNullable();\n table.string('user_agent').nullable();\n table.string('ip_address').nullable();\n table.string('device_name').nullable();\n table.timestamp('created_at', { useTz: true }).notNullable();\n table.timestamp('last_used_at', { useTz: true }).notNullable();\n table.timestamp('expires_at', { useTz: true }).notNullable();\n\n // Index for querying sessions by user\n table.index('user_id');\n // Index for cleaning up expired sessions\n table.index('expires_at');\n\n // Add custom meta columns if provided\n if (addCustomColumns) {\n addCustomColumns(table);\n }\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/knex.ts","../src/migration.ts"],"sourcesContent":["export { KnexStorageProvider, type KnexConfig, runInTransaction } from './knex';\nexport { createUsersTableMigration, createSessionsTableMigration } from './migration';\n","import type { Knex } from 'knex';\nimport * as continuationLocalStorage from 'continuation-local-storage';\nimport type {\n CreateSessionInput,\n CreateUserInput,\n FindUsersOptions,\n FindUsersResult,\n IStorageProvider,\n Session,\n SessionId,\n SessionMeta,\n User,\n UserFilter,\n UserId,\n UserMeta,\n} from '@xcelsior/auth';\n\nconst CLS_KNEX_CLIENT_KEY = 'KNEX_CLIENT';\n\nexport const knexClientStore = continuationLocalStorage.createNamespace('auth-adapter-knex');\n\nexport const runInTransaction = <T>(knex: Knex, fnt: () => Promise<T>) => {\n return knexClientStore.runAndReturn(async () => {\n knexClientStore.set(CLS_KNEX_CLIENT_KEY, knex);\n return await fnt();\n });\n};\n\n/** Convert camelCase string to snake_case */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/** Convert snake_case string to camelCase */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/** Core user DB columns that are not part of meta */\nconst CORE_USER_DB_FIELDS = new Set([\n 'id',\n 'email',\n 'first_name',\n 'last_name',\n 'password_hash',\n 'roles',\n 'is_email_verified',\n 'verification_token',\n 'reset_password_token',\n 'reset_password_expires',\n 'created_at',\n 'updated_at',\n]);\n\n/** Core session DB columns that are not part of meta */\nconst CORE_SESSION_DB_FIELDS = new Set([\n 'id',\n 'user_id',\n 'refresh_token_hash',\n 'user_agent',\n 'ip_address',\n 'device_name',\n 'created_at',\n 'last_used_at',\n 'expires_at',\n]);\n\nexport interface KnexConfig {\n /** Pre-configured Knex instance */\n knex: Knex;\n /** Table name for users */\n tableName: string;\n /** Table name for sessions */\n sessionsTableName: string;\n /** Optional schema (for PostgreSQL) */\n schema?: string;\n}\n\nexport class KnexStorageProvider<\n Meta extends UserMeta = Record<string, any>,\n SMeta extends SessionMeta = Record<string, any>,\n> implements IStorageProvider<Meta, SMeta>\n{\n private knex: Knex;\n private tableName: string;\n private sessionsTableName: string;\n private schema?: string;\n\n constructor(config: KnexConfig) {\n this.knex = config.knex;\n this.tableName = config.tableName;\n this.sessionsTableName = config.sessionsTableName;\n this.schema = config.schema;\n }\n\n private table(knex: Knex) {\n const query = knex(this.tableName);\n if (this.schema) {\n return query.withSchema(this.schema);\n }\n return query;\n }\n\n private sessionsTable(knex: Knex) {\n const query = knex(this.sessionsTableName);\n if (this.schema) {\n return query.withSchema(this.schema);\n }\n return query;\n }\n\n // ==================== User Methods ====================\n\n async createUser(user: CreateUserInput<Meta>): Promise<User<Meta>> {\n return this.withClient(async (knex) => {\n const dbUser = this.toDbUser(user);\n const query = knex(this.tableName);\n if (this.schema) {\n query.withSchema(this.schema);\n }\n const result = await query.insert(dbUser).returning('id');\n const insertedId = result?.[0]?.id ?? result?.[0] ?? user.id;\n const finalId = user.id ?? insertedId;\n return { ...user, id: finalId } as User<Meta>;\n });\n }\n\n async getUserById(id: UserId): Promise<User<Meta> | null> {\n return this.withClient(async (knex) => {\n const row = await this.table(knex).where({ id }).first();\n return row ? this.fromDbUser(row) : null;\n });\n }\n\n async getUserByEmail(email: string): Promise<User<Meta> | null> {\n return this.withClient(async (knex) => {\n const row = await this.table(knex).where({ email }).first();\n return row ? this.fromDbUser(row) : null;\n });\n }\n\n async getUserByResetPasswordToken(resetPasswordToken: string): Promise<User<Meta> | null> {\n return this.withClient(async (knex) => {\n const row = await this.table(knex)\n .where({ reset_password_token: resetPasswordToken })\n .first();\n return row ? this.fromDbUser(row) : null;\n });\n }\n\n async getUserByVerifyEmailToken(verifyEmailToken: string): Promise<User<Meta> | null> {\n return this.withClient(async (knex) => {\n const row = await this.table(knex)\n .where({ verification_token: verifyEmailToken })\n .first();\n return row ? this.fromDbUser(row) : null;\n });\n }\n\n async updateUser(id: UserId, updates: Partial<User<Meta>>): Promise<void> {\n return this.withClient(async (knex) => {\n const dbUpdates = this.toDbUpdates(updates);\n\n if (Object.keys(dbUpdates).length === 0) {\n return;\n }\n\n await this.table(knex).where({ id }).update(dbUpdates);\n });\n }\n\n async deleteUser(id: UserId): Promise<void> {\n return this.withClient(async (knex) => {\n // Delete all user sessions first\n const sessionsQuery = knex(this.sessionsTableName);\n if (this.schema) {\n sessionsQuery.withSchema(this.schema);\n }\n await sessionsQuery.where({ user_id: id }).delete();\n // Then delete the user\n const userQuery = knex(this.tableName);\n if (this.schema) {\n userQuery.withSchema(this.schema);\n }\n await userQuery.where({ id }).delete();\n });\n }\n\n async findUsers(\n filter: UserFilter,\n options: FindUsersOptions = {}\n ): Promise<FindUsersResult<Meta>> {\n return this.withClient(async (knex) => {\n const limit = options.limit ?? 50;\n\n let query = this.table(knex).clone();\n\n // Email exact match\n if (filter.email) {\n query = query.where('email', filter.email);\n }\n\n // Email contains (partial match)\n if (filter.emailContains) {\n query = query.where('email', 'like', `%${filter.emailContains}%`);\n }\n\n // Email verification status\n if (filter.isEmailVerified !== undefined) {\n query = query.where('is_email_verified', filter.isEmailVerified);\n }\n\n // Roles filtering - user must have ALL specified roles\n if (filter.roles && filter.roles.length > 0) {\n for (const role of filter.roles) {\n // Use JSON contains - works for PostgreSQL (jsonb) and MySQL (json)\n // For SQLite, we fall back to LIKE on the JSON string\n query = query.whereRaw(\n this.knex.client.config.client === 'sqlite3'\n ? `roles LIKE ?`\n : `roles @> ?`,\n this.knex.client.config.client === 'sqlite3'\n ? [`%\"${role}\"%`]\n : [JSON.stringify([role])]\n );\n }\n }\n\n // HasAnyRole - user must have at least ONE of specified roles\n if (filter.hasAnyRole && filter.hasAnyRole.length > 0) {\n query = query.where((builder: Knex.QueryBuilder) => {\n for (const role of filter.hasAnyRole!) {\n builder.orWhereRaw(\n this.knex.client.config.client === 'sqlite3'\n ? `roles LIKE ?`\n : `roles @> ?`,\n this.knex.client.config.client === 'sqlite3'\n ? [`%\"${role}\"%`]\n : [JSON.stringify([role])]\n );\n }\n });\n }\n\n // Apply pagination\n if (options.cursor) {\n const cursorData = JSON.parse(Buffer.from(options.cursor, 'base64').toString());\n query = query.where('id', '>', cursorData.lastId);\n }\n\n // Order by id for consistent pagination\n query = query.orderBy('id', 'asc').limit(limit + 1);\n\n const rows = await query;\n const hasMore = rows.length > limit;\n const resultRows = hasMore ? rows.slice(0, limit) : rows;\n const users = resultRows.map((row: Record<string, unknown>) => this.fromDbUser(row));\n\n let nextCursor: string | undefined;\n if (hasMore && resultRows.length > 0) {\n const lastUser = resultRows[resultRows.length - 1];\n nextCursor = Buffer.from(JSON.stringify({ lastId: lastUser.id })).toString(\n 'base64'\n );\n }\n\n return { users, nextCursor } as FindUsersResult<Meta>;\n });\n }\n\n // ==================== Session Methods ====================\n\n async createSession(session: CreateSessionInput<SMeta>): Promise<Session<SMeta>> {\n return this.withClient(async (knex) => {\n const dbSession = this.toDbSession(session);\n const result = await this.sessionsTable(knex).insert(dbSession).returning('id');\n // PostgreSQL returns [{ id }] from .returning(), SQLite returns [insertedId]\n const insertedId = result?.[0]?.id ?? result?.[0] ?? session.id;\n const finalId = session.id ?? insertedId;\n return { ...session, id: finalId } as Session<SMeta>;\n });\n }\n\n async getSessionById(id: SessionId): Promise<Session<SMeta> | null> {\n return this.withClient(async (knex) => {\n const row = await this.sessionsTable(knex).where({ id }).first();\n return row ? this.fromDbSession(row) : null;\n });\n }\n\n async getSessionsByUserId(userId: UserId): Promise<Session<SMeta>[]> {\n return this.withClient(async (knex) => {\n const rows = await this.sessionsTable(knex).where({ user_id: userId });\n return rows.map((row: Record<string, unknown>) => this.fromDbSession(row));\n });\n }\n\n async updateSession(\n id: SessionId,\n updates: Partial<Session<SMeta>> & Record<string, unknown>\n ): Promise<void> {\n return this.withClient(async (knex) => {\n const dbUpdates = this.toDbSessionUpdates(updates);\n\n if (Object.keys(dbUpdates).length === 0) {\n return;\n }\n\n await this.sessionsTable(knex).where({ id }).update(dbUpdates);\n });\n }\n\n async deleteSession(id: SessionId): Promise<void> {\n return this.withClient(async (knex) => {\n await this.sessionsTable(knex).where({ id }).delete();\n });\n }\n\n async deleteAllUserSessions(userId: UserId): Promise<void> {\n return this.withClient(async (knex) => {\n await this.sessionsTable(knex).where({ user_id: userId }).delete();\n });\n }\n\n async deleteExpiredSessions(): Promise<void> {\n return this.withClient(async (knex) => {\n const now = new Date().toISOString();\n await this.sessionsTable(knex).where('expires_at', '<', now).delete();\n });\n }\n\n // ==================== User Mapping Helpers ====================\n\n /**\n * Safely convert a DB date value (Date object or string) to ISO 8601 string\n */\n private toDateString(value: unknown): string {\n if (value instanceof Date) return value.toISOString();\n return String(value);\n }\n\n /**\n * Convert User object to database row format (snake_case).\n * Meta fields are flattened as individual snake_case columns.\n */\n private toDbUser(user: CreateUserInput<Meta>): Record<string, unknown> {\n const now = new Date().toISOString();\n const row: Record<string, unknown> = {\n email: user.email,\n first_name: user.firstName ?? null,\n last_name: user.lastName ?? null,\n password_hash: user.passwordHash,\n roles: JSON.stringify(user.roles),\n is_email_verified: user.isEmailVerified ? 1 : 0,\n verification_token: user.verificationToken ?? null,\n reset_password_token: user.resetPasswordToken ?? null,\n reset_password_expires: user.resetPasswordExpires ?? null,\n created_at: user.createdAt ?? now,\n updated_at: user.updatedAt ?? now,\n };\n // Only include id if provided (for string UUIDs)\n // Omit for auto-increment DBs\n if (user.id !== undefined) {\n row.id = user.id;\n }\n // Flatten meta fields as individual snake_case columns\n if (user.meta) {\n for (const [key, value] of Object.entries(user.meta)) {\n row[camelToSnake(key)] = value;\n }\n }\n return row;\n }\n\n /**\n * Convert database row to User object (camelCase).\n * Non-core columns are gathered into the typed `meta` object.\n */\n private fromDbUser(row: Record<string, unknown>): User<Meta> {\n const meta: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(row)) {\n if (!CORE_USER_DB_FIELDS.has(key)) {\n meta[snakeToCamel(key)] = value;\n }\n }\n\n return {\n id: row.id as string | number,\n email: row.email as string,\n firstName: row.first_name as string | undefined,\n lastName: row.last_name as string | undefined,\n passwordHash: row.password_hash as string,\n roles: typeof row.roles === 'string' ? JSON.parse(row.roles) : row.roles,\n isEmailVerified: Boolean(row.is_email_verified),\n verificationToken: row.verification_token as string | undefined,\n resetPasswordToken: row.reset_password_token as string | undefined,\n resetPasswordExpires: row.reset_password_expires\n ? this.toDateString(row.reset_password_expires)\n : undefined,\n createdAt: this.toDateString(row.created_at),\n updatedAt: this.toDateString(row.updated_at),\n ...(Object.keys(meta).length > 0 ? { meta: meta as Meta } : {}),\n } as User<Meta>;\n }\n\n /**\n * Convert partial User updates to database format.\n * Meta fields in updates are flattened as individual snake_case columns.\n */\n private toDbUpdates(updates: Partial<User<Meta>>): Record<string, unknown> {\n const dbUpdates: Record<string, unknown> = {};\n\n if (updates.email !== undefined) {\n dbUpdates.email = updates.email;\n }\n if ('firstName' in updates) {\n dbUpdates.first_name = updates.firstName ?? null;\n }\n if ('lastName' in updates) {\n dbUpdates.last_name = updates.lastName ?? null;\n }\n if (updates.passwordHash !== undefined) {\n dbUpdates.password_hash = updates.passwordHash;\n }\n if (updates.roles !== undefined) {\n dbUpdates.roles = JSON.stringify(updates.roles);\n }\n if (updates.isEmailVerified !== undefined) {\n dbUpdates.is_email_verified = updates.isEmailVerified ? 1 : 0;\n }\n if ('verificationToken' in updates) {\n dbUpdates.verification_token = updates.verificationToken ?? null;\n }\n if ('resetPasswordToken' in updates) {\n dbUpdates.reset_password_token = updates.resetPasswordToken ?? null;\n }\n if ('resetPasswordExpires' in updates) {\n dbUpdates.reset_password_expires = updates.resetPasswordExpires ?? null;\n }\n if (updates.updatedAt !== undefined) {\n dbUpdates.updated_at = updates.updatedAt;\n }\n // Flatten meta fields as individual snake_case columns\n if (updates.meta) {\n for (const [key, value] of Object.entries(updates.meta)) {\n dbUpdates[camelToSnake(key)] = value;\n }\n }\n\n return dbUpdates;\n }\n\n // ==================== Session Mapping Helpers ====================\n\n /**\n * Convert Session object to database row format (snake_case).\n * Meta fields are flattened as individual snake_case columns.\n */\n private toDbSession(session: CreateSessionInput<SMeta>): Record<string, unknown> {\n const row: Record<string, unknown> = {\n user_id: session.userId,\n refresh_token_hash: session.refreshTokenHash,\n user_agent: session.userAgent ?? null,\n ip_address: session.ipAddress ?? null,\n device_name: session.deviceName ?? null,\n created_at: session.createdAt,\n last_used_at: session.lastUsedAt,\n expires_at: session.expiresAt,\n };\n // Only include id if provided (for string UUIDs)\n // Omit for auto-increment DBs\n if (session.id !== undefined) {\n row.id = session.id;\n }\n // Flatten meta fields as individual snake_case columns\n if (session.meta) {\n for (const [key, value] of Object.entries(session.meta)) {\n row[camelToSnake(key)] = value;\n }\n }\n return row;\n }\n\n /**\n * Convert database row to Session object (camelCase).\n * Non-core columns are gathered into the typed `meta` object.\n */\n private fromDbSession(row: Record<string, unknown>): Session<SMeta> {\n const meta: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(row)) {\n if (!CORE_SESSION_DB_FIELDS.has(key)) {\n meta[snakeToCamel(key)] = value;\n }\n }\n\n return {\n id: row.id as string | number,\n userId: row.user_id as string | number,\n refreshTokenHash: row.refresh_token_hash as string,\n userAgent: row.user_agent as string | undefined,\n ipAddress: row.ip_address as string | undefined,\n deviceName: row.device_name as string | undefined,\n createdAt: this.toDateString(row.created_at),\n lastUsedAt: this.toDateString(row.last_used_at),\n expiresAt: this.toDateString(row.expires_at),\n ...(Object.keys(meta).length > 0 ? { meta: meta as SMeta } : {}),\n } as Session<SMeta>;\n }\n\n /**\n * Convert partial Session updates to database format.\n * Meta fields in updates are flattened as individual snake_case columns.\n */\n private toDbSessionUpdates(\n updates: Partial<Session<SMeta>> & Record<string, unknown>\n ): Record<string, unknown> {\n const dbUpdates: Record<string, unknown> = {};\n\n if (updates.refreshTokenHash !== undefined) {\n dbUpdates.refresh_token_hash = updates.refreshTokenHash;\n }\n if ('userAgent' in updates) {\n dbUpdates.user_agent = updates.userAgent ?? null;\n }\n if ('ipAddress' in updates) {\n dbUpdates.ip_address = updates.ipAddress ?? null;\n }\n if ('deviceName' in updates) {\n dbUpdates.device_name = updates.deviceName ?? null;\n }\n if (updates.lastUsedAt !== undefined) {\n dbUpdates.last_used_at = updates.lastUsedAt;\n }\n if (updates.expiresAt !== undefined) {\n dbUpdates.expires_at = updates.expiresAt;\n }\n // Flatten meta fields as individual snake_case columns\n if (updates.meta) {\n for (const [key, value] of Object.entries(updates.meta)) {\n dbUpdates[camelToSnake(key)] = value;\n }\n }\n\n return dbUpdates;\n }\n\n private isTransactionClient = (client: Knex) => {\n return client && 'commit' in client && 'rollback' in client;\n };\n\n protected async withClient<T>(\n fnt: (client: Knex) => Promise<T>,\n transactional: boolean = false\n ): Promise<T> {\n const existingClient: any = knexClientStore.get(CLS_KNEX_CLIENT_KEY);\n\n if (existingClient && (!transactional || this.isTransactionClient(existingClient))) {\n return await fnt(existingClient);\n }\n\n const client = this.knex;\n\n if (transactional) {\n return await client.transaction(async (trx) => {\n return await fnt(trx);\n });\n }\n\n return await fnt(client);\n }\n}\n","import type { Knex } from 'knex';\n\n/**\n * SQL migration helper to create the users table\n * Can be used with Knex migrations\n *\n * @param knex - Knex instance\n * @param tableName - Table name for users\n * @param schemaOrCustomColumns - Optional schema name (for PostgreSQL) or callback to add custom columns\n * @param customColumns - Optional callback to add custom columns to the table (when schema is provided)\n *\n * @example\n * ```ts\n * // In your migration file\n * import { createUsersTableMigration, createSessionsTableMigration } from '@xcelsior/auth-adapter-knex';\n *\n * export async function up(knex: Knex): Promise<void> {\n * await createUsersTableMigration(knex, 'users');\n * await createSessionsTableMigration(knex, 'sessions');\n * }\n *\n * // With custom meta columns\n * export async function up(knex: Knex): Promise<void> {\n * await createUsersTableMigration(knex, 'users', (table) => {\n * table.string('phone').nullable();\n * table.string('company').nullable();\n * });\n * await createSessionsTableMigration(knex, 'sessions');\n * }\n *\n * // With schema and custom meta columns\n * export async function up(knex: Knex): Promise<void> {\n * await createUsersTableMigration(knex, 'users', 'my_schema', (table) => {\n * table.string('phone').nullable();\n * table.string('company').nullable();\n * });\n * }\n *\n * export async function down(knex: Knex): Promise<void> {\n * await knex.schema.dropTableIfExists('sessions');\n * await knex.schema.dropTableIfExists('users');\n * }\n * ```\n */\nexport async function createUsersTableMigration(\n knex: Knex,\n tableName: string,\n schemaOrCustomColumns?: string | ((table: Knex.CreateTableBuilder) => void),\n customColumns?: (table: Knex.CreateTableBuilder) => void\n): Promise<void> {\n const schema = typeof schemaOrCustomColumns === 'string' ? schemaOrCustomColumns : undefined;\n const addCustomColumns =\n typeof schemaOrCustomColumns === 'function' ? schemaOrCustomColumns : customColumns;\n const schemaBuilder = schema ? knex.schema.withSchema(schema) : knex.schema;\n\n await schemaBuilder.createTable(tableName, (table) => {\n table.string('id').primary();\n table.string('email').notNullable().unique();\n table.string('first_name').nullable();\n table.string('last_name').nullable();\n table.string('password_hash').notNullable();\n table.jsonb('roles').notNullable();\n table.boolean('is_email_verified').notNullable().defaultTo(false);\n table.string('verification_token').nullable();\n table.string('reset_password_token').nullable();\n table.timestamp('reset_password_expires', { useTz: true }).nullable();\n table.timestamp('created_at', { useTz: true }).notNullable();\n table.timestamp('updated_at', { useTz: true }).notNullable();\n\n // Indexes for common lookups\n table.index('email');\n table.index('verification_token');\n table.index('reset_password_token');\n\n // Add custom meta columns if provided\n if (addCustomColumns) {\n addCustomColumns(table);\n }\n });\n}\n\n/**\n * SQL migration helper to create the sessions table\n * Can be used with Knex migrations\n *\n * @param knex - Knex instance\n * @param tableName - Table name for sessions\n * @param schemaOrCustomColumns - Optional schema name (for PostgreSQL) or callback to add custom columns\n * @param customColumns - Optional callback to add custom columns to the table (when schema is provided)\n *\n * @example\n * ```ts\n * // With custom meta columns\n * await createSessionsTableMigration(knex, 'sessions', (table) => {\n * table.string('organization_id').nullable();\n * });\n *\n * // With schema and custom meta columns\n * await createSessionsTableMigration(knex, 'sessions', 'my_schema', (table) => {\n * table.string('organization_id').nullable();\n * });\n * ```\n */\nexport async function createSessionsTableMigration(\n knex: Knex,\n tableName: string,\n schemaOrCustomColumns?: string | ((table: Knex.CreateTableBuilder) => void),\n customColumns?: (table: Knex.CreateTableBuilder) => void\n): Promise<void> {\n const schema = typeof schemaOrCustomColumns === 'string' ? schemaOrCustomColumns : undefined;\n const addCustomColumns =\n typeof schemaOrCustomColumns === 'function' ? schemaOrCustomColumns : customColumns;\n const schemaBuilder = schema ? knex.schema.withSchema(schema) : knex.schema;\n\n await schemaBuilder.createTable(tableName, (table) => {\n table.string('id').primary();\n table.string('user_id').notNullable();\n table.string('refresh_token_hash').notNullable();\n table.string('user_agent').nullable();\n table.string('ip_address').nullable();\n table.string('device_name').nullable();\n table.timestamp('created_at', { useTz: true }).notNullable();\n table.timestamp('last_used_at', { useTz: true }).notNullable();\n table.timestamp('expires_at', { useTz: true }).notNullable();\n\n // Index for querying sessions by user\n table.index('user_id');\n // Index for cleaning up expired sessions\n table.index('expires_at');\n\n // Add custom meta columns if provided\n if (addCustomColumns) {\n addCustomColumns(table);\n }\n });\n}\n"],"mappings":"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","names":[]}
|