@datar-platform/better-auth-dynamodb 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +101 -1
- package/README.md +75 -10
- package/dist/index.d.ts +155 -3
- package/dist/index.js +636 -95
- package/dist/index.js.map +1 -1
- package/package.json +11 -8
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapter.ts","../src/pagination.ts","../src/planner.ts","../src/stores/default/derive-index-map.ts","../src/stores/default/single-table-store.ts","../src/stores/default/key-codec.ts","../src/stores/default/schema.ts"],"sourcesContent":["import type { CleanedWhere } from \"@better-auth/core/db/adapter\";\nimport type { BetterAuthOptions } from \"better-auth\";\nimport type { AdapterFactory } from \"better-auth/adapters\";\nimport { createAdapterFactory } from \"better-auth/adapters\";\n\nimport type { QueryPlan } from \"./planner\";\nimport type { DynamoAdapterConfig, DynamoStore, StoreItem } from \"./types\";\nimport {\n applySort,\n applyWindow,\n drainPages,\n matchesResidual,\n} from \"./pagination\";\nimport { planQuery } from \"./planner\";\nimport { deriveIndexMap } from \"./stores/default/derive-index-map\";\nimport { createSingleTableStore } from \"./stores/default/single-table-store\";\n\n/**\n * A generic DynamoDB adapter for Better Auth.\n *\n * The factory handles field mapping, JSON/date/boolean coercion, ID generation\n * and `where`-clause normalization; this adapter only implements the raw DB\n * operations. Those operations are expressed against a pluggable\n * {@link DynamoStore} and a declarative access-pattern map, so the same core\n * drives both the built-in single-table store and any custom store.\n *\n * @example\n * ```ts\n * betterAuth({ database: dynamoAdapter({ tableName: \"auth\", region: \"us-east-1\" }) });\n * ```\n */\nexport const dynamoAdapter = (\n config: DynamoAdapterConfig = {},\n): AdapterFactory<BetterAuthOptions> => {\n return createAdapterFactory({\n config: {\n adapterId: \"dynamodb\",\n adapterName: \"DynamoDB\",\n // DynamoDB keys are strings; let Better Auth generate string ids.\n supportsNumericIds: false,\n // We serialize JSON ourselves via the SDK document client.\n supportsJSON: true,\n // No native Date type — stored as ISO strings (see transforms below).\n supportsDates: false,\n supportsBooleans: true,\n usePlural: false,\n debugLogs: config.debugLogs,\n\n // Dates <-> ISO strings, since supportsDates is false.\n customTransformInput: ({ data, fieldAttributes }) =>\n fieldAttributes.type === \"date\" && data instanceof Date\n ? data.toISOString()\n : data,\n customTransformOutput: ({ data, fieldAttributes }) =>\n fieldAttributes.type === \"date\" && typeof data === \"string\"\n ? new Date(data)\n : data,\n },\n\n adapter: ({ schema, debugLog, getDefaultModelName }) => {\n const indexMap = config.indexMap ?? deriveIndexMap(schema);\n const store: DynamoStore =\n config.store ??\n createSingleTableStore({\n tableName: config.tableName,\n region: config.region,\n endpoint: config.endpoint,\n indexMap,\n });\n\n /** Resolve every row matching `where`, using the best access path. */\n const queryAll = async (\n model: string,\n where?: CleanedWhere[],\n ): Promise<StoreItem[]> => {\n const clauses = where ?? [];\n const plan = planQuery(model, clauses, indexMap);\n debugLog(\"queryAll\", { model, plan: plan.kind });\n\n let candidates: StoreItem[];\n if (plan.kind === \"byId\") {\n const one = await store.getById(model, plan.id);\n candidates = one ? [one] : [];\n } else if (plan.kind === \"index\") {\n candidates = await drainPages((cursor) =>\n store.queryIndex({\n model,\n index: plan.index,\n key: plan.key,\n cursor,\n }),\n );\n } else {\n candidates = await drainPages((cursor) =>\n store.listByType({ model, cursor }),\n );\n }\n return candidates.filter((item) =>\n matchesResidual(item, plan.residual),\n );\n };\n\n /** Find the primary id for a `where`, cheaply if it names `id` directly. */\n const resolveId = async (\n model: string,\n where: CleanedWhere[],\n ): Promise<string | null> => {\n const plan: QueryPlan = planQuery(model, where, indexMap);\n if (plan.kind === \"byId\") return plan.id;\n const [first] = await queryAll(model, where);\n return first ? String(first.id) : null;\n };\n\n return {\n async create<T extends Record<string, any>>({\n model,\n data,\n }: {\n model: string;\n data: T;\n }) {\n const m = getDefaultModelName(model);\n const created = await store.put(m, data);\n return created as T;\n },\n\n async update<T>({\n model,\n where,\n update,\n }: {\n model: string;\n where: CleanedWhere[];\n update: T;\n }) {\n const m = getDefaultModelName(model);\n const id = await resolveId(m, where);\n if (!id) return null;\n const updated = await store.update(m, id, update as StoreItem);\n return updated as T | null;\n },\n\n async updateMany({\n model,\n where,\n update,\n }: {\n model: string;\n where: CleanedWhere[];\n update: Record<string, any>;\n }) {\n const m = getDefaultModelName(model);\n const items = await queryAll(m, where);\n await runBatched(items, (item) =>\n store.update(m, String(item.id), update),\n );\n return items.length;\n },\n\n async delete({\n model,\n where,\n }: {\n model: string;\n where: CleanedWhere[];\n }) {\n const m = getDefaultModelName(model);\n const id = await resolveId(m, where);\n if (id) await store.deleteById(m, id);\n },\n\n async deleteMany({\n model,\n where,\n }: {\n model: string;\n where: CleanedWhere[];\n }) {\n const m = getDefaultModelName(model);\n const items = await queryAll(m, where);\n await runBatched(items, (item) =>\n store.deleteById(m, String(item.id)),\n );\n return items.length;\n },\n\n async findOne<T>({\n model,\n where,\n }: {\n model: string;\n where: CleanedWhere[];\n }) {\n const m = getDefaultModelName(model);\n const [first] = await queryAll(m, where);\n return (first ?? null) as T | null;\n },\n\n async findMany<T>({\n model,\n where,\n limit,\n sortBy,\n offset,\n }: {\n model: string;\n where?: CleanedWhere[];\n limit: number;\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\n offset?: number;\n }) {\n const m = getDefaultModelName(model);\n const items = applyWindow(\n applySort(await queryAll(m, where), sortBy),\n offset,\n limit,\n );\n return items as T[];\n },\n\n async count({\n model,\n where,\n }: {\n model: string;\n where?: CleanedWhere[];\n }) {\n const m = getDefaultModelName(model);\n const clauses = where ?? [];\n const plan = planQuery(m, clauses, indexMap);\n // Fast path: no residual predicates and the store can count natively.\n if (plan.residual.length === 0 && store.count) {\n const fast = await store.count(\n plan.kind === \"index\"\n ? { model: m, index: plan.index, key: plan.key }\n : { model: m },\n );\n if (fast != null) return fast;\n }\n return (await queryAll(m, clauses)).length;\n },\n\n async consumeOne<T>({\n model,\n where,\n }: {\n model: string;\n where: CleanedWhere[];\n }) {\n const m = getDefaultModelName(model);\n const id = await resolveId(m, where);\n if (!id) return null;\n if (store.consumeOne) return (await store.consumeOne(m, id)) as T | null;\n // Fallback for stores without a native atomic delete-and-return: not\n // safe under true concurrency, but correct for the common single\n // in-flight verification case.\n const item = await store.getById(m, id);\n if (!item) return null;\n await store.deleteById(m, id);\n return item as T;\n },\n\n async incrementOne<T>({\n model,\n where,\n increment,\n set,\n }: {\n model: string;\n where: CleanedWhere[];\n increment: Record<string, number>;\n set?: Record<string, any>;\n }) {\n const m = getDefaultModelName(model);\n const id = await resolveId(m, where);\n if (!id) return null;\n if (store.incrementOne) {\n return (await store.incrementOne(m, id, { increment, set })) as\n | T\n | null;\n }\n // Fallback for stores without a native atomic add/set: not safe\n // under true concurrency, but correct for the common single\n // in-flight update case.\n const current = await store.getById(m, id);\n if (!current) return null;\n const patch: StoreItem = { ...set };\n for (const [field, delta] of Object.entries(increment)) {\n patch[field] = (Number(current[field]) || 0) + delta;\n }\n return (await store.update(m, id, patch)) as T | null;\n },\n\n ...(store.createSchema\n ? {\n createSchema: (props: { file?: string; tables: unknown }) =>\n store.createSchema!(props),\n }\n : {}),\n };\n },\n });\n};\n\n/** Run an async op over items in small concurrent chunks to avoid throttling. */\nasync function runBatched<T>(\n items: T[],\n op: (item: T) => Promise<unknown>,\n chunkSize = 10,\n): Promise<void> {\n for (let i = 0; i < items.length; i += chunkSize) {\n await Promise.all(items.slice(i, i + chunkSize).map(op));\n }\n}\n","import type { CleanedWhere } from \"@better-auth/core/db/adapter\";\n\nimport type { QueryPage, StoreItem } from \"./types\";\n\n/**\n * Drain every page of a paginated store query into a single array.\n *\n * DynamoDB paginates at 1 MB / index-page boundaries regardless of match count,\n * so a single `.query()` can silently return a partial result. Draining here —\n * rather than trusting one page — is what makes `findMany`/`count` correct\n * (and removes the old adapter's implicit 10 000-row cap).\n */\nexport async function drainPages(\n fetch: (cursor: unknown) => Promise<QueryPage>,\n): Promise<StoreItem[]> {\n const out: StoreItem[] = [];\n let cursor: unknown = undefined;\n do {\n const page = await fetch(cursor);\n out.push(...page.items);\n cursor = page.cursor;\n } while (cursor);\n return out;\n}\n\nfunction matchOne(item: StoreItem, w: CleanedWhere): boolean {\n const actual = item[w.field];\n const expected = w.value;\n switch (w.operator) {\n case \"eq\":\n return actual === expected;\n case \"ne\":\n return actual !== expected;\n case \"lt\":\n return actual < (expected as any);\n case \"lte\":\n return actual <= (expected as any);\n case \"gt\":\n return actual > (expected as any);\n case \"gte\":\n return actual >= (expected as any);\n case \"in\":\n return Array.isArray(expected) && (expected as any[]).includes(actual);\n case \"not_in\":\n return Array.isArray(expected) && !(expected as any[]).includes(actual);\n case \"contains\":\n return String(actual).includes(String(expected));\n case \"starts_with\":\n return String(actual).startsWith(String(expected));\n case \"ends_with\":\n return String(actual).endsWith(String(expected));\n default:\n return actual === expected;\n }\n}\n\n/**\n * Apply the residual `where` clauses in memory, honoring Better Auth's\n * connector semantics: all `AND` clauses must match, and — if any `OR` clauses\n * are present — at least one of them must also match. An empty residual passes.\n */\nexport function matchesResidual(\n item: StoreItem,\n residual: CleanedWhere[],\n): boolean {\n const andOk = residual\n .filter((w) => w.connector !== \"OR\")\n .every((w) => matchOne(item, w));\n if (!andOk) return false;\n\n const or = residual.filter((w) => w.connector === \"OR\");\n return or.length === 0 || or.some((w) => matchOne(item, w));\n}\n\n/** Stable in-memory sort matching Better Auth's `sortBy` contract. */\nexport function applySort(\n items: StoreItem[],\n sortBy?: { field: string; direction: \"asc\" | \"desc\" },\n): StoreItem[] {\n if (!sortBy) return items;\n const { field, direction } = sortBy;\n const dir = direction === \"asc\" ? 1 : -1;\n return [...items].sort((a, b) => {\n if (a[field] < b[field]) return -dir;\n if (a[field] > b[field]) return dir;\n return 0;\n });\n}\n\n/** Apply `offset`/`limit` (a `limit` of 0 or undefined means \"no cap\"). */\nexport function applyWindow(\n items: StoreItem[],\n offset?: number,\n limit?: number,\n): StoreItem[] {\n let out = items;\n if (offset && offset > 0) out = out.slice(offset);\n if (limit && limit > 0) out = out.slice(0, limit);\n return out;\n}\n","import type { CleanedWhere } from \"@better-auth/core/db/adapter\";\n\nimport type { IndexMap } from \"./index-map\";\nimport type { StoreItem } from \"./types\";\n\n/**\n * A resolved query plan: how the adapter will fetch candidate rows for a\n * `where` clause before applying any residual (in-memory) filtering.\n *\n * - `byId` — a direct primary-key get (the cheapest path).\n * - `index` — a logical index query with a resolved partition/sort key.\n * - `listByType`— no index matched; list all rows of the model and filter.\n *\n * `residual` holds the `where` clauses NOT satisfied by the chosen access path;\n * they are applied in memory by {@link matchesResidual}.\n */\nexport type QueryPlan =\n | { kind: \"byId\"; id: string; residual: CleanedWhere[] }\n | { kind: \"index\"; index: string; key: StoreItem; residual: CleanedWhere[] }\n | { kind: \"listByType\"; residual: CleanedWhere[] };\n\n/** A clause usable for index selection: `eq`, `AND`-connected. */\nfunction isKeyable(w: CleanedWhere): boolean {\n return w.operator === \"eq\" && w.connector === \"AND\" && w.value != null;\n}\n\n/**\n * Choose the most selective access path for `where` against a model's declared\n * access patterns. Falls back to `listByType` when nothing matches.\n *\n * Index selection only ever consumes `eq`/`AND` clauses; everything else (and\n * any `eq` clause not part of the chosen key) becomes residual.\n */\nexport function planQuery(\n model: string,\n where: CleanedWhere[],\n indexMap: IndexMap,\n): QueryPlan {\n const eqByField = new Map<string, CleanedWhere>();\n for (const w of where) {\n if (isKeyable(w) && !eqByField.has(w.field)) eqByField.set(w.field, w);\n }\n\n // Cheapest: a direct id lookup. Everything else filters in memory.\n const idClause = eqByField.get(\"id\");\n if (idClause) {\n return {\n kind: \"byId\",\n id: String(idClause.value),\n residual: where.filter((w) => w !== idClause),\n };\n }\n\n const patterns = indexMap[model] ?? [];\n for (const pattern of patterns) {\n if (!pattern.pk.every((f) => eqByField.has(f))) continue;\n\n const key: StoreItem = {};\n const consumed = new Set<CleanedWhere>();\n for (const f of pattern.pk) {\n const clause = eqByField.get(f)!;\n key[f] = clause.value;\n consumed.add(clause);\n }\n // Attach as many leading sort-key fields as are present (prefix match).\n for (const f of pattern.sk ?? []) {\n const clause = eqByField.get(f);\n if (!clause) break;\n key[f] = clause.value;\n consumed.add(clause);\n }\n\n return {\n kind: \"index\",\n index: pattern.index,\n key,\n residual: where.filter((w) => !consumed.has(w)),\n };\n }\n\n return { kind: \"listByType\", residual: where };\n}\n","import type { BetterAuthDBSchema } from \"@better-auth/core/db\";\n\nimport type { AccessPattern, IndexMap } from \"../../index-map\";\n\n/** Logical index name for a single-field lookup (e.g. `email` -> `by_email`). */\nexport function lookupIndexName(field: string): string {\n return `by_${field}`;\n}\n\n/**\n * Derive a logical access-pattern map from a Better Auth schema.\n *\n * A single-field lookup index is created for every field that is either:\n * - `unique` (e.g. `user.email`, `session.token`) — direct lookups, or\n * - a `references` foreign key (e.g. `account.userId`) — by-parent lookups, or\n * - explicitly flagged `index: true`.\n *\n * This is what lets the built-in store serve any Better Auth model or plugin\n * (two-factor, passkey, api-key, …) out of the box: whatever fields the schema\n * marks as looked-up get an index, with no hand-maintained per-model table.\n */\nexport function deriveIndexMap(schema: BetterAuthDBSchema): IndexMap {\n const map: IndexMap = {};\n\n for (const [model, table] of Object.entries(schema)) {\n const patterns: AccessPattern[] = [];\n const seen = new Set<string>();\n\n const add = (field: string) => {\n if (field === \"id\" || seen.has(field)) return;\n seen.add(field);\n patterns.push({ index: lookupIndexName(field), pk: [field] });\n };\n\n // `unique` fields first (most selective), then foreign keys, then `index`.\n for (const [field, attr] of Object.entries(table.fields)) {\n if (attr.unique) add(field);\n }\n for (const [field, attr] of Object.entries(table.fields)) {\n if (attr.references) add(field);\n }\n for (const [field, attr] of Object.entries(table.fields)) {\n if (attr.index) add(field);\n }\n\n if (patterns.length > 0) map[model] = patterns;\n }\n\n return map;\n}\n","import { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\nimport {\n DeleteCommand,\n DynamoDBDocumentClient,\n GetCommand,\n PutCommand,\n QueryCommand,\n UpdateCommand,\n} from \"@aws-sdk/lib-dynamodb\";\n\nimport type { IndexMap } from \"../../index-map\";\nimport type { DynamoStore, QueryPage, StoreItem } from \"../../types\";\nimport type { SlotAssignment } from \"./key-codec\";\nimport {\n assignSlots,\n encodeKeys,\n encodeLookupQuery,\n primaryKey,\n stripReserved,\n TYPE_INDEX,\n TYPE_PK,\n} from \"./key-codec\";\nimport { generateSchemaFile } from \"./schema\";\n\nexport interface SingleTableStoreOptions {\n /** DynamoDB table name. Falls back to `DYNAMODB_TABLE_NAME`, then `\"better-auth\"`. */\n tableName?: string;\n /** AWS region (passed to the DynamoDB client). */\n region?: string;\n /** Endpoint override, e.g. DynamoDB Local or LocalStack (`http://localhost:4566`). */\n endpoint?: string;\n /** Resolved logical access-pattern map (derived or user-supplied). */\n indexMap: IndexMap;\n /** Pre-built document client (used by tests, e.g. against DynamoDB Local). */\n documentClient?: DynamoDBDocumentClient;\n}\n\n/**\n * A zero-dependency (beyond the AWS SDK) DynamoDB store for Better Auth.\n *\n * Uses a single table with a `byType` GSI plus generic lookup GSIs. It owns all\n * physical key encoding via the key codec, so the generic adapter core drives it\n * through logical index names only. Works with any Better Auth model or plugin\n * whose looked-up fields are described by the (typically schema-derived) index\n * map.\n */\nexport function createSingleTableStore(\n opts: SingleTableStoreOptions,\n): DynamoStore {\n const tableName =\n opts.tableName ?? process.env.DYNAMODB_TABLE_NAME ?? \"better-auth\";\n const doc =\n opts.documentClient ??\n DynamoDBDocumentClient.from(\n new DynamoDBClient({\n ...(opts.region ? { region: opts.region } : {}),\n ...(opts.endpoint ? { endpoint: opts.endpoint } : {}),\n }),\n { marshallOptions: { removeUndefinedValues: true } },\n );\n const indexMap = opts.indexMap;\n const assignment: SlotAssignment = assignSlots(indexMap);\n\n const query = async (\n input: Omit<\n ConstructorParameters<typeof QueryCommand>[0],\n \"TableName\" | \"ExclusiveStartKey\"\n >,\n cursor?: unknown,\n countOnly = false,\n ): Promise<{ items: StoreItem[]; count: number; cursor?: unknown }> => {\n const res = await doc.send(\n new QueryCommand({\n TableName: tableName,\n ExclusiveStartKey: cursor as Record<string, unknown> | undefined,\n ...(countOnly ? { Select: \"COUNT\" } : {}),\n ...input,\n }),\n );\n return {\n items: countOnly ? [] : (res.Items ?? []).map((i) => stripReserved(i)!),\n count: res.Count ?? 0,\n cursor: res.LastEvaluatedKey,\n };\n };\n\n return {\n async put(model, item) {\n await doc.send(\n new PutCommand({\n TableName: tableName,\n Item: { ...item, ...encodeKeys(model, item, indexMap, assignment) },\n }),\n );\n return item;\n },\n\n async getById(model, id) {\n const res = await doc.send(\n new GetCommand({ TableName: tableName, Key: primaryKey(model, id) }),\n );\n return stripReserved(res.Item);\n },\n\n async update(model, id, patch) {\n const res = await doc.send(\n new GetCommand({ TableName: tableName, Key: primaryKey(model, id) }),\n );\n const existing = stripReserved(res.Item);\n if (!existing) return null;\n const merged = { ...existing, ...patch };\n await doc.send(\n new PutCommand({\n TableName: tableName,\n Item: {\n ...merged,\n ...encodeKeys(model, merged, indexMap, assignment),\n },\n }),\n );\n return merged;\n },\n\n async deleteById(model, id) {\n await doc.send(\n new DeleteCommand({ TableName: tableName, Key: primaryKey(model, id) }),\n );\n },\n\n async consumeOne(model, id) {\n const res = await doc.send(\n new DeleteCommand({\n TableName: tableName,\n Key: primaryKey(model, id),\n ReturnValues: \"ALL_OLD\",\n }),\n );\n return stripReserved(res.Attributes);\n },\n\n async incrementOne(model, id, { increment, set }) {\n const names: Record<string, string> = {};\n const values: Record<string, unknown> = {};\n const addClauses: string[] = [];\n const setClauses: string[] = [];\n let i = 0;\n for (const [field, delta] of Object.entries(increment)) {\n const nameKey = `#f${i}`;\n const valueKey = `:v${i}`;\n names[nameKey] = field;\n values[valueKey] = delta;\n addClauses.push(`${nameKey} ${valueKey}`);\n i++;\n }\n for (const [field, value] of Object.entries(set ?? {})) {\n const nameKey = `#f${i}`;\n const valueKey = `:v${i}`;\n names[nameKey] = field;\n values[valueKey] = value;\n setClauses.push(`${nameKey} = ${valueKey}`);\n i++;\n }\n const expression = [\n setClauses.length ? `SET ${setClauses.join(\", \")}` : null,\n addClauses.length ? `ADD ${addClauses.join(\", \")}` : null,\n ]\n .filter(Boolean)\n .join(\" \");\n const res = await doc.send(\n new UpdateCommand({\n TableName: tableName,\n Key: primaryKey(model, id),\n UpdateExpression: expression,\n ExpressionAttributeNames: names,\n ExpressionAttributeValues: values,\n ReturnValues: \"ALL_NEW\",\n }),\n );\n return stripReserved(res.Attributes);\n },\n\n async queryIndex({ model, index, key, cursor }): Promise<QueryPage> {\n const lookup = encodeLookupQuery(model, index, key, indexMap, assignment);\n const names: Record<string, string> = { \"#pk\": lookup.pkAttr };\n const values: Record<string, unknown> = { \":pk\": lookup.pkValue };\n let condition = \"#pk = :pk\";\n if (lookup.skPrefix) {\n names[\"#sk\"] = lookup.skAttr;\n values[\":skp\"] = lookup.skPrefix;\n condition += \" AND begins_with(#sk, :skp)\";\n }\n const { items, cursor: next } = await query(\n {\n IndexName: lookup.indexName,\n KeyConditionExpression: condition,\n ExpressionAttributeNames: names,\n ExpressionAttributeValues: values,\n },\n cursor,\n );\n return { items, cursor: next };\n },\n\n async listByType({ model, cursor }): Promise<QueryPage> {\n const { items, cursor: next } = await query(\n {\n IndexName: TYPE_INDEX,\n KeyConditionExpression: \"#tpk = :tpk\",\n ExpressionAttributeNames: { \"#tpk\": TYPE_PK },\n ExpressionAttributeValues: { \":tpk\": model },\n },\n cursor,\n );\n return { items, cursor: next };\n },\n\n async count({ model, index, key }) {\n let base: Parameters<typeof query>[0];\n if (index && key) {\n const lookup = encodeLookupQuery(\n model,\n index,\n key,\n indexMap,\n assignment,\n );\n base = {\n IndexName: lookup.indexName,\n KeyConditionExpression: \"#pk = :pk\",\n ExpressionAttributeNames: { \"#pk\": lookup.pkAttr },\n ExpressionAttributeValues: { \":pk\": lookup.pkValue },\n };\n } else {\n base = {\n IndexName: TYPE_INDEX,\n KeyConditionExpression: \"#tpk = :tpk\",\n ExpressionAttributeNames: { \"#tpk\": TYPE_PK },\n ExpressionAttributeValues: { \":tpk\": model },\n };\n }\n let total = 0;\n let cursor: unknown = undefined;\n do {\n const page = await query(base, cursor, true);\n total += page.count;\n cursor = page.cursor;\n } while (cursor);\n return total;\n },\n\n createSchema: ({ file }) =>\n Promise.resolve(\n generateSchemaFile({\n tableName,\n lookupSlots: assignment.maxSlots,\n file,\n }),\n ),\n };\n}\n","import type { IndexMap } from \"../../index-map\";\nimport type { StoreItem } from \"../../types\";\n\n/**\n * Physical key encoding for the built-in single-table store.\n *\n * All reserved attributes are namespaced under `__ba_` so they never collide\n * with Better Auth field names, and are stripped before items are returned. The\n * table has a `PK`/`SK` primary key, one `byType` GSI (to list a whole model),\n * and N generic lookup GSIs — one physical slot per logical index a model uses.\n */\nexport const PK = \"__ba_pk\";\nexport const SK = \"__ba_sk\";\nexport const TYPE_PK = \"__ba_tpk\";\nexport const TYPE_SK = \"__ba_tsk\";\nexport const TYPE_INDEX = \"byType\";\nconst SK_CONST = \"#\";\nconst RESERVED_PREFIX = \"__ba_\";\n\nexport const gsiName = (slot: number): string => `lookup${slot}`;\nexport const gsiPk = (slot: number): string => `__ba_g${slot}pk`;\nexport const gsiSk = (slot: number): string => `__ba_g${slot}sk`;\n\nconst enc = (v: unknown): string => String(v);\nconst joinValues = (values: unknown[]): string => values.map(enc).join(\"#\");\n\n/** Physical slot assignment: model -> (logical index name -> GSI slot number). */\nexport interface SlotAssignment {\n slots: Record<string, Record<string, number>>;\n maxSlots: number;\n}\n\n/**\n * Assign each logical index to a physical GSI slot. Because indexes on\n * different models never coexist on one item, a model's Nth index always maps\n * to slot N — so the table needs only `max(indexes per model)` lookup GSIs.\n */\nexport function assignSlots(indexMap: IndexMap): SlotAssignment {\n const slots: Record<string, Record<string, number>> = {};\n let maxSlots = 0;\n for (const [model, patterns] of Object.entries(indexMap)) {\n const modelSlots: Record<string, number> = {};\n patterns.forEach((pattern, i) => {\n modelSlots[pattern.index] = i + 1;\n });\n slots[model] = modelSlots;\n maxSlots = Math.max(maxSlots, patterns.length);\n }\n return { slots, maxSlots };\n}\n\nexport const primaryKey = (model: string, id: string) => ({\n [PK]: `${model}#${id}`,\n [SK]: SK_CONST,\n});\n\nconst lookupPkValue = (model: string, index: string, values: unknown[]) =>\n `${model}#${index}#${joinValues(values)}`;\n\n/**\n * Compute the reserved key attributes to store alongside an item: the primary\n * key, the type-index key, and a lookup key for every index whose partition\n * fields are all present on the item.\n */\nexport function encodeKeys(\n model: string,\n item: StoreItem,\n indexMap: IndexMap,\n assignment: SlotAssignment,\n): StoreItem {\n const id = String(item.id);\n const keys: StoreItem = {\n ...primaryKey(model, id),\n [TYPE_PK]: model,\n [TYPE_SK]: `${enc(item.createdAt ?? \"\")}#${id}`,\n };\n\n for (const pattern of indexMap[model] ?? []) {\n const pkValues = pattern.pk.map((f) => item[f]);\n if (pkValues.some((v) => v == null)) continue; // index doesn't apply to this row\n const slot = assignment.slots[model]?.[pattern.index];\n if (!slot) continue;\n keys[gsiPk(slot)] = lookupPkValue(model, pattern.index, pkValues);\n const skValues = (pattern.sk ?? []).map((f) => item[f]);\n keys[gsiSk(slot)] = skValues.some((v) => v == null)\n ? `${id}`\n : `${joinValues(skValues)}#${id}`;\n }\n\n return keys;\n}\n\n/** A resolved lookup query against one GSI slot. */\nexport interface LookupQuery {\n indexName: string;\n pkAttr: string;\n pkValue: string;\n skAttr: string;\n /** When set, narrow with `begins_with(skAttr, skPrefix)`. */\n skPrefix?: string;\n}\n\n/**\n * Translate a logical index query into a concrete GSI query. Uses the\n * partition key always, and a `begins_with` sort-key prefix when the caller\n * supplied any leading sort-key field values.\n */\nexport function encodeLookupQuery(\n model: string,\n index: string,\n key: StoreItem,\n indexMap: IndexMap,\n assignment: SlotAssignment,\n): LookupQuery {\n const slot = assignment.slots[model]?.[index];\n if (!slot) {\n throw new Error(\n `No physical slot for index \"${index}\" on model \"${model}\". ` +\n `Ensure the index is present in the adapter's index map.`,\n );\n }\n const pattern = (indexMap[model] ?? []).find((p) => p.index === index)!;\n const pkValues = pattern.pk.map((f) => key[f]);\n const skPresent = (pattern.sk ?? [])\n .map((f) => key[f])\n .filter((v) => v != null);\n\n return {\n indexName: gsiName(slot),\n pkAttr: gsiPk(slot),\n pkValue: lookupPkValue(model, index, pkValues),\n skAttr: gsiSk(slot),\n skPrefix: skPresent.length > 0 ? `${joinValues(skPresent)}#` : undefined,\n };\n}\n\n/** Remove all reserved (`__ba_`) attributes, yielding clean Better Auth data. */\nexport function stripReserved(\n item: StoreItem | undefined | null,\n): StoreItem | null {\n if (!item) return null;\n const clean: StoreItem = {};\n for (const [k, v] of Object.entries(item)) {\n if (!k.startsWith(RESERVED_PREFIX)) clean[k] = v;\n }\n return clean;\n}\n","import type {\n CreateTableCommandInput,\n DynamoDBClient,\n} from \"@aws-sdk/client-dynamodb\";\nimport {\n CreateTableCommand,\n ResourceInUseException,\n waitUntilTableExists,\n} from \"@aws-sdk/client-dynamodb\";\n\nimport {\n gsiName,\n gsiPk,\n gsiSk,\n PK,\n SK,\n TYPE_INDEX,\n TYPE_PK,\n TYPE_SK,\n} from \"./key-codec\";\n\n/** Build the CreateTable input for the built-in store's single-table layout. */\nexport function buildTableDefinition(\n tableName: string,\n lookupSlots: number,\n): CreateTableCommandInput {\n const attr = (name: string) => ({\n AttributeName: name,\n AttributeType: \"S\" as const,\n });\n\n const attributeDefinitions = [\n attr(PK),\n attr(SK),\n attr(TYPE_PK),\n attr(TYPE_SK),\n ];\n const globalSecondaryIndexes = [\n {\n IndexName: TYPE_INDEX,\n KeySchema: [\n { AttributeName: TYPE_PK, KeyType: \"HASH\" as const },\n { AttributeName: TYPE_SK, KeyType: \"RANGE\" as const },\n ],\n Projection: { ProjectionType: \"ALL\" as const },\n },\n ];\n\n for (let slot = 1; slot <= lookupSlots; slot++) {\n attributeDefinitions.push(attr(gsiPk(slot)), attr(gsiSk(slot)));\n globalSecondaryIndexes.push({\n IndexName: gsiName(slot),\n KeySchema: [\n { AttributeName: gsiPk(slot), KeyType: \"HASH\" as const },\n { AttributeName: gsiSk(slot), KeyType: \"RANGE\" as const },\n ],\n Projection: { ProjectionType: \"ALL\" as const },\n });\n }\n\n return {\n TableName: tableName,\n BillingMode: \"PAY_PER_REQUEST\",\n KeySchema: [\n { AttributeName: PK, KeyType: \"HASH\" },\n { AttributeName: SK, KeyType: \"RANGE\" },\n ],\n AttributeDefinitions: attributeDefinitions,\n GlobalSecondaryIndexes: globalSecondaryIndexes,\n };\n}\n\n/**\n * Create the table if it does not already exist and wait until it is active.\n * Idempotent — safe to call at startup or in test `beforeAll` hooks.\n */\nexport async function ensureSchema(opts: {\n client: DynamoDBClient;\n tableName: string;\n lookupSlots: number;\n}): Promise<void> {\n const input = buildTableDefinition(opts.tableName, opts.lookupSlots);\n try {\n await opts.client.send(new CreateTableCommand(input));\n } catch (error) {\n if (!(error instanceof ResourceInUseException)) throw error; // already exists\n }\n await waitUntilTableExists(\n { client: opts.client, maxWaitTime: 60 },\n { TableName: opts.tableName },\n );\n}\n\n/**\n * Better Auth CLI `generate` hook: emit a portable CloudFormation template for\n * the built-in store's table. Table name and lookup-GSI count are configurable\n * (unlike a hardcoded layout), so the generated stack matches the adapter's\n * actual access patterns.\n */\nexport function generateSchemaFile(opts: {\n tableName: string;\n lookupSlots: number;\n file?: string;\n}): { code: string; path: string; overwrite: boolean } {\n const table = buildTableDefinition(opts.tableName, opts.lookupSlots);\n const template = {\n AWSTemplateFormatVersion: \"2010-09-09\",\n Resources: {\n BetterAuthTable: { Type: \"AWS::DynamoDB::Table\", Properties: table },\n },\n };\n const code = `/**\n * Auto-generated DynamoDB CloudFormation template for @datar-platform/better-auth-dynamodb.\n * Table: ${opts.tableName} — ${opts.lookupSlots} lookup GSI(s) + 1 byType GSI.\n * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html\n */\nexport const dynamoDBSchema = ${JSON.stringify(template, null, 2)} as const;\n\nexport default dynamoDBSchema;\n`;\n return {\n code,\n path: opts.file ?? \"dynamodb-cloudformation.ts\",\n overwrite: true,\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/adapter.ts","../src/errors.ts","../src/pagination.ts","../src/planner.ts","../src/stores/default/derive-index-map.ts","../src/stores/default/single-table-store.ts","../src/stores/default/key-codec.ts","../src/stores/default/schema.ts"],"sourcesContent":["import type { CleanedWhere } from \"@better-auth/core/db/adapter\";\nimport type { BetterAuthOptions } from \"better-auth\";\nimport type { AdapterFactory } from \"better-auth/adapters\";\nimport { createAdapterFactory } from \"better-auth/adapters\";\n\nimport { UnsupportedQueryError } from \"./errors\";\nimport type { QueryPlan } from \"./planner\";\nimport type { DynamoAdapterConfig, DynamoStore, StoreItem } from \"./types\";\nimport {\n applySort,\n applyWindow,\n DEFAULT_MAX_PAGES,\n drainPages,\n matchesResidual,\n} from \"./pagination\";\nimport { planQuery } from \"./planner\";\nimport { deriveIndexMap } from \"./stores/default/derive-index-map\";\nimport { createSingleTableStore } from \"./stores/default/single-table-store\";\n\n/**\n * A generic DynamoDB adapter for Better Auth.\n *\n * The factory handles field mapping, JSON/date/boolean coercion, ID generation\n * and `where`-clause normalization; this adapter only implements the raw DB\n * operations. Those operations are expressed against a pluggable\n * {@link DynamoStore} and a declarative access-pattern map, so the same core\n * drives both the built-in single-table store and any custom store.\n *\n * @example\n * ```ts\n * betterAuth({ database: dynamoAdapter({ tableName: \"auth\", region: \"us-east-1\" }) });\n * ```\n */\nexport const dynamoAdapter = (\n config: DynamoAdapterConfig = {},\n): AdapterFactory<BetterAuthOptions> => {\n return createAdapterFactory({\n config: {\n adapterId: \"dynamodb\",\n adapterName: \"DynamoDB\",\n // DynamoDB keys are strings; let Better Auth generate string ids.\n supportsNumericIds: false,\n // We serialize JSON ourselves via the SDK document client.\n supportsJSON: true,\n // No native Date type — stored as ISO strings (see transforms below).\n supportsDates: false,\n supportsBooleans: true,\n usePlural: false,\n debugLogs: config.debugLogs,\n\n // Dates <-> ISO strings, since supportsDates is false.\n customTransformInput: ({ data, fieldAttributes }) =>\n fieldAttributes.type === \"date\" && data instanceof Date\n ? data.toISOString()\n : data,\n customTransformOutput: ({ data, fieldAttributes }) =>\n fieldAttributes.type === \"date\" && typeof data === \"string\"\n ? new Date(data)\n : data,\n },\n\n adapter: ({ schema, debugLog, getDefaultModelName, getFieldName }) => {\n const indexMap = config.indexMap ?? deriveIndexMap(schema);\n const maxPages = config.maxPages ?? DEFAULT_MAX_PAGES;\n const store: DynamoStore =\n config.store ??\n createSingleTableStore({\n tableName: config.tableName,\n region: config.region,\n endpoint: config.endpoint,\n documentClient: config.documentClient,\n atomicUniqueness: config.atomicUniqueness,\n maxPages,\n pageSize: config.pageSize,\n ttl: config.ttl,\n indexMap,\n });\n\n /** Resolve every row matching `where`, using the best access path. */\n const queryAll = async (\n model: string,\n where?: CleanedWhere[],\n ): Promise<StoreItem[]> => {\n const clauses = where ?? [];\n const plan = planQuery(model, clauses, indexMap);\n debugLog(\"queryAll\", { model, plan: plan.kind });\n\n let candidates: StoreItem[];\n if (plan.kind === \"byId\") {\n const one = await store.getById(model, plan.id);\n candidates = one ? [one] : [];\n } else if (plan.kind === \"byIds\") {\n const found = await mapBatched(plan.ids, (id) =>\n store.getById(model, id),\n );\n candidates = found.filter((item): item is StoreItem => item !== null);\n } else if (plan.kind === \"index\") {\n candidates = await drainPages(\n (cursor) =>\n store.queryIndex({\n model,\n index: plan.index,\n key: plan.key,\n cursor,\n }),\n maxPages,\n `index query on \"${model}\"`,\n );\n } else {\n if (!config.unsafeAllowScan) {\n throw new UnsupportedQueryError(\n `better-auth-dynamodb: no index can serve this query on ` +\n `\"${model}\" (${describeWhere(clauses)}), so it would have to ` +\n `read every row of the model and filter in memory. Add the ` +\n `field to the index map — or to the Better Auth schema as ` +\n `\\`unique\\`, \\`references\\`, or \\`index: true\\` — or set ` +\n `\\`unsafeAllowScan: true\\` to accept the cost.`,\n );\n }\n candidates = await drainPages(\n (cursor) => store.listByType({ model, cursor }),\n maxPages,\n `model scan of \"${model}\"`,\n );\n }\n return candidates.filter((item) =>\n matchesResidual(item, plan.residual),\n );\n };\n\n /** Find the primary id for a `where`, cheaply if it names `id` directly. */\n const resolveId = async (\n model: string,\n where: CleanedWhere[],\n ): Promise<string | null> => {\n const plan: QueryPlan = planQuery(model, where, indexMap);\n // The shortcut is only sound when `id` was the *whole* predicate.\n // `where: [id = x, status = \"active\"]` must still check the status, or\n // a guarded update would fire against a row that does not qualify.\n if (plan.kind === \"byId\" && plan.residual.length === 0) return plan.id;\n const [first] = await queryAll(model, where);\n return first ? String(first.id) : null;\n };\n\n /**\n * Narrow rows to the requested fields. Better Auth asks for projection at\n * the API surface; DynamoDB could do it server-side, but only by giving\n * up the reserved key attributes the store needs, so it happens here.\n */\n const project = (\n model: string,\n items: StoreItem[],\n select?: string[],\n ): StoreItem[] => {\n if (!select?.length) return items;\n // `select` arrives in Better Auth's field names; rows are stored under\n // whatever `fieldName` the schema maps them to, so the names have to be\n // translated before picking or every field comes back undefined.\n const stored = select.map((field) => getFieldName({ model, field }));\n return items.map((item) => {\n const picked: StoreItem = {};\n for (const field of stored) {\n if (field in item) picked[field] = item[field];\n }\n return picked;\n });\n };\n\n return {\n async create<T extends Record<string, any>>({\n model,\n data,\n }: {\n model: string;\n data: T;\n }) {\n const m = getDefaultModelName(model);\n const created = await store.put(m, data);\n return created as T;\n },\n\n async update<T>({\n model,\n where,\n update,\n }: {\n model: string;\n where: CleanedWhere[];\n update: T;\n }) {\n const m = getDefaultModelName(model);\n const id = await resolveId(m, where);\n if (!id) return null;\n const updated = await store.update(m, id, update as StoreItem);\n return updated as T | null;\n },\n\n async updateMany({\n model,\n where,\n update,\n }: {\n model: string;\n where: CleanedWhere[];\n update: Record<string, any>;\n }) {\n const m = getDefaultModelName(model);\n const items = await queryAll(m, where);\n await runBatched(items, (item) =>\n store.update(m, String(item.id), update),\n );\n return items.length;\n },\n\n async delete({\n model,\n where,\n }: {\n model: string;\n where: CleanedWhere[];\n }) {\n const m = getDefaultModelName(model);\n const id = await resolveId(m, where);\n if (id) await store.deleteById(m, id);\n },\n\n async deleteMany({\n model,\n where,\n }: {\n model: string;\n where: CleanedWhere[];\n }) {\n const m = getDefaultModelName(model);\n const items = await queryAll(m, where);\n await runBatched(items, (item) =>\n store.deleteById(m, String(item.id)),\n );\n return items.length;\n },\n\n async findOne<T>({\n model,\n where,\n select,\n }: {\n model: string;\n where: CleanedWhere[];\n select?: string[];\n }) {\n const m = getDefaultModelName(model);\n const [first] = project(m, await queryAll(m, where), select);\n return (first ?? null) as T | null;\n },\n\n async findMany<T>({\n model,\n where,\n limit,\n sortBy,\n offset,\n select,\n }: {\n model: string;\n where?: CleanedWhere[];\n limit: number;\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\n offset?: number;\n select?: string[];\n }) {\n const m = getDefaultModelName(model);\n // Projection runs last: sorting by a field the caller did not select\n // must still work.\n const items = project(\n m,\n applyWindow(\n applySort(await queryAll(m, where), sortBy),\n offset,\n limit,\n ),\n select,\n );\n return items as T[];\n },\n\n async count({\n model,\n where,\n }: {\n model: string;\n where?: CleanedWhere[];\n }) {\n const m = getDefaultModelName(model);\n const clauses = where ?? [];\n const plan = planQuery(m, clauses, indexMap);\n // Fast path: no residual predicates and the store can count natively.\n // Only for the two plans a native count actually describes — counting\n // a `byId`/`byIds` plan this way would count the whole model instead.\n if (plan.residual.length === 0 && store.count) {\n if (plan.kind === \"index\") {\n const fast = await store.count({\n model: m,\n index: plan.index,\n key: plan.key,\n });\n if (fast != null) return fast;\n } else if (plan.kind === \"listByType\") {\n const fast = await store.count({ model: m });\n if (fast != null) return fast;\n }\n }\n return (await queryAll(m, clauses)).length;\n },\n\n async consumeOne<T>({\n model,\n where,\n }: {\n model: string;\n where: CleanedWhere[];\n }) {\n const m = getDefaultModelName(model);\n const id = await resolveId(m, where);\n if (!id) return null;\n if (store.consumeOne)\n return (await store.consumeOne(m, id)) as T | null;\n // Fallback for stores without a native atomic delete-and-return: not\n // safe under true concurrency, but correct for the common single\n // in-flight verification case.\n const item = await store.getById(m, id);\n if (!item) return null;\n await store.deleteById(m, id);\n return item as T;\n },\n\n async incrementOne<T>({\n model,\n where,\n increment,\n set,\n }: {\n model: string;\n where: CleanedWhere[];\n increment: Record<string, number>;\n set?: Record<string, any>;\n }) {\n const m = getDefaultModelName(model);\n const id = await resolveId(m, where);\n if (!id) return null;\n if (store.incrementOne) {\n return (await store.incrementOne(m, id, {\n increment,\n set,\n })) as T | null;\n }\n // Fallback for stores without a native atomic add/set: not safe\n // under true concurrency, but correct for the common single\n // in-flight update case.\n const current = await store.getById(m, id);\n if (!current) return null;\n const patch: StoreItem = { ...set };\n for (const [field, delta] of Object.entries(increment)) {\n patch[field] = (Number(current[field]) || 0) + delta;\n }\n return (await store.update(m, id, patch)) as T | null;\n },\n\n ...(store.createSchema\n ? {\n createSchema: (props: { file?: string; tables: unknown }) =>\n store.createSchema!(props),\n }\n : {}),\n };\n },\n });\n};\n\n/** Render a `where` for an error message, without leaking the values. */\nfunction describeWhere(where: CleanedWhere[]): string {\n if (where.length === 0) return \"no where clause\";\n return where.map((w) => `${w.field} ${w.operator}`).join(\", \");\n}\n\n/** Map items through an async op in small concurrent chunks, keeping order. */\nasync function mapBatched<T, R>(\n items: T[],\n op: (item: T) => Promise<R>,\n chunkSize = 10,\n): Promise<R[]> {\n const out: R[] = [];\n for (let i = 0; i < items.length; i += chunkSize) {\n out.push(...(await Promise.all(items.slice(i, i + chunkSize).map(op))));\n }\n return out;\n}\n\n/** Run an async op over items in small concurrent chunks to avoid throttling. */\nasync function runBatched<T>(\n items: T[],\n op: (item: T) => Promise<unknown>,\n chunkSize = 10,\n): Promise<void> {\n for (let i = 0; i < items.length; i += chunkSize) {\n await Promise.all(items.slice(i, i + chunkSize).map(op));\n }\n}\n","/**\n * Error types raised by this adapter.\n *\n * Everything thrown from the adapter core or the built-in store derives from\n * {@link DynamoDBAdapterError}, so a consumer can distinguish \"the adapter\n * refused/failed\" from a raw AWS SDK error escaping the seam.\n */\nexport class DynamoDBAdapterError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"DynamoDBAdapterError\";\n }\n}\n\n/**\n * A write lost a race against a uniqueness constraint — another row already\n * holds the value for a `unique` field (e.g. `user.email`, `organization.slug`).\n */\nexport class UniqueConstraintError extends DynamoDBAdapterError {\n constructor(\n readonly model: string,\n readonly fields: string[],\n options?: ErrorOptions,\n ) {\n super(\n `better-auth-dynamodb: unique constraint violation on \"${model}\" ` +\n `(duplicate value for ${fields.join(\", \")})`,\n options,\n );\n this.name = \"UniqueConstraintError\";\n }\n}\n\n/**\n * The requested access pattern cannot be served without a table/model scan, and\n * scans have not been explicitly enabled.\n */\nexport class UnsupportedQueryError extends DynamoDBAdapterError {\n constructor(message: string) {\n super(message);\n this.name = \"UnsupportedQueryError\";\n }\n}\n\n/**\n * A guarded write was rejected because the row changed between the adapter's\n * read and its write. Better Auth's own retry/null semantics decide what\n * happens next; this exists so the cause is legible rather than an opaque\n * `TransactionCanceledException`.\n */\nexport class OptimisticLockError extends DynamoDBAdapterError {\n constructor(model: string, id: string, options?: ErrorOptions) {\n super(\n `better-auth-dynamodb: concurrent modification of \"${model}\" row \"${id}\" ` +\n `— the record changed between read and write`,\n options,\n );\n this.name = \"OptimisticLockError\";\n }\n}\n\nconst named = (error: unknown, name: string): boolean =>\n typeof error === \"object\" &&\n error !== null &&\n \"name\" in error &&\n (error as { name?: unknown }).name === name;\n\n/** True for a bare `ConditionalCheckFailedException` from a non-transactional write. */\nexport const isConditionalCheckFailed = (error: unknown): boolean =>\n named(error, \"ConditionalCheckFailedException\");\n\n/** True for any cancelled `TransactWriteItems`, whatever the reason. */\nexport const isTransactionCanceled = (error: unknown): boolean =>\n named(error, \"TransactionCanceledException\");\n\n/**\n * The per-action cancellation codes AWS attaches to a cancelled transaction\n * (e.g. `ConditionalCheckFailed`, `TransactionConflict`, `ThrottlingError`,\n * `ValidationError`, or `None` for actions that were fine).\n */\nexport function transactionCancellationCodes(error: unknown): string[] {\n if (\n typeof error !== \"object\" ||\n error === null ||\n !(\"CancellationReasons\" in error)\n ) {\n return [];\n }\n const reasons = (error as { CancellationReasons?: unknown })\n .CancellationReasons;\n if (!Array.isArray(reasons)) return [];\n return reasons\n .map((reason) =>\n typeof reason === \"object\" && reason !== null && \"Code\" in reason\n ? (reason as { Code?: unknown }).Code\n : undefined,\n )\n .filter((code): code is string => typeof code === \"string\");\n}\n\n/**\n * True only when a transaction was cancelled *because a condition failed* —\n * not because of throttling, a transaction conflict, or a validation error.\n *\n * This distinction matters: reporting a throttled write as \"email already\n * taken\" is a user-visible lie, and it is the failure mode of any code that\n * treats `TransactionCanceledException` as a uniqueness violation wholesale.\n */\nexport const isConditionalTransactionCanceled = (error: unknown): boolean =>\n isTransactionCanceled(error) &&\n transactionCancellationCodes(error).includes(\"ConditionalCheckFailed\");\n\n/**\n * True when a transaction was cancelled because another transaction was\n * touching the same item. Transient contention, not a decision about the data —\n * so the right response is to try again, not to give up.\n */\nexport const isTransactionConflict = (error: unknown): boolean =>\n isTransactionCanceled(error) &&\n transactionCancellationCodes(error).includes(\"TransactionConflict\");\n","import type { CleanedWhere } from \"@better-auth/core/db/adapter\";\n\nimport { DynamoDBAdapterError } from \"./errors\";\nimport type { QueryPage, StoreItem } from \"./types\";\n\n/** Default cap on pages drained for one logical query. */\nexport const DEFAULT_MAX_PAGES = 25;\n\n/**\n * Drain every page of a paginated store query into a single array.\n *\n * DynamoDB paginates at 1 MB / index-page boundaries regardless of match count,\n * so a single `.query()` can silently return a partial result. Draining here —\n * rather than trusting one page — is what makes `findMany`/`count` correct\n * (and removes the old adapter's implicit 10 000-row cap).\n *\n * Draining is bounded by `maxPages`. Hitting the cap throws: an auth query that\n * silently returns some of its rows is a correctness bug wearing a success\n * response.\n */\nexport async function drainPages(\n fetch: (cursor: unknown) => Promise<QueryPage>,\n maxPages = DEFAULT_MAX_PAGES,\n label = \"query\",\n): Promise<StoreItem[]> {\n const out: StoreItem[] = [];\n let cursor: unknown = undefined;\n let pages = 0;\n do {\n const page = await fetch(cursor);\n out.push(...page.items);\n cursor = page.cursor;\n if (++pages >= maxPages && cursor) {\n throw new DynamoDBAdapterError(\n `better-auth-dynamodb: ${label} exceeded maxPages (${maxPages}) with ` +\n `more pages remaining. Raise maxPages or narrow the query — ` +\n `returning a partial result here would silently drop rows.`,\n );\n }\n } while (cursor);\n return out;\n}\n\nfunction matchOne(item: StoreItem, w: CleanedWhere): boolean {\n // `mode: \"insensitive\"` asks for case-insensitive comparison. It only has\n // meaning between two strings; anything else keeps exact semantics.\n const fold = (v: unknown): unknown =>\n w.mode === \"insensitive\" && typeof v === \"string\" ? v.toLowerCase() : v;\n\n const actual = fold(item[w.field]) as any;\n const expected = Array.isArray(w.value)\n ? (w.value as unknown[]).map(fold)\n : fold(w.value);\n switch (w.operator) {\n case \"eq\":\n return actual === expected;\n case \"ne\":\n return actual !== expected;\n case \"lt\":\n return actual < (expected as any);\n case \"lte\":\n return actual <= (expected as any);\n case \"gt\":\n return actual > (expected as any);\n case \"gte\":\n return actual >= (expected as any);\n case \"in\":\n return Array.isArray(expected) && expected.includes(actual);\n case \"not_in\":\n return Array.isArray(expected) && !expected.includes(actual);\n case \"contains\":\n return String(actual).includes(String(expected));\n case \"starts_with\":\n return String(actual).startsWith(String(expected));\n case \"ends_with\":\n return String(actual).endsWith(String(expected));\n default:\n return actual === expected;\n }\n}\n\n/**\n * Apply the residual `where` clauses in memory, honoring Better Auth's\n * connector semantics: all `AND` clauses must match, and — if any `OR` clauses\n * are present — at least one of them must also match. An empty residual passes.\n */\nexport function matchesResidual(\n item: StoreItem,\n residual: CleanedWhere[],\n): boolean {\n const andOk = residual\n .filter((w) => w.connector !== \"OR\")\n .every((w) => matchOne(item, w));\n if (!andOk) return false;\n\n const or = residual.filter((w) => w.connector === \"OR\");\n return or.length === 0 || or.some((w) => matchOne(item, w));\n}\n\n/** Stable in-memory sort matching Better Auth's `sortBy` contract. */\nexport function applySort(\n items: StoreItem[],\n sortBy?: { field: string; direction: \"asc\" | \"desc\" },\n): StoreItem[] {\n if (!sortBy) return items;\n const { field, direction } = sortBy;\n const dir = direction === \"asc\" ? 1 : -1;\n return [...items].sort((a, b) => {\n if (a[field] < b[field]) return -dir;\n if (a[field] > b[field]) return dir;\n return 0;\n });\n}\n\n/** Apply `offset`/`limit` (a `limit` of 0 or undefined means \"no cap\"). */\nexport function applyWindow(\n items: StoreItem[],\n offset?: number,\n limit?: number,\n): StoreItem[] {\n let out = items;\n if (offset && offset > 0) out = out.slice(offset);\n if (limit && limit > 0) out = out.slice(0, limit);\n return out;\n}\n","import type { CleanedWhere } from \"@better-auth/core/db/adapter\";\n\nimport type { IndexMap } from \"./index-map\";\nimport type { StoreItem } from \"./types\";\n\n/**\n * A resolved query plan: how the adapter will fetch candidate rows for a\n * `where` clause before applying any residual (in-memory) filtering.\n *\n * - `byId` — a direct primary-key get (the cheapest path).\n * - `byIds` — a bounded set of primary-key gets (`id in [...]`).\n * - `index` — a logical index query with a resolved partition/sort key.\n * - `listByType`— no index matched; list all rows of the model and filter.\n *\n * `residual` holds the `where` clauses NOT satisfied by the chosen access path;\n * they are applied in memory by {@link matchesResidual}.\n */\nexport type QueryPlan =\n | { kind: \"byId\"; id: string; residual: CleanedWhere[] }\n | { kind: \"byIds\"; ids: string[]; residual: CleanedWhere[] }\n | { kind: \"index\"; index: string; key: StoreItem; residual: CleanedWhere[] }\n | { kind: \"listByType\"; residual: CleanedWhere[] };\n\n/**\n * A clause usable for index selection: `eq`, `AND`-connected, case-sensitive.\n *\n * `mode: \"insensitive\"` is deliberately excluded. DynamoDB keys are compared\n * byte-for-byte, so serving a case-insensitive equality from an index would\n * silently miss every row whose casing differs from the query. Those clauses\n * stay residual and are matched in memory instead.\n */\nfunction isKeyable(w: CleanedWhere): boolean {\n return (\n w.operator === \"eq\" &&\n w.connector === \"AND\" &&\n w.value != null &&\n w.mode !== \"insensitive\"\n );\n}\n\n/**\n * Choose the most selective access path for `where` against a model's declared\n * access patterns. Falls back to `listByType` when nothing matches.\n *\n * Index selection only ever consumes `eq`/`AND` clauses; everything else (and\n * any `eq` clause not part of the chosen key) becomes residual.\n */\nexport function planQuery(\n model: string,\n where: CleanedWhere[],\n indexMap: IndexMap,\n): QueryPlan {\n const eqByField = new Map<string, CleanedWhere>();\n for (const w of where) {\n if (isKeyable(w) && !eqByField.has(w.field)) eqByField.set(w.field, w);\n }\n\n // Cheapest: a direct id lookup. Everything else filters in memory.\n const idClause = eqByField.get(\"id\");\n if (idClause) {\n return {\n kind: \"byId\",\n id: String(idClause.value),\n residual: where.filter((w) => w !== idClause),\n };\n }\n\n // `id in [...]` is a bounded set of primary-key gets, not a scan. Better\n // Auth uses it to batch-load rows it already has the ids for (loading an\n // organization's members, for instance), and DynamoDB serves it directly.\n const idIn = where.find(\n (w) =>\n w.field === \"id\" &&\n w.operator === \"in\" &&\n w.connector === \"AND\" &&\n Array.isArray(w.value),\n );\n if (idIn) {\n return {\n kind: \"byIds\",\n ids: (idIn.value as unknown[]).map(String),\n residual: where.filter((w) => w !== idIn),\n };\n }\n\n const patterns = indexMap[model] ?? [];\n for (const pattern of patterns) {\n if (!pattern.pk.every((f) => eqByField.has(f))) continue;\n\n const key: StoreItem = {};\n const consumed = new Set<CleanedWhere>();\n for (const f of pattern.pk) {\n const clause = eqByField.get(f)!;\n key[f] = clause.value;\n consumed.add(clause);\n }\n // Attach as many leading sort-key fields as are present (prefix match).\n for (const f of pattern.sk ?? []) {\n const clause = eqByField.get(f);\n if (!clause) break;\n key[f] = clause.value;\n consumed.add(clause);\n }\n\n return {\n kind: \"index\",\n index: pattern.index,\n key,\n residual: where.filter((w) => !consumed.has(w)),\n };\n }\n\n return { kind: \"listByType\", residual: where };\n}\n","import type { BetterAuthDBSchema } from \"@better-auth/core/db\";\n\nimport type { AccessPattern, IndexMap } from \"../../index-map\";\n\n/** Logical index name for a single-field lookup (e.g. `email` -> `by_email`). */\nexport function lookupIndexName(field: string): string {\n return `by_${field}`;\n}\n\n/**\n * Derive a logical access-pattern map from a Better Auth schema.\n *\n * A single-field lookup index is created for every field that is either:\n * - `unique` (e.g. `user.email`, `session.token`) — direct lookups, or\n * - a `references` foreign key (e.g. `account.userId`) — by-parent lookups, or\n * - explicitly flagged `index: true`.\n *\n * This is what lets the built-in store serve any Better Auth model or plugin\n * (two-factor, passkey, api-key, …) out of the box: whatever fields the schema\n * marks as looked-up get an index, with no hand-maintained per-model table.\n */\nexport function deriveIndexMap(schema: BetterAuthDBSchema): IndexMap {\n const map: IndexMap = {};\n\n for (const [model, table] of Object.entries(schema)) {\n const patterns: AccessPattern[] = [];\n const seen = new Set<string>();\n\n const add = (field: string, unique = false) => {\n if (field === \"id\" || seen.has(field)) return;\n seen.add(field);\n patterns.push({ index: lookupIndexName(field), pk: [field], unique });\n };\n\n // `unique` fields first (most selective), then foreign keys, then `index`.\n for (const [field, attr] of Object.entries(table.fields)) {\n if (attr.unique) add(field, true);\n }\n for (const [field, attr] of Object.entries(table.fields)) {\n if (attr.references) add(field);\n }\n for (const [field, attr] of Object.entries(table.fields)) {\n if (attr.index) add(field);\n }\n\n if (patterns.length > 0) map[model] = patterns;\n }\n\n return map;\n}\n","import { randomUUID } from \"node:crypto\";\n\nimport { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\nimport {\n DeleteCommand,\n DynamoDBDocumentClient,\n GetCommand,\n PutCommand,\n QueryCommand,\n TransactWriteCommand,\n type TransactWriteCommandInput,\n UpdateCommand,\n} from \"@aws-sdk/lib-dynamodb\";\n\nimport {\n DynamoDBAdapterError,\n isConditionalCheckFailed,\n isConditionalTransactionCanceled,\n isTransactionConflict,\n OptimisticLockError,\n UniqueConstraintError,\n} from \"../../errors\";\nimport type { AccessPattern, IndexMap } from \"../../index-map\";\nimport type {\n DynamoStore,\n QueryPage,\n StoreItem,\n TtlOptions,\n} from \"../../types\";\nimport type { SlotAssignment } from \"./key-codec\";\nimport {\n assignSlots,\n DEFAULT_TTL_ATTRIBUTE,\n encodeKeys,\n encodeLookupQuery,\n PK,\n primaryKey,\n REVISION,\n stripReserved,\n TYPE_INDEX,\n TYPE_PK,\n uniqueMarkerKey,\n} from \"./key-codec\";\nimport { generateSchemaFile } from \"./schema\";\n\ntype TransactItem = NonNullable<\n TransactWriteCommandInput[\"TransactItems\"]\n>[number];\n\n/** DynamoDB's hard limit on actions in one `TransactWriteItems` call. */\nconst MAX_TRANSACT_ITEMS = 100;\n\n/** Attempts at a guarded read-modify-write before giving up on the race. */\nconst MAX_WRITE_ATTEMPTS = 3;\n\n/** Default cap on pages drained for one logical query. */\nconst DEFAULT_MAX_PAGES = 25;\n\nexport interface SingleTableStoreOptions {\n /** DynamoDB table name. Falls back to `DYNAMODB_TABLE_NAME`, then `\"better-auth\"`. */\n tableName?: string;\n /** AWS region (passed to the DynamoDB client). */\n region?: string;\n /** Endpoint override, e.g. DynamoDB Local or LocalStack (`http://localhost:4566`). */\n endpoint?: string;\n /** Resolved logical access-pattern map (derived or user-supplied). */\n indexMap: IndexMap;\n /**\n * Pre-built document client. Preferred in production, so your application\n * owns credentials, region, middleware, tracing, and marshalling.\n */\n documentClient?: DynamoDBDocumentClient;\n /**\n * Enforce `unique` schema fields with transactional marker rows. Default\n * `true`. Turning it off halves the write cost of creates and restores\n * Better Auth's application-layer check-then-insert, which can admit\n * duplicates under concurrency.\n */\n atomicUniqueness?: boolean;\n /** Max DynamoDB pages drained for one internal count. Default 25. */\n maxPages?: number;\n /** Optional per-request DynamoDB `Limit`. Does not change logical results. */\n pageSize?: number;\n /** Adapter-managed DynamoDB TTL. Omit (or `false`) to disable entirely. */\n ttl?: TtlOptions | false;\n}\n\nconst epochSeconds = (value: unknown): number | undefined => {\n const date =\n value instanceof Date\n ? value\n : typeof value === \"string\" || typeof value === \"number\"\n ? new Date(value)\n : undefined;\n if (!date || Number.isNaN(date.getTime())) return undefined;\n return Math.floor(date.getTime() / 1000);\n};\n\n/**\n * A zero-dependency (beyond the AWS SDK) DynamoDB store for Better Auth.\n *\n * Uses a single table with a `byType` GSI plus generic lookup GSIs. It owns all\n * physical key encoding via the key codec, so the generic adapter core drives it\n * through logical index names only. Works with any Better Auth model or plugin\n * whose looked-up fields are described by the (typically schema-derived) index\n * map.\n *\n * Every mutation is guarded: creates are conditional on the row not already\n * existing, updates and deletes carry an optimistic revision check, and\n * uniqueness markers move in the same transaction as the row they describe.\n */\nexport function createSingleTableStore(\n opts: SingleTableStoreOptions,\n): DynamoStore {\n const tableName =\n opts.tableName ?? process.env.DYNAMODB_TABLE_NAME ?? \"better-auth\";\n const doc =\n opts.documentClient ??\n DynamoDBDocumentClient.from(\n new DynamoDBClient({\n ...(opts.region ? { region: opts.region } : {}),\n ...(opts.endpoint ? { endpoint: opts.endpoint } : {}),\n }),\n { marshallOptions: { removeUndefinedValues: true } },\n );\n const indexMap = opts.indexMap;\n const assignment: SlotAssignment = assignSlots(indexMap);\n const atomicUniqueness = opts.atomicUniqueness ?? true;\n const maxPages = opts.maxPages ?? DEFAULT_MAX_PAGES;\n const ttl = opts.ttl === false ? undefined : opts.ttl;\n const ttlAttribute = ttl?.attributeName ?? DEFAULT_TTL_ATTRIBUTE;\n\n // ---------------------------------------------------------------- TTL ----\n\n const ttlFieldFor = (model: string): string | undefined =>\n ttl ? (ttl.fields?.[model] ?? ttl.defaultField) : undefined;\n\n const ttlAttributesFor = (model: string, item: StoreItem): StoreItem => {\n const field = ttlFieldFor(model);\n if (!field) return {};\n const seconds = epochSeconds(item[field]);\n return seconds === undefined ? {} : { [ttlAttribute]: seconds };\n };\n\n /**\n * DynamoDB reaps expired rows lazily — up to a couple of days late. Treating\n * the TTL attribute as logical expiry on read is what makes an expired\n * session unusable the moment it expires rather than whenever AWS gets round\n * to the delete.\n */\n const isExpired = (item: StoreItem | undefined | null): boolean => {\n if (!item || !ttl) return false;\n const seconds = item[ttlAttribute];\n return (\n typeof seconds === \"number\" && seconds <= Math.floor(Date.now() / 1000)\n );\n };\n\n const modelHasTtl = (model: string): boolean => Boolean(ttlFieldFor(model));\n\n // ----------------------------------------------------------- uniqueness --\n\n const uniquePatternsFor = (model: string): AccessPattern[] =>\n atomicUniqueness ? (indexMap[model] ?? []).filter((p) => p.unique) : [];\n\n const uniqueFieldNames = (model: string): string[] =>\n uniquePatternsFor(model).flatMap((p) => p.pk);\n\n const markerItem = (\n model: string,\n pattern: AccessPattern,\n values: unknown[],\n ownerId: string,\n source: StoreItem,\n ): StoreItem => ({\n ...uniqueMarkerKey(model, pattern.index, values),\n __ba_owner: ownerId,\n ...ttlAttributesFor(model, source),\n });\n\n const applicableValues = (\n pattern: AccessPattern,\n item: StoreItem,\n ): unknown[] | null => {\n const values = pattern.pk.map((f) => item[f]);\n return values.some((v) => v == null) ? null : values;\n };\n\n const uniqueMarkerPuts = (\n model: string,\n item: StoreItem,\n id: string,\n ): TransactItem[] =>\n uniquePatternsFor(model).flatMap((pattern) => {\n const values = applicableValues(pattern, item);\n if (!values) return [];\n return [\n {\n Put: {\n TableName: tableName,\n Item: markerItem(model, pattern, values, id, item),\n ConditionExpression: \"attribute_not_exists(#pk)\",\n ExpressionAttributeNames: { \"#pk\": PK },\n },\n },\n ];\n });\n\n const uniqueMarkerDeletes = (\n model: string,\n item: StoreItem | null,\n ): TransactItem[] => {\n if (!item) return [];\n return uniquePatternsFor(model).flatMap((pattern) => {\n const values = applicableValues(pattern, item);\n if (!values) return [];\n return [\n {\n Delete: {\n TableName: tableName,\n Key: uniqueMarkerKey(model, pattern.index, values),\n },\n },\n ];\n });\n };\n\n /**\n * Marker moves for an update. A pattern whose value did not change is\n * skipped: deleting and re-putting the same key inside one transaction is a\n * self-conflict DynamoDB rejects outright.\n */\n const uniqueMarkerDiff = (\n model: string,\n before: StoreItem,\n after: StoreItem,\n id: string,\n ): TransactItem[] => {\n const items: TransactItem[] = [];\n for (const pattern of uniquePatternsFor(model)) {\n const oldValues = applicableValues(pattern, before);\n const newValues = applicableValues(pattern, after);\n if (\n oldValues?.length === newValues?.length &&\n oldValues?.every((v, i) => v === newValues![i])\n ) {\n continue;\n }\n if (oldValues) {\n items.push({\n Delete: {\n TableName: tableName,\n Key: uniqueMarkerKey(model, pattern.index, oldValues),\n },\n });\n }\n if (newValues) {\n items.push({\n Put: {\n TableName: tableName,\n Item: markerItem(model, pattern, newValues, id, after),\n ConditionExpression: \"attribute_not_exists(#pk)\",\n ExpressionAttributeNames: { \"#pk\": PK },\n },\n });\n }\n }\n return items;\n };\n\n // ---------------------------------------------------------- transactions --\n\n const keyOf = (action: TransactItem): string => {\n const target =\n action.Put?.Item ?? action.Delete?.Key ?? action.Update?.Key ?? {};\n return `${String((target as StoreItem)[PK])}`;\n };\n\n /**\n * DynamoDB rejects a transaction that touches the same item twice, and caps\n * one at 100 actions. Both produce opaque server-side errors, so they are\n * checked here where the cause is still known.\n */\n const validateTransaction = (model: string, items: TransactItem[]): void => {\n if (items.length > MAX_TRANSACT_ITEMS) {\n throw new DynamoDBAdapterError(\n `better-auth-dynamodb: a single write to \"${model}\" would need ` +\n `${items.length} transaction actions, over DynamoDB's ` +\n `${MAX_TRANSACT_ITEMS}-action limit. Reduce the number of unique ` +\n `fields on this model.`,\n );\n }\n const seen = new Set<string>();\n for (const item of items) {\n const key = keyOf(item);\n if (seen.has(key)) {\n throw new DynamoDBAdapterError(\n `better-auth-dynamodb: internal error — two actions in one ` +\n `transaction target the same item on \"${model}\". Please report this.`,\n );\n }\n seen.add(key);\n }\n };\n\n const sendTransaction = async (\n model: string,\n items: TransactItem[],\n ): Promise<void> => {\n validateTransaction(model, items);\n await doc.send(\n new TransactWriteCommand({\n TransactItems: items,\n // Gives the SDK's internal retries of this one send a stable token.\n // Not cross-invocation idempotency.\n ClientRequestToken: randomUUID(),\n }),\n );\n };\n\n const asUniqueConflict = (model: string, err: unknown): never => {\n if (isConditionalTransactionCanceled(err)) {\n throw new UniqueConstraintError(model, uniqueFieldNames(model), {\n cause: err,\n });\n }\n throw err;\n };\n\n // ------------------------------------------------------ revision guards --\n\n /** Read the raw (reserved attributes intact) row — needed for its revision. */\n const readRaw = async (\n model: string,\n id: string,\n ): Promise<StoreItem | null> => {\n const res = await doc.send(\n new GetCommand({\n TableName: tableName,\n Key: primaryKey(model, id),\n ConsistentRead: true,\n }),\n );\n if (!res.Item || isExpired(res.Item)) return null;\n return res.Item as StoreItem;\n };\n\n /**\n * Condition that the row still carries the revision we read. Rows written\n * before revisions existed are matched on the revision being absent, so they\n * migrate in place on their first write rather than becoming unmutatable.\n */\n const revisionGuard = (existing: StoreItem) => {\n const revision = existing[REVISION];\n return revision === undefined\n ? {\n ConditionExpression:\n \"attribute_exists(#pk) AND attribute_not_exists(#rev)\",\n ExpressionAttributeNames: { \"#pk\": PK, \"#rev\": REVISION },\n }\n : {\n ConditionExpression: \"attribute_exists(#pk) AND #rev = :rev\",\n ExpressionAttributeNames: { \"#pk\": PK, \"#rev\": REVISION },\n ExpressionAttributeValues: { \":rev\": revision },\n };\n };\n\n /** The write was refused by a condition we set, rather than failing outright. */\n const rejectedByGuard = (err: unknown): boolean =>\n isConditionalCheckFailed(err) || isConditionalTransactionCanceled(err);\n\n // ---------------------------------------------------------------- reads --\n\n const query = async (\n input: Omit<\n ConstructorParameters<typeof QueryCommand>[0],\n \"TableName\" | \"ExclusiveStartKey\"\n >,\n cursor?: unknown,\n countOnly = false,\n ): Promise<{ items: StoreItem[]; count: number; cursor?: unknown }> => {\n const res = await doc.send(\n new QueryCommand({\n TableName: tableName,\n ExclusiveStartKey: cursor as Record<string, unknown> | undefined,\n ...(countOnly ? { Select: \"COUNT\" } : {}),\n ...(opts.pageSize ? { Limit: opts.pageSize } : {}),\n ...input,\n }),\n );\n return {\n items: countOnly\n ? []\n : (res.Items ?? [])\n .filter((item) => !isExpired(item as StoreItem))\n .map((i) => stripReserved(i)!),\n count: res.Count ?? 0,\n cursor: res.LastEvaluatedKey,\n };\n };\n\n // --------------------------------------------------------------- writes --\n\n const putEntity = (model: string, item: StoreItem): TransactItem => ({\n Put: {\n TableName: tableName,\n Item: {\n ...item,\n ...encodeKeys(model, item, indexMap, assignment),\n ...ttlAttributesFor(model, item),\n [REVISION]: randomUUID(),\n },\n ConditionExpression: \"attribute_not_exists(#pk)\",\n ExpressionAttributeNames: { \"#pk\": PK },\n },\n });\n\n /**\n * Guarded read-modify-write. `build` turns the current raw row into the\n * transaction that replaces it; a lost race re-reads and tries again rather\n * than surfacing a spurious failure for what is usually a benign interleave.\n */\n const guardedWrite = async <T>(\n model: string,\n id: string,\n build: (existing: StoreItem) => { items: TransactItem[]; result: T },\n onMissing: () => T,\n ): Promise<T> => {\n for (let attempt = 1; attempt <= MAX_WRITE_ATTEMPTS; attempt++) {\n const existing = await readRaw(model, id);\n if (!existing) return onMissing();\n const { items, result } = build(existing);\n try {\n await sendTransaction(model, items);\n return result;\n } catch (err) {\n // Two retryable shapes: our own guard refused the write because the row\n // moved, or DynamoDB hit contention on the item. Anything else is real.\n const guarded = rejectedByGuard(err);\n if (!guarded && !isTransactionConflict(err)) throw err;\n // A uniqueness marker collision is terminal; a stale revision is not.\n if (guarded && (await lostToUniqueness(model, id, existing))) {\n return asUniqueConflict(model, err);\n }\n if (attempt === MAX_WRITE_ATTEMPTS) {\n throw new OptimisticLockError(model, id, { cause: err });\n }\n }\n }\n /* c8 ignore next */\n throw new OptimisticLockError(model, id);\n };\n\n /**\n * Distinguish the two reasons a guarded write is cancelled: the row moved\n * under us (retryable), or a unique marker is held by somebody else\n * (terminal). DynamoDB reports both as `ConditionalCheckFailed`, so the row\n * is re-read to tell them apart.\n */\n const lostToUniqueness = async (\n model: string,\n id: string,\n previous: StoreItem,\n ): Promise<boolean> => {\n if (uniquePatternsFor(model).length === 0) return false;\n const current = await readRaw(model, id);\n // The revision is unchanged, so the row did not move — the marker did.\n return Boolean(current) && current![REVISION] === previous[REVISION];\n };\n\n return {\n async put(model, item) {\n const id = String(item.id);\n const entity = putEntity(model, item);\n const markers = uniqueMarkerPuts(model, item, id);\n\n try {\n if (markers.length === 0) {\n await doc.send(new PutCommand({ ...entity.Put! }));\n } else {\n await sendTransaction(model, [entity, ...markers]);\n }\n } catch (err) {\n if (isConditionalCheckFailed(err)) {\n throw new DynamoDBAdapterError(\n `better-auth-dynamodb: a \"${model}\" row with id \"${id}\" already exists.`,\n { cause: err },\n );\n }\n asUniqueConflict(model, err);\n }\n return item;\n },\n\n async getById(model, id) {\n const raw = await readRaw(model, id);\n return stripReserved(raw);\n },\n\n async update(model, id, patch) {\n return guardedWrite<StoreItem | null>(\n model,\n id,\n (existing) => {\n const clean = stripReserved(existing)!;\n const merged = { ...clean, ...patch };\n return {\n items: [\n {\n Put: {\n TableName: tableName,\n Item: {\n ...merged,\n ...encodeKeys(model, merged, indexMap, assignment),\n ...ttlAttributesFor(model, merged),\n [REVISION]: randomUUID(),\n },\n ...revisionGuard(existing),\n },\n },\n ...uniqueMarkerDiff(model, clean, merged, id),\n ],\n result: merged,\n };\n },\n () => null,\n );\n },\n\n async deleteById(model, id) {\n await guardedWrite<void>(\n model,\n id,\n (existing) => ({\n items: [\n {\n Delete: {\n TableName: tableName,\n Key: primaryKey(model, id),\n ...revisionGuard(existing),\n },\n },\n ...uniqueMarkerDeletes(model, stripReserved(existing)),\n ],\n result: undefined,\n }),\n () => undefined,\n );\n },\n\n async consumeOne(model, id) {\n // With no markers to move, a single conditional delete is already atomic\n // and returns what it removed — no read needed.\n if (uniquePatternsFor(model).length === 0) {\n const res = await doc.send(\n new DeleteCommand({\n TableName: tableName,\n Key: primaryKey(model, id),\n ReturnValues: \"ALL_OLD\",\n }),\n );\n const item = res.Attributes as StoreItem | undefined;\n return isExpired(item) ? null : stripReserved(item);\n }\n\n // Otherwise the revision guard is what makes exactly one caller win: the\n // loser's condition fails against an already-deleted row.\n const existing = await readRaw(model, id);\n if (!existing) return null;\n const clean = stripReserved(existing)!;\n try {\n await sendTransaction(model, [\n {\n Delete: {\n TableName: tableName,\n Key: primaryKey(model, id),\n ...revisionGuard(existing),\n },\n },\n ...uniqueMarkerDeletes(model, clean),\n ]);\n } catch (err) {\n if (rejectedByGuard(err)) return null;\n throw err;\n }\n return clean;\n },\n\n async incrementOne(model, id, { increment, set }) {\n const indexedFields = new Set(\n (indexMap[model] ?? []).flatMap((p) => [...p.pk, ...(p.sk ?? [])]),\n );\n const touchesIndex = Object.keys(set ?? {}).some((f) =>\n indexedFields.has(f),\n );\n const ttlField = ttlFieldFor(model);\n const touchesTtl = Boolean(ttlField && set && ttlField in set);\n\n // A native ADD cannot also re-encode GSI keys or the TTL attribute, so a\n // `set` that moves an indexed or TTL field goes the read-modify-write\n // route instead of silently leaving stale index rows behind.\n if (touchesIndex || touchesTtl) {\n return guardedWrite<StoreItem | null>(\n model,\n id,\n (existing) => {\n const clean = stripReserved(existing)!;\n const merged = { ...clean, ...(set ?? {}) };\n for (const [field, delta] of Object.entries(increment)) {\n const current = merged[field];\n merged[field] =\n (typeof current === \"number\" ? current : 0) + delta;\n }\n return {\n items: [\n {\n Put: {\n TableName: tableName,\n Item: {\n ...merged,\n ...encodeKeys(model, merged, indexMap, assignment),\n ...ttlAttributesFor(model, merged),\n [REVISION]: randomUUID(),\n },\n ...revisionGuard(existing),\n },\n },\n ...uniqueMarkerDiff(model, clean, merged, id),\n ],\n result: merged,\n };\n },\n () => null,\n );\n }\n\n const names: Record<string, string> = { \"#pk\": PK, \"#rev\": REVISION };\n const values: Record<string, unknown> = { \":rev\": randomUUID() };\n const addClauses: string[] = [];\n const setClauses: string[] = [\"#rev = :rev\"];\n let i = 0;\n for (const [field, delta] of Object.entries(increment)) {\n names[`#f${i}`] = field;\n values[`:v${i}`] = delta;\n addClauses.push(`#f${i} :v${i}`);\n i++;\n }\n for (const [field, value] of Object.entries(set ?? {})) {\n names[`#f${i}`] = field;\n values[`:v${i}`] = value;\n setClauses.push(`#f${i} = :v${i}`);\n i++;\n }\n const expression = [\n `SET ${setClauses.join(\", \")}`,\n addClauses.length ? `ADD ${addClauses.join(\", \")}` : null,\n ]\n .filter(Boolean)\n .join(\" \");\n\n try {\n const res = await doc.send(\n new UpdateCommand({\n TableName: tableName,\n Key: primaryKey(model, id),\n UpdateExpression: expression,\n // Without this, `ADD` would happily create the row it was told to\n // increment, turning \"increment an existing counter\" into an upsert.\n ConditionExpression: \"attribute_exists(#pk)\",\n ExpressionAttributeNames: names,\n ExpressionAttributeValues: values,\n ReturnValues: \"ALL_NEW\",\n }),\n );\n const item = res.Attributes as StoreItem | undefined;\n return isExpired(item) ? null : stripReserved(item);\n } catch (err) {\n if (isConditionalCheckFailed(err)) return null;\n throw err;\n }\n },\n\n async queryIndex({ model, index, key, cursor }): Promise<QueryPage> {\n const lookup = encodeLookupQuery(model, index, key, indexMap, assignment);\n const names: Record<string, string> = { \"#pk\": lookup.pkAttr };\n const values: Record<string, unknown> = { \":pk\": lookup.pkValue };\n let condition = \"#pk = :pk\";\n if (lookup.skPrefix) {\n names[\"#sk\"] = lookup.skAttr;\n values[\":skp\"] = lookup.skPrefix;\n condition += \" AND begins_with(#sk, :skp)\";\n }\n const { items, cursor: next } = await query(\n {\n IndexName: lookup.indexName,\n KeyConditionExpression: condition,\n ExpressionAttributeNames: names,\n ExpressionAttributeValues: values,\n },\n cursor,\n );\n return { items, cursor: next };\n },\n\n async listByType({ model, cursor }): Promise<QueryPage> {\n const { items, cursor: next } = await query(\n {\n IndexName: TYPE_INDEX,\n KeyConditionExpression: \"#tpk = :tpk\",\n ExpressionAttributeNames: { \"#tpk\": TYPE_PK },\n ExpressionAttributeValues: { \":tpk\": model },\n },\n cursor,\n );\n return { items, cursor: next };\n },\n\n async count({ model, index, key }) {\n // `Select: COUNT` counts rows DynamoDB has not reaped yet, which would\n // over-report a model whose rows expire logically. Fall back to draining.\n if (modelHasTtl(model)) return null;\n\n let base: Parameters<typeof query>[0];\n if (index && key) {\n const lookup = encodeLookupQuery(\n model,\n index,\n key,\n indexMap,\n assignment,\n );\n base = {\n IndexName: lookup.indexName,\n KeyConditionExpression: \"#pk = :pk\",\n ExpressionAttributeNames: { \"#pk\": lookup.pkAttr },\n ExpressionAttributeValues: { \":pk\": lookup.pkValue },\n };\n } else {\n base = {\n IndexName: TYPE_INDEX,\n KeyConditionExpression: \"#tpk = :tpk\",\n ExpressionAttributeNames: { \"#tpk\": TYPE_PK },\n ExpressionAttributeValues: { \":tpk\": model },\n };\n }\n let total = 0;\n let cursor: unknown = undefined;\n let pages = 0;\n do {\n const page = await query(base, cursor, true);\n total += page.count;\n cursor = page.cursor;\n if (++pages >= maxPages && cursor) {\n throw new DynamoDBAdapterError(\n `better-auth-dynamodb: counting \"${model}\" exceeded maxPages ` +\n `(${maxPages}) with more pages remaining. Raise maxPages or ` +\n `narrow the query — returning a partial count would be wrong.`,\n );\n }\n } while (cursor);\n return total;\n },\n\n createSchema: ({ file }) =>\n Promise.resolve(\n generateSchemaFile({\n tableName,\n lookupSlots: assignment.maxSlots,\n file,\n ttlAttribute: ttl ? ttlAttribute : undefined,\n }),\n ),\n };\n}\n","import { createHash } from \"node:crypto\";\n\nimport { DynamoDBAdapterError } from \"../../errors\";\nimport type { IndexMap } from \"../../index-map\";\nimport type { StoreItem } from \"../../types\";\n\n/**\n * Physical key encoding for the built-in single-table store.\n *\n * All reserved attributes are namespaced under `__ba_` so they never collide\n * with Better Auth field names, and are stripped before items are returned. The\n * table has a `PK`/`SK` primary key, one `byType` GSI (to list a whole model),\n * and N generic lookup GSIs — one physical slot per logical index a model uses.\n *\n * ## Why keys are length-prefixed\n *\n * Every variable component of a key is written as `s<byteLength>:<value>`. A\n * naive `a#b#c` join lets a value containing the delimiter forge a different\n * key: with composite partition keys, `(\"a#b\", \"c\")` and `(\"a\", \"b#c\")` encode\n * identically, and a sort-key `begins_with(\"cred#\")` probe matches a row whose\n * value is literally `cred#ential`. Length prefixes make that structurally\n * impossible — `s4:cred#` can never be a prefix of `s10:credential#` — so no\n * field value, whatever a provider or a user puts in it, can be made to collide\n * with or over-match another row's key.\n */\nexport const PK = \"__ba_pk\";\nexport const SK = \"__ba_sk\";\nexport const TYPE_PK = \"__ba_tpk\";\nexport const TYPE_SK = \"__ba_tsk\";\nexport const TYPE_INDEX = \"byType\";\n/** Hidden per-row revision, used as the optimistic-write guard. */\nexport const REVISION = \"__ba_rev\";\n/** Default DynamoDB TTL attribute (epoch seconds). Configurable per store. */\nexport const DEFAULT_TTL_ATTRIBUTE = \"__ba_ttl\";\nconst SK_CONST = \"#\";\nconst RESERVED_PREFIX = \"__ba_\";\n\n/** Row-kind discriminators. Fixed literals, never derived from user data. */\nconst ENTITY_PREFIX = \"E\";\nconst LOOKUP_PREFIX = \"L\";\nconst UNIQUE_PREFIX = \"U\";\n\n/** DynamoDB's own key size limits. */\nconst MAX_PARTITION_KEY_BYTES = 2048;\nconst MAX_SORT_KEY_BYTES = 1024;\n\n/**\n * Byte budget for a single encoded field value before it is hashed instead.\n *\n * The threshold is fixed rather than derived from the assembled key, because a\n * value must encode identically no matter which key it lands in — otherwise a\n * write and its matching lookup would disagree.\n */\nconst MAX_VALUE_COMPONENT_BYTES = 256;\n\nexport const gsiName = (slot: number): string => `lookup${slot}`;\nexport const gsiPk = (slot: number): string => `__ba_g${slot}pk`;\nexport const gsiSk = (slot: number): string => `__ba_g${slot}sk`;\n\nconst byteLength = (value: string): number => Buffer.byteLength(value, \"utf8\");\n\n/** `s<byteLength>:<value>` — the self-delimiting component encoding. */\nconst component = (value: string): string => `s${byteLength(value)}:${value}`;\n\n/**\n * Type-tagged rendering of a field value, so `1`, `\"1\"`, and `true` cannot\n * share a key. Dates normalise to ISO-8601 so an equivalent `Date` and string\n * agree.\n */\nexport function encodeValue(value: unknown): string {\n if (value === null || value === undefined) return \"null:\";\n if (value instanceof Date) return `date:${value.toISOString()}`;\n switch (typeof value) {\n case \"string\":\n return `string:${value}`;\n case \"number\":\n return `number:${value}`;\n case \"boolean\":\n return `boolean:${value}`;\n case \"bigint\":\n return `bigint:${value}`;\n case \"symbol\":\n return `symbol:${value.description ?? \"\"}`;\n case \"function\":\n return \"function:\";\n default:\n // Objects have no meaningful key form; JSON at least keeps them distinct\n // rather than collapsing every one of them to \"[object Object]\".\n return `object:${JSON.stringify(value)}`;\n }\n}\n\n/**\n * Encode one field value as a key component, hashing it when it is too long to\n * sit in a DynamoDB key. `hs:` and `s<digits>:` are disjoint prefixes, so a\n * hashed component can never be confused with a literal one.\n */\nfunction valueComponent(value: unknown): string {\n const typed = encodeValue(value);\n if (byteLength(typed) > MAX_VALUE_COMPONENT_BYTES) {\n return `hs:${createHash(\"sha256\").update(typed).digest(\"hex\")}`;\n }\n return component(typed);\n}\n\nconst joinValues = (values: unknown[]): string =>\n values.map(valueComponent).join(\"#\");\n\nfunction checkedKey(\n value: string,\n label: string,\n maxBytes: number,\n keyType: \"partition\" | \"sort\",\n): string {\n if (byteLength(value) <= maxBytes) return value;\n throw new DynamoDBAdapterError(\n `better-auth-dynamodb: ${label} exceeds DynamoDB's ${maxBytes}-byte ` +\n `${keyType} key limit. Field values are hashed past ` +\n `${MAX_VALUE_COMPONENT_BYTES} bytes, so this means the model name, ` +\n `index name, or record id is itself too long.`,\n );\n}\n\nconst partitionKey = (value: string, label: string): string =>\n checkedKey(value, label, MAX_PARTITION_KEY_BYTES, \"partition\");\n\nconst sortKey = (value: string, label: string): string =>\n checkedKey(value, label, MAX_SORT_KEY_BYTES, \"sort\");\n\n/** Physical slot assignment: model -> (logical index name -> GSI slot number). */\nexport interface SlotAssignment {\n slots: Record<string, Record<string, number>>;\n maxSlots: number;\n}\n\n/**\n * Assign each logical index to a physical GSI slot. Because indexes on\n * different models never coexist on one item, a model's Nth index always maps\n * to slot N — so the table needs only `max(indexes per model)` lookup GSIs.\n */\nexport function assignSlots(indexMap: IndexMap): SlotAssignment {\n const slots: Record<string, Record<string, number>> = {};\n let maxSlots = 0;\n for (const [model, patterns] of Object.entries(indexMap)) {\n const modelSlots: Record<string, number> = {};\n patterns.forEach((pattern, i) => {\n modelSlots[pattern.index] = i + 1;\n });\n slots[model] = modelSlots;\n maxSlots = Math.max(maxSlots, patterns.length);\n }\n return { slots, maxSlots };\n}\n\nexport const primaryKey = (model: string, id: string) => ({\n [PK]: partitionKey(\n `${ENTITY_PREFIX}#${component(model)}#${component(id)}`,\n \"entity partition key\",\n ),\n [SK]: SK_CONST,\n});\n\n/**\n * One marker row per (model, unique index, value) tuple. Written in the same\n * transaction as the entity row with `attribute_not_exists`, which is what\n * makes uniqueness a property of the database rather than of Better Auth's\n * check-then-insert.\n */\nexport const uniqueMarkerKey = (\n model: string,\n index: string,\n values: unknown[],\n) => ({\n [PK]: partitionKey(\n `${UNIQUE_PREFIX}#${component(model)}#${component(index)}#${joinValues(values)}`,\n \"unique marker partition key\",\n ),\n [SK]: SK_CONST,\n});\n\nconst lookupPkValue = (model: string, index: string, values: unknown[]) =>\n partitionKey(\n `${LOOKUP_PREFIX}#${component(model)}#${component(index)}#${joinValues(values)}`,\n \"lookup partition key\",\n );\n\n/**\n * Compute the reserved key attributes to store alongside an item: the primary\n * key, the type-index key, and a lookup key for every index whose partition\n * fields are all present on the item.\n */\nexport function encodeKeys(\n model: string,\n item: StoreItem,\n indexMap: IndexMap,\n assignment: SlotAssignment,\n): StoreItem {\n const id = String(item.id);\n const keys: StoreItem = {\n ...primaryKey(model, id),\n [TYPE_PK]: model,\n // Deliberately *not* length-prefixed: this sort key exists to order a\n // model's rows by creation time, and a byte-length prefix would sort\n // lexicographically by length instead of chronologically. It is never used\n // for `begins_with` lookups, so it carries no collision risk.\n [TYPE_SK]: sortKey(\n `${String(item.createdAt ?? \"\")}#${id}`,\n \"type sort key\",\n ),\n };\n\n for (const pattern of indexMap[model] ?? []) {\n const pkValues = pattern.pk.map((f) => item[f]);\n if (pkValues.some((v) => v == null)) continue; // index doesn't apply to this row\n const slot = assignment.slots[model]?.[pattern.index];\n if (!slot) continue;\n keys[gsiPk(slot)] = lookupPkValue(model, pattern.index, pkValues);\n const skValues = (pattern.sk ?? []).map((f) => item[f]);\n keys[gsiSk(slot)] = sortKey(\n skValues.some((v) => v == null)\n ? component(id)\n : `${joinValues(skValues)}#${component(id)}`,\n \"lookup sort key\",\n );\n }\n\n return keys;\n}\n\n/** A resolved lookup query against one GSI slot. */\nexport interface LookupQuery {\n indexName: string;\n pkAttr: string;\n pkValue: string;\n skAttr: string;\n /** When set, narrow with `begins_with(skAttr, skPrefix)`. */\n skPrefix?: string;\n}\n\n/**\n * Translate a logical index query into a concrete GSI query. Uses the\n * partition key always, and a `begins_with` sort-key prefix when the caller\n * supplied any leading sort-key field values.\n */\nexport function encodeLookupQuery(\n model: string,\n index: string,\n key: StoreItem,\n indexMap: IndexMap,\n assignment: SlotAssignment,\n): LookupQuery {\n const slot = assignment.slots[model]?.[index];\n if (!slot) {\n throw new DynamoDBAdapterError(\n `No physical slot for index \"${index}\" on model \"${model}\". ` +\n `Ensure the index is present in the adapter's index map.`,\n );\n }\n const pattern = (indexMap[model] ?? []).find((p) => p.index === index)!;\n const pkValues = pattern.pk.map((f) => key[f]);\n const skPresent = (pattern.sk ?? [])\n .map((f) => key[f])\n .filter((v) => v != null);\n\n return {\n indexName: gsiName(slot),\n pkAttr: gsiPk(slot),\n pkValue: lookupPkValue(model, index, pkValues),\n skAttr: gsiSk(slot),\n skPrefix: skPresent.length > 0 ? `${joinValues(skPresent)}#` : undefined,\n };\n}\n\n/** Remove all reserved (`__ba_`) attributes, yielding clean Better Auth data. */\nexport function stripReserved(\n item: StoreItem | undefined | null,\n): StoreItem | null {\n if (!item) return null;\n const clean: StoreItem = {};\n for (const [k, v] of Object.entries(item)) {\n if (!k.startsWith(RESERVED_PREFIX)) clean[k] = v;\n }\n return clean;\n}\n","import type {\n CreateTableCommandInput,\n DynamoDBClient,\n} from \"@aws-sdk/client-dynamodb\";\nimport {\n CreateTableCommand,\n ResourceInUseException,\n UpdateTimeToLiveCommand,\n waitUntilTableExists,\n} from \"@aws-sdk/client-dynamodb\";\n\nimport {\n gsiName,\n gsiPk,\n gsiSk,\n PK,\n SK,\n TYPE_INDEX,\n TYPE_PK,\n TYPE_SK,\n} from \"./key-codec\";\n\n/** Build the CreateTable input for the built-in store's single-table layout. */\nexport function buildTableDefinition(\n tableName: string,\n lookupSlots: number,\n): CreateTableCommandInput {\n const attr = (name: string) => ({\n AttributeName: name,\n AttributeType: \"S\" as const,\n });\n\n const attributeDefinitions = [\n attr(PK),\n attr(SK),\n attr(TYPE_PK),\n attr(TYPE_SK),\n ];\n const globalSecondaryIndexes = [\n {\n IndexName: TYPE_INDEX,\n KeySchema: [\n { AttributeName: TYPE_PK, KeyType: \"HASH\" as const },\n { AttributeName: TYPE_SK, KeyType: \"RANGE\" as const },\n ],\n Projection: { ProjectionType: \"ALL\" as const },\n },\n ];\n\n for (let slot = 1; slot <= lookupSlots; slot++) {\n attributeDefinitions.push(attr(gsiPk(slot)), attr(gsiSk(slot)));\n globalSecondaryIndexes.push({\n IndexName: gsiName(slot),\n KeySchema: [\n { AttributeName: gsiPk(slot), KeyType: \"HASH\" as const },\n { AttributeName: gsiSk(slot), KeyType: \"RANGE\" as const },\n ],\n Projection: { ProjectionType: \"ALL\" as const },\n });\n }\n\n return {\n TableName: tableName,\n BillingMode: \"PAY_PER_REQUEST\",\n KeySchema: [\n { AttributeName: PK, KeyType: \"HASH\" },\n { AttributeName: SK, KeyType: \"RANGE\" },\n ],\n AttributeDefinitions: attributeDefinitions,\n GlobalSecondaryIndexes: globalSecondaryIndexes,\n };\n}\n\n/**\n * Create the table if it does not already exist and wait until it is active.\n * Idempotent — safe to call at startup or in test `beforeAll` hooks.\n */\nexport async function ensureSchema(opts: {\n client: DynamoDBClient;\n tableName: string;\n lookupSlots: number;\n /**\n * Enable DynamoDB TTL on this attribute. Pass the same value the adapter is\n * configured with (`ttl.attributeName`, default `__ba_ttl`) — the adapter\n * writes the attribute, but only the table setting makes AWS act on it.\n */\n ttlAttribute?: string;\n}): Promise<void> {\n const input = buildTableDefinition(opts.tableName, opts.lookupSlots);\n try {\n await opts.client.send(new CreateTableCommand(input));\n } catch (error) {\n if (!(error instanceof ResourceInUseException)) throw error; // already exists\n }\n await waitUntilTableExists(\n { client: opts.client, maxWaitTime: 60 },\n { TableName: opts.tableName },\n );\n\n if (!opts.ttlAttribute) return;\n try {\n await opts.client.send(\n new UpdateTimeToLiveCommand({\n TableName: opts.tableName,\n TimeToLiveSpecification: {\n Enabled: true,\n AttributeName: opts.ttlAttribute,\n },\n }),\n );\n } catch (error) {\n // Re-enabling TTL on the same attribute is a no-op AWS reports as an\n // error; anything else is a real provisioning failure.\n if (!isAlreadyEnabled(error)) throw error;\n }\n}\n\nconst isAlreadyEnabled = (error: unknown): boolean =>\n typeof error === \"object\" &&\n error !== null &&\n \"name\" in error &&\n (error as { name?: unknown }).name === \"ValidationException\" &&\n typeof (error as unknown as { message?: unknown }).message === \"string\" &&\n (error as unknown as { message: string }).message.includes(\"already\");\n\n/**\n * Better Auth CLI `generate` hook: emit a portable CloudFormation template for\n * the built-in store's table. Table name and lookup-GSI count are configurable\n * (unlike a hardcoded layout), so the generated stack matches the adapter's\n * actual access patterns.\n */\nexport function generateSchemaFile(opts: {\n tableName: string;\n lookupSlots: number;\n file?: string;\n /** When set, the template enables DynamoDB TTL on this attribute. */\n ttlAttribute?: string;\n}): { code: string; path: string; overwrite: boolean } {\n const table = {\n ...buildTableDefinition(opts.tableName, opts.lookupSlots),\n ...(opts.ttlAttribute\n ? {\n TimeToLiveSpecification: {\n Enabled: true,\n AttributeName: opts.ttlAttribute,\n },\n }\n : {}),\n };\n const template = {\n AWSTemplateFormatVersion: \"2010-09-09\",\n Resources: {\n BetterAuthTable: { Type: \"AWS::DynamoDB::Table\", Properties: table },\n },\n };\n const code = `/**\n * Auto-generated DynamoDB CloudFormation template for @datar-platform/better-auth-dynamodb.\n * Table: ${opts.tableName} — ${opts.lookupSlots} lookup GSI(s) + 1 byType GSI.\n * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html\n */\nexport const dynamoDBSchema = ${JSON.stringify(template, null, 2)} as const;\n\nexport default dynamoDBSchema;\n`;\n return {\n code,\n path: opts.file ?? \"dynamodb-cloudformation.ts\",\n overwrite: true,\n };\n}\n"],"mappings":"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","names":["DEFAULT_MAX_PAGES"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datar-platform/better-auth-dynamodb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A generic DynamoDB adapter for Better Auth. Bring your own store or use the built-in single-table store.",
|
|
6
6
|
"type": "module",
|
|
@@ -51,16 +51,16 @@
|
|
|
51
51
|
"lint": "eslint --flag unstable_native_nodejs_ts_config",
|
|
52
52
|
"typecheck": "tsc --noEmit",
|
|
53
53
|
"test": "vitest run",
|
|
54
|
-
"test:
|
|
55
|
-
"test:
|
|
54
|
+
"test:watch": "vitest",
|
|
55
|
+
"test:integration": "vitest run --config vitest.integration.config.ts"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@aws-sdk/client-dynamodb": "^3.687.0",
|
|
59
59
|
"@aws-sdk/lib-dynamodb": "^3.687.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
|
-
"@better-auth/core": ">=1.
|
|
63
|
-
"better-auth": ">=1.
|
|
62
|
+
"@better-auth/core": ">=1.7.0",
|
|
63
|
+
"better-auth": ">=1.7.0"
|
|
64
64
|
},
|
|
65
65
|
"peerDependenciesMeta": {
|
|
66
66
|
"@better-auth/core": {
|
|
@@ -68,15 +68,18 @@
|
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@better-auth/core": "1.
|
|
71
|
+
"@better-auth/core": "1.7.3",
|
|
72
|
+
"@better-auth/test-utils": "1.7.3",
|
|
72
73
|
"@eslint/js": "^9.38.0",
|
|
73
74
|
"@types/node": "^22.18.12",
|
|
74
|
-
"better-auth": "1.
|
|
75
|
+
"better-auth": "1.7.3",
|
|
75
76
|
"eslint": "^9.38.0",
|
|
76
77
|
"prettier": "^3.6.2",
|
|
78
|
+
"testcontainers": "^12.1.0",
|
|
77
79
|
"tsup": "^8.3.0",
|
|
78
80
|
"typescript": "^5.9.3",
|
|
79
81
|
"typescript-eslint": "^8.46.3",
|
|
80
|
-
"
|
|
82
|
+
"vite": "^7.3.6",
|
|
83
|
+
"vitest": "^4.1.11"
|
|
81
84
|
}
|
|
82
85
|
}
|