@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 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
- resolve(results);
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 id = data.id || crypto.randomUUID();
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
- const idCheck = where.find((w) => w.field === "id" && (w.operator === "eq" || w.operator === void 0));
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 joinWhere = [{ field: "userId", value: result.id }];
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
- if (typeof obj[key] === "string" && /^\d{4}-\d{2}-\d{2}T/.test(obj[key])) {
126
- obj[key] = new Date(obj[key]);
127
- } else if (typeof obj[key] === "object" && obj[key] !== null) {
128
- if (Array.isArray(obj[key])) {
129
- obj[key].forEach((item) => fixDates(item));
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
- resolve(results);
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 id = data.id || crypto.randomUUID();
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
- const idCheck = where.find((w) => w.field === "id" && (w.operator === "eq" || w.operator === void 0));
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 joinWhere = [{ field: "userId", value: result.id }];
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
- if (typeof obj[key] === "string" && /^\d{4}-\d{2}-\d{2}T/.test(obj[key])) {
100
- obj[key] = new Date(obj[key]);
101
- } else if (typeof obj[key] === "object" && obj[key] !== null) {
102
- if (Array.isArray(obj[key])) {
103
- obj[key].forEach((item) => fixDates(item));
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
  },
@@ -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.9.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.9.0",
24
- "@topgunbuild/core": "0.9.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",