@topgunbuild/adapter-better-auth 0.9.0 → 0.10.1
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 +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +39 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +39 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -8,6 +8,14 @@ interface TopGunAdapterOptions {
|
|
|
8
8
|
* Default: "auth_user", "auth_session", etc.
|
|
9
9
|
*/
|
|
10
10
|
modelMap?: Record<string, string>;
|
|
11
|
+
/** Wait for client storage to be ready before accepting requests (default: true) */
|
|
12
|
+
waitForReady?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Map model names to their foreign key field for join operations.
|
|
15
|
+
* Default: "userId" for all models.
|
|
16
|
+
* Example: { account: "ownerId", session: "userId" }
|
|
17
|
+
*/
|
|
18
|
+
foreignKeyMap?: Record<string, string>;
|
|
11
19
|
}
|
|
12
20
|
declare const topGunAdapter: (adapterOptions: TopGunAdapterOptions) => DBAdapterInstance;
|
|
13
21
|
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,14 @@ interface TopGunAdapterOptions {
|
|
|
8
8
|
* Default: "auth_user", "auth_session", etc.
|
|
9
9
|
*/
|
|
10
10
|
modelMap?: Record<string, string>;
|
|
11
|
+
/** Wait for client storage to be ready before accepting requests (default: true) */
|
|
12
|
+
waitForReady?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Map model names to their foreign key field for join operations.
|
|
15
|
+
* Default: "userId" for all models.
|
|
16
|
+
* Example: { account: "ownerId", session: "userId" }
|
|
17
|
+
*/
|
|
18
|
+
foreignKeyMap?: Record<string, string>;
|
|
11
19
|
}
|
|
12
20
|
declare const topGunAdapter: (adapterOptions: TopGunAdapterOptions) => DBAdapterInstance;
|
|
13
21
|
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,18 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
32
32
|
const getMapName = (model) => {
|
|
33
33
|
return modelMap[model] || `auth_${model}`;
|
|
34
34
|
};
|
|
35
|
+
const shouldWaitForReady = adapterOptions.waitForReady ?? true;
|
|
36
|
+
let isReady = false;
|
|
37
|
+
let readyPromise = null;
|
|
38
|
+
const ensureReady = async () => {
|
|
39
|
+
if (!shouldWaitForReady || isReady) return;
|
|
40
|
+
if (!readyPromise) {
|
|
41
|
+
readyPromise = client.start().then(() => {
|
|
42
|
+
isReady = true;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
await readyPromise;
|
|
46
|
+
};
|
|
35
47
|
const whereToPredicate = (where) => {
|
|
36
48
|
if (!where || where.length === 0) return void 0;
|
|
37
49
|
const predicates = where.map((w) => {
|
|
@@ -76,43 +88,44 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
76
88
|
const runQuery = async (model, where, sort, limit, offset) => {
|
|
77
89
|
const mapName = getMapName(model);
|
|
78
90
|
const predicate = where ? whereToPredicate(where) : void 0;
|
|
91
|
+
const effectiveLimit = limit && offset ? limit + offset : limit;
|
|
79
92
|
const filter = {
|
|
80
93
|
predicate,
|
|
81
94
|
sort,
|
|
82
|
-
limit
|
|
83
|
-
offset
|
|
95
|
+
limit: effectiveLimit
|
|
96
|
+
// TopGun uses cursor-based pagination; offset is handled client-side for BetterAuth compatibility
|
|
84
97
|
};
|
|
85
98
|
return new Promise((resolve) => {
|
|
86
99
|
const handle = client.query(mapName, filter);
|
|
87
100
|
const unsubscribe = handle.subscribe((results) => {
|
|
88
101
|
unsubscribe();
|
|
89
|
-
|
|
102
|
+
const sliced = offset ? results.slice(offset, offset + (limit || results.length)) : results;
|
|
103
|
+
resolve(sliced);
|
|
90
104
|
});
|
|
91
105
|
});
|
|
92
106
|
};
|
|
93
107
|
return {
|
|
94
108
|
id: "topgun-adapter",
|
|
95
109
|
async create({ model, data }) {
|
|
110
|
+
await ensureReady();
|
|
96
111
|
const mapName = getMapName(model);
|
|
97
|
-
const
|
|
112
|
+
const dataWithId = data;
|
|
113
|
+
const id = dataWithId.id || crypto.randomUUID();
|
|
98
114
|
const record = { ...data, id };
|
|
99
115
|
const map = client.getMap(mapName);
|
|
100
116
|
map.set(id, record);
|
|
101
117
|
return record;
|
|
102
118
|
},
|
|
103
119
|
async findOne({ model, where, select, join }) {
|
|
104
|
-
|
|
105
|
-
if (idCheck && where.length === 1) {
|
|
106
|
-
const mapName = getMapName(model);
|
|
107
|
-
const map = client.getMap(mapName);
|
|
108
|
-
}
|
|
120
|
+
await ensureReady();
|
|
109
121
|
const results = await runQuery(model, where, void 0, 1);
|
|
110
122
|
if (results.length > 0) {
|
|
111
123
|
const result = results[0];
|
|
112
124
|
if (join) {
|
|
113
125
|
for (const [joinModel, joinConfig] of Object.entries(join)) {
|
|
114
126
|
if (joinConfig === false) continue;
|
|
115
|
-
const
|
|
127
|
+
const foreignKey = adapterOptions.foreignKeyMap?.[joinModel] ?? "userId";
|
|
128
|
+
const joinWhere = [{ field: foreignKey, value: result.id }];
|
|
116
129
|
const limit = typeof joinConfig === "object" ? joinConfig.limit : void 0;
|
|
117
130
|
const joinResults = await runQuery(joinModel, joinWhere, void 0, limit);
|
|
118
131
|
const pluralName = joinModel.endsWith("s") ? joinModel : joinModel + "s";
|
|
@@ -122,11 +135,16 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
122
135
|
const fixDates = (obj) => {
|
|
123
136
|
if (!obj) return obj;
|
|
124
137
|
for (const key in obj) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
const value = obj[key];
|
|
139
|
+
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
|
|
140
|
+
obj[key] = new Date(value);
|
|
141
|
+
} else if (typeof value === "object" && value !== null) {
|
|
142
|
+
if (Array.isArray(value)) {
|
|
143
|
+
value.forEach((item) => {
|
|
144
|
+
if (typeof item === "object" && item !== null) {
|
|
145
|
+
fixDates(item);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
130
148
|
}
|
|
131
149
|
}
|
|
132
150
|
}
|
|
@@ -151,10 +169,12 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
151
169
|
return null;
|
|
152
170
|
},
|
|
153
171
|
async findMany({ model, where, limit, offset, sortBy }) {
|
|
172
|
+
await ensureReady();
|
|
154
173
|
const results = await runQuery(model, where, sortBy ? { [sortBy.field]: sortBy.direction } : void 0, limit, offset);
|
|
155
174
|
return results;
|
|
156
175
|
},
|
|
157
176
|
async update({ model, where, update }) {
|
|
177
|
+
await ensureReady();
|
|
158
178
|
const results = await runQuery(model, where);
|
|
159
179
|
if (results.length === 0) return null;
|
|
160
180
|
const mapName = getMapName(model);
|
|
@@ -165,6 +185,7 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
165
185
|
return updatedItem;
|
|
166
186
|
},
|
|
167
187
|
async updateMany({ model, where, update }) {
|
|
188
|
+
await ensureReady();
|
|
168
189
|
const results = await runQuery(model, where);
|
|
169
190
|
const mapName = getMapName(model);
|
|
170
191
|
const map = client.getMap(mapName);
|
|
@@ -174,6 +195,7 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
174
195
|
return results.length;
|
|
175
196
|
},
|
|
176
197
|
async delete({ model, where }) {
|
|
198
|
+
await ensureReady();
|
|
177
199
|
const results = await runQuery(model, where);
|
|
178
200
|
const mapName = getMapName(model);
|
|
179
201
|
const map = client.getMap(mapName);
|
|
@@ -182,6 +204,7 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
182
204
|
}
|
|
183
205
|
},
|
|
184
206
|
async deleteMany({ model, where }) {
|
|
207
|
+
await ensureReady();
|
|
185
208
|
const results = await runQuery(model, where);
|
|
186
209
|
const mapName = getMapName(model);
|
|
187
210
|
const map = client.getMap(mapName);
|
|
@@ -191,6 +214,7 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
191
214
|
return results.length;
|
|
192
215
|
},
|
|
193
216
|
async count({ model, where }) {
|
|
217
|
+
await ensureReady();
|
|
194
218
|
const results = await runQuery(model, where);
|
|
195
219
|
return results.length;
|
|
196
220
|
},
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/TopGunAdapter.ts"],"sourcesContent":["export * from './TopGunAdapter';\n\n","import { TopGunClient, Predicates } from '@topgunbuild/client';\nimport type { BetterAuthOptions } from 'better-auth';\nimport type { \n DBAdapter, \n Where,\n DBAdapterInstance\n} from 'better-auth/adapters';\nimport type { PredicateNode } from '@topgunbuild/core';\n\nexport interface TopGunAdapterOptions {\n client: TopGunClient;\n /**\n * Map model names to TopGun map names.\n * Default: \"auth_user\", \"auth_session\", etc.\n */\n modelMap?: Record<string, string>;\n}\n\nexport const topGunAdapter = (adapterOptions: TopGunAdapterOptions): DBAdapterInstance => {\n return (options: BetterAuthOptions): DBAdapter => {\n const { client, modelMap = {} } = adapterOptions;\n\n const getMapName = (model: string) => {\n return modelMap[model] || `auth_${model}`;\n };\n\n const whereToPredicate = (where: Where[]): PredicateNode | undefined => {\n if (!where || where.length === 0) return undefined;\n\n const predicates: PredicateNode[] = where.map(w => {\n const field = w.field;\n const value = w.value;\n \n switch (w.operator) {\n case 'eq': return Predicates.equal(field, value);\n case 'ne': return Predicates.notEqual(field, value);\n case 'lt': return Predicates.lessThan(field, value);\n case 'lte': return Predicates.lessThanOrEqual(field, value);\n case 'gt': return Predicates.greaterThan(field, value);\n case 'gte': return Predicates.greaterThanOrEqual(field, value);\n case 'contains': return Predicates.like(field, `%${value}%`);\n case 'starts_with': return Predicates.like(field, `${value}%`);\n case 'ends_with': return Predicates.like(field, `%${value}`);\n case 'in': \n if (Array.isArray(value)) {\n return Predicates.or(...value.map(v => Predicates.equal(field, v)));\n }\n return Predicates.equal(field, value);\n case 'not_in':\n if (Array.isArray(value)) {\n return Predicates.and(...value.map(v => Predicates.notEqual(field, v)));\n }\n return Predicates.notEqual(field, value);\n default: return Predicates.equal(field, value);\n }\n });\n\n // BetterAuth Where[] implies AND\n if (predicates.length === 1) return predicates[0];\n return Predicates.and(...predicates);\n };\n\n const runQuery = async <T>(model: string, where?: Where[], sort?: any, limit?: number, offset?: number): Promise<T[]> => {\n const mapName = getMapName(model);\n const predicate = where ? whereToPredicate(where) : undefined;\n \n const filter = {\n predicate,\n sort,\n limit,\n offset\n };\n\n // We use client.query which subscribes. We wait for the first result.\n // TopGun QueryHandle is reactive. We need a one-shot fetch.\n \n return new Promise((resolve) => {\n const handle = client.query<T>(mapName, filter);\n \n // Subscribe returns an unsubscribe function\n const unsubscribe = handle.subscribe((results: T[]) => {\n unsubscribe();\n resolve(results);\n });\n });\n };\n\n return {\n id: 'topgun-adapter',\n \n async create({ model, data }) {\n const mapName = getMapName(model);\n const id = (data as any).id || crypto.randomUUID();\n const record = { ...data, id };\n \n // Use LWWMap for standard records\n const map = client.getMap<string, any>(mapName);\n map.set(id, record);\n \n // map.set is optimistic and writes to local storage/sync engine.\n // Ideally we wait for confirmation? TopGun doesn't expose Promise for set completion easily \n // (it returns the record). But SyncEngine queues it.\n \n return record as any;\n },\n\n async findOne({ model, where, select, join }) {\n // Optimization: If where is just ID check, use getMap().get()\n const idCheck = where.find(w => w.field === 'id' && (w.operator === 'eq' || w.operator === undefined));\n if (idCheck && where.length === 1) {\n const mapName = getMapName(model);\n const map = client.getMap<string, any>(mapName);\n // LWWMap.get is synchronous from memory (loaded from storage).\n // If we haven't loaded yet, we might miss it.\n // Ideally we should ensure map is loaded.\n // TopGunClient.getMap returns immediately but starts restoring in background.\n // This creates a race condition for cold start.\n \n // Workaround: Use runQuery which waits for initial load via QueryHandle -> loadInitialLocalData\n // But for simple ID, query is overkill?\n // Let's use runQuery to be safe and consistent.\n }\n\n const results = await runQuery<any>(model, where, undefined, 1);\n \n if (results.length > 0) {\n const result = results[0];\n\n // Handle Join\n if (join) {\n for (const [joinModel, joinConfig] of Object.entries(join)) {\n if (joinConfig === false) continue;\n \n // Assume standard relation on userId\n // TODO: Handle custom foreign keys if Better Auth passes them or we infer them\n const joinWhere: Where[] = [{ field: 'userId', value: result.id }];\n \n const limit = typeof joinConfig === 'object' ? joinConfig.limit : undefined;\n \n const joinResults = await runQuery(joinModel, joinWhere, undefined, limit);\n \n // Attach to result using pluralized name (simple heuristic)\n const pluralName = joinModel.endsWith('s') ? joinModel : joinModel + 's';\n (result as any)[pluralName] = joinResults;\n }\n }\n\n // console.log(`[Adapter] findOne final result:`, result);\n \n // Ensure Dates are Date objects if they are strings (basic fix for JSON/serialization issues)\n const fixDates = (obj: any) => {\n if (!obj) return obj;\n for (const key in obj) {\n if (typeof obj[key] === 'string' && /^\\d{4}-\\d{2}-\\d{2}T/.test(obj[key])) {\n obj[key] = new Date(obj[key]);\n } else if (typeof obj[key] === 'object' && obj[key] !== null) {\n if (Array.isArray(obj[key])) {\n obj[key].forEach((item: any) => fixDates(item));\n }\n }\n }\n return obj;\n };\n fixDates(result);\n\n if (select) {\n const selected: any = {};\n select.forEach(field => selected[field] = result[field]);\n // Ensure joined props are kept if they are not in select? \n // Usually select applies to the main model fields. \n // If join is requested, it implies we want those too.\n if (join) {\n for (const joinModel of Object.keys(join)) {\n const propName = joinModel.endsWith('s') ? joinModel : joinModel + 's';\n if ((result as any)[propName]) {\n selected[propName] = (result as any)[propName];\n }\n }\n }\n return selected;\n }\n return result;\n }\n return null;\n },\n\n async findMany({ model, where, limit, offset, sortBy }) {\n const results = await runQuery<any>(model, where, sortBy ? {[sortBy.field]: sortBy.direction} : undefined, limit, offset);\n return results;\n },\n\n async update({ model, where, update }) {\n // We need to find the records first to update them\n const results = await runQuery<any>(model, where);\n if (results.length === 0) return null;\n\n const mapName = getMapName(model);\n const map = client.getMap<string, any>(mapName);\n \n // Update implies modifying existing. \n // If multiple matches, update only first? The interface says \"Update may not return the updated data if multiple where clauses are provided\".\n // Usually update finds one.\n // But if where is implicit AND, it finds specific set.\n // Standard behavior for 'update' (singular) is update ONE.\n \n const item = results[0];\n const updatedItem = { ...item, ...update };\n map.set(item.id, updatedItem);\n \n return updatedItem;\n },\n\n async updateMany({ model, where, update }) {\n const results = await runQuery<any>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, any>(mapName);\n \n for (const item of results) {\n map.set(item.id, { ...item, ...update });\n }\n return results.length;\n },\n\n async delete({ model, where }) {\n const results = await runQuery<any>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, any>(mapName);\n \n if (results.length > 0) {\n map.remove(results[0].id);\n }\n },\n\n async deleteMany({ model, where }) {\n const results = await runQuery<any>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, any>(mapName);\n \n for (const item of results) {\n map.remove(item.id);\n }\n return results.length;\n },\n \n async count({ model, where }) {\n const results = await runQuery<any>(model, where);\n return results.length;\n },\n\n async transaction(callback) {\n // TopGun doesn't support atomic multi-map transactions yet.\n // We execute sequentially as per BetterAuth fallback.\n // But DBTransactionAdapter is Omit<DBAdapter, \"transaction\">.\n // We just pass 'this' as the transaction adapter (cast it).\n return callback(this as any); \n }\n };\n };\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/TopGunAdapter.ts"],"sourcesContent":["export * from './TopGunAdapter';\n\n","import { TopGunClient, Predicates } from '@topgunbuild/client';\nimport type { BetterAuthOptions } from 'better-auth';\nimport type {\n DBAdapter,\n Where,\n DBAdapterInstance\n} from 'better-auth/adapters';\nimport type { PredicateNode } from '@topgunbuild/core';\n\n/**\n * Base interface for all BetterAuth records stored in TopGun.\n * Allows string-indexed properties for flexibility with different model types.\n */\ninterface AuthRecord {\n id: string;\n [key: string]: unknown;\n}\n\n/**\n * Sort direction for query ordering.\n */\ntype SortDirection = 'asc' | 'desc';\n\n/**\n * Sort specification mapping field names to sort directions.\n */\ntype SortSpec = Record<string, SortDirection>;\n\nexport interface TopGunAdapterOptions {\n client: TopGunClient;\n /**\n * Map model names to TopGun map names.\n * Default: \"auth_user\", \"auth_session\", etc.\n */\n modelMap?: Record<string, string>;\n /** Wait for client storage to be ready before accepting requests (default: true) */\n waitForReady?: boolean;\n /**\n * Map model names to their foreign key field for join operations.\n * Default: \"userId\" for all models.\n * Example: { account: \"ownerId\", session: \"userId\" }\n */\n foreignKeyMap?: Record<string, string>;\n}\n\nexport const topGunAdapter = (adapterOptions: TopGunAdapterOptions): DBAdapterInstance => {\n return (options: BetterAuthOptions): DBAdapter => {\n const { client, modelMap = {} } = adapterOptions;\n\n const getMapName = (model: string) => {\n return modelMap[model] || `auth_${model}`;\n };\n\n // Ready state tracking for cold start race condition fix\n const shouldWaitForReady = adapterOptions.waitForReady ?? true;\n let isReady = false;\n let readyPromise: Promise<void> | null = null;\n\n const ensureReady = async (): Promise<void> => {\n if (!shouldWaitForReady || isReady) return;\n if (!readyPromise) {\n // client.start() ensures storage is initialized and loaded\n readyPromise = client.start().then(() => {\n isReady = true;\n });\n }\n await readyPromise;\n };\n\n const whereToPredicate = (where: Where[]): PredicateNode | undefined => {\n if (!where || where.length === 0) return undefined;\n\n const predicates: PredicateNode[] = where.map(w => {\n const field = w.field;\n const value = w.value;\n \n switch (w.operator) {\n case 'eq': return Predicates.equal(field, value);\n case 'ne': return Predicates.notEqual(field, value);\n case 'lt': return Predicates.lessThan(field, value);\n case 'lte': return Predicates.lessThanOrEqual(field, value);\n case 'gt': return Predicates.greaterThan(field, value);\n case 'gte': return Predicates.greaterThanOrEqual(field, value);\n case 'contains': return Predicates.like(field, `%${value}%`);\n case 'starts_with': return Predicates.like(field, `${value}%`);\n case 'ends_with': return Predicates.like(field, `%${value}`);\n case 'in': \n if (Array.isArray(value)) {\n return Predicates.or(...value.map(v => Predicates.equal(field, v)));\n }\n return Predicates.equal(field, value);\n case 'not_in':\n if (Array.isArray(value)) {\n return Predicates.and(...value.map(v => Predicates.notEqual(field, v)));\n }\n return Predicates.notEqual(field, value);\n default: return Predicates.equal(field, value);\n }\n });\n\n // BetterAuth Where[] implies AND\n if (predicates.length === 1) return predicates[0];\n return Predicates.and(...predicates);\n };\n\n /**\n * Run a query against TopGun.\n *\n * Note: BetterAuth uses offset-based pagination, but TopGun uses cursor-based pagination.\n * For BetterAuth compatibility, we fetch limit+offset results and slice client-side.\n * This is acceptable for auth queries which typically have small result sets.\n */\n const runQuery = async <T extends AuthRecord>(model: string, where?: Where[], sort?: SortSpec, limit?: number, offset?: number): Promise<T[]> => {\n const mapName = getMapName(model);\n const predicate = where ? whereToPredicate(where) : undefined;\n\n // For BetterAuth offset compatibility, we request more results and slice\n const effectiveLimit = limit && offset ? limit + offset : limit;\n\n const filter = {\n predicate,\n sort,\n limit: effectiveLimit,\n // TopGun uses cursor-based pagination; offset is handled client-side for BetterAuth compatibility\n };\n\n // We use client.query which subscribes. We wait for the first result.\n // TopGun QueryHandle is reactive. We need a one-shot fetch.\n\n return new Promise((resolve) => {\n const handle = client.query<T>(mapName, filter);\n\n // Subscribe returns an unsubscribe function\n const unsubscribe = handle.subscribe((results: T[]) => {\n unsubscribe();\n // Apply offset client-side for BetterAuth compatibility\n const sliced = offset ? results.slice(offset, offset + (limit || results.length)) : results;\n resolve(sliced);\n });\n });\n };\n\n // Type assertion needed because BetterAuth's DBAdapter uses method-level generics\n // that TypeScript can't verify at compile time. Our AuthRecord constraint provides\n // internal type safety while the adapter boundary requires runtime type flexibility.\n return {\n id: 'topgun-adapter',\n\n async create({ model, data }) {\n await ensureReady();\n const mapName = getMapName(model);\n const dataWithId = data as Partial<AuthRecord> & Record<string, unknown>;\n const id = dataWithId.id || crypto.randomUUID();\n const record: AuthRecord = { ...data, id };\n\n // Use LWWMap for standard records\n const map = client.getMap<string, AuthRecord>(mapName);\n map.set(id, record);\n\n // map.set is optimistic and writes to local storage/sync engine.\n // Ideally we wait for confirmation? TopGun doesn't expose Promise for set completion easily\n // (it returns the record). But SyncEngine queues it.\n\n // Type assertion needed to match DBAdapter's generic return type\n return record as unknown as typeof data & { id: string };\n },\n\n async findOne({ model, where, select, join }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where, undefined, 1);\n \n if (results.length > 0) {\n const result = results[0];\n\n // Handle Join\n if (join) {\n for (const [joinModel, joinConfig] of Object.entries(join)) {\n if (joinConfig === false) continue;\n\n const foreignKey = adapterOptions.foreignKeyMap?.[joinModel] ?? 'userId';\n const joinWhere: Where[] = [{ field: foreignKey, value: result.id }];\n \n const limit = typeof joinConfig === 'object' ? joinConfig.limit : undefined;\n \n const joinResults = await runQuery(joinModel, joinWhere, undefined, limit);\n \n // Attach to result using pluralized name (simple heuristic)\n const pluralName = joinModel.endsWith('s') ? joinModel : joinModel + 's';\n result[pluralName] = joinResults;\n }\n }\n\n // console.log(`[Adapter] findOne final result:`, result);\n \n // Ensure Dates are Date objects if they are strings (basic fix for JSON/serialization issues)\n const fixDates = (obj: Record<string, unknown>): Record<string, unknown> => {\n if (!obj) return obj;\n for (const key in obj) {\n const value = obj[key];\n if (typeof value === 'string' && /^\\d{4}-\\d{2}-\\d{2}T/.test(value)) {\n obj[key] = new Date(value);\n } else if (typeof value === 'object' && value !== null) {\n if (Array.isArray(value)) {\n value.forEach((item: unknown) => {\n if (typeof item === 'object' && item !== null) {\n fixDates(item as Record<string, unknown>);\n }\n });\n }\n }\n }\n return obj;\n };\n fixDates(result);\n\n if (select) {\n const selected: Partial<AuthRecord> = {};\n select.forEach(field => selected[field] = result[field]);\n // Ensure joined props are kept if they are not in select?\n // Usually select applies to the main model fields.\n // If join is requested, it implies we want those too.\n if (join) {\n for (const joinModel of Object.keys(join)) {\n const propName = joinModel.endsWith('s') ? joinModel : joinModel + 's';\n if (result[propName]) {\n selected[propName] = result[propName];\n }\n }\n }\n // Type assertion needed to match DBAdapter's generic return type\n return selected as unknown as Record<string, unknown>;\n }\n // Type assertion needed to match DBAdapter's generic return type\n return result as unknown as Record<string, unknown>;\n }\n return null;\n },\n\n async findMany({ model, where, limit, offset, sortBy }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where, sortBy ? {[sortBy.field]: sortBy.direction} : undefined, limit, offset);\n // Type assertion needed to match DBAdapter's generic return type\n return results as unknown as Record<string, unknown>[];\n },\n\n async update({ model, where, update }) {\n await ensureReady();\n // We need to find the records first to update them\n const results = await runQuery<AuthRecord>(model, where);\n if (results.length === 0) return null;\n\n const mapName = getMapName(model);\n const map = client.getMap<string, AuthRecord>(mapName);\n\n // Update implies modifying existing.\n // If multiple matches, update only first? The interface says \"Update may not return the updated data if multiple where clauses are provided\".\n // Usually update finds one.\n // But if where is implicit AND, it finds specific set.\n // Standard behavior for 'update' (singular) is update ONE.\n\n const item = results[0];\n const updatedItem = { ...item, ...update };\n map.set(item.id, updatedItem);\n\n // Type assertion needed to match DBAdapter's generic return type\n return updatedItem as unknown as Record<string, unknown>;\n },\n\n async updateMany({ model, where, update }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, AuthRecord>(mapName);\n\n for (const item of results) {\n map.set(item.id, { ...item, ...update });\n }\n return results.length;\n },\n\n async delete({ model, where }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, AuthRecord>(mapName);\n\n if (results.length > 0) {\n map.remove(results[0].id);\n }\n },\n\n async deleteMany({ model, where }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, AuthRecord>(mapName);\n\n for (const item of results) {\n map.remove(item.id);\n }\n return results.length;\n },\n \n async count({ model, where }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where);\n return results.length;\n },\n\n async transaction(callback) {\n // TopGun doesn't support atomic multi-map transactions yet.\n // We execute sequentially as per BetterAuth fallback.\n // But DBTransactionAdapter is Omit<DBAdapter, \"transaction\">.\n // We just pass 'this' as the transaction adapter (cast it).\n return callback(this as Omit<DBAdapter, 'transaction'>);\n }\n } as DBAdapter;\n };\n};\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -6,6 +6,18 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
6
6
|
const getMapName = (model) => {
|
|
7
7
|
return modelMap[model] || `auth_${model}`;
|
|
8
8
|
};
|
|
9
|
+
const shouldWaitForReady = adapterOptions.waitForReady ?? true;
|
|
10
|
+
let isReady = false;
|
|
11
|
+
let readyPromise = null;
|
|
12
|
+
const ensureReady = async () => {
|
|
13
|
+
if (!shouldWaitForReady || isReady) return;
|
|
14
|
+
if (!readyPromise) {
|
|
15
|
+
readyPromise = client.start().then(() => {
|
|
16
|
+
isReady = true;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
await readyPromise;
|
|
20
|
+
};
|
|
9
21
|
const whereToPredicate = (where) => {
|
|
10
22
|
if (!where || where.length === 0) return void 0;
|
|
11
23
|
const predicates = where.map((w) => {
|
|
@@ -50,43 +62,44 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
50
62
|
const runQuery = async (model, where, sort, limit, offset) => {
|
|
51
63
|
const mapName = getMapName(model);
|
|
52
64
|
const predicate = where ? whereToPredicate(where) : void 0;
|
|
65
|
+
const effectiveLimit = limit && offset ? limit + offset : limit;
|
|
53
66
|
const filter = {
|
|
54
67
|
predicate,
|
|
55
68
|
sort,
|
|
56
|
-
limit
|
|
57
|
-
offset
|
|
69
|
+
limit: effectiveLimit
|
|
70
|
+
// TopGun uses cursor-based pagination; offset is handled client-side for BetterAuth compatibility
|
|
58
71
|
};
|
|
59
72
|
return new Promise((resolve) => {
|
|
60
73
|
const handle = client.query(mapName, filter);
|
|
61
74
|
const unsubscribe = handle.subscribe((results) => {
|
|
62
75
|
unsubscribe();
|
|
63
|
-
|
|
76
|
+
const sliced = offset ? results.slice(offset, offset + (limit || results.length)) : results;
|
|
77
|
+
resolve(sliced);
|
|
64
78
|
});
|
|
65
79
|
});
|
|
66
80
|
};
|
|
67
81
|
return {
|
|
68
82
|
id: "topgun-adapter",
|
|
69
83
|
async create({ model, data }) {
|
|
84
|
+
await ensureReady();
|
|
70
85
|
const mapName = getMapName(model);
|
|
71
|
-
const
|
|
86
|
+
const dataWithId = data;
|
|
87
|
+
const id = dataWithId.id || crypto.randomUUID();
|
|
72
88
|
const record = { ...data, id };
|
|
73
89
|
const map = client.getMap(mapName);
|
|
74
90
|
map.set(id, record);
|
|
75
91
|
return record;
|
|
76
92
|
},
|
|
77
93
|
async findOne({ model, where, select, join }) {
|
|
78
|
-
|
|
79
|
-
if (idCheck && where.length === 1) {
|
|
80
|
-
const mapName = getMapName(model);
|
|
81
|
-
const map = client.getMap(mapName);
|
|
82
|
-
}
|
|
94
|
+
await ensureReady();
|
|
83
95
|
const results = await runQuery(model, where, void 0, 1);
|
|
84
96
|
if (results.length > 0) {
|
|
85
97
|
const result = results[0];
|
|
86
98
|
if (join) {
|
|
87
99
|
for (const [joinModel, joinConfig] of Object.entries(join)) {
|
|
88
100
|
if (joinConfig === false) continue;
|
|
89
|
-
const
|
|
101
|
+
const foreignKey = adapterOptions.foreignKeyMap?.[joinModel] ?? "userId";
|
|
102
|
+
const joinWhere = [{ field: foreignKey, value: result.id }];
|
|
90
103
|
const limit = typeof joinConfig === "object" ? joinConfig.limit : void 0;
|
|
91
104
|
const joinResults = await runQuery(joinModel, joinWhere, void 0, limit);
|
|
92
105
|
const pluralName = joinModel.endsWith("s") ? joinModel : joinModel + "s";
|
|
@@ -96,11 +109,16 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
96
109
|
const fixDates = (obj) => {
|
|
97
110
|
if (!obj) return obj;
|
|
98
111
|
for (const key in obj) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
112
|
+
const value = obj[key];
|
|
113
|
+
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
|
|
114
|
+
obj[key] = new Date(value);
|
|
115
|
+
} else if (typeof value === "object" && value !== null) {
|
|
116
|
+
if (Array.isArray(value)) {
|
|
117
|
+
value.forEach((item) => {
|
|
118
|
+
if (typeof item === "object" && item !== null) {
|
|
119
|
+
fixDates(item);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
104
122
|
}
|
|
105
123
|
}
|
|
106
124
|
}
|
|
@@ -125,10 +143,12 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
125
143
|
return null;
|
|
126
144
|
},
|
|
127
145
|
async findMany({ model, where, limit, offset, sortBy }) {
|
|
146
|
+
await ensureReady();
|
|
128
147
|
const results = await runQuery(model, where, sortBy ? { [sortBy.field]: sortBy.direction } : void 0, limit, offset);
|
|
129
148
|
return results;
|
|
130
149
|
},
|
|
131
150
|
async update({ model, where, update }) {
|
|
151
|
+
await ensureReady();
|
|
132
152
|
const results = await runQuery(model, where);
|
|
133
153
|
if (results.length === 0) return null;
|
|
134
154
|
const mapName = getMapName(model);
|
|
@@ -139,6 +159,7 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
139
159
|
return updatedItem;
|
|
140
160
|
},
|
|
141
161
|
async updateMany({ model, where, update }) {
|
|
162
|
+
await ensureReady();
|
|
142
163
|
const results = await runQuery(model, where);
|
|
143
164
|
const mapName = getMapName(model);
|
|
144
165
|
const map = client.getMap(mapName);
|
|
@@ -148,6 +169,7 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
148
169
|
return results.length;
|
|
149
170
|
},
|
|
150
171
|
async delete({ model, where }) {
|
|
172
|
+
await ensureReady();
|
|
151
173
|
const results = await runQuery(model, where);
|
|
152
174
|
const mapName = getMapName(model);
|
|
153
175
|
const map = client.getMap(mapName);
|
|
@@ -156,6 +178,7 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
156
178
|
}
|
|
157
179
|
},
|
|
158
180
|
async deleteMany({ model, where }) {
|
|
181
|
+
await ensureReady();
|
|
159
182
|
const results = await runQuery(model, where);
|
|
160
183
|
const mapName = getMapName(model);
|
|
161
184
|
const map = client.getMap(mapName);
|
|
@@ -165,6 +188,7 @@ var topGunAdapter = (adapterOptions) => {
|
|
|
165
188
|
return results.length;
|
|
166
189
|
},
|
|
167
190
|
async count({ model, where }) {
|
|
191
|
+
await ensureReady();
|
|
168
192
|
const results = await runQuery(model, where);
|
|
169
193
|
return results.length;
|
|
170
194
|
},
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/TopGunAdapter.ts"],"sourcesContent":["import { TopGunClient, Predicates } from '@topgunbuild/client';\nimport type { BetterAuthOptions } from 'better-auth';\nimport type { \n DBAdapter, \n Where,\n DBAdapterInstance\n} from 'better-auth/adapters';\nimport type { PredicateNode } from '@topgunbuild/core';\n\nexport interface TopGunAdapterOptions {\n client: TopGunClient;\n /**\n * Map model names to TopGun map names.\n * Default: \"auth_user\", \"auth_session\", etc.\n */\n modelMap?: Record<string, string>;\n}\n\nexport const topGunAdapter = (adapterOptions: TopGunAdapterOptions): DBAdapterInstance => {\n return (options: BetterAuthOptions): DBAdapter => {\n const { client, modelMap = {} } = adapterOptions;\n\n const getMapName = (model: string) => {\n return modelMap[model] || `auth_${model}`;\n };\n\n const whereToPredicate = (where: Where[]): PredicateNode | undefined => {\n if (!where || where.length === 0) return undefined;\n\n const predicates: PredicateNode[] = where.map(w => {\n const field = w.field;\n const value = w.value;\n \n switch (w.operator) {\n case 'eq': return Predicates.equal(field, value);\n case 'ne': return Predicates.notEqual(field, value);\n case 'lt': return Predicates.lessThan(field, value);\n case 'lte': return Predicates.lessThanOrEqual(field, value);\n case 'gt': return Predicates.greaterThan(field, value);\n case 'gte': return Predicates.greaterThanOrEqual(field, value);\n case 'contains': return Predicates.like(field, `%${value}%`);\n case 'starts_with': return Predicates.like(field, `${value}%`);\n case 'ends_with': return Predicates.like(field, `%${value}`);\n case 'in': \n if (Array.isArray(value)) {\n return Predicates.or(...value.map(v => Predicates.equal(field, v)));\n }\n return Predicates.equal(field, value);\n case 'not_in':\n if (Array.isArray(value)) {\n return Predicates.and(...value.map(v => Predicates.notEqual(field, v)));\n }\n return Predicates.notEqual(field, value);\n default: return Predicates.equal(field, value);\n }\n });\n\n // BetterAuth Where[] implies AND\n if (predicates.length === 1) return predicates[0];\n return Predicates.and(...predicates);\n };\n\n const runQuery = async <T>(model: string, where?: Where[], sort?: any, limit?: number, offset?: number): Promise<T[]> => {\n const mapName = getMapName(model);\n const predicate = where ? whereToPredicate(where) : undefined;\n \n const filter = {\n predicate,\n sort,\n limit,\n offset\n };\n\n // We use client.query which subscribes. We wait for the first result.\n // TopGun QueryHandle is reactive. We need a one-shot fetch.\n \n return new Promise((resolve) => {\n const handle = client.query<T>(mapName, filter);\n \n // Subscribe returns an unsubscribe function\n const unsubscribe = handle.subscribe((results: T[]) => {\n unsubscribe();\n resolve(results);\n });\n });\n };\n\n return {\n id: 'topgun-adapter',\n \n async create({ model, data }) {\n const mapName = getMapName(model);\n const id = (data as any).id || crypto.randomUUID();\n const record = { ...data, id };\n \n // Use LWWMap for standard records\n const map = client.getMap<string, any>(mapName);\n map.set(id, record);\n \n // map.set is optimistic and writes to local storage/sync engine.\n // Ideally we wait for confirmation? TopGun doesn't expose Promise for set completion easily \n // (it returns the record). But SyncEngine queues it.\n \n return record as any;\n },\n\n async findOne({ model, where, select, join }) {\n // Optimization: If where is just ID check, use getMap().get()\n const idCheck = where.find(w => w.field === 'id' && (w.operator === 'eq' || w.operator === undefined));\n if (idCheck && where.length === 1) {\n const mapName = getMapName(model);\n const map = client.getMap<string, any>(mapName);\n // LWWMap.get is synchronous from memory (loaded from storage).\n // If we haven't loaded yet, we might miss it.\n // Ideally we should ensure map is loaded.\n // TopGunClient.getMap returns immediately but starts restoring in background.\n // This creates a race condition for cold start.\n \n // Workaround: Use runQuery which waits for initial load via QueryHandle -> loadInitialLocalData\n // But for simple ID, query is overkill?\n // Let's use runQuery to be safe and consistent.\n }\n\n const results = await runQuery<any>(model, where, undefined, 1);\n \n if (results.length > 0) {\n const result = results[0];\n\n // Handle Join\n if (join) {\n for (const [joinModel, joinConfig] of Object.entries(join)) {\n if (joinConfig === false) continue;\n \n // Assume standard relation on userId\n // TODO: Handle custom foreign keys if Better Auth passes them or we infer them\n const joinWhere: Where[] = [{ field: 'userId', value: result.id }];\n \n const limit = typeof joinConfig === 'object' ? joinConfig.limit : undefined;\n \n const joinResults = await runQuery(joinModel, joinWhere, undefined, limit);\n \n // Attach to result using pluralized name (simple heuristic)\n const pluralName = joinModel.endsWith('s') ? joinModel : joinModel + 's';\n (result as any)[pluralName] = joinResults;\n }\n }\n\n // console.log(`[Adapter] findOne final result:`, result);\n \n // Ensure Dates are Date objects if they are strings (basic fix for JSON/serialization issues)\n const fixDates = (obj: any) => {\n if (!obj) return obj;\n for (const key in obj) {\n if (typeof obj[key] === 'string' && /^\\d{4}-\\d{2}-\\d{2}T/.test(obj[key])) {\n obj[key] = new Date(obj[key]);\n } else if (typeof obj[key] === 'object' && obj[key] !== null) {\n if (Array.isArray(obj[key])) {\n obj[key].forEach((item: any) => fixDates(item));\n }\n }\n }\n return obj;\n };\n fixDates(result);\n\n if (select) {\n const selected: any = {};\n select.forEach(field => selected[field] = result[field]);\n // Ensure joined props are kept if they are not in select? \n // Usually select applies to the main model fields. \n // If join is requested, it implies we want those too.\n if (join) {\n for (const joinModel of Object.keys(join)) {\n const propName = joinModel.endsWith('s') ? joinModel : joinModel + 's';\n if ((result as any)[propName]) {\n selected[propName] = (result as any)[propName];\n }\n }\n }\n return selected;\n }\n return result;\n }\n return null;\n },\n\n async findMany({ model, where, limit, offset, sortBy }) {\n const results = await runQuery<any>(model, where, sortBy ? {[sortBy.field]: sortBy.direction} : undefined, limit, offset);\n return results;\n },\n\n async update({ model, where, update }) {\n // We need to find the records first to update them\n const results = await runQuery<any>(model, where);\n if (results.length === 0) return null;\n\n const mapName = getMapName(model);\n const map = client.getMap<string, any>(mapName);\n \n // Update implies modifying existing. \n // If multiple matches, update only first? The interface says \"Update may not return the updated data if multiple where clauses are provided\".\n // Usually update finds one.\n // But if where is implicit AND, it finds specific set.\n // Standard behavior for 'update' (singular) is update ONE.\n \n const item = results[0];\n const updatedItem = { ...item, ...update };\n map.set(item.id, updatedItem);\n \n return updatedItem;\n },\n\n async updateMany({ model, where, update }) {\n const results = await runQuery<any>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, any>(mapName);\n \n for (const item of results) {\n map.set(item.id, { ...item, ...update });\n }\n return results.length;\n },\n\n async delete({ model, where }) {\n const results = await runQuery<any>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, any>(mapName);\n \n if (results.length > 0) {\n map.remove(results[0].id);\n }\n },\n\n async deleteMany({ model, where }) {\n const results = await runQuery<any>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, any>(mapName);\n \n for (const item of results) {\n map.remove(item.id);\n }\n return results.length;\n },\n \n async count({ model, where }) {\n const results = await runQuery<any>(model, where);\n return results.length;\n },\n\n async transaction(callback) {\n // TopGun doesn't support atomic multi-map transactions yet.\n // We execute sequentially as per BetterAuth fallback.\n // But DBTransactionAdapter is Omit<DBAdapter, \"transaction\">.\n // We just pass 'this' as the transaction adapter (cast it).\n return callback(this as any); \n }\n };\n };\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/TopGunAdapter.ts"],"sourcesContent":["import { TopGunClient, Predicates } from '@topgunbuild/client';\nimport type { BetterAuthOptions } from 'better-auth';\nimport type {\n DBAdapter,\n Where,\n DBAdapterInstance\n} from 'better-auth/adapters';\nimport type { PredicateNode } from '@topgunbuild/core';\n\n/**\n * Base interface for all BetterAuth records stored in TopGun.\n * Allows string-indexed properties for flexibility with different model types.\n */\ninterface AuthRecord {\n id: string;\n [key: string]: unknown;\n}\n\n/**\n * Sort direction for query ordering.\n */\ntype SortDirection = 'asc' | 'desc';\n\n/**\n * Sort specification mapping field names to sort directions.\n */\ntype SortSpec = Record<string, SortDirection>;\n\nexport interface TopGunAdapterOptions {\n client: TopGunClient;\n /**\n * Map model names to TopGun map names.\n * Default: \"auth_user\", \"auth_session\", etc.\n */\n modelMap?: Record<string, string>;\n /** Wait for client storage to be ready before accepting requests (default: true) */\n waitForReady?: boolean;\n /**\n * Map model names to their foreign key field for join operations.\n * Default: \"userId\" for all models.\n * Example: { account: \"ownerId\", session: \"userId\" }\n */\n foreignKeyMap?: Record<string, string>;\n}\n\nexport const topGunAdapter = (adapterOptions: TopGunAdapterOptions): DBAdapterInstance => {\n return (options: BetterAuthOptions): DBAdapter => {\n const { client, modelMap = {} } = adapterOptions;\n\n const getMapName = (model: string) => {\n return modelMap[model] || `auth_${model}`;\n };\n\n // Ready state tracking for cold start race condition fix\n const shouldWaitForReady = adapterOptions.waitForReady ?? true;\n let isReady = false;\n let readyPromise: Promise<void> | null = null;\n\n const ensureReady = async (): Promise<void> => {\n if (!shouldWaitForReady || isReady) return;\n if (!readyPromise) {\n // client.start() ensures storage is initialized and loaded\n readyPromise = client.start().then(() => {\n isReady = true;\n });\n }\n await readyPromise;\n };\n\n const whereToPredicate = (where: Where[]): PredicateNode | undefined => {\n if (!where || where.length === 0) return undefined;\n\n const predicates: PredicateNode[] = where.map(w => {\n const field = w.field;\n const value = w.value;\n \n switch (w.operator) {\n case 'eq': return Predicates.equal(field, value);\n case 'ne': return Predicates.notEqual(field, value);\n case 'lt': return Predicates.lessThan(field, value);\n case 'lte': return Predicates.lessThanOrEqual(field, value);\n case 'gt': return Predicates.greaterThan(field, value);\n case 'gte': return Predicates.greaterThanOrEqual(field, value);\n case 'contains': return Predicates.like(field, `%${value}%`);\n case 'starts_with': return Predicates.like(field, `${value}%`);\n case 'ends_with': return Predicates.like(field, `%${value}`);\n case 'in': \n if (Array.isArray(value)) {\n return Predicates.or(...value.map(v => Predicates.equal(field, v)));\n }\n return Predicates.equal(field, value);\n case 'not_in':\n if (Array.isArray(value)) {\n return Predicates.and(...value.map(v => Predicates.notEqual(field, v)));\n }\n return Predicates.notEqual(field, value);\n default: return Predicates.equal(field, value);\n }\n });\n\n // BetterAuth Where[] implies AND\n if (predicates.length === 1) return predicates[0];\n return Predicates.and(...predicates);\n };\n\n /**\n * Run a query against TopGun.\n *\n * Note: BetterAuth uses offset-based pagination, but TopGun uses cursor-based pagination.\n * For BetterAuth compatibility, we fetch limit+offset results and slice client-side.\n * This is acceptable for auth queries which typically have small result sets.\n */\n const runQuery = async <T extends AuthRecord>(model: string, where?: Where[], sort?: SortSpec, limit?: number, offset?: number): Promise<T[]> => {\n const mapName = getMapName(model);\n const predicate = where ? whereToPredicate(where) : undefined;\n\n // For BetterAuth offset compatibility, we request more results and slice\n const effectiveLimit = limit && offset ? limit + offset : limit;\n\n const filter = {\n predicate,\n sort,\n limit: effectiveLimit,\n // TopGun uses cursor-based pagination; offset is handled client-side for BetterAuth compatibility\n };\n\n // We use client.query which subscribes. We wait for the first result.\n // TopGun QueryHandle is reactive. We need a one-shot fetch.\n\n return new Promise((resolve) => {\n const handle = client.query<T>(mapName, filter);\n\n // Subscribe returns an unsubscribe function\n const unsubscribe = handle.subscribe((results: T[]) => {\n unsubscribe();\n // Apply offset client-side for BetterAuth compatibility\n const sliced = offset ? results.slice(offset, offset + (limit || results.length)) : results;\n resolve(sliced);\n });\n });\n };\n\n // Type assertion needed because BetterAuth's DBAdapter uses method-level generics\n // that TypeScript can't verify at compile time. Our AuthRecord constraint provides\n // internal type safety while the adapter boundary requires runtime type flexibility.\n return {\n id: 'topgun-adapter',\n\n async create({ model, data }) {\n await ensureReady();\n const mapName = getMapName(model);\n const dataWithId = data as Partial<AuthRecord> & Record<string, unknown>;\n const id = dataWithId.id || crypto.randomUUID();\n const record: AuthRecord = { ...data, id };\n\n // Use LWWMap for standard records\n const map = client.getMap<string, AuthRecord>(mapName);\n map.set(id, record);\n\n // map.set is optimistic and writes to local storage/sync engine.\n // Ideally we wait for confirmation? TopGun doesn't expose Promise for set completion easily\n // (it returns the record). But SyncEngine queues it.\n\n // Type assertion needed to match DBAdapter's generic return type\n return record as unknown as typeof data & { id: string };\n },\n\n async findOne({ model, where, select, join }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where, undefined, 1);\n \n if (results.length > 0) {\n const result = results[0];\n\n // Handle Join\n if (join) {\n for (const [joinModel, joinConfig] of Object.entries(join)) {\n if (joinConfig === false) continue;\n\n const foreignKey = adapterOptions.foreignKeyMap?.[joinModel] ?? 'userId';\n const joinWhere: Where[] = [{ field: foreignKey, value: result.id }];\n \n const limit = typeof joinConfig === 'object' ? joinConfig.limit : undefined;\n \n const joinResults = await runQuery(joinModel, joinWhere, undefined, limit);\n \n // Attach to result using pluralized name (simple heuristic)\n const pluralName = joinModel.endsWith('s') ? joinModel : joinModel + 's';\n result[pluralName] = joinResults;\n }\n }\n\n // console.log(`[Adapter] findOne final result:`, result);\n \n // Ensure Dates are Date objects if they are strings (basic fix for JSON/serialization issues)\n const fixDates = (obj: Record<string, unknown>): Record<string, unknown> => {\n if (!obj) return obj;\n for (const key in obj) {\n const value = obj[key];\n if (typeof value === 'string' && /^\\d{4}-\\d{2}-\\d{2}T/.test(value)) {\n obj[key] = new Date(value);\n } else if (typeof value === 'object' && value !== null) {\n if (Array.isArray(value)) {\n value.forEach((item: unknown) => {\n if (typeof item === 'object' && item !== null) {\n fixDates(item as Record<string, unknown>);\n }\n });\n }\n }\n }\n return obj;\n };\n fixDates(result);\n\n if (select) {\n const selected: Partial<AuthRecord> = {};\n select.forEach(field => selected[field] = result[field]);\n // Ensure joined props are kept if they are not in select?\n // Usually select applies to the main model fields.\n // If join is requested, it implies we want those too.\n if (join) {\n for (const joinModel of Object.keys(join)) {\n const propName = joinModel.endsWith('s') ? joinModel : joinModel + 's';\n if (result[propName]) {\n selected[propName] = result[propName];\n }\n }\n }\n // Type assertion needed to match DBAdapter's generic return type\n return selected as unknown as Record<string, unknown>;\n }\n // Type assertion needed to match DBAdapter's generic return type\n return result as unknown as Record<string, unknown>;\n }\n return null;\n },\n\n async findMany({ model, where, limit, offset, sortBy }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where, sortBy ? {[sortBy.field]: sortBy.direction} : undefined, limit, offset);\n // Type assertion needed to match DBAdapter's generic return type\n return results as unknown as Record<string, unknown>[];\n },\n\n async update({ model, where, update }) {\n await ensureReady();\n // We need to find the records first to update them\n const results = await runQuery<AuthRecord>(model, where);\n if (results.length === 0) return null;\n\n const mapName = getMapName(model);\n const map = client.getMap<string, AuthRecord>(mapName);\n\n // Update implies modifying existing.\n // If multiple matches, update only first? The interface says \"Update may not return the updated data if multiple where clauses are provided\".\n // Usually update finds one.\n // But if where is implicit AND, it finds specific set.\n // Standard behavior for 'update' (singular) is update ONE.\n\n const item = results[0];\n const updatedItem = { ...item, ...update };\n map.set(item.id, updatedItem);\n\n // Type assertion needed to match DBAdapter's generic return type\n return updatedItem as unknown as Record<string, unknown>;\n },\n\n async updateMany({ model, where, update }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, AuthRecord>(mapName);\n\n for (const item of results) {\n map.set(item.id, { ...item, ...update });\n }\n return results.length;\n },\n\n async delete({ model, where }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, AuthRecord>(mapName);\n\n if (results.length > 0) {\n map.remove(results[0].id);\n }\n },\n\n async deleteMany({ model, where }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where);\n const mapName = getMapName(model);\n const map = client.getMap<string, AuthRecord>(mapName);\n\n for (const item of results) {\n map.remove(item.id);\n }\n return results.length;\n },\n \n async count({ model, where }) {\n await ensureReady();\n const results = await runQuery<AuthRecord>(model, where);\n return results.length;\n },\n\n async transaction(callback) {\n // TopGun doesn't support atomic multi-map transactions yet.\n // We execute sequentially as per BetterAuth fallback.\n // But DBTransactionAdapter is Omit<DBAdapter, \"transaction\">.\n // We just pass 'this' as the transaction adapter (cast it).\n return callback(this as Omit<DBAdapter, 'transaction'>);\n }\n } as DBAdapter;\n };\n};\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topgunbuild/adapter-better-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"better-auth": "^1.0.0",
|
|
23
|
-
"@topgunbuild/client": "0.
|
|
24
|
-
"@topgunbuild/core": "0.
|
|
23
|
+
"@topgunbuild/client": "0.10.1",
|
|
24
|
+
"@topgunbuild/core": "0.10.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/jest": "^29.5.14",
|