@xylex-group/better-auth-athena 1.0.2 → 1.0.3

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # better-auth-athena
2
2
 
3
- current version: `1.0.2`
3
+ current version: `1.0.3`
4
4
  A Better-Auth database adapter for the `@xylex-group/athena` gateway. It lets Better-Auth read and write data through Athena while keeping column names in `snake_case` as required by the gateway.
5
5
 
6
6
  ## Installation
package/dist/index.cjs CHANGED
@@ -271,7 +271,7 @@ var athenaAdapter = (config) => {
271
271
  }) => {
272
272
  const db = ensureDbClient();
273
273
  const updateData = toDbRecord(update);
274
- let builder = db.from(model).update(updateData);
274
+ let builder = db.from(model).update({ set: updateData });
275
275
  for (const clause of where) {
276
276
  builder = applyWhere(
277
277
  builder,
@@ -299,7 +299,7 @@ var athenaAdapter = (config) => {
299
299
  }) => {
300
300
  const db = ensureDbClient();
301
301
  const updateData = toDbRecord(update);
302
- let builder = db.from(model).update(updateData);
302
+ let builder = db.from(model).update({ set: updateData });
303
303
  for (const clause of where) {
304
304
  builder = applyWhere(
305
305
  builder,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/config.ts"],"sourcesContent":["import {\r\n createAdapterFactory,\r\n type AdapterFactory,\r\n type DBAdapterDebugLogOption,\r\n} from \"better-auth/adapters\";\r\nimport type { BetterAuthOptions } from \"better-auth\";\r\nimport {\r\n createClient,\r\n type SupabaseClient as AthenaClient,\r\n} from \"@xylex-group/athena\";\r\nimport { getAthenaGlobalConfig } from \"./config\";\r\n\r\nfunction toSnakeCase(key: string): string {\r\n // `userId` -> `user_id`, `createdAt` -> `created_at`\r\n return key\r\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\r\n .replace(/__/g, \"_\")\r\n .toLowerCase();\r\n}\r\n\r\nfunction toCamelCase(key: string): string {\r\n // `user_id` -> `userId`, `created_at` -> `createdAt`\r\n return key.replace(/_([a-z0-9])/g, (_, ch: string) => ch.toUpperCase());\r\n}\r\n\r\nfunction hasUppercase(key: string): boolean {\r\n return /[A-Z]/.test(key);\r\n}\r\n\r\nfunction mapKeys<T extends Record<string, unknown>>(\r\n obj: T,\r\n mapKey: (k: string) => string,\r\n): Record<string, unknown> {\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(obj)) out[mapKey(k)] = v;\r\n return out;\r\n}\r\n\r\nfunction mapRowToBetterAuth<T>(row: T): T {\r\n if (!row || typeof row !== \"object\") return row;\r\n if (Array.isArray(row)) return row.map(mapRowToBetterAuth) as unknown as T;\r\n return mapKeys(row as Record<string, unknown>, toCamelCase) as T;\r\n}\r\n\r\nfunction isLikelyIsoDateString(value: string): boolean {\r\n // Fast-path: Better Auth commonly uses ISO-8601 timestamps for `*At` fields.\r\n if (!/^\\d{4}-\\d{2}-\\d{2}T/.test(value)) return false;\r\n const ms = Date.parse(value);\r\n return Number.isFinite(ms);\r\n}\r\n\r\nfunction isTimestampKey(key: string): boolean {\r\n return key.endsWith(\"At\") || key.endsWith(\"_at\") || key === \"expires\";\r\n}\r\n\r\nfunction coerceDateFields<T extends Record<string, unknown>>(data: T): T {\r\n const out: Record<string, unknown> = { ...data };\r\n for (const [key, val] of Object.entries(out)) {\r\n if (val == null) continue;\r\n if (\r\n typeof val === \"string\" &&\r\n isTimestampKey(key) &&\r\n isLikelyIsoDateString(val)\r\n ) {\r\n out[key] = new Date(val);\r\n }\r\n }\r\n return out as T;\r\n}\r\n\r\nfunction toDbRecord<T extends Record<string, unknown>>(\r\n data: T,\r\n): Record<string, unknown> {\r\n // Better Auth uses camelCase; Athena gateway expects snake_case column names.\r\n const withDbKeys = mapKeys(data, (k) =>\r\n hasUppercase(k) ? toSnakeCase(k) : k,\r\n );\r\n // Coerce ISO strings to Date; gateway must cast JSON string to timestamptz (e.g. $1::timestamptz).\r\n return coerceDateFields(withDbKeys);\r\n}\r\n\r\n/**\r\n * Configuration options for the Athena adapter.\r\n */\r\nexport interface AthenaAdapterConfig {\r\n /**\r\n * The URL of your Athena gateway.\r\n */\r\n url?: string;\r\n /**\r\n * The API key for authenticating with the Athena gateway.\r\n */\r\n apiKey?: string;\r\n /**\r\n * The client name sent in requests to the Athena gateway.\r\n */\r\n client?: string;\r\n\r\n /**\r\n * Optional override for the YAML config path.\r\n * Defaults to `./config.yaml` (resolved from `process.cwd()`).\r\n */\r\n configPath?: string;\r\n\r\n /**\r\n * When enabled, the adapter will reload `config.yaml` on changes.\r\n *\r\n * @default true\r\n */\r\n watchConfig?: boolean;\r\n /**\r\n * Helps you debug issues with the adapter.\r\n */\r\n debugLogs?: DBAdapterDebugLogOption;\r\n /**\r\n * If the table names in the schema are plural.\r\n *\r\n * @default false\r\n */\r\n usePlural?: boolean;\r\n}\r\n\r\ntype AthenaFilterBuilder = {\r\n eq(col: string, val: unknown): AthenaFilterBuilder;\r\n neq(col: string, val: unknown): AthenaFilterBuilder;\r\n gt(col: string, val: unknown): AthenaFilterBuilder;\r\n gte(col: string, val: unknown): AthenaFilterBuilder;\r\n lt(col: string, val: unknown): AthenaFilterBuilder;\r\n lte(col: string, val: unknown): AthenaFilterBuilder;\r\n in(col: string, vals: unknown[]): AthenaFilterBuilder;\r\n not(col: string, op?: string, val?: unknown): AthenaFilterBuilder;\r\n like(col: string, val: string): AthenaFilterBuilder;\r\n};\r\n\r\n/**\r\n * Apply a Better-Auth `CleanedWhere` clause to an Athena filter-chain builder.\r\n */\r\nfunction applyWhere<T extends AthenaFilterBuilder>(\r\n builder: T,\r\n field: string,\r\n operator: string,\r\n value: unknown,\r\n columnMapper: (col: string) => string = (col) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col,\r\n): T {\r\n const dbField = columnMapper(field);\r\n switch (operator) {\r\n case \"eq\":\r\n return builder.eq(dbField, value) as T;\r\n case \"ne\":\r\n return builder.neq(dbField, value) as T;\r\n case \"gt\":\r\n return builder.gt(dbField, value) as T;\r\n case \"gte\":\r\n return builder.gte(dbField, value) as T;\r\n case \"lt\":\r\n return builder.lt(dbField, value) as T;\r\n case \"lte\":\r\n return builder.lte(dbField, value) as T;\r\n case \"in\":\r\n return builder.in(dbField, value as unknown[]) as T;\r\n case \"not_in\":\r\n return builder.not(dbField, \"in\", value) as T;\r\n case \"contains\":\r\n return builder.like(dbField, `%${value}%`) as T;\r\n case \"starts_with\":\r\n return builder.like(dbField, `${value}%`) as T;\r\n case \"ends_with\":\r\n return builder.like(dbField, `%${value}`) as T;\r\n default:\r\n return builder.eq(dbField, value) as T;\r\n }\r\n}\r\n\r\ntype WhereClause = { field: string; operator: string; value: unknown };\r\n\r\nfunction isMissingColumnError(error: unknown): boolean {\r\n const msg = String(error ?? \"\");\r\n return (\r\n msg.includes(\"specified column does not exist\") ||\r\n msg.includes(\"column does not exist\")\r\n );\r\n}\r\n\r\n/**\r\n * Create a Better-Auth database adapter backed by @xylex-group/athena.\r\n *\r\n * Column names are kept in snake_case as required by the Athena gateway.\r\n *\r\n * @example\r\n * ```ts\r\n * import { betterAuth } from \"better-auth\";\r\n * import { athenaAdapter } from \"better-auth-athena\";\r\n *\r\n * export const auth = betterAuth({\r\n * database: athenaAdapter({\r\n * url: process.env.ATHENA_URL!,\r\n * apiKey: process.env.ATHENA_API_KEY!,\r\n * client: \"my-app\",\r\n * }),\r\n * });\r\n * ```\r\n */\r\nexport const athenaAdapter = (\r\n config: AthenaAdapterConfig,\r\n): AdapterFactory<BetterAuthOptions> => {\r\n let dbClient: AthenaClient | null = null;\r\n let lastDbConfigVersion = -1;\r\n\r\n const shouldUseFixedConfig =\r\n typeof config.url === \"string\" &&\r\n config.url.length > 0 &&\r\n typeof config.apiKey === \"string\" &&\r\n config.apiKey.length > 0;\r\n\r\n function ensureDbClient(): AthenaClient {\r\n if (shouldUseFixedConfig) {\r\n if (!dbClient) {\r\n dbClient = createClient(config.url!, config.apiKey!, {\r\n client: config.client,\r\n });\r\n }\r\n return dbClient;\r\n }\r\n\r\n const { config: globalConfig, version } = getAthenaGlobalConfig({\r\n configPath: config.configPath,\r\n watch: config.watchConfig ?? true,\r\n });\r\n\r\n const url = config.url ?? globalConfig.athena.url;\r\n const apiKey = config.apiKey ?? globalConfig.athena.apiKey;\r\n const client = config.client ?? globalConfig.athena.client;\r\n\r\n if (!url || !apiKey) {\r\n throw new Error(\r\n `[AthenaAdapter] Missing Athena connection details. Set both 'athena.url' and 'athena.apiKey' in config.yaml (or pass 'url'/'apiKey' to athenaAdapter).`,\r\n );\r\n }\r\n\r\n if (!dbClient || version !== lastDbConfigVersion) {\r\n dbClient = createClient(url, apiKey, { client });\r\n lastDbConfigVersion = version;\r\n }\r\n\r\n return dbClient;\r\n }\r\n\r\n return createAdapterFactory({\r\n config: {\r\n adapterId: \"athena\",\r\n adapterName: \"Athena Adapter\",\r\n usePlural: config.usePlural ?? false,\r\n debugLogs: config.debugLogs ?? false,\r\n // Athena/Postgres supports all these natively\r\n supportsJSON: true,\r\n supportsDates: true,\r\n supportsBooleans: true,\r\n supportsNumericIds: true,\r\n supportsUUIDs: true,\r\n },\r\n adapter: () => {\r\n return {\r\n // ------------------------------------------------------------------\r\n // CREATE\r\n // ------------------------------------------------------------------\r\n create: async <T extends Record<string, unknown>>({\r\n model,\r\n data,\r\n }: {\r\n model: string;\r\n data: T;\r\n select?: string[];\r\n }) => {\r\n const db = ensureDbClient();\r\n const insertData = toDbRecord(data);\r\n const { data: result, error } = await db\r\n .from(model)\r\n .insert(insertData)\r\n .select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] create on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n // Athena returns the inserted row(s); take the first one.\r\n const row = Array.isArray(result) ? result[0] : result;\r\n return mapRowToBetterAuth((row ?? insertData) as T);\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // UPDATE\r\n // ------------------------------------------------------------------\r\n update: async <T>({\r\n model,\r\n where,\r\n update,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n update: T;\r\n }) => {\r\n const db = ensureDbClient();\r\n const updateData = toDbRecord(update as Record<string, unknown>);\r\n let builder = db.from(model).update(updateData);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] update on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n const row = Array.isArray(result) ? result[0] : result;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // UPDATE MANY\r\n // ------------------------------------------------------------------\r\n updateMany: async ({\r\n model,\r\n where,\r\n update,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n update: Record<string, unknown>;\r\n }) => {\r\n const db = ensureDbClient();\r\n const updateData = toDbRecord(update);\r\n let builder = db.from(model).update(updateData);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] updateMany on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(result) ? result.length : result ? 1 : 0;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // DELETE\r\n // ------------------------------------------------------------------\r\n delete: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n let builder = db.from(model);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { error } = await builder.delete();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] delete on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // DELETE MANY\r\n // ------------------------------------------------------------------\r\n deleteMany: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n let builder = db.from(model);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.delete().select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] deleteMany on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(result) ? result.length : result ? 1 : 0;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // FIND ONE\r\n // ------------------------------------------------------------------\r\n findOne: async <T>({\r\n model,\r\n where,\r\n select,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n select?: string[];\r\n join?: unknown;\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n const columns =\r\n select && select.length > 0\r\n ? select.map((c) => columnMapper(c)).join(\", \")\r\n : undefined;\r\n\r\n let builder = db.from(model).select(columns);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.limit(1);\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] findOne on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n\r\n const rows = Array.isArray(retry.result)\r\n ? retry.result\r\n : retry.result\r\n ? [retry.result]\r\n : [];\r\n const row = rows[0] ?? null;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n }\r\n\r\n throw new Error(\r\n `[AthenaAdapter] findOne on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n const rows = Array.isArray(first.result)\r\n ? first.result\r\n : first.result\r\n ? [first.result]\r\n : [];\r\n const row = rows[0] ?? null;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // FIND MANY\r\n // ------------------------------------------------------------------\r\n findMany: async <T>({\r\n model,\r\n where,\r\n limit,\r\n sortBy,\r\n offset,\r\n select,\r\n }: {\r\n model: string;\r\n where?: WhereClause[];\r\n limit: number;\r\n select?: string[];\r\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\r\n offset?: number;\r\n join?: unknown;\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n const columns =\r\n select && select.length > 0\r\n ? select.map((c) => columnMapper(c)).join(\", \")\r\n : undefined;\r\n\r\n let builder = db.from(model).select(columns);\r\n\r\n if (where) {\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n }\r\n\r\n if (limit !== undefined) {\r\n builder = builder.limit(limit);\r\n }\r\n\r\n if (offset !== undefined) {\r\n builder = builder.offset(offset);\r\n }\r\n\r\n const { data: result, error } = await builder;\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n const pickRows = (res: unknown) =>\r\n (Array.isArray(res) ? res : []) as Record<string, unknown>[];\r\n\r\n const applySort = (rows: T[]) => {\r\n if (!sortBy) return rows;\r\n const sortField = sortBy.field;\r\n rows.sort((a, b) => {\r\n const aVal = (a as Record<string, unknown>)[sortField];\r\n const bVal = (b as Record<string, unknown>)[sortField];\r\n if (aVal == null && bVal == null) return 0;\r\n if (aVal == null) return sortBy.direction === \"asc\" ? -1 : 1;\r\n if (bVal == null) return sortBy.direction === \"asc\" ? 1 : -1;\r\n const cmp =\r\n typeof aVal === \"string\" && typeof bVal === \"string\"\r\n ? aVal.localeCompare(bVal)\r\n : aVal < bVal\r\n ? -1\r\n : aVal > bVal\r\n ? 1\r\n : 0;\r\n return sortBy.direction === \"asc\" ? cmp : -cmp;\r\n });\r\n return rows;\r\n };\r\n\r\n const mapAndSort = (rows: Record<string, unknown>[]) => {\r\n const betterAuthRows = rows.map((r) =>\r\n mapRowToBetterAuth(r),\r\n ) as unknown as T[];\r\n return applySort(betterAuthRows);\r\n };\r\n\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] findMany on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n return mapAndSort(pickRows(retry.result));\r\n }\r\n\r\n throw new Error(\r\n `[AthenaAdapter] findMany on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n return mapAndSort(pickRows(first.result));\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // COUNT\r\n // ------------------------------------------------------------------\r\n count: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where?: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n let builder = db.from(model).select();\r\n\r\n if (where) {\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n }\r\n\r\n const { data: result, error } = await builder;\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] count on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n return Array.isArray(retry.result) ? retry.result.length : 0;\r\n }\r\n throw new Error(\r\n `[AthenaAdapter] count on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(first.result) ? first.result.length : 0;\r\n },\r\n };\r\n },\r\n });\r\n};\r\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport YAML from \"yaml\";\r\n\r\nexport type AthenaGlobalConfig = {\r\n athena: {\r\n url: string;\r\n apiKey: string;\r\n client?: string;\r\n };\r\n};\r\n\r\n// Defaults written to `config.yaml` if it doesn't exist.\r\n// These values are intentionally placeholders; the adapter will throw if\r\n// `url`/`apiKey` are still unset when used.\r\nexport const defaultAthenaGlobalConfig: AthenaGlobalConfig = {\r\n athena: {\r\n url: \"http://localhost:3000\",\r\n apiKey: \"\",\r\n client: \"better-auth\",\r\n },\r\n};\r\n\r\nexport const DEFAULT_CONFIG_FILENAME = \"config.yaml\";\r\n\r\nfunction resolveConfigPath(configPath?: string): string {\r\n if (configPath) return path.resolve(configPath);\r\n return path.resolve(process.cwd(), DEFAULT_CONFIG_FILENAME);\r\n}\r\n\r\nlet cached: AthenaGlobalConfig | null = null;\r\nlet cachedConfigPath: string | null = null;\r\nlet version = 0;\r\n\r\nlet watcher: fs.FSWatcher | null = null;\r\n\r\nfunction isObject(value: unknown): value is Record<string, unknown> {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\r\n}\r\n\r\nfunction deepMerge<T extends Record<string, unknown>>(base: T, partial: unknown): T {\r\n if (!isObject(partial)) return base;\r\n const out: Record<string, unknown> = { ...base };\r\n for (const [k, v] of Object.entries(partial)) {\r\n if (v && isObject(v) && isObject(out[k])) {\r\n out[k] = deepMerge(out[k] as Record<string, unknown>, v);\r\n } else {\r\n out[k] = v;\r\n }\r\n }\r\n return out as T;\r\n}\r\n\r\nfunction ensureConfigFile(configPath: string): void {\r\n const dir = path.dirname(configPath);\r\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\r\n\r\n if (!fs.existsSync(configPath)) {\r\n const yaml = YAML.stringify(defaultAthenaGlobalConfig);\r\n fs.writeFileSync(configPath, yaml, \"utf-8\");\r\n }\r\n}\r\n\r\nfunction readConfigFromDisk(configPath: string): AthenaGlobalConfig {\r\n ensureConfigFile(configPath);\r\n const raw = fs.readFileSync(configPath, \"utf-8\");\r\n const parsed = YAML.parse(raw) as unknown;\r\n return deepMerge(defaultAthenaGlobalConfig, parsed);\r\n}\r\n\r\nfunction startWatcher(configPath: string): void {\r\n // Avoid multiple watchers when multiple adapter instances are created.\r\n if (cachedConfigPath !== null && cachedConfigPath !== configPath && watcher) {\r\n try {\r\n watcher.close();\r\n } catch {\r\n // ignore\r\n }\r\n watcher = null;\r\n }\r\n if (watcher || cachedConfigPath === configPath) return;\r\n\r\n try {\r\n watcher = fs.watch(configPath, { persistent: false }, (event) => {\r\n if (event !== \"change\" && event !== \"rename\") return;\r\n try {\r\n cached = readConfigFromDisk(configPath);\r\n version += 1;\r\n } catch {\r\n // Keep last known good config if reload fails.\r\n }\r\n });\r\n cachedConfigPath = configPath;\r\n } catch {\r\n // If watching isn't supported in the environment, just run without it.\r\n }\r\n}\r\n\r\nexport function getAthenaGlobalConfig(options?: {\r\n configPath?: string;\r\n watch?: boolean;\r\n}): { config: AthenaGlobalConfig; version: number } {\r\n const configPath = resolveConfigPath(options?.configPath);\r\n const shouldWatch = options?.watch ?? true;\r\n\r\n if (!cached || cachedConfigPath !== configPath) {\r\n cached = readConfigFromDisk(configPath);\r\n cachedConfigPath = configPath;\r\n version += 1;\r\n }\r\n\r\n if (shouldWatch) startWatcher(configPath);\r\n\r\n return { config: cached, version };\r\n}\r\n\r\n"],"mappings":"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","names":["path","fs","YAML","version","rows","row"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts"],"sourcesContent":["import {\r\n createAdapterFactory,\r\n type AdapterFactory,\r\n type DBAdapterDebugLogOption,\r\n} from \"better-auth/adapters\";\r\nimport type { BetterAuthOptions } from \"better-auth\";\r\nimport {\r\n createClient,\r\n type SupabaseClient as AthenaClient,\r\n} from \"@xylex-group/athena\";\r\nimport { getAthenaGlobalConfig } from \"./config\";\r\n\r\nfunction toSnakeCase(key: string): string {\r\n // `userId` -> `user_id`, `createdAt` -> `created_at`\r\n return key\r\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\r\n .replace(/__/g, \"_\")\r\n .toLowerCase();\r\n}\r\n\r\nfunction toCamelCase(key: string): string {\r\n // `user_id` -> `userId`, `created_at` -> `createdAt`\r\n return key.replace(/_([a-z0-9])/g, (_, ch: string) => ch.toUpperCase());\r\n}\r\n\r\nfunction hasUppercase(key: string): boolean {\r\n return /[A-Z]/.test(key);\r\n}\r\n\r\nfunction mapKeys<T extends Record<string, unknown>>(\r\n obj: T,\r\n mapKey: (k: string) => string,\r\n): Record<string, unknown> {\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(obj)) out[mapKey(k)] = v;\r\n return out;\r\n}\r\n\r\nfunction mapRowToBetterAuth<T>(row: T): T {\r\n if (!row || typeof row !== \"object\") return row;\r\n if (Array.isArray(row)) return row.map(mapRowToBetterAuth) as unknown as T;\r\n return mapKeys(row as Record<string, unknown>, toCamelCase) as T;\r\n}\r\n\r\nfunction isLikelyIsoDateString(value: string): boolean {\r\n // Fast-path: Better Auth commonly uses ISO-8601 timestamps for `*At` fields.\r\n if (!/^\\d{4}-\\d{2}-\\d{2}T/.test(value)) return false;\r\n const ms = Date.parse(value);\r\n return Number.isFinite(ms);\r\n}\r\n\r\nfunction isTimestampKey(key: string): boolean {\r\n return key.endsWith(\"At\") || key.endsWith(\"_at\") || key === \"expires\";\r\n}\r\n\r\nfunction coerceDateFields<T extends Record<string, unknown>>(data: T): T {\r\n const out: Record<string, unknown> = { ...data };\r\n for (const [key, val] of Object.entries(out)) {\r\n if (val == null) continue;\r\n if (\r\n typeof val === \"string\" &&\r\n isTimestampKey(key) &&\r\n isLikelyIsoDateString(val)\r\n ) {\r\n out[key] = new Date(val);\r\n }\r\n }\r\n return out as T;\r\n}\r\n\r\nfunction toDbRecord<T extends Record<string, unknown>>(\r\n data: T,\r\n): Record<string, unknown> {\r\n // Better Auth uses camelCase; Athena gateway expects snake_case column names.\r\n const withDbKeys = mapKeys(data, (k) =>\r\n hasUppercase(k) ? toSnakeCase(k) : k,\r\n );\r\n // Coerce ISO strings to Date; gateway must cast JSON string to timestamptz (e.g. $1::timestamptz).\r\n return coerceDateFields(withDbKeys);\r\n}\r\n\r\n/**\r\n * Configuration options for the Athena adapter.\r\n */\r\nexport interface AthenaAdapterConfig {\r\n /**\r\n * The URL of your Athena gateway.\r\n */\r\n url?: string;\r\n /**\r\n * The API key for authenticating with the Athena gateway.\r\n */\r\n apiKey?: string;\r\n /**\r\n * The client name sent in requests to the Athena gateway.\r\n */\r\n client?: string;\r\n\r\n /**\r\n * Optional override for the YAML config path.\r\n * Defaults to `./config.yaml` (resolved from `process.cwd()`).\r\n */\r\n configPath?: string;\r\n\r\n /**\r\n * When enabled, the adapter will reload `config.yaml` on changes.\r\n *\r\n * @default true\r\n */\r\n watchConfig?: boolean;\r\n /**\r\n * Helps you debug issues with the adapter.\r\n */\r\n debugLogs?: DBAdapterDebugLogOption;\r\n /**\r\n * If the table names in the schema are plural.\r\n *\r\n * @default false\r\n */\r\n usePlural?: boolean;\r\n}\r\n\r\ntype AthenaFilterBuilder = {\r\n eq(col: string, val: unknown): AthenaFilterBuilder;\r\n neq(col: string, val: unknown): AthenaFilterBuilder;\r\n gt(col: string, val: unknown): AthenaFilterBuilder;\r\n gte(col: string, val: unknown): AthenaFilterBuilder;\r\n lt(col: string, val: unknown): AthenaFilterBuilder;\r\n lte(col: string, val: unknown): AthenaFilterBuilder;\r\n in(col: string, vals: unknown[]): AthenaFilterBuilder;\r\n not(col: string, op?: string, val?: unknown): AthenaFilterBuilder;\r\n like(col: string, val: string): AthenaFilterBuilder;\r\n};\r\n\r\n/**\r\n * Apply a Better-Auth `CleanedWhere` clause to an Athena filter-chain builder.\r\n */\r\nfunction applyWhere<T extends AthenaFilterBuilder>(\r\n builder: T,\r\n field: string,\r\n operator: string,\r\n value: unknown,\r\n columnMapper: (col: string) => string = (col) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col,\r\n): T {\r\n const dbField = columnMapper(field);\r\n switch (operator) {\r\n case \"eq\":\r\n return builder.eq(dbField, value) as T;\r\n case \"ne\":\r\n return builder.neq(dbField, value) as T;\r\n case \"gt\":\r\n return builder.gt(dbField, value) as T;\r\n case \"gte\":\r\n return builder.gte(dbField, value) as T;\r\n case \"lt\":\r\n return builder.lt(dbField, value) as T;\r\n case \"lte\":\r\n return builder.lte(dbField, value) as T;\r\n case \"in\":\r\n return builder.in(dbField, value as unknown[]) as T;\r\n case \"not_in\":\r\n return builder.not(dbField, \"in\", value) as T;\r\n case \"contains\":\r\n return builder.like(dbField, `%${value}%`) as T;\r\n case \"starts_with\":\r\n return builder.like(dbField, `${value}%`) as T;\r\n case \"ends_with\":\r\n return builder.like(dbField, `%${value}`) as T;\r\n default:\r\n return builder.eq(dbField, value) as T;\r\n }\r\n}\r\n\r\ntype WhereClause = { field: string; operator: string; value: unknown };\r\n\r\nfunction isMissingColumnError(error: unknown): boolean {\r\n const msg = String(error ?? \"\");\r\n return (\r\n msg.includes(\"specified column does not exist\") ||\r\n msg.includes(\"column does not exist\")\r\n );\r\n}\r\n\r\n/**\r\n * Create a Better-Auth database adapter backed by @xylex-group/athena.\r\n *\r\n * Column names are kept in snake_case as required by the Athena gateway.\r\n *\r\n * @example\r\n * ```ts\r\n * import { betterAuth } from \"better-auth\";\r\n * import { athenaAdapter } from \"better-auth-athena\";\r\n *\r\n * export const auth = betterAuth({\r\n * database: athenaAdapter({\r\n * url: process.env.ATHENA_URL!,\r\n * apiKey: process.env.ATHENA_API_KEY!,\r\n * client: \"my-app\",\r\n * }),\r\n * });\r\n * ```\r\n */\r\nexport const athenaAdapter = (\r\n config: AthenaAdapterConfig,\r\n): AdapterFactory<BetterAuthOptions> => {\r\n let dbClient: AthenaClient | null = null;\r\n let lastDbConfigVersion = -1;\r\n\r\n const shouldUseFixedConfig =\r\n typeof config.url === \"string\" &&\r\n config.url.length > 0 &&\r\n typeof config.apiKey === \"string\" &&\r\n config.apiKey.length > 0;\r\n\r\n function ensureDbClient(): AthenaClient {\r\n if (shouldUseFixedConfig) {\r\n if (!dbClient) {\r\n dbClient = createClient(config.url!, config.apiKey!, {\r\n client: config.client,\r\n });\r\n }\r\n return dbClient;\r\n }\r\n\r\n const { config: globalConfig, version } = getAthenaGlobalConfig({\r\n configPath: config.configPath,\r\n watch: config.watchConfig ?? true,\r\n });\r\n\r\n const url = config.url ?? globalConfig.athena.url;\r\n const apiKey = config.apiKey ?? globalConfig.athena.apiKey;\r\n const client = config.client ?? globalConfig.athena.client;\r\n\r\n if (!url || !apiKey) {\r\n throw new Error(\r\n `[AthenaAdapter] Missing Athena connection details. Set both 'athena.url' and 'athena.apiKey' in config.yaml (or pass 'url'/'apiKey' to athenaAdapter).`,\r\n );\r\n }\r\n\r\n if (!dbClient || version !== lastDbConfigVersion) {\r\n dbClient = createClient(url, apiKey, { client });\r\n lastDbConfigVersion = version;\r\n }\r\n\r\n return dbClient;\r\n }\r\n\r\n return createAdapterFactory({\r\n config: {\r\n adapterId: \"athena\",\r\n adapterName: \"Athena Adapter\",\r\n usePlural: config.usePlural ?? false,\r\n debugLogs: config.debugLogs ?? false,\r\n // Athena/Postgres supports all these natively\r\n supportsJSON: true,\r\n supportsDates: true,\r\n supportsBooleans: true,\r\n supportsNumericIds: true,\r\n supportsUUIDs: true,\r\n },\r\n adapter: () => {\r\n return {\r\n // ------------------------------------------------------------------\r\n // CREATE\r\n // ------------------------------------------------------------------\r\n create: async <T extends Record<string, unknown>>({\r\n model,\r\n data,\r\n }: {\r\n model: string;\r\n data: T;\r\n select?: string[];\r\n }) => {\r\n const db = ensureDbClient();\r\n const insertData = toDbRecord(data);\r\n const { data: result, error } = await db\r\n .from(model)\r\n .insert(insertData)\r\n .select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] create on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n // Athena returns the inserted row(s); take the first one.\r\n const row = Array.isArray(result) ? result[0] : result;\r\n return mapRowToBetterAuth((row ?? insertData) as T);\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // UPDATE\r\n // ------------------------------------------------------------------\r\n update: async <T>({\r\n model,\r\n where,\r\n update,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n update: T;\r\n }) => {\r\n const db = ensureDbClient();\r\n const updateData = toDbRecord(update as Record<string, unknown>);\r\n // Athena gateway expects update_body to be wrapped in `{ set: ... }` (or `{ data: ... }`).\r\n let builder = db.from(model).update({ set: updateData });\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] update on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n const row = Array.isArray(result) ? result[0] : result;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // UPDATE MANY\r\n // ------------------------------------------------------------------\r\n updateMany: async ({\r\n model,\r\n where,\r\n update,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n update: Record<string, unknown>;\r\n }) => {\r\n const db = ensureDbClient();\r\n const updateData = toDbRecord(update);\r\n // Athena gateway expects update_body to be wrapped in `{ set: ... }` (or `{ data: ... }`).\r\n let builder = db.from(model).update({ set: updateData });\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] updateMany on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(result) ? result.length : result ? 1 : 0;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // DELETE\r\n // ------------------------------------------------------------------\r\n delete: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n let builder = db.from(model);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { error } = await builder.delete();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] delete on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // DELETE MANY\r\n // ------------------------------------------------------------------\r\n deleteMany: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n let builder = db.from(model);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.delete().select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] deleteMany on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(result) ? result.length : result ? 1 : 0;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // FIND ONE\r\n // ------------------------------------------------------------------\r\n findOne: async <T>({\r\n model,\r\n where,\r\n select,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n select?: string[];\r\n join?: unknown;\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n const columns =\r\n select && select.length > 0\r\n ? select.map((c) => columnMapper(c)).join(\", \")\r\n : undefined;\r\n\r\n let builder = db.from(model).select(columns);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.limit(1);\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] findOne on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n\r\n const rows = Array.isArray(retry.result)\r\n ? retry.result\r\n : retry.result\r\n ? [retry.result]\r\n : [];\r\n const row = rows[0] ?? null;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n }\r\n\r\n throw new Error(\r\n `[AthenaAdapter] findOne on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n const rows = Array.isArray(first.result)\r\n ? first.result\r\n : first.result\r\n ? [first.result]\r\n : [];\r\n const row = rows[0] ?? null;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // FIND MANY\r\n // ------------------------------------------------------------------\r\n findMany: async <T>({\r\n model,\r\n where,\r\n limit,\r\n sortBy,\r\n offset,\r\n select,\r\n }: {\r\n model: string;\r\n where?: WhereClause[];\r\n limit: number;\r\n select?: string[];\r\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\r\n offset?: number;\r\n join?: unknown;\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n const columns =\r\n select && select.length > 0\r\n ? select.map((c) => columnMapper(c)).join(\", \")\r\n : undefined;\r\n\r\n let builder = db.from(model).select(columns);\r\n\r\n if (where) {\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n }\r\n\r\n if (limit !== undefined) {\r\n builder = builder.limit(limit);\r\n }\r\n\r\n if (offset !== undefined) {\r\n builder = builder.offset(offset);\r\n }\r\n\r\n const { data: result, error } = await builder;\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n const pickRows = (res: unknown) =>\r\n (Array.isArray(res) ? res : []) as Record<string, unknown>[];\r\n\r\n const applySort = (rows: T[]) => {\r\n if (!sortBy) return rows;\r\n const sortField = sortBy.field;\r\n rows.sort((a, b) => {\r\n const aVal = (a as Record<string, unknown>)[sortField];\r\n const bVal = (b as Record<string, unknown>)[sortField];\r\n if (aVal == null && bVal == null) return 0;\r\n if (aVal == null) return sortBy.direction === \"asc\" ? -1 : 1;\r\n if (bVal == null) return sortBy.direction === \"asc\" ? 1 : -1;\r\n const cmp =\r\n typeof aVal === \"string\" && typeof bVal === \"string\"\r\n ? aVal.localeCompare(bVal)\r\n : aVal < bVal\r\n ? -1\r\n : aVal > bVal\r\n ? 1\r\n : 0;\r\n return sortBy.direction === \"asc\" ? cmp : -cmp;\r\n });\r\n return rows;\r\n };\r\n\r\n const mapAndSort = (rows: Record<string, unknown>[]) => {\r\n const betterAuthRows = rows.map((r) =>\r\n mapRowToBetterAuth(r),\r\n ) as unknown as T[];\r\n return applySort(betterAuthRows);\r\n };\r\n\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] findMany on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n return mapAndSort(pickRows(retry.result));\r\n }\r\n\r\n throw new Error(\r\n `[AthenaAdapter] findMany on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n return mapAndSort(pickRows(first.result));\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // COUNT\r\n // ------------------------------------------------------------------\r\n count: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where?: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n let builder = db.from(model).select();\r\n\r\n if (where) {\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n }\r\n\r\n const { data: result, error } = await builder;\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] count on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n return Array.isArray(retry.result) ? retry.result.length : 0;\r\n }\r\n throw new Error(\r\n `[AthenaAdapter] count on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(first.result) ? first.result.length : 0;\r\n },\r\n };\r\n },\r\n });\r\n};\r\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport YAML from \"yaml\";\r\n\r\nexport type AthenaGlobalConfig = {\r\n athena: {\r\n url: string;\r\n apiKey: string;\r\n client?: string;\r\n };\r\n};\r\n\r\n// Defaults written to `config.yaml` if it doesn't exist.\r\n// These values are intentionally placeholders; the adapter will throw if\r\n// `url`/`apiKey` are still unset when used.\r\nexport const defaultAthenaGlobalConfig: AthenaGlobalConfig = {\r\n athena: {\r\n url: \"http://localhost:3000\",\r\n apiKey: \"\",\r\n client: \"better-auth\",\r\n },\r\n};\r\n\r\nexport const DEFAULT_CONFIG_FILENAME = \"config.yaml\";\r\n\r\nfunction resolveConfigPath(configPath?: string): string {\r\n if (configPath) return path.resolve(configPath);\r\n return path.resolve(process.cwd(), DEFAULT_CONFIG_FILENAME);\r\n}\r\n\r\nlet cached: AthenaGlobalConfig | null = null;\r\nlet cachedConfigPath: string | null = null;\r\nlet version = 0;\r\n\r\nlet watcher: fs.FSWatcher | null = null;\r\n\r\nfunction isObject(value: unknown): value is Record<string, unknown> {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\r\n}\r\n\r\nfunction deepMerge<T extends Record<string, unknown>>(base: T, partial: unknown): T {\r\n if (!isObject(partial)) return base;\r\n const out: Record<string, unknown> = { ...base };\r\n for (const [k, v] of Object.entries(partial)) {\r\n if (v && isObject(v) && isObject(out[k])) {\r\n out[k] = deepMerge(out[k] as Record<string, unknown>, v);\r\n } else {\r\n out[k] = v;\r\n }\r\n }\r\n return out as T;\r\n}\r\n\r\nfunction ensureConfigFile(configPath: string): void {\r\n const dir = path.dirname(configPath);\r\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\r\n\r\n if (!fs.existsSync(configPath)) {\r\n const yaml = YAML.stringify(defaultAthenaGlobalConfig);\r\n fs.writeFileSync(configPath, yaml, \"utf-8\");\r\n }\r\n}\r\n\r\nfunction readConfigFromDisk(configPath: string): AthenaGlobalConfig {\r\n ensureConfigFile(configPath);\r\n const raw = fs.readFileSync(configPath, \"utf-8\");\r\n const parsed = YAML.parse(raw) as unknown;\r\n return deepMerge(defaultAthenaGlobalConfig, parsed);\r\n}\r\n\r\nfunction startWatcher(configPath: string): void {\r\n // Avoid multiple watchers when multiple adapter instances are created.\r\n if (cachedConfigPath !== null && cachedConfigPath !== configPath && watcher) {\r\n try {\r\n watcher.close();\r\n } catch {\r\n // ignore\r\n }\r\n watcher = null;\r\n }\r\n if (watcher || cachedConfigPath === configPath) return;\r\n\r\n try {\r\n watcher = fs.watch(configPath, { persistent: false }, (event) => {\r\n if (event !== \"change\" && event !== \"rename\") return;\r\n try {\r\n cached = readConfigFromDisk(configPath);\r\n version += 1;\r\n } catch {\r\n // Keep last known good config if reload fails.\r\n }\r\n });\r\n cachedConfigPath = configPath;\r\n } catch {\r\n // If watching isn't supported in the environment, just run without it.\r\n }\r\n}\r\n\r\nexport function getAthenaGlobalConfig(options?: {\r\n configPath?: string;\r\n watch?: boolean;\r\n}): { config: AthenaGlobalConfig; version: number } {\r\n const configPath = resolveConfigPath(options?.configPath);\r\n const shouldWatch = options?.watch ?? true;\r\n\r\n if (!cached || cachedConfigPath !== configPath) {\r\n cached = readConfigFromDisk(configPath);\r\n cachedConfigPath = configPath;\r\n version += 1;\r\n }\r\n\r\n if (shouldWatch) startWatcher(configPath);\r\n\r\n return { config: cached, version };\r\n}\r\n\r\n"],"mappings":"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","names":["path","fs","YAML","version","rows","row"]}
package/dist/index.js CHANGED
@@ -241,7 +241,7 @@ var athenaAdapter = (config) => {
241
241
  }) => {
242
242
  const db = ensureDbClient();
243
243
  const updateData = toDbRecord(update);
244
- let builder = db.from(model).update(updateData);
244
+ let builder = db.from(model).update({ set: updateData });
245
245
  for (const clause of where) {
246
246
  builder = applyWhere(
247
247
  builder,
@@ -269,7 +269,7 @@ var athenaAdapter = (config) => {
269
269
  }) => {
270
270
  const db = ensureDbClient();
271
271
  const updateData = toDbRecord(update);
272
- let builder = db.from(model).update(updateData);
272
+ let builder = db.from(model).update({ set: updateData });
273
273
  for (const clause of where) {
274
274
  builder = applyWhere(
275
275
  builder,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/config.ts"],"sourcesContent":["import {\r\n createAdapterFactory,\r\n type AdapterFactory,\r\n type DBAdapterDebugLogOption,\r\n} from \"better-auth/adapters\";\r\nimport type { BetterAuthOptions } from \"better-auth\";\r\nimport {\r\n createClient,\r\n type SupabaseClient as AthenaClient,\r\n} from \"@xylex-group/athena\";\r\nimport { getAthenaGlobalConfig } from \"./config\";\r\n\r\nfunction toSnakeCase(key: string): string {\r\n // `userId` -> `user_id`, `createdAt` -> `created_at`\r\n return key\r\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\r\n .replace(/__/g, \"_\")\r\n .toLowerCase();\r\n}\r\n\r\nfunction toCamelCase(key: string): string {\r\n // `user_id` -> `userId`, `created_at` -> `createdAt`\r\n return key.replace(/_([a-z0-9])/g, (_, ch: string) => ch.toUpperCase());\r\n}\r\n\r\nfunction hasUppercase(key: string): boolean {\r\n return /[A-Z]/.test(key);\r\n}\r\n\r\nfunction mapKeys<T extends Record<string, unknown>>(\r\n obj: T,\r\n mapKey: (k: string) => string,\r\n): Record<string, unknown> {\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(obj)) out[mapKey(k)] = v;\r\n return out;\r\n}\r\n\r\nfunction mapRowToBetterAuth<T>(row: T): T {\r\n if (!row || typeof row !== \"object\") return row;\r\n if (Array.isArray(row)) return row.map(mapRowToBetterAuth) as unknown as T;\r\n return mapKeys(row as Record<string, unknown>, toCamelCase) as T;\r\n}\r\n\r\nfunction isLikelyIsoDateString(value: string): boolean {\r\n // Fast-path: Better Auth commonly uses ISO-8601 timestamps for `*At` fields.\r\n if (!/^\\d{4}-\\d{2}-\\d{2}T/.test(value)) return false;\r\n const ms = Date.parse(value);\r\n return Number.isFinite(ms);\r\n}\r\n\r\nfunction isTimestampKey(key: string): boolean {\r\n return key.endsWith(\"At\") || key.endsWith(\"_at\") || key === \"expires\";\r\n}\r\n\r\nfunction coerceDateFields<T extends Record<string, unknown>>(data: T): T {\r\n const out: Record<string, unknown> = { ...data };\r\n for (const [key, val] of Object.entries(out)) {\r\n if (val == null) continue;\r\n if (\r\n typeof val === \"string\" &&\r\n isTimestampKey(key) &&\r\n isLikelyIsoDateString(val)\r\n ) {\r\n out[key] = new Date(val);\r\n }\r\n }\r\n return out as T;\r\n}\r\n\r\nfunction toDbRecord<T extends Record<string, unknown>>(\r\n data: T,\r\n): Record<string, unknown> {\r\n // Better Auth uses camelCase; Athena gateway expects snake_case column names.\r\n const withDbKeys = mapKeys(data, (k) =>\r\n hasUppercase(k) ? toSnakeCase(k) : k,\r\n );\r\n // Coerce ISO strings to Date; gateway must cast JSON string to timestamptz (e.g. $1::timestamptz).\r\n return coerceDateFields(withDbKeys);\r\n}\r\n\r\n/**\r\n * Configuration options for the Athena adapter.\r\n */\r\nexport interface AthenaAdapterConfig {\r\n /**\r\n * The URL of your Athena gateway.\r\n */\r\n url?: string;\r\n /**\r\n * The API key for authenticating with the Athena gateway.\r\n */\r\n apiKey?: string;\r\n /**\r\n * The client name sent in requests to the Athena gateway.\r\n */\r\n client?: string;\r\n\r\n /**\r\n * Optional override for the YAML config path.\r\n * Defaults to `./config.yaml` (resolved from `process.cwd()`).\r\n */\r\n configPath?: string;\r\n\r\n /**\r\n * When enabled, the adapter will reload `config.yaml` on changes.\r\n *\r\n * @default true\r\n */\r\n watchConfig?: boolean;\r\n /**\r\n * Helps you debug issues with the adapter.\r\n */\r\n debugLogs?: DBAdapterDebugLogOption;\r\n /**\r\n * If the table names in the schema are plural.\r\n *\r\n * @default false\r\n */\r\n usePlural?: boolean;\r\n}\r\n\r\ntype AthenaFilterBuilder = {\r\n eq(col: string, val: unknown): AthenaFilterBuilder;\r\n neq(col: string, val: unknown): AthenaFilterBuilder;\r\n gt(col: string, val: unknown): AthenaFilterBuilder;\r\n gte(col: string, val: unknown): AthenaFilterBuilder;\r\n lt(col: string, val: unknown): AthenaFilterBuilder;\r\n lte(col: string, val: unknown): AthenaFilterBuilder;\r\n in(col: string, vals: unknown[]): AthenaFilterBuilder;\r\n not(col: string, op?: string, val?: unknown): AthenaFilterBuilder;\r\n like(col: string, val: string): AthenaFilterBuilder;\r\n};\r\n\r\n/**\r\n * Apply a Better-Auth `CleanedWhere` clause to an Athena filter-chain builder.\r\n */\r\nfunction applyWhere<T extends AthenaFilterBuilder>(\r\n builder: T,\r\n field: string,\r\n operator: string,\r\n value: unknown,\r\n columnMapper: (col: string) => string = (col) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col,\r\n): T {\r\n const dbField = columnMapper(field);\r\n switch (operator) {\r\n case \"eq\":\r\n return builder.eq(dbField, value) as T;\r\n case \"ne\":\r\n return builder.neq(dbField, value) as T;\r\n case \"gt\":\r\n return builder.gt(dbField, value) as T;\r\n case \"gte\":\r\n return builder.gte(dbField, value) as T;\r\n case \"lt\":\r\n return builder.lt(dbField, value) as T;\r\n case \"lte\":\r\n return builder.lte(dbField, value) as T;\r\n case \"in\":\r\n return builder.in(dbField, value as unknown[]) as T;\r\n case \"not_in\":\r\n return builder.not(dbField, \"in\", value) as T;\r\n case \"contains\":\r\n return builder.like(dbField, `%${value}%`) as T;\r\n case \"starts_with\":\r\n return builder.like(dbField, `${value}%`) as T;\r\n case \"ends_with\":\r\n return builder.like(dbField, `%${value}`) as T;\r\n default:\r\n return builder.eq(dbField, value) as T;\r\n }\r\n}\r\n\r\ntype WhereClause = { field: string; operator: string; value: unknown };\r\n\r\nfunction isMissingColumnError(error: unknown): boolean {\r\n const msg = String(error ?? \"\");\r\n return (\r\n msg.includes(\"specified column does not exist\") ||\r\n msg.includes(\"column does not exist\")\r\n );\r\n}\r\n\r\n/**\r\n * Create a Better-Auth database adapter backed by @xylex-group/athena.\r\n *\r\n * Column names are kept in snake_case as required by the Athena gateway.\r\n *\r\n * @example\r\n * ```ts\r\n * import { betterAuth } from \"better-auth\";\r\n * import { athenaAdapter } from \"better-auth-athena\";\r\n *\r\n * export const auth = betterAuth({\r\n * database: athenaAdapter({\r\n * url: process.env.ATHENA_URL!,\r\n * apiKey: process.env.ATHENA_API_KEY!,\r\n * client: \"my-app\",\r\n * }),\r\n * });\r\n * ```\r\n */\r\nexport const athenaAdapter = (\r\n config: AthenaAdapterConfig,\r\n): AdapterFactory<BetterAuthOptions> => {\r\n let dbClient: AthenaClient | null = null;\r\n let lastDbConfigVersion = -1;\r\n\r\n const shouldUseFixedConfig =\r\n typeof config.url === \"string\" &&\r\n config.url.length > 0 &&\r\n typeof config.apiKey === \"string\" &&\r\n config.apiKey.length > 0;\r\n\r\n function ensureDbClient(): AthenaClient {\r\n if (shouldUseFixedConfig) {\r\n if (!dbClient) {\r\n dbClient = createClient(config.url!, config.apiKey!, {\r\n client: config.client,\r\n });\r\n }\r\n return dbClient;\r\n }\r\n\r\n const { config: globalConfig, version } = getAthenaGlobalConfig({\r\n configPath: config.configPath,\r\n watch: config.watchConfig ?? true,\r\n });\r\n\r\n const url = config.url ?? globalConfig.athena.url;\r\n const apiKey = config.apiKey ?? globalConfig.athena.apiKey;\r\n const client = config.client ?? globalConfig.athena.client;\r\n\r\n if (!url || !apiKey) {\r\n throw new Error(\r\n `[AthenaAdapter] Missing Athena connection details. Set both 'athena.url' and 'athena.apiKey' in config.yaml (or pass 'url'/'apiKey' to athenaAdapter).`,\r\n );\r\n }\r\n\r\n if (!dbClient || version !== lastDbConfigVersion) {\r\n dbClient = createClient(url, apiKey, { client });\r\n lastDbConfigVersion = version;\r\n }\r\n\r\n return dbClient;\r\n }\r\n\r\n return createAdapterFactory({\r\n config: {\r\n adapterId: \"athena\",\r\n adapterName: \"Athena Adapter\",\r\n usePlural: config.usePlural ?? false,\r\n debugLogs: config.debugLogs ?? false,\r\n // Athena/Postgres supports all these natively\r\n supportsJSON: true,\r\n supportsDates: true,\r\n supportsBooleans: true,\r\n supportsNumericIds: true,\r\n supportsUUIDs: true,\r\n },\r\n adapter: () => {\r\n return {\r\n // ------------------------------------------------------------------\r\n // CREATE\r\n // ------------------------------------------------------------------\r\n create: async <T extends Record<string, unknown>>({\r\n model,\r\n data,\r\n }: {\r\n model: string;\r\n data: T;\r\n select?: string[];\r\n }) => {\r\n const db = ensureDbClient();\r\n const insertData = toDbRecord(data);\r\n const { data: result, error } = await db\r\n .from(model)\r\n .insert(insertData)\r\n .select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] create on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n // Athena returns the inserted row(s); take the first one.\r\n const row = Array.isArray(result) ? result[0] : result;\r\n return mapRowToBetterAuth((row ?? insertData) as T);\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // UPDATE\r\n // ------------------------------------------------------------------\r\n update: async <T>({\r\n model,\r\n where,\r\n update,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n update: T;\r\n }) => {\r\n const db = ensureDbClient();\r\n const updateData = toDbRecord(update as Record<string, unknown>);\r\n let builder = db.from(model).update(updateData);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] update on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n const row = Array.isArray(result) ? result[0] : result;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // UPDATE MANY\r\n // ------------------------------------------------------------------\r\n updateMany: async ({\r\n model,\r\n where,\r\n update,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n update: Record<string, unknown>;\r\n }) => {\r\n const db = ensureDbClient();\r\n const updateData = toDbRecord(update);\r\n let builder = db.from(model).update(updateData);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] updateMany on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(result) ? result.length : result ? 1 : 0;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // DELETE\r\n // ------------------------------------------------------------------\r\n delete: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n let builder = db.from(model);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { error } = await builder.delete();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] delete on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // DELETE MANY\r\n // ------------------------------------------------------------------\r\n deleteMany: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n let builder = db.from(model);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.delete().select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] deleteMany on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(result) ? result.length : result ? 1 : 0;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // FIND ONE\r\n // ------------------------------------------------------------------\r\n findOne: async <T>({\r\n model,\r\n where,\r\n select,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n select?: string[];\r\n join?: unknown;\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n const columns =\r\n select && select.length > 0\r\n ? select.map((c) => columnMapper(c)).join(\", \")\r\n : undefined;\r\n\r\n let builder = db.from(model).select(columns);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.limit(1);\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] findOne on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n\r\n const rows = Array.isArray(retry.result)\r\n ? retry.result\r\n : retry.result\r\n ? [retry.result]\r\n : [];\r\n const row = rows[0] ?? null;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n }\r\n\r\n throw new Error(\r\n `[AthenaAdapter] findOne on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n const rows = Array.isArray(first.result)\r\n ? first.result\r\n : first.result\r\n ? [first.result]\r\n : [];\r\n const row = rows[0] ?? null;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // FIND MANY\r\n // ------------------------------------------------------------------\r\n findMany: async <T>({\r\n model,\r\n where,\r\n limit,\r\n sortBy,\r\n offset,\r\n select,\r\n }: {\r\n model: string;\r\n where?: WhereClause[];\r\n limit: number;\r\n select?: string[];\r\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\r\n offset?: number;\r\n join?: unknown;\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n const columns =\r\n select && select.length > 0\r\n ? select.map((c) => columnMapper(c)).join(\", \")\r\n : undefined;\r\n\r\n let builder = db.from(model).select(columns);\r\n\r\n if (where) {\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n }\r\n\r\n if (limit !== undefined) {\r\n builder = builder.limit(limit);\r\n }\r\n\r\n if (offset !== undefined) {\r\n builder = builder.offset(offset);\r\n }\r\n\r\n const { data: result, error } = await builder;\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n const pickRows = (res: unknown) =>\r\n (Array.isArray(res) ? res : []) as Record<string, unknown>[];\r\n\r\n const applySort = (rows: T[]) => {\r\n if (!sortBy) return rows;\r\n const sortField = sortBy.field;\r\n rows.sort((a, b) => {\r\n const aVal = (a as Record<string, unknown>)[sortField];\r\n const bVal = (b as Record<string, unknown>)[sortField];\r\n if (aVal == null && bVal == null) return 0;\r\n if (aVal == null) return sortBy.direction === \"asc\" ? -1 : 1;\r\n if (bVal == null) return sortBy.direction === \"asc\" ? 1 : -1;\r\n const cmp =\r\n typeof aVal === \"string\" && typeof bVal === \"string\"\r\n ? aVal.localeCompare(bVal)\r\n : aVal < bVal\r\n ? -1\r\n : aVal > bVal\r\n ? 1\r\n : 0;\r\n return sortBy.direction === \"asc\" ? cmp : -cmp;\r\n });\r\n return rows;\r\n };\r\n\r\n const mapAndSort = (rows: Record<string, unknown>[]) => {\r\n const betterAuthRows = rows.map((r) =>\r\n mapRowToBetterAuth(r),\r\n ) as unknown as T[];\r\n return applySort(betterAuthRows);\r\n };\r\n\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] findMany on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n return mapAndSort(pickRows(retry.result));\r\n }\r\n\r\n throw new Error(\r\n `[AthenaAdapter] findMany on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n return mapAndSort(pickRows(first.result));\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // COUNT\r\n // ------------------------------------------------------------------\r\n count: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where?: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n let builder = db.from(model).select();\r\n\r\n if (where) {\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n }\r\n\r\n const { data: result, error } = await builder;\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] count on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n return Array.isArray(retry.result) ? retry.result.length : 0;\r\n }\r\n throw new Error(\r\n `[AthenaAdapter] count on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(first.result) ? first.result.length : 0;\r\n },\r\n };\r\n },\r\n });\r\n};\r\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport YAML from \"yaml\";\r\n\r\nexport type AthenaGlobalConfig = {\r\n athena: {\r\n url: string;\r\n apiKey: string;\r\n client?: string;\r\n };\r\n};\r\n\r\n// Defaults written to `config.yaml` if it doesn't exist.\r\n// These values are intentionally placeholders; the adapter will throw if\r\n// `url`/`apiKey` are still unset when used.\r\nexport const defaultAthenaGlobalConfig: AthenaGlobalConfig = {\r\n athena: {\r\n url: \"http://localhost:3000\",\r\n apiKey: \"\",\r\n client: \"better-auth\",\r\n },\r\n};\r\n\r\nexport const DEFAULT_CONFIG_FILENAME = \"config.yaml\";\r\n\r\nfunction resolveConfigPath(configPath?: string): string {\r\n if (configPath) return path.resolve(configPath);\r\n return path.resolve(process.cwd(), DEFAULT_CONFIG_FILENAME);\r\n}\r\n\r\nlet cached: AthenaGlobalConfig | null = null;\r\nlet cachedConfigPath: string | null = null;\r\nlet version = 0;\r\n\r\nlet watcher: fs.FSWatcher | null = null;\r\n\r\nfunction isObject(value: unknown): value is Record<string, unknown> {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\r\n}\r\n\r\nfunction deepMerge<T extends Record<string, unknown>>(base: T, partial: unknown): T {\r\n if (!isObject(partial)) return base;\r\n const out: Record<string, unknown> = { ...base };\r\n for (const [k, v] of Object.entries(partial)) {\r\n if (v && isObject(v) && isObject(out[k])) {\r\n out[k] = deepMerge(out[k] as Record<string, unknown>, v);\r\n } else {\r\n out[k] = v;\r\n }\r\n }\r\n return out as T;\r\n}\r\n\r\nfunction ensureConfigFile(configPath: string): void {\r\n const dir = path.dirname(configPath);\r\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\r\n\r\n if (!fs.existsSync(configPath)) {\r\n const yaml = YAML.stringify(defaultAthenaGlobalConfig);\r\n fs.writeFileSync(configPath, yaml, \"utf-8\");\r\n }\r\n}\r\n\r\nfunction readConfigFromDisk(configPath: string): AthenaGlobalConfig {\r\n ensureConfigFile(configPath);\r\n const raw = fs.readFileSync(configPath, \"utf-8\");\r\n const parsed = YAML.parse(raw) as unknown;\r\n return deepMerge(defaultAthenaGlobalConfig, parsed);\r\n}\r\n\r\nfunction startWatcher(configPath: string): void {\r\n // Avoid multiple watchers when multiple adapter instances are created.\r\n if (cachedConfigPath !== null && cachedConfigPath !== configPath && watcher) {\r\n try {\r\n watcher.close();\r\n } catch {\r\n // ignore\r\n }\r\n watcher = null;\r\n }\r\n if (watcher || cachedConfigPath === configPath) return;\r\n\r\n try {\r\n watcher = fs.watch(configPath, { persistent: false }, (event) => {\r\n if (event !== \"change\" && event !== \"rename\") return;\r\n try {\r\n cached = readConfigFromDisk(configPath);\r\n version += 1;\r\n } catch {\r\n // Keep last known good config if reload fails.\r\n }\r\n });\r\n cachedConfigPath = configPath;\r\n } catch {\r\n // If watching isn't supported in the environment, just run without it.\r\n }\r\n}\r\n\r\nexport function getAthenaGlobalConfig(options?: {\r\n configPath?: string;\r\n watch?: boolean;\r\n}): { config: AthenaGlobalConfig; version: number } {\r\n const configPath = resolveConfigPath(options?.configPath);\r\n const shouldWatch = options?.watch ?? true;\r\n\r\n if (!cached || cachedConfigPath !== configPath) {\r\n cached = readConfigFromDisk(configPath);\r\n cachedConfigPath = configPath;\r\n version += 1;\r\n }\r\n\r\n if (shouldWatch) startWatcher(configPath);\r\n\r\n return { config: cached, version };\r\n}\r\n\r\n"],"mappings":"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","names":["version","rows","row"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts"],"sourcesContent":["import {\r\n createAdapterFactory,\r\n type AdapterFactory,\r\n type DBAdapterDebugLogOption,\r\n} from \"better-auth/adapters\";\r\nimport type { BetterAuthOptions } from \"better-auth\";\r\nimport {\r\n createClient,\r\n type SupabaseClient as AthenaClient,\r\n} from \"@xylex-group/athena\";\r\nimport { getAthenaGlobalConfig } from \"./config\";\r\n\r\nfunction toSnakeCase(key: string): string {\r\n // `userId` -> `user_id`, `createdAt` -> `created_at`\r\n return key\r\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\r\n .replace(/__/g, \"_\")\r\n .toLowerCase();\r\n}\r\n\r\nfunction toCamelCase(key: string): string {\r\n // `user_id` -> `userId`, `created_at` -> `createdAt`\r\n return key.replace(/_([a-z0-9])/g, (_, ch: string) => ch.toUpperCase());\r\n}\r\n\r\nfunction hasUppercase(key: string): boolean {\r\n return /[A-Z]/.test(key);\r\n}\r\n\r\nfunction mapKeys<T extends Record<string, unknown>>(\r\n obj: T,\r\n mapKey: (k: string) => string,\r\n): Record<string, unknown> {\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(obj)) out[mapKey(k)] = v;\r\n return out;\r\n}\r\n\r\nfunction mapRowToBetterAuth<T>(row: T): T {\r\n if (!row || typeof row !== \"object\") return row;\r\n if (Array.isArray(row)) return row.map(mapRowToBetterAuth) as unknown as T;\r\n return mapKeys(row as Record<string, unknown>, toCamelCase) as T;\r\n}\r\n\r\nfunction isLikelyIsoDateString(value: string): boolean {\r\n // Fast-path: Better Auth commonly uses ISO-8601 timestamps for `*At` fields.\r\n if (!/^\\d{4}-\\d{2}-\\d{2}T/.test(value)) return false;\r\n const ms = Date.parse(value);\r\n return Number.isFinite(ms);\r\n}\r\n\r\nfunction isTimestampKey(key: string): boolean {\r\n return key.endsWith(\"At\") || key.endsWith(\"_at\") || key === \"expires\";\r\n}\r\n\r\nfunction coerceDateFields<T extends Record<string, unknown>>(data: T): T {\r\n const out: Record<string, unknown> = { ...data };\r\n for (const [key, val] of Object.entries(out)) {\r\n if (val == null) continue;\r\n if (\r\n typeof val === \"string\" &&\r\n isTimestampKey(key) &&\r\n isLikelyIsoDateString(val)\r\n ) {\r\n out[key] = new Date(val);\r\n }\r\n }\r\n return out as T;\r\n}\r\n\r\nfunction toDbRecord<T extends Record<string, unknown>>(\r\n data: T,\r\n): Record<string, unknown> {\r\n // Better Auth uses camelCase; Athena gateway expects snake_case column names.\r\n const withDbKeys = mapKeys(data, (k) =>\r\n hasUppercase(k) ? toSnakeCase(k) : k,\r\n );\r\n // Coerce ISO strings to Date; gateway must cast JSON string to timestamptz (e.g. $1::timestamptz).\r\n return coerceDateFields(withDbKeys);\r\n}\r\n\r\n/**\r\n * Configuration options for the Athena adapter.\r\n */\r\nexport interface AthenaAdapterConfig {\r\n /**\r\n * The URL of your Athena gateway.\r\n */\r\n url?: string;\r\n /**\r\n * The API key for authenticating with the Athena gateway.\r\n */\r\n apiKey?: string;\r\n /**\r\n * The client name sent in requests to the Athena gateway.\r\n */\r\n client?: string;\r\n\r\n /**\r\n * Optional override for the YAML config path.\r\n * Defaults to `./config.yaml` (resolved from `process.cwd()`).\r\n */\r\n configPath?: string;\r\n\r\n /**\r\n * When enabled, the adapter will reload `config.yaml` on changes.\r\n *\r\n * @default true\r\n */\r\n watchConfig?: boolean;\r\n /**\r\n * Helps you debug issues with the adapter.\r\n */\r\n debugLogs?: DBAdapterDebugLogOption;\r\n /**\r\n * If the table names in the schema are plural.\r\n *\r\n * @default false\r\n */\r\n usePlural?: boolean;\r\n}\r\n\r\ntype AthenaFilterBuilder = {\r\n eq(col: string, val: unknown): AthenaFilterBuilder;\r\n neq(col: string, val: unknown): AthenaFilterBuilder;\r\n gt(col: string, val: unknown): AthenaFilterBuilder;\r\n gte(col: string, val: unknown): AthenaFilterBuilder;\r\n lt(col: string, val: unknown): AthenaFilterBuilder;\r\n lte(col: string, val: unknown): AthenaFilterBuilder;\r\n in(col: string, vals: unknown[]): AthenaFilterBuilder;\r\n not(col: string, op?: string, val?: unknown): AthenaFilterBuilder;\r\n like(col: string, val: string): AthenaFilterBuilder;\r\n};\r\n\r\n/**\r\n * Apply a Better-Auth `CleanedWhere` clause to an Athena filter-chain builder.\r\n */\r\nfunction applyWhere<T extends AthenaFilterBuilder>(\r\n builder: T,\r\n field: string,\r\n operator: string,\r\n value: unknown,\r\n columnMapper: (col: string) => string = (col) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col,\r\n): T {\r\n const dbField = columnMapper(field);\r\n switch (operator) {\r\n case \"eq\":\r\n return builder.eq(dbField, value) as T;\r\n case \"ne\":\r\n return builder.neq(dbField, value) as T;\r\n case \"gt\":\r\n return builder.gt(dbField, value) as T;\r\n case \"gte\":\r\n return builder.gte(dbField, value) as T;\r\n case \"lt\":\r\n return builder.lt(dbField, value) as T;\r\n case \"lte\":\r\n return builder.lte(dbField, value) as T;\r\n case \"in\":\r\n return builder.in(dbField, value as unknown[]) as T;\r\n case \"not_in\":\r\n return builder.not(dbField, \"in\", value) as T;\r\n case \"contains\":\r\n return builder.like(dbField, `%${value}%`) as T;\r\n case \"starts_with\":\r\n return builder.like(dbField, `${value}%`) as T;\r\n case \"ends_with\":\r\n return builder.like(dbField, `%${value}`) as T;\r\n default:\r\n return builder.eq(dbField, value) as T;\r\n }\r\n}\r\n\r\ntype WhereClause = { field: string; operator: string; value: unknown };\r\n\r\nfunction isMissingColumnError(error: unknown): boolean {\r\n const msg = String(error ?? \"\");\r\n return (\r\n msg.includes(\"specified column does not exist\") ||\r\n msg.includes(\"column does not exist\")\r\n );\r\n}\r\n\r\n/**\r\n * Create a Better-Auth database adapter backed by @xylex-group/athena.\r\n *\r\n * Column names are kept in snake_case as required by the Athena gateway.\r\n *\r\n * @example\r\n * ```ts\r\n * import { betterAuth } from \"better-auth\";\r\n * import { athenaAdapter } from \"better-auth-athena\";\r\n *\r\n * export const auth = betterAuth({\r\n * database: athenaAdapter({\r\n * url: process.env.ATHENA_URL!,\r\n * apiKey: process.env.ATHENA_API_KEY!,\r\n * client: \"my-app\",\r\n * }),\r\n * });\r\n * ```\r\n */\r\nexport const athenaAdapter = (\r\n config: AthenaAdapterConfig,\r\n): AdapterFactory<BetterAuthOptions> => {\r\n let dbClient: AthenaClient | null = null;\r\n let lastDbConfigVersion = -1;\r\n\r\n const shouldUseFixedConfig =\r\n typeof config.url === \"string\" &&\r\n config.url.length > 0 &&\r\n typeof config.apiKey === \"string\" &&\r\n config.apiKey.length > 0;\r\n\r\n function ensureDbClient(): AthenaClient {\r\n if (shouldUseFixedConfig) {\r\n if (!dbClient) {\r\n dbClient = createClient(config.url!, config.apiKey!, {\r\n client: config.client,\r\n });\r\n }\r\n return dbClient;\r\n }\r\n\r\n const { config: globalConfig, version } = getAthenaGlobalConfig({\r\n configPath: config.configPath,\r\n watch: config.watchConfig ?? true,\r\n });\r\n\r\n const url = config.url ?? globalConfig.athena.url;\r\n const apiKey = config.apiKey ?? globalConfig.athena.apiKey;\r\n const client = config.client ?? globalConfig.athena.client;\r\n\r\n if (!url || !apiKey) {\r\n throw new Error(\r\n `[AthenaAdapter] Missing Athena connection details. Set both 'athena.url' and 'athena.apiKey' in config.yaml (or pass 'url'/'apiKey' to athenaAdapter).`,\r\n );\r\n }\r\n\r\n if (!dbClient || version !== lastDbConfigVersion) {\r\n dbClient = createClient(url, apiKey, { client });\r\n lastDbConfigVersion = version;\r\n }\r\n\r\n return dbClient;\r\n }\r\n\r\n return createAdapterFactory({\r\n config: {\r\n adapterId: \"athena\",\r\n adapterName: \"Athena Adapter\",\r\n usePlural: config.usePlural ?? false,\r\n debugLogs: config.debugLogs ?? false,\r\n // Athena/Postgres supports all these natively\r\n supportsJSON: true,\r\n supportsDates: true,\r\n supportsBooleans: true,\r\n supportsNumericIds: true,\r\n supportsUUIDs: true,\r\n },\r\n adapter: () => {\r\n return {\r\n // ------------------------------------------------------------------\r\n // CREATE\r\n // ------------------------------------------------------------------\r\n create: async <T extends Record<string, unknown>>({\r\n model,\r\n data,\r\n }: {\r\n model: string;\r\n data: T;\r\n select?: string[];\r\n }) => {\r\n const db = ensureDbClient();\r\n const insertData = toDbRecord(data);\r\n const { data: result, error } = await db\r\n .from(model)\r\n .insert(insertData)\r\n .select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] create on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n // Athena returns the inserted row(s); take the first one.\r\n const row = Array.isArray(result) ? result[0] : result;\r\n return mapRowToBetterAuth((row ?? insertData) as T);\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // UPDATE\r\n // ------------------------------------------------------------------\r\n update: async <T>({\r\n model,\r\n where,\r\n update,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n update: T;\r\n }) => {\r\n const db = ensureDbClient();\r\n const updateData = toDbRecord(update as Record<string, unknown>);\r\n // Athena gateway expects update_body to be wrapped in `{ set: ... }` (or `{ data: ... }`).\r\n let builder = db.from(model).update({ set: updateData });\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] update on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n const row = Array.isArray(result) ? result[0] : result;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // UPDATE MANY\r\n // ------------------------------------------------------------------\r\n updateMany: async ({\r\n model,\r\n where,\r\n update,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n update: Record<string, unknown>;\r\n }) => {\r\n const db = ensureDbClient();\r\n const updateData = toDbRecord(update);\r\n // Athena gateway expects update_body to be wrapped in `{ set: ... }` (or `{ data: ... }`).\r\n let builder = db.from(model).update({ set: updateData });\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] updateMany on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(result) ? result.length : result ? 1 : 0;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // DELETE\r\n // ------------------------------------------------------------------\r\n delete: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n let builder = db.from(model);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { error } = await builder.delete();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] delete on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // DELETE MANY\r\n // ------------------------------------------------------------------\r\n deleteMany: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n let builder = db.from(model);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.delete().select();\r\n\r\n if (error) {\r\n throw new Error(\r\n `[AthenaAdapter] deleteMany on \"${model}\" failed: ${error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(result) ? result.length : result ? 1 : 0;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // FIND ONE\r\n // ------------------------------------------------------------------\r\n findOne: async <T>({\r\n model,\r\n where,\r\n select,\r\n }: {\r\n model: string;\r\n where: WhereClause[];\r\n select?: string[];\r\n join?: unknown;\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n const columns =\r\n select && select.length > 0\r\n ? select.map((c) => columnMapper(c)).join(\", \")\r\n : undefined;\r\n\r\n let builder = db.from(model).select(columns);\r\n\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n\r\n const { data: result, error } = await builder.limit(1);\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] findOne on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n\r\n const rows = Array.isArray(retry.result)\r\n ? retry.result\r\n : retry.result\r\n ? [retry.result]\r\n : [];\r\n const row = rows[0] ?? null;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n }\r\n\r\n throw new Error(\r\n `[AthenaAdapter] findOne on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n const rows = Array.isArray(first.result)\r\n ? first.result\r\n : first.result\r\n ? [first.result]\r\n : [];\r\n const row = rows[0] ?? null;\r\n return (row ? mapRowToBetterAuth(row as T) : null) as T | null;\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // FIND MANY\r\n // ------------------------------------------------------------------\r\n findMany: async <T>({\r\n model,\r\n where,\r\n limit,\r\n sortBy,\r\n offset,\r\n select,\r\n }: {\r\n model: string;\r\n where?: WhereClause[];\r\n limit: number;\r\n select?: string[];\r\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\r\n offset?: number;\r\n join?: unknown;\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n const columns =\r\n select && select.length > 0\r\n ? select.map((c) => columnMapper(c)).join(\", \")\r\n : undefined;\r\n\r\n let builder = db.from(model).select(columns);\r\n\r\n if (where) {\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n }\r\n\r\n if (limit !== undefined) {\r\n builder = builder.limit(limit);\r\n }\r\n\r\n if (offset !== undefined) {\r\n builder = builder.offset(offset);\r\n }\r\n\r\n const { data: result, error } = await builder;\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n const pickRows = (res: unknown) =>\r\n (Array.isArray(res) ? res : []) as Record<string, unknown>[];\r\n\r\n const applySort = (rows: T[]) => {\r\n if (!sortBy) return rows;\r\n const sortField = sortBy.field;\r\n rows.sort((a, b) => {\r\n const aVal = (a as Record<string, unknown>)[sortField];\r\n const bVal = (b as Record<string, unknown>)[sortField];\r\n if (aVal == null && bVal == null) return 0;\r\n if (aVal == null) return sortBy.direction === \"asc\" ? -1 : 1;\r\n if (bVal == null) return sortBy.direction === \"asc\" ? 1 : -1;\r\n const cmp =\r\n typeof aVal === \"string\" && typeof bVal === \"string\"\r\n ? aVal.localeCompare(bVal)\r\n : aVal < bVal\r\n ? -1\r\n : aVal > bVal\r\n ? 1\r\n : 0;\r\n return sortBy.direction === \"asc\" ? cmp : -cmp;\r\n });\r\n return rows;\r\n };\r\n\r\n const mapAndSort = (rows: Record<string, unknown>[]) => {\r\n const betterAuthRows = rows.map((r) =>\r\n mapRowToBetterAuth(r),\r\n ) as unknown as T[];\r\n return applySort(betterAuthRows);\r\n };\r\n\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] findMany on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n return mapAndSort(pickRows(retry.result));\r\n }\r\n\r\n throw new Error(\r\n `[AthenaAdapter] findMany on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n return mapAndSort(pickRows(first.result));\r\n },\r\n\r\n // ------------------------------------------------------------------\r\n // COUNT\r\n // ------------------------------------------------------------------\r\n count: async ({\r\n model,\r\n where,\r\n }: {\r\n model: string;\r\n where?: WhereClause[];\r\n }) => {\r\n const db = ensureDbClient();\r\n\r\n const snakeMapper = (col: string) =>\r\n hasUppercase(col) ? toSnakeCase(col) : col;\r\n const identityMapper = (col: string) => col;\r\n\r\n const run = async (columnMapper: (col: string) => string) => {\r\n let builder = db.from(model).select();\r\n\r\n if (where) {\r\n for (const clause of where) {\r\n builder = applyWhere(\r\n builder,\r\n clause.field,\r\n clause.operator,\r\n clause.value,\r\n columnMapper,\r\n );\r\n }\r\n }\r\n\r\n const { data: result, error } = await builder;\r\n return { result, error };\r\n };\r\n\r\n const first = await run(snakeMapper);\r\n if (first.error) {\r\n if (isMissingColumnError(first.error)) {\r\n const retry = await run(identityMapper);\r\n if (retry.error) {\r\n throw new Error(\r\n `[AthenaAdapter] count on \"${model}\" failed: ${retry.error}`,\r\n );\r\n }\r\n return Array.isArray(retry.result) ? retry.result.length : 0;\r\n }\r\n throw new Error(\r\n `[AthenaAdapter] count on \"${model}\" failed: ${first.error}`,\r\n );\r\n }\r\n\r\n return Array.isArray(first.result) ? first.result.length : 0;\r\n },\r\n };\r\n },\r\n });\r\n};\r\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport YAML from \"yaml\";\r\n\r\nexport type AthenaGlobalConfig = {\r\n athena: {\r\n url: string;\r\n apiKey: string;\r\n client?: string;\r\n };\r\n};\r\n\r\n// Defaults written to `config.yaml` if it doesn't exist.\r\n// These values are intentionally placeholders; the adapter will throw if\r\n// `url`/`apiKey` are still unset when used.\r\nexport const defaultAthenaGlobalConfig: AthenaGlobalConfig = {\r\n athena: {\r\n url: \"http://localhost:3000\",\r\n apiKey: \"\",\r\n client: \"better-auth\",\r\n },\r\n};\r\n\r\nexport const DEFAULT_CONFIG_FILENAME = \"config.yaml\";\r\n\r\nfunction resolveConfigPath(configPath?: string): string {\r\n if (configPath) return path.resolve(configPath);\r\n return path.resolve(process.cwd(), DEFAULT_CONFIG_FILENAME);\r\n}\r\n\r\nlet cached: AthenaGlobalConfig | null = null;\r\nlet cachedConfigPath: string | null = null;\r\nlet version = 0;\r\n\r\nlet watcher: fs.FSWatcher | null = null;\r\n\r\nfunction isObject(value: unknown): value is Record<string, unknown> {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\r\n}\r\n\r\nfunction deepMerge<T extends Record<string, unknown>>(base: T, partial: unknown): T {\r\n if (!isObject(partial)) return base;\r\n const out: Record<string, unknown> = { ...base };\r\n for (const [k, v] of Object.entries(partial)) {\r\n if (v && isObject(v) && isObject(out[k])) {\r\n out[k] = deepMerge(out[k] as Record<string, unknown>, v);\r\n } else {\r\n out[k] = v;\r\n }\r\n }\r\n return out as T;\r\n}\r\n\r\nfunction ensureConfigFile(configPath: string): void {\r\n const dir = path.dirname(configPath);\r\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\r\n\r\n if (!fs.existsSync(configPath)) {\r\n const yaml = YAML.stringify(defaultAthenaGlobalConfig);\r\n fs.writeFileSync(configPath, yaml, \"utf-8\");\r\n }\r\n}\r\n\r\nfunction readConfigFromDisk(configPath: string): AthenaGlobalConfig {\r\n ensureConfigFile(configPath);\r\n const raw = fs.readFileSync(configPath, \"utf-8\");\r\n const parsed = YAML.parse(raw) as unknown;\r\n return deepMerge(defaultAthenaGlobalConfig, parsed);\r\n}\r\n\r\nfunction startWatcher(configPath: string): void {\r\n // Avoid multiple watchers when multiple adapter instances are created.\r\n if (cachedConfigPath !== null && cachedConfigPath !== configPath && watcher) {\r\n try {\r\n watcher.close();\r\n } catch {\r\n // ignore\r\n }\r\n watcher = null;\r\n }\r\n if (watcher || cachedConfigPath === configPath) return;\r\n\r\n try {\r\n watcher = fs.watch(configPath, { persistent: false }, (event) => {\r\n if (event !== \"change\" && event !== \"rename\") return;\r\n try {\r\n cached = readConfigFromDisk(configPath);\r\n version += 1;\r\n } catch {\r\n // Keep last known good config if reload fails.\r\n }\r\n });\r\n cachedConfigPath = configPath;\r\n } catch {\r\n // If watching isn't supported in the environment, just run without it.\r\n }\r\n}\r\n\r\nexport function getAthenaGlobalConfig(options?: {\r\n configPath?: string;\r\n watch?: boolean;\r\n}): { config: AthenaGlobalConfig; version: number } {\r\n const configPath = resolveConfigPath(options?.configPath);\r\n const shouldWatch = options?.watch ?? true;\r\n\r\n if (!cached || cachedConfigPath !== configPath) {\r\n cached = readConfigFromDisk(configPath);\r\n cachedConfigPath = configPath;\r\n version += 1;\r\n }\r\n\r\n if (shouldWatch) startWatcher(configPath);\r\n\r\n return { config: cached, version };\r\n}\r\n\r\n"],"mappings":"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","names":["version","rows","row"]}
package/package.json CHANGED
@@ -63,5 +63,5 @@
63
63
  },
64
64
  "type": "module",
65
65
  "types": "./dist/index.d.ts",
66
- "version": "1.0.2"
66
+ "version": "1.0.3"
67
67
  }