@better-auth/api-key 1.6.5 → 1.6.7

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/client.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as API_KEY_ERROR_CODES, t as PACKAGE_VERSION } from "./version-FQc8oTAW.mjs";
1
+ import { n as API_KEY_ERROR_CODES, t as PACKAGE_VERSION } from "./version-Da1Ahr-J.mjs";
2
2
  //#region src/client.ts
3
3
  const apiKeyClient = () => {
4
4
  return {
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as API_KEY_ERROR_CODES, t as PACKAGE_VERSION } from "./version-FQc8oTAW.mjs";
1
+ import { n as API_KEY_ERROR_CODES, t as PACKAGE_VERSION } from "./version-Da1Ahr-J.mjs";
2
2
  import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
3
3
  import { base64Url } from "@better-auth/utils/base64";
4
4
  import { createHash } from "@better-auth/utils/hash";
@@ -10,11 +10,13 @@ import { APIError as APIError$1 } from "@better-auth/core/error";
10
10
  import { generateId } from "@better-auth/core/utils/id";
11
11
  import { safeJSONParse } from "@better-auth/core/utils/json";
12
12
  import * as z from "zod";
13
+ import { mapConcurrent } from "@better-auth/core/utils/async";
13
14
  import { isDevelopment, isTest } from "@better-auth/core/env";
14
15
  import { isValidIP, normalizeIP } from "@better-auth/core/utils/ip";
15
16
  import { role } from "better-auth/plugins/access";
16
17
  import { parseJSON } from "better-auth/client";
17
18
  //#region src/adapter.ts
19
+ const STORAGE_CONCURRENCY = 10;
18
20
  /**
19
21
  * Parses double-stringified metadata synchronously without updating the database.
20
22
  * Use this for reading metadata, then call migrateLegacyMetadataInBackground for DB updates.
@@ -165,15 +167,10 @@ async function getApiKeyByIdFromStorage(ctx, id, storage) {
165
167
  return deserializeApiKey(await storage.get(key));
166
168
  }
167
169
  /**
168
- * Store API key in secondary storage
170
+ * Read-modify-write the ref list:
171
+ * used only when the list is the source of truth.
169
172
  */
170
- async function setApiKeyInStorage(ctx, apiKey, storage, ttl) {
171
- const serialized = serializeApiKey(apiKey);
172
- const hashedKey = apiKey.key;
173
- const id = apiKey.id;
174
- await storage.set(getStorageKeyByHashedKey(hashedKey), serialized, ttl);
175
- await storage.set(getStorageKeyById(id), serialized, ttl);
176
- const refKey = getStorageKeyByReferenceId(apiKey.referenceId);
173
+ async function modifyRefList(storage, refKey, modify) {
177
174
  const refListData = await storage.get(refKey);
178
175
  let keyIds = [];
179
176
  if (refListData && typeof refListData === "string") try {
@@ -182,32 +179,42 @@ async function setApiKeyInStorage(ctx, apiKey, storage, ttl) {
182
179
  keyIds = [];
183
180
  }
184
181
  else if (Array.isArray(refListData)) keyIds = refListData;
185
- if (!keyIds.includes(id)) {
186
- keyIds.push(id);
187
- await storage.set(refKey, JSON.stringify(keyIds));
182
+ const next = modify(keyIds);
183
+ if (next.length === 0) await storage.delete(refKey);
184
+ else await storage.set(refKey, JSON.stringify(next));
185
+ }
186
+ async function setApiKeyInStorage(_ctx, apiKey, storage, ttl, opts) {
187
+ const serialized = serializeApiKey(apiKey);
188
+ const refKey = getStorageKeyByReferenceId(apiKey.referenceId);
189
+ if (opts.fallbackToDatabase) {
190
+ await Promise.all([
191
+ storage.set(getStorageKeyByHashedKey(apiKey.key), serialized, ttl),
192
+ storage.set(getStorageKeyById(apiKey.id), serialized, ttl),
193
+ storage.delete(refKey)
194
+ ]);
195
+ return;
188
196
  }
197
+ await Promise.all([storage.set(getStorageKeyByHashedKey(apiKey.key), serialized, ttl), storage.set(getStorageKeyById(apiKey.id), serialized, ttl)]);
198
+ await modifyRefList(storage, refKey, (ids) => ids.includes(apiKey.id) ? ids : [...ids, apiKey.id]);
189
199
  }
190
200
  /**
191
201
  * Delete API key from secondary storage
192
202
  */
193
- async function deleteApiKeyFromStorage(ctx, apiKey, storage) {
194
- const hashedKey = apiKey.key;
195
- const id = apiKey.id;
196
- const referenceId = apiKey.referenceId;
197
- await storage.delete(getStorageKeyByHashedKey(hashedKey));
198
- await storage.delete(getStorageKeyById(id));
199
- const refKey = getStorageKeyByReferenceId(referenceId);
200
- const refListData = await storage.get(refKey);
201
- let keyIds = [];
202
- if (refListData && typeof refListData === "string") try {
203
- keyIds = JSON.parse(refListData);
204
- } catch {
205
- keyIds = [];
203
+ async function deleteApiKeyFromStorage(ctx, apiKey, storage, opts) {
204
+ const refKey = getStorageKeyByReferenceId(apiKey.referenceId);
205
+ if (opts.fallbackToDatabase) {
206
+ await Promise.all([
207
+ storage.delete(getStorageKeyByHashedKey(apiKey.key)),
208
+ storage.delete(getStorageKeyById(apiKey.id)),
209
+ storage.delete(refKey)
210
+ ]);
211
+ return;
206
212
  }
207
- else if (Array.isArray(refListData)) keyIds = refListData;
208
- const filteredIds = keyIds.filter((keyId) => keyId !== id);
209
- if (filteredIds.length === 0) await storage.delete(refKey);
210
- else await storage.set(refKey, JSON.stringify(filteredIds));
213
+ await Promise.all([
214
+ storage.delete(getStorageKeyByHashedKey(apiKey.key)),
215
+ storage.delete(getStorageKeyById(apiKey.id)),
216
+ modifyRefList(storage, refKey, (ids) => ids.filter((keyId) => keyId !== apiKey.id))
217
+ ]);
211
218
  }
212
219
  /**
213
220
  * Unified getter for API keys with support for all storage modes
@@ -233,7 +240,7 @@ async function getApiKey$1(ctx, hashedKey, opts) {
233
240
  value: hashedKey
234
241
  }]
235
242
  });
236
- if (dbKey && storage) await setApiKeyInStorage(ctx, dbKey, storage, calculateTTL(dbKey));
243
+ if (dbKey && storage) await setApiKeyInStorage(ctx, dbKey, storage, calculateTTL(dbKey), opts);
237
244
  return dbKey;
238
245
  }
239
246
  if (opts.storage === "secondary-storage") {
@@ -272,7 +279,7 @@ async function getApiKeyById(ctx, id, opts) {
272
279
  value: id
273
280
  }]
274
281
  });
275
- if (dbKey && storage) await setApiKeyInStorage(ctx, dbKey, storage, calculateTTL(dbKey));
282
+ if (dbKey && storage) await setApiKeyInStorage(ctx, dbKey, storage, calculateTTL(dbKey), opts);
276
283
  return dbKey;
277
284
  }
278
285
  if (opts.storage === "secondary-storage") {
@@ -296,7 +303,7 @@ async function setApiKey(ctx, apiKey, opts) {
296
303
  if (opts.storage === "database") return;
297
304
  if (opts.storage === "secondary-storage") {
298
305
  if (!storage) throw new Error("Secondary storage is required when storage mode is 'secondary-storage'");
299
- await setApiKeyInStorage(ctx, apiKey, storage, ttl);
306
+ await setApiKeyInStorage(ctx, apiKey, storage, ttl, opts);
300
307
  return;
301
308
  }
302
309
  }
@@ -308,7 +315,7 @@ async function deleteApiKey$1(ctx, apiKey, opts) {
308
315
  if (opts.storage === "database") return;
309
316
  if (opts.storage === "secondary-storage") {
310
317
  if (!storage) throw new Error("Secondary storage is required when storage mode is 'secondary-storage'");
311
- await deleteApiKeyFromStorage(ctx, apiKey, storage);
318
+ await deleteApiKeyFromStorage(ctx, apiKey, storage, opts);
312
319
  return;
313
320
  }
314
321
  }
@@ -378,11 +385,7 @@ async function listApiKeys$1(ctx, referenceId, opts, paginationOpts) {
378
385
  }
379
386
  else if (Array.isArray(refListData)) keyIds = refListData;
380
387
  if (keyIds.length > 0) {
381
- const apiKeys = [];
382
- for (const id of keyIds) {
383
- const apiKey = await getApiKeyByIdFromStorage(ctx, id, storage);
384
- if (apiKey) apiKeys.push(apiKey);
385
- }
388
+ const apiKeys = (await mapConcurrent(keyIds, (id) => getApiKeyByIdFromStorage(ctx, id, storage), { concurrency: STORAGE_CONCURRENCY })).filter((key) => key !== null && key !== void 0);
386
389
  return {
387
390
  apiKeys: applySortingAndPagination(apiKeys, sortBy, sortDirection, limit, offset),
388
391
  total: apiKeys.length
@@ -409,11 +412,8 @@ async function listApiKeys$1(ctx, referenceId, opts, paginationOpts) {
409
412
  }]
410
413
  })]);
411
414
  if (storage && dbKeys.length > 0) {
412
- const keyIds = [];
413
- for (const apiKey of dbKeys) {
414
- await setApiKeyInStorage(ctx, apiKey, storage, calculateTTL(apiKey));
415
- keyIds.push(apiKey.id);
416
- }
415
+ await mapConcurrent(dbKeys, (apiKey) => setApiKeyInStorage(ctx, apiKey, storage, calculateTTL(apiKey), opts), { concurrency: STORAGE_CONCURRENCY });
416
+ const keyIds = dbKeys.map((apiKey) => apiKey.id);
417
417
  await storage.set(refKey, JSON.stringify(keyIds));
418
418
  }
419
419
  return {
@@ -442,11 +442,7 @@ async function listApiKeys$1(ctx, referenceId, opts, paginationOpts) {
442
442
  apiKeys: [],
443
443
  total: 0
444
444
  };
445
- const apiKeys = [];
446
- for (const id of keyIds) {
447
- const apiKey = await getApiKeyByIdFromStorage(ctx, id, storage);
448
- if (apiKey) apiKeys.push(apiKey);
449
- }
445
+ const apiKeys = (await mapConcurrent(keyIds, (id) => getApiKeyByIdFromStorage(ctx, id, storage), { concurrency: STORAGE_CONCURRENCY })).filter((key) => key !== null && key !== void 0);
450
446
  return {
451
447
  apiKeys: applySortingAndPagination(apiKeys, sortBy, sortDirection, limit, offset),
452
448
  total: apiKeys.length
@@ -1285,18 +1281,16 @@ function listApiKeys({ configurations, schema, deleteAllExpiredApiKeys }) {
1285
1281
  const storageKey = getStorageIdentifier(config);
1286
1282
  if (!storageGroups.has(storageKey)) storageGroups.set(storageKey, config);
1287
1283
  }
1284
+ const groupResults = await Promise.all([...storageGroups.values()].map((opts) => listApiKeys$1(ctx, referenceId, opts, {
1285
+ limit: void 0,
1286
+ offset: void 0,
1287
+ sortBy: ctx.query?.sortBy,
1288
+ sortDirection: ctx.query?.sortDirection
1289
+ })));
1288
1290
  const seenIds = /* @__PURE__ */ new Set();
1289
- for (const opts of storageGroups.values()) {
1290
- const { apiKeys } = await listApiKeys$1(ctx, referenceId, opts, {
1291
- limit: void 0,
1292
- offset: void 0,
1293
- sortBy: ctx.query?.sortBy,
1294
- sortDirection: ctx.query?.sortDirection
1295
- });
1296
- for (const key of apiKeys) if (!seenIds.has(key.id)) {
1297
- seenIds.add(key.id);
1298
- allApiKeys.push(key);
1299
- }
1291
+ for (const { apiKeys } of groupResults) for (const key of apiKeys) if (!seenIds.has(key.id)) {
1292
+ seenIds.add(key.id);
1293
+ allApiKeys.push(key);
1300
1294
  }
1301
1295
  }
1302
1296
  let filteredApiKeys = allApiKeys.filter((key) => {
@@ -35,6 +35,6 @@ const API_KEY_ERROR_CODES = defineErrorCodes({
35
35
  });
36
36
  //#endregion
37
37
  //#region src/version.ts
38
- const PACKAGE_VERSION = "1.6.5";
38
+ const PACKAGE_VERSION = "1.6.7";
39
39
  //#endregion
40
40
  export { API_KEY_ERROR_CODES as n, PACKAGE_VERSION as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/api-key",
3
- "version": "1.6.5",
3
+ "version": "1.6.7",
4
4
  "description": "API Key plugin for Better Auth.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -61,13 +61,13 @@
61
61
  },
62
62
  "devDependencies": {
63
63
  "tsdown": "0.21.1",
64
- "@better-auth/core": "1.6.5",
65
- "better-auth": "1.6.5"
64
+ "@better-auth/core": "1.6.7",
65
+ "better-auth": "1.6.7"
66
66
  },
67
67
  "peerDependencies": {
68
68
  "@better-auth/utils": "0.4.0",
69
- "@better-auth/core": "^1.6.5",
70
- "better-auth": "^1.6.5"
69
+ "@better-auth/core": "^1.6.7",
70
+ "better-auth": "^1.6.7"
71
71
  },
72
72
  "scripts": {
73
73
  "build": "tsdown",