@proofkit/better-auth 0.3.1-beta.0 → 0.3.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
- import { AdapterDebugLogs, CleanedWhere } from 'better-auth/adapters';
1
+ import { CleanedWhere, DBAdapterDebugLogOption } from 'better-auth/adapters';
2
2
  import { FmOdataConfig } from './odata.js';
3
- interface FileMakerAdapterConfig {
4
- debugLogs?: AdapterDebugLogs;
3
+ export interface FileMakerAdapterConfig {
4
+ debugLogs?: DBAdapterDebugLogOption;
5
5
  usePlural?: boolean;
6
6
  odata: FmOdataConfig;
7
7
  }
@@ -9,5 +9,4 @@ export interface AdapterOptions {
9
9
  config: FileMakerAdapterConfig;
10
10
  }
11
11
  export declare function parseWhere(where?: CleanedWhere[]): string;
12
- export declare const FileMakerAdapter: (_config?: FileMakerAdapterConfig) => (options: import('better-auth').BetterAuthOptions) => import('better-auth').Adapter;
13
- export {};
12
+ export declare const FileMakerAdapter: (_config?: FileMakerAdapterConfig) => import('better-auth/adapters').AdapterFactory;
@@ -117,7 +117,7 @@ const FileMakerAdapter = (_config = defaultConfig) => {
117
117
  ...config.odata,
118
118
  logging: config.debugLogs ? "verbose" : "none"
119
119
  });
120
- return createAdapter({
120
+ const adapterFactory = createAdapter({
121
121
  config: {
122
122
  adapterId: "filemaker",
123
123
  adapterName: "FileMaker",
@@ -314,6 +314,8 @@ const FileMakerAdapter = (_config = defaultConfig) => {
314
314
  };
315
315
  }
316
316
  });
317
+ adapterFactory.filemakerConfig = config;
318
+ return adapterFactory;
317
319
  };
318
320
  export {
319
321
  FileMakerAdapter,
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","sources":["../../src/adapter.ts"],"sourcesContent":["/** biome-ignore-all lint/suspicious/noExplicitAny: library code */\nimport { logger } from \"better-auth\";\nimport { type AdapterDebugLogs, type CleanedWhere, createAdapter } from \"better-auth/adapters\";\nimport buildQuery from \"odata-query\";\nimport { prettifyError, z } from \"zod/v4\";\nimport { createRawFetch, type FmOdataConfig } from \"./odata\";\n\nconst configSchema = z.object({\n debugLogs: z.unknown().optional(),\n usePlural: z.boolean().optional(),\n odata: z.object({\n serverUrl: z.url(),\n auth: z.union([z.object({ username: z.string(), password: z.string() }), z.object({ apiKey: z.string() })]),\n database: z.string().endsWith(\".fmp12\"),\n }),\n});\n\ninterface FileMakerAdapterConfig {\n /**\n * Helps you debug issues with the adapter.\n */\n debugLogs?: AdapterDebugLogs;\n /**\n * If the table names in the schema are plural.\n */\n usePlural?: boolean;\n\n /**\n * Connection details for the FileMaker server.\n */\n odata: FmOdataConfig;\n}\n\nexport interface AdapterOptions {\n config: FileMakerAdapterConfig;\n}\n\nconst defaultConfig: Required<FileMakerAdapterConfig> = {\n debugLogs: false,\n usePlural: false,\n odata: {\n serverUrl: \"\",\n auth: { username: \"\", password: \"\" },\n database: \"\",\n },\n};\n\n// Regex patterns for field validation and ISO date detection\nconst FIELD_SPECIAL_CHARS_REGEX = /[\\s_]/;\nconst ISO_DATE_REGEX = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z?$/;\n\n/**\n * Parse the where clause to an OData filter string.\n * @param where - The where clause to parse.\n * @returns The OData filter string.\n * @internal\n */\nexport function parseWhere(where?: CleanedWhere[]): string {\n if (!where || where.length === 0) {\n return \"\";\n }\n\n // Helper to quote field names with special chars or if field is 'id'\n function quoteField(field: string, value?: any) {\n // Never quote for null or date values (per test expectations)\n if (value === null || value instanceof Date) {\n return field;\n }\n // Always quote if field is 'id' or has space or underscore\n if (field === \"id\" || FIELD_SPECIAL_CHARS_REGEX.test(field)) {\n return `\"${field}\"`;\n }\n return field;\n }\n\n // Helper to format values for OData\n function formatValue(value: any): string {\n if (value === null) {\n return \"null\";\n }\n if (typeof value === \"boolean\") {\n return value ? \"true\" : \"false\";\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (Array.isArray(value)) {\n return `(${value.map(formatValue).join(\",\")})`;\n }\n\n // Handle strings - check if it's an ISO date string first\n if (typeof value === \"string\") {\n // Check if it's an ISO date string (YYYY-MM-DDTHH:mm:ss.sssZ format)\n if (ISO_DATE_REGEX.test(value)) {\n return value; // Return ISO date strings without quotes\n }\n return `'${value.replace(/'/g, \"''\")}'`; // Regular strings get quotes\n }\n\n return value?.toString() ?? \"\";\n }\n\n // Map our operators to OData\n const opMap: Record<string, string> = {\n eq: \"eq\",\n ne: \"ne\",\n lt: \"lt\",\n lte: \"le\",\n gt: \"gt\",\n gte: \"ge\",\n };\n\n // Build each clause\n const clauses: string[] = [];\n for (let i = 0; i < where.length; i++) {\n const cond = where[i];\n if (!cond) {\n continue;\n }\n const field = quoteField(cond.field, cond.value);\n let clause = \"\";\n switch (cond.operator) {\n case \"eq\":\n case \"ne\":\n case \"lt\":\n case \"lte\":\n case \"gt\":\n case \"gte\":\n clause = `${field} ${opMap[cond.operator]} ${formatValue(cond.value)}`;\n break;\n case \"in\":\n if (Array.isArray(cond.value)) {\n clause = cond.value.map((v) => `${field} eq ${formatValue(v)}`).join(\" or \");\n clause = `(${clause})`;\n }\n break;\n case \"contains\":\n clause = `contains(${field}, ${formatValue(cond.value)})`;\n break;\n case \"starts_with\":\n clause = `startswith(${field}, ${formatValue(cond.value)})`;\n break;\n case \"ends_with\":\n clause = `endswith(${field}, ${formatValue(cond.value)})`;\n break;\n default:\n clause = `${field} eq ${formatValue(cond.value)}`;\n }\n clauses.push(clause);\n // Add connector if not last\n if (i < where.length - 1) {\n clauses.push((cond.connector || \"and\").toLowerCase());\n }\n }\n return clauses.join(\" \");\n}\n\nexport const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig) => {\n const parsed = configSchema.loose().safeParse(_config);\n\n if (!parsed.success) {\n throw new Error(`Invalid configuration: ${prettifyError(parsed.error)}`);\n }\n const config = parsed.data;\n\n const { fetch } = createRawFetch({\n ...config.odata,\n logging: config.debugLogs ? \"verbose\" : \"none\",\n });\n\n return createAdapter({\n config: {\n adapterId: \"filemaker\",\n adapterName: \"FileMaker\",\n usePlural: config.usePlural ?? false, // Whether the table names in the schema are plural.\n debugLogs: config.debugLogs ?? false, // Whether to enable debug logs.\n supportsJSON: false, // Whether the database supports JSON. (Default: false)\n supportsDates: false, // Whether the database supports dates. (Default: true)\n supportsBooleans: false, // Whether the database supports booleans. (Default: true)\n supportsNumericIds: false, // Whether the database supports auto-incrementing numeric IDs. (Default: true)\n },\n adapter: () => {\n return {\n create: async ({ data, model }) => {\n if (model === \"session\") {\n console.log(\"session\", data);\n }\n\n const result = await fetch(`/${model}`, {\n method: \"POST\",\n body: data,\n output: z.looseObject({ id: z.string() }),\n });\n\n if (result.error) {\n throw new Error(\"Failed to create record\");\n }\n\n return result.data as any;\n },\n count: async ({ model, where }) => {\n const filter = parseWhere(where);\n logger.debug(\"$filter\", filter);\n\n const query = buildQuery({\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const result = await fetch(`/${model}/$count${query}`, {\n method: \"GET\",\n output: z.object({ value: z.number() }),\n });\n if (!result.data) {\n throw new Error(\"Failed to count records\");\n }\n return (result.data?.value as any) ?? 0;\n },\n findOne: async ({ model, where }) => {\n const filter = parseWhere(where);\n logger.debug(\"$filter\", filter);\n\n const query = buildQuery({\n top: 1,\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const result = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.any()) }),\n });\n if (result.error) {\n throw new Error(\"Failed to find record\");\n }\n return (result.data?.value?.[0] as any) ?? null;\n },\n findMany: async ({ model, where, limit, offset, sortBy }) => {\n const filter = parseWhere(where);\n logger.debug(\"FIND MANY\", { where, filter });\n\n const query = buildQuery({\n top: limit,\n skip: offset,\n orderBy: sortBy ? `${sortBy.field} ${sortBy.direction ?? \"asc\"}` : undefined,\n filter: filter.length > 0 ? filter : undefined,\n });\n logger.debug(\"QUERY\", query);\n\n const result = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.any()) }),\n });\n logger.debug(\"RESULT\", result);\n\n if (result.error) {\n throw new Error(\"Failed to find records\");\n }\n\n return (result.data?.value as any) ?? [];\n },\n delete: async ({ model, where }) => {\n const filter = parseWhere(where);\n console.log(\"DELETE\", { model, where, filter });\n logger.debug(\"$filter\", filter);\n\n // Find a single id matching the filter\n const query = buildQuery({\n top: 1,\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const toDelete = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.object({ id: z.string() })) }),\n });\n\n const id = toDelete.data?.value?.[0]?.id;\n if (!id) {\n // Nothing to delete\n return;\n }\n\n const result = await fetch(`/${model}('${id}')`, {\n method: \"DELETE\",\n });\n if (result.error) {\n console.log(\"DELETE ERROR\", result.error);\n throw new Error(\"Failed to delete record\");\n }\n },\n deleteMany: async ({ model, where }) => {\n const filter = parseWhere(where);\n console.log(\"DELETE MANY\", { model, where, filter });\n\n // Find all ids matching the filter\n const query = buildQuery({\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const rows = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.object({ id: z.string() })) }),\n });\n\n const ids = rows.data?.value?.map((r: any) => r.id) ?? [];\n let deleted = 0;\n for (const id of ids) {\n const res = await fetch(`/${model}('${id}')`, {\n method: \"DELETE\",\n });\n if (!res.error) {\n deleted++;\n }\n }\n return deleted;\n },\n update: async ({ model, where, update }) => {\n const filter = parseWhere(where);\n logger.debug(\"UPDATE\", { model, where, update });\n logger.debug(\"$filter\", filter);\n // Find one id to update\n const query = buildQuery({\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const existing = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.object({ id: z.string() })) }),\n });\n logger.debug(\"EXISTING\", existing.data);\n\n const id = existing.data?.value?.[0]?.id;\n if (!id) {\n return null;\n }\n\n const patchRes = await fetch(`/${model}('${id}')`, {\n method: \"PATCH\",\n body: update,\n });\n logger.debug(\"PATCH RES\", patchRes.data);\n if (patchRes.error) {\n return null;\n }\n\n // Read back the updated record\n const readBack = await fetch(`/${model}('${id}')`, {\n method: \"GET\",\n output: z.record(z.string(), z.unknown()),\n });\n logger.debug(\"READ BACK\", readBack.data);\n return (readBack.data as any) ?? null;\n },\n updateMany: async ({ model, where, update }) => {\n const filter = parseWhere(where);\n // Find all ids matching the filter\n const query = buildQuery({\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const rows = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.object({ id: z.string() })) }),\n });\n\n const ids = rows.data?.value?.map((r: any) => r.id) ?? [];\n let updated = 0;\n for (const id of ids) {\n const res = await fetch(`/${model}('${id}')`, {\n method: \"PATCH\",\n body: update,\n });\n if (!res.error) {\n updated++;\n }\n }\n return updated as any;\n },\n };\n },\n });\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.js","sources":["../../src/adapter.ts"],"sourcesContent":["/** biome-ignore-all lint/suspicious/noExplicitAny: library code */\nimport { logger } from \"better-auth\";\nimport { type CleanedWhere, createAdapter, type DBAdapterDebugLogOption } from \"better-auth/adapters\";\nimport buildQuery from \"odata-query\";\nimport { prettifyError, z } from \"zod/v4\";\nimport { createRawFetch, type FmOdataConfig } from \"./odata\";\n\nconst configSchema = z.object({\n debugLogs: z.unknown().optional(),\n usePlural: z.boolean().optional(),\n odata: z.object({\n serverUrl: z.url(),\n auth: z.union([z.object({ username: z.string(), password: z.string() }), z.object({ apiKey: z.string() })]),\n database: z.string().endsWith(\".fmp12\"),\n }),\n});\n\nexport interface FileMakerAdapterConfig {\n /**\n * Helps you debug issues with the adapter.\n */\n debugLogs?: DBAdapterDebugLogOption;\n /**\n * If the table names in the schema are plural.\n */\n usePlural?: boolean;\n\n /**\n * Connection details for the FileMaker server.\n */\n odata: FmOdataConfig;\n}\n\nexport interface AdapterOptions {\n config: FileMakerAdapterConfig;\n}\n\nconst defaultConfig: Required<FileMakerAdapterConfig> = {\n debugLogs: false,\n usePlural: false,\n odata: {\n serverUrl: \"\",\n auth: { username: \"\", password: \"\" },\n database: \"\",\n },\n};\n\n// Regex patterns for field validation and ISO date detection\nconst FIELD_SPECIAL_CHARS_REGEX = /[\\s_]/;\nconst ISO_DATE_REGEX = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z?$/;\n\n/**\n * Parse the where clause to an OData filter string.\n * @param where - The where clause to parse.\n * @returns The OData filter string.\n * @internal\n */\nexport function parseWhere(where?: CleanedWhere[]): string {\n if (!where || where.length === 0) {\n return \"\";\n }\n\n // Helper to quote field names with special chars or if field is 'id'\n function quoteField(field: string, value?: any) {\n // Never quote for null or date values (per test expectations)\n if (value === null || value instanceof Date) {\n return field;\n }\n // Always quote if field is 'id' or has space or underscore\n if (field === \"id\" || FIELD_SPECIAL_CHARS_REGEX.test(field)) {\n return `\"${field}\"`;\n }\n return field;\n }\n\n // Helper to format values for OData\n function formatValue(value: any): string {\n if (value === null) {\n return \"null\";\n }\n if (typeof value === \"boolean\") {\n return value ? \"true\" : \"false\";\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (Array.isArray(value)) {\n return `(${value.map(formatValue).join(\",\")})`;\n }\n\n // Handle strings - check if it's an ISO date string first\n if (typeof value === \"string\") {\n // Check if it's an ISO date string (YYYY-MM-DDTHH:mm:ss.sssZ format)\n if (ISO_DATE_REGEX.test(value)) {\n return value; // Return ISO date strings without quotes\n }\n return `'${value.replace(/'/g, \"''\")}'`; // Regular strings get quotes\n }\n\n return value?.toString() ?? \"\";\n }\n\n // Map our operators to OData\n const opMap: Record<string, string> = {\n eq: \"eq\",\n ne: \"ne\",\n lt: \"lt\",\n lte: \"le\",\n gt: \"gt\",\n gte: \"ge\",\n };\n\n // Build each clause\n const clauses: string[] = [];\n for (let i = 0; i < where.length; i++) {\n const cond = where[i];\n if (!cond) {\n continue;\n }\n const field = quoteField(cond.field, cond.value);\n let clause = \"\";\n switch (cond.operator) {\n case \"eq\":\n case \"ne\":\n case \"lt\":\n case \"lte\":\n case \"gt\":\n case \"gte\":\n clause = `${field} ${opMap[cond.operator]} ${formatValue(cond.value)}`;\n break;\n case \"in\":\n if (Array.isArray(cond.value)) {\n clause = cond.value.map((v) => `${field} eq ${formatValue(v)}`).join(\" or \");\n clause = `(${clause})`;\n }\n break;\n case \"contains\":\n clause = `contains(${field}, ${formatValue(cond.value)})`;\n break;\n case \"starts_with\":\n clause = `startswith(${field}, ${formatValue(cond.value)})`;\n break;\n case \"ends_with\":\n clause = `endswith(${field}, ${formatValue(cond.value)})`;\n break;\n default:\n clause = `${field} eq ${formatValue(cond.value)}`;\n }\n clauses.push(clause);\n // Add connector if not last\n if (i < where.length - 1) {\n clauses.push((cond.connector || \"and\").toLowerCase());\n }\n }\n return clauses.join(\" \");\n}\n\nexport const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig) => {\n const parsed = configSchema.loose().safeParse(_config);\n\n if (!parsed.success) {\n throw new Error(`Invalid configuration: ${prettifyError(parsed.error)}`);\n }\n const config = parsed.data;\n\n const { fetch } = createRawFetch({\n ...config.odata,\n logging: config.debugLogs ? \"verbose\" : \"none\",\n });\n\n const adapterFactory = createAdapter({\n config: {\n adapterId: \"filemaker\",\n adapterName: \"FileMaker\",\n usePlural: config.usePlural ?? false, // Whether the table names in the schema are plural.\n debugLogs: config.debugLogs ?? false, // Whether to enable debug logs.\n supportsJSON: false, // Whether the database supports JSON. (Default: false)\n supportsDates: false, // Whether the database supports dates. (Default: true)\n supportsBooleans: false, // Whether the database supports booleans. (Default: true)\n supportsNumericIds: false, // Whether the database supports auto-incrementing numeric IDs. (Default: true)\n },\n adapter: () => {\n return {\n create: async ({ data, model }) => {\n if (model === \"session\") {\n console.log(\"session\", data);\n }\n\n const result = await fetch(`/${model}`, {\n method: \"POST\",\n body: data,\n output: z.looseObject({ id: z.string() }),\n });\n\n if (result.error) {\n throw new Error(\"Failed to create record\");\n }\n\n return result.data as any;\n },\n count: async ({ model, where }) => {\n const filter = parseWhere(where);\n logger.debug(\"$filter\", filter);\n\n const query = buildQuery({\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const result = await fetch(`/${model}/$count${query}`, {\n method: \"GET\",\n output: z.object({ value: z.number() }),\n });\n if (!result.data) {\n throw new Error(\"Failed to count records\");\n }\n return (result.data?.value as any) ?? 0;\n },\n findOne: async ({ model, where }) => {\n const filter = parseWhere(where);\n logger.debug(\"$filter\", filter);\n\n const query = buildQuery({\n top: 1,\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const result = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.any()) }),\n });\n if (result.error) {\n throw new Error(\"Failed to find record\");\n }\n return (result.data?.value?.[0] as any) ?? null;\n },\n findMany: async ({ model, where, limit, offset, sortBy }) => {\n const filter = parseWhere(where);\n logger.debug(\"FIND MANY\", { where, filter });\n\n const query = buildQuery({\n top: limit,\n skip: offset,\n orderBy: sortBy ? `${sortBy.field} ${sortBy.direction ?? \"asc\"}` : undefined,\n filter: filter.length > 0 ? filter : undefined,\n });\n logger.debug(\"QUERY\", query);\n\n const result = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.any()) }),\n });\n logger.debug(\"RESULT\", result);\n\n if (result.error) {\n throw new Error(\"Failed to find records\");\n }\n\n return (result.data?.value as any) ?? [];\n },\n delete: async ({ model, where }) => {\n const filter = parseWhere(where);\n console.log(\"DELETE\", { model, where, filter });\n logger.debug(\"$filter\", filter);\n\n // Find a single id matching the filter\n const query = buildQuery({\n top: 1,\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const toDelete = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.object({ id: z.string() })) }),\n });\n\n const id = toDelete.data?.value?.[0]?.id;\n if (!id) {\n // Nothing to delete\n return;\n }\n\n const result = await fetch(`/${model}('${id}')`, {\n method: \"DELETE\",\n });\n if (result.error) {\n console.log(\"DELETE ERROR\", result.error);\n throw new Error(\"Failed to delete record\");\n }\n },\n deleteMany: async ({ model, where }) => {\n const filter = parseWhere(where);\n console.log(\"DELETE MANY\", { model, where, filter });\n\n // Find all ids matching the filter\n const query = buildQuery({\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const rows = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.object({ id: z.string() })) }),\n });\n\n const ids = rows.data?.value?.map((r: any) => r.id) ?? [];\n let deleted = 0;\n for (const id of ids) {\n const res = await fetch(`/${model}('${id}')`, {\n method: \"DELETE\",\n });\n if (!res.error) {\n deleted++;\n }\n }\n return deleted;\n },\n update: async ({ model, where, update }) => {\n const filter = parseWhere(where);\n logger.debug(\"UPDATE\", { model, where, update });\n logger.debug(\"$filter\", filter);\n // Find one id to update\n const query = buildQuery({\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const existing = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.object({ id: z.string() })) }),\n });\n logger.debug(\"EXISTING\", existing.data);\n\n const id = existing.data?.value?.[0]?.id;\n if (!id) {\n return null;\n }\n\n const patchRes = await fetch(`/${model}('${id}')`, {\n method: \"PATCH\",\n body: update,\n });\n logger.debug(\"PATCH RES\", patchRes.data);\n if (patchRes.error) {\n return null;\n }\n\n // Read back the updated record\n const readBack = await fetch(`/${model}('${id}')`, {\n method: \"GET\",\n output: z.record(z.string(), z.unknown()),\n });\n logger.debug(\"READ BACK\", readBack.data);\n return (readBack.data as any) ?? null;\n },\n updateMany: async ({ model, where, update }) => {\n const filter = parseWhere(where);\n // Find all ids matching the filter\n const query = buildQuery({\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const rows = await fetch(`/${model}${query}`, {\n method: \"GET\",\n output: z.object({ value: z.array(z.object({ id: z.string() })) }),\n });\n\n const ids = rows.data?.value?.map((r: any) => r.id) ?? [];\n let updated = 0;\n for (const id of ids) {\n const res = await fetch(`/${model}('${id}')`, {\n method: \"PATCH\",\n body: update,\n });\n if (!res.error) {\n updated++;\n }\n }\n return updated as any;\n },\n };\n },\n });\n\n // Expose the FileMaker config for CLI access\n (adapterFactory as any).filemakerConfig = config as FileMakerAdapterConfig;\n return adapterFactory;\n};\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"add-svelte-kit-env-modules.js","sources":["../../../../src/better-auth-cli/utils/add-svelte-kit-env-modules.ts"],"sourcesContent":["export function addSvelteKitEnvModules(aliases: Record<string, string>) {\n aliases[\"$env/dynamic/private\"] = createDataUriModule(createDynamicEnvModule());\n aliases[\"$env/dynamic/public\"] = createDataUriModule(createDynamicEnvModule());\n aliases[\"$env/static/private\"] = createDataUriModule(createStaticEnvModule(filterPrivateEnv(\"PUBLIC_\", \"\")));\n aliases[\"$env/static/public\"] = createDataUriModule(createStaticEnvModule(filterPublicEnv(\"PUBLIC_\", \"\")));\n}\n\nfunction createDataUriModule(module: string) {\n return `data:text/javascript;charset=utf-8,${encodeURIComponent(module)}`;\n}\n\nfunction createStaticEnvModule(env: Record<string, string>) {\n const declarations = Object.keys(env)\n .filter((k) => validIdentifier.test(k) && !reserved.has(k))\n .map((k) => `export const ${k} = ${JSON.stringify(env[k])};`);\n\n return `\n ${declarations.join(\"\\n\")}\n // jiti dirty hack: .unknown\n `;\n}\n\nfunction createDynamicEnvModule() {\n return `\n export const env = process.env;\n // jiti dirty hack: .unknown\n `;\n}\n\nexport function filterPrivateEnv(publicPrefix: string, privatePrefix: string) {\n return Object.fromEntries(\n Object.entries(process.env).filter(\n ([k]) => k.startsWith(privatePrefix) && (publicPrefix === \"\" || !k.startsWith(publicPrefix)),\n ),\n ) as Record<string, string>;\n}\n\nexport function filterPublicEnv(publicPrefix: string, privatePrefix: string) {\n return Object.fromEntries(\n Object.entries(process.env).filter(\n ([k]) => k.startsWith(publicPrefix) && (privatePrefix === \"\" || !k.startsWith(privatePrefix)),\n ),\n ) as Record<string, string>;\n}\n\nconst validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;\nconst reserved = new Set([\n \"do\",\n \"if\",\n \"in\",\n \"for\",\n \"let\",\n \"new\",\n \"try\",\n \"var\",\n \"case\",\n \"else\",\n \"enum\",\n \"eval\",\n \"null\",\n \"this\",\n \"true\",\n \"void\",\n \"with\",\n \"await\",\n \"break\",\n \"catch\",\n \"class\",\n \"const\",\n \"false\",\n \"super\",\n \"throw\",\n \"while\",\n \"yield\",\n \"delete\",\n \"export\",\n \"import\",\n \"public\",\n \"return\",\n \"static\",\n \"switch\",\n \"typeof\",\n \"default\",\n \"extends\",\n \"finally\",\n \"package\",\n \"private\",\n \"continue\",\n \"debugger\",\n \"function\",\n \"arguments\",\n \"interface\",\n \"protected\",\n \"implements\",\n \"instanceof\",\n]);\n"],"names":[],"mappings":"AAAO,SAAS,uBAAuB,SAAiC;AACtE,UAAQ,sBAAsB,IAAI,oBAAoB,uBAAA,CAAwB;AAC9E,UAAQ,qBAAqB,IAAI,oBAAoB,uBAAA,CAAwB;AACrE,UAAA,qBAAqB,IAAI,oBAAoB,sBAAsB,iBAAiB,WAAW,EAAE,CAAC,CAAC;AACnG,UAAA,oBAAoB,IAAI,oBAAoB,sBAAsB,gBAAgB,WAAW,EAAE,CAAC,CAAC;AAC3G;AAEA,SAAS,oBAAoB,QAAgB;AACpC,SAAA,sCAAsC,mBAAmB,MAAM,CAAC;AACzE;AAEA,SAAS,sBAAsB,KAA6B;AAC1D,QAAM,eAAe,OAAO,KAAK,GAAG,EACjC,OAAO,CAAC,MAAM,gBAAgB,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,EACzD,IAAI,CAAC,MAAM,gBAAgB,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,CAAC,GAAG;AAEvD,SAAA;AAAA,IACL,aAAa,KAAK,IAAI,CAAC;AAAA;AAAA;AAG3B;AAEA,SAAS,yBAAyB;AACzB,SAAA;AAAA;AAAA;AAAA;AAIT;AAEgB,SAAA,iBAAiB,cAAsB,eAAuB;AAC5E,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,QAAQ,GAAG,EAAE;AAAA,MAC1B,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,aAAa,KAA6B,CAAC,EAAE,WAAW,YAAY;AAAA,IAAA;AAAA,EAE9F;AACF;AAEgB,SAAA,gBAAgB,cAAsB,eAAuB;AAC3E,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,QAAQ,GAAG,EAAE;AAAA,MAC1B,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,YAAY,KAAM,kBAAkB;AAAA,IAAiC;AAAA,EAE/F;AACF;AAEA,MAAM,kBAAkB;AACxB,MAAM,+BAAe,IAAI;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;"}
1
+ {"version":3,"file":"add-svelte-kit-env-modules.js","sources":["../../../../src/better-auth-cli/utils/add-svelte-kit-env-modules.ts"],"sourcesContent":["export function addSvelteKitEnvModules(aliases: Record<string, string>) {\n aliases[\"$env/dynamic/private\"] = createDataUriModule(createDynamicEnvModule());\n aliases[\"$env/dynamic/public\"] = createDataUriModule(createDynamicEnvModule());\n aliases[\"$env/static/private\"] = createDataUriModule(createStaticEnvModule(filterPrivateEnv(\"PUBLIC_\", \"\")));\n aliases[\"$env/static/public\"] = createDataUriModule(createStaticEnvModule(filterPublicEnv(\"PUBLIC_\", \"\")));\n}\n\nfunction createDataUriModule(module: string) {\n return `data:text/javascript;charset=utf-8,${encodeURIComponent(module)}`;\n}\n\nfunction createStaticEnvModule(env: Record<string, string>) {\n const declarations = Object.keys(env)\n .filter((k) => validIdentifier.test(k) && !reserved.has(k))\n .map((k) => `export const ${k} = ${JSON.stringify(env[k])};`);\n\n return `\n ${declarations.join(\"\\n\")}\n // jiti dirty hack: .unknown\n `;\n}\n\nfunction createDynamicEnvModule() {\n return `\n export const env = process.env;\n // jiti dirty hack: .unknown\n `;\n}\n\nexport function filterPrivateEnv(publicPrefix: string, privatePrefix: string) {\n return Object.fromEntries(\n Object.entries(process.env).filter(\n ([k]) => k.startsWith(privatePrefix) && (publicPrefix === \"\" || !k.startsWith(publicPrefix)),\n ),\n ) as Record<string, string>;\n}\n\nexport function filterPublicEnv(publicPrefix: string, privatePrefix: string) {\n return Object.fromEntries(\n Object.entries(process.env).filter(\n ([k]) => k.startsWith(publicPrefix) && (privatePrefix === \"\" || !k.startsWith(privatePrefix)),\n ),\n ) as Record<string, string>;\n}\n\nconst validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;\nconst reserved = new Set([\n \"do\",\n \"if\",\n \"in\",\n \"for\",\n \"let\",\n \"new\",\n \"try\",\n \"var\",\n \"case\",\n \"else\",\n \"enum\",\n \"eval\",\n \"null\",\n \"this\",\n \"true\",\n \"void\",\n \"with\",\n \"await\",\n \"break\",\n \"catch\",\n \"class\",\n \"const\",\n \"false\",\n \"super\",\n \"throw\",\n \"while\",\n \"yield\",\n \"delete\",\n \"export\",\n \"import\",\n \"public\",\n \"return\",\n \"static\",\n \"switch\",\n \"typeof\",\n \"default\",\n \"extends\",\n \"finally\",\n \"package\",\n \"private\",\n \"continue\",\n \"debugger\",\n \"function\",\n \"arguments\",\n \"interface\",\n \"protected\",\n \"implements\",\n \"instanceof\",\n]);\n"],"names":[],"mappings":"AAAO,SAAS,uBAAuB,SAAiC;AACtE,UAAQ,sBAAsB,IAAI,oBAAoB,uBAAA,CAAwB;AAC9E,UAAQ,qBAAqB,IAAI,oBAAoB,uBAAA,CAAwB;AAC7E,UAAQ,qBAAqB,IAAI,oBAAoB,sBAAsB,iBAAiB,WAAW,EAAE,CAAC,CAAC;AAC3G,UAAQ,oBAAoB,IAAI,oBAAoB,sBAAsB,gBAAgB,WAAW,EAAE,CAAC,CAAC;AAC3G;AAEA,SAAS,oBAAoB,QAAgB;AAC3C,SAAO,sCAAsC,mBAAmB,MAAM,CAAC;AACzE;AAEA,SAAS,sBAAsB,KAA6B;AAC1D,QAAM,eAAe,OAAO,KAAK,GAAG,EACjC,OAAO,CAAC,MAAM,gBAAgB,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,EACzD,IAAI,CAAC,MAAM,gBAAgB,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,CAAC,GAAG;AAE9D,SAAO;AAAA,IACL,aAAa,KAAK,IAAI,CAAC;AAAA;AAAA;AAG3B;AAEA,SAAS,yBAAyB;AAChC,SAAO;AAAA;AAAA;AAAA;AAIT;AAEO,SAAS,iBAAiB,cAAsB,eAAuB;AAC5E,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,QAAQ,GAAG,EAAE;AAAA,MAC1B,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,aAAa,KAA6B,CAAC,EAAE,WAAW,YAAY;AAAA,IAAA;AAAA,EAC5F;AAEJ;AAEO,SAAS,gBAAgB,cAAsB,eAAuB;AAC3E,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,QAAQ,GAAG,EAAE;AAAA,MAC1B,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,YAAY,KAAM,kBAAkB;AAAA,IAAiC;AAAA,EAC7F;AAEJ;AAEA,MAAM,kBAAkB;AACxB,MAAM,+BAAe,IAAI;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;"}
@@ -1 +1 @@
1
- {"version":3,"file":"get-config.js","sources":["../../../../src/better-auth-cli/utils/get-config.ts"],"sourcesContent":["import fs, { existsSync } from \"node:fs\";\nimport path from \"node:path\";\n// @ts-expect-error not typed\nimport babelPresetReact from \"@babel/preset-react\";\n// @ts-expect-error not typed\nimport babelPresetTypeScript from \"@babel/preset-typescript\";\nimport type { BetterAuthOptions } from \"better-auth\";\nimport { BetterAuthError, logger } from \"better-auth\";\nimport { loadConfig } from \"c12\";\nimport { addSvelteKitEnvModules } from \"./add-svelte-kit-env-modules\";\nimport { getTsconfigInfo } from \"./get-tsconfig-info\";\n\nlet possiblePaths = [\"auth.ts\", \"auth.tsx\", \"auth.js\", \"auth.jsx\", \"auth.server.js\", \"auth.server.ts\"];\n\npossiblePaths = [\n ...possiblePaths,\n ...possiblePaths.map((it) => `lib/server/${it}`),\n ...possiblePaths.map((it) => `server/${it}`),\n ...possiblePaths.map((it) => `lib/${it}`),\n ...possiblePaths.map((it) => `utils/${it}`),\n];\npossiblePaths = [\n ...possiblePaths,\n ...possiblePaths.map((it) => `src/${it}`),\n ...possiblePaths.map((it) => `app/${it}`),\n];\n\nfunction getPathAliases(cwd: string): Record<string, string> | null {\n const tsConfigPath = path.join(cwd, \"tsconfig.json\");\n if (!fs.existsSync(tsConfigPath)) {\n return null;\n }\n try {\n const tsConfig = getTsconfigInfo(cwd);\n const { paths = {}, baseUrl = \".\" } = tsConfig.compilerOptions || {};\n const result: Record<string, string> = {};\n const obj = Object.entries(paths) as [string, string[]][];\n for (const [alias, aliasPaths] of obj) {\n for (const aliasedPath of aliasPaths) {\n const resolvedBaseUrl = path.join(cwd, baseUrl);\n const finalAlias = alias.slice(-1) === \"*\" ? alias.slice(0, -1) : alias;\n const finalAliasedPath = aliasedPath.slice(-1) === \"*\" ? aliasedPath.slice(0, -1) : aliasedPath;\n\n result[finalAlias || \"\"] = path.join(resolvedBaseUrl, finalAliasedPath);\n }\n }\n addSvelteKitEnvModules(result);\n return result;\n } catch (error) {\n console.error(error);\n throw new BetterAuthError(\"Error parsing tsconfig.json\");\n }\n}\n/**\n * .tsx files are not supported by Jiti.\n */\nconst jitiOptions = (cwd: string) => {\n const alias = getPathAliases(cwd) || {};\n return {\n transformOptions: {\n babel: {\n presets: [\n [\n babelPresetTypeScript,\n {\n isTSX: true,\n allExtensions: true,\n },\n ],\n [babelPresetReact, { runtime: \"automatic\" }],\n ],\n },\n },\n extensions: [\".ts\", \".tsx\", \".js\", \".jsx\"],\n alias,\n };\n};\nexport async function getConfig({\n cwd,\n configPath,\n shouldThrowOnError = false,\n}: {\n cwd: string;\n configPath?: string;\n shouldThrowOnError?: boolean;\n}) {\n try {\n let configFile: BetterAuthOptions | null = null;\n if (configPath) {\n let resolvedPath: string = path.join(cwd, configPath);\n if (existsSync(configPath)) {\n resolvedPath = configPath; // If the configPath is a file, use it as is, as it means the path wasn't relative.\n }\n const { config } = await loadConfig<{\n auth: {\n options: BetterAuthOptions;\n };\n default?: {\n options: BetterAuthOptions;\n };\n }>({\n configFile: resolvedPath,\n dotenv: true,\n jitiOptions: jitiOptions(cwd),\n });\n if (!(config.auth || config.default)) {\n if (shouldThrowOnError) {\n throw new Error(\n `Couldn't read your auth config in ${resolvedPath}. Make sure to default export your auth instance or to export as a variable named auth.`,\n );\n }\n logger.error(\n `[#better-auth]: Couldn't read your auth config in ${resolvedPath}. Make sure to default export your auth instance or to export as a variable named auth.`,\n );\n process.exit(1);\n }\n configFile = config.auth?.options || config.default?.options || null;\n }\n\n if (!configFile) {\n for (const possiblePath of possiblePaths) {\n try {\n const { config } = await loadConfig<{\n auth: {\n options: BetterAuthOptions;\n };\n default?: {\n options: BetterAuthOptions;\n };\n }>({\n configFile: possiblePath,\n jitiOptions: jitiOptions(cwd),\n });\n const hasConfig = Object.keys(config).length > 0;\n if (hasConfig) {\n configFile = config.auth?.options || config.default?.options || null;\n if (!configFile) {\n if (shouldThrowOnError) {\n throw new Error(\n \"Couldn't read your auth config. Make sure to default export your auth instance or to export as a variable named auth.\",\n );\n }\n logger.error(\"[#better-auth]: Couldn't read your auth config.\");\n console.log(\"\");\n logger.info(\n \"[#better-auth]: Make sure to default export your auth instance or to export as a variable named auth.\",\n );\n process.exit(1);\n }\n break;\n }\n } catch (e) {\n if (\n typeof e === \"object\" &&\n e &&\n \"message\" in e &&\n typeof e.message === \"string\" &&\n e.message.includes(\"This module cannot be imported from a Client Component module\")\n ) {\n if (shouldThrowOnError) {\n throw new Error(\n `Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`,\n );\n }\n logger.error(\n `Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`,\n );\n process.exit(1);\n }\n if (shouldThrowOnError) {\n throw e;\n }\n logger.error(\"[#better-auth]: Couldn't read your auth config.\", e);\n process.exit(1);\n }\n }\n }\n return configFile;\n } catch (e) {\n if (\n typeof e === \"object\" &&\n e &&\n \"message\" in e &&\n typeof e.message === \"string\" &&\n e.message.includes(\"This module cannot be imported from a Client Component module\")\n ) {\n if (shouldThrowOnError) {\n throw new Error(\n `Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`,\n );\n }\n logger.error(\n `Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`,\n );\n process.exit(1);\n }\n if (shouldThrowOnError) {\n throw e;\n }\n\n logger.error(\"Couldn't read your auth config.\", e);\n process.exit(1);\n }\n}\n\nexport { possiblePaths };\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"get-config.js","sources":["../../../../src/better-auth-cli/utils/get-config.ts"],"sourcesContent":["import fs, { existsSync } from \"node:fs\";\nimport path from \"node:path\";\n// @ts-expect-error not typed\nimport babelPresetReact from \"@babel/preset-react\";\n// @ts-expect-error not typed\nimport babelPresetTypeScript from \"@babel/preset-typescript\";\nimport type { BetterAuthOptions } from \"better-auth\";\nimport { BetterAuthError, logger } from \"better-auth\";\nimport { loadConfig } from \"c12\";\nimport { addSvelteKitEnvModules } from \"./add-svelte-kit-env-modules\";\nimport { getTsconfigInfo } from \"./get-tsconfig-info\";\n\nlet possiblePaths = [\"auth.ts\", \"auth.tsx\", \"auth.js\", \"auth.jsx\", \"auth.server.js\", \"auth.server.ts\"];\n\npossiblePaths = [\n ...possiblePaths,\n ...possiblePaths.map((it) => `lib/server/${it}`),\n ...possiblePaths.map((it) => `server/${it}`),\n ...possiblePaths.map((it) => `lib/${it}`),\n ...possiblePaths.map((it) => `utils/${it}`),\n];\npossiblePaths = [\n ...possiblePaths,\n ...possiblePaths.map((it) => `src/${it}`),\n ...possiblePaths.map((it) => `app/${it}`),\n];\n\nfunction getPathAliases(cwd: string): Record<string, string> | null {\n const tsConfigPath = path.join(cwd, \"tsconfig.json\");\n if (!fs.existsSync(tsConfigPath)) {\n return null;\n }\n try {\n const tsConfig = getTsconfigInfo(cwd);\n const { paths = {}, baseUrl = \".\" } = tsConfig.compilerOptions || {};\n const result: Record<string, string> = {};\n const obj = Object.entries(paths) as [string, string[]][];\n for (const [alias, aliasPaths] of obj) {\n for (const aliasedPath of aliasPaths) {\n const resolvedBaseUrl = path.join(cwd, baseUrl);\n const finalAlias = alias.slice(-1) === \"*\" ? alias.slice(0, -1) : alias;\n const finalAliasedPath = aliasedPath.slice(-1) === \"*\" ? aliasedPath.slice(0, -1) : aliasedPath;\n\n result[finalAlias || \"\"] = path.join(resolvedBaseUrl, finalAliasedPath);\n }\n }\n addSvelteKitEnvModules(result);\n return result;\n } catch (error) {\n console.error(error);\n throw new BetterAuthError(\"Error parsing tsconfig.json\");\n }\n}\n/**\n * .tsx files are not supported by Jiti.\n */\nconst jitiOptions = (cwd: string) => {\n const alias = getPathAliases(cwd) || {};\n return {\n transformOptions: {\n babel: {\n presets: [\n [\n babelPresetTypeScript,\n {\n isTSX: true,\n allExtensions: true,\n },\n ],\n [babelPresetReact, { runtime: \"automatic\" }],\n ],\n },\n },\n extensions: [\".ts\", \".tsx\", \".js\", \".jsx\"],\n alias,\n };\n};\nexport async function getConfig({\n cwd,\n configPath,\n shouldThrowOnError = false,\n}: {\n cwd: string;\n configPath?: string;\n shouldThrowOnError?: boolean;\n}) {\n try {\n let configFile: BetterAuthOptions | null = null;\n if (configPath) {\n let resolvedPath: string = path.join(cwd, configPath);\n if (existsSync(configPath)) {\n resolvedPath = configPath; // If the configPath is a file, use it as is, as it means the path wasn't relative.\n }\n const { config } = await loadConfig<{\n auth: {\n options: BetterAuthOptions;\n };\n default?: {\n options: BetterAuthOptions;\n };\n }>({\n configFile: resolvedPath,\n dotenv: true,\n jitiOptions: jitiOptions(cwd),\n });\n if (!(config.auth || config.default)) {\n if (shouldThrowOnError) {\n throw new Error(\n `Couldn't read your auth config in ${resolvedPath}. Make sure to default export your auth instance or to export as a variable named auth.`,\n );\n }\n logger.error(\n `[#better-auth]: Couldn't read your auth config in ${resolvedPath}. Make sure to default export your auth instance or to export as a variable named auth.`,\n );\n process.exit(1);\n }\n configFile = config.auth?.options || config.default?.options || null;\n }\n\n if (!configFile) {\n for (const possiblePath of possiblePaths) {\n try {\n const { config } = await loadConfig<{\n auth: {\n options: BetterAuthOptions;\n };\n default?: {\n options: BetterAuthOptions;\n };\n }>({\n configFile: possiblePath,\n jitiOptions: jitiOptions(cwd),\n });\n const hasConfig = Object.keys(config).length > 0;\n if (hasConfig) {\n configFile = config.auth?.options || config.default?.options || null;\n if (!configFile) {\n if (shouldThrowOnError) {\n throw new Error(\n \"Couldn't read your auth config. Make sure to default export your auth instance or to export as a variable named auth.\",\n );\n }\n logger.error(\"[#better-auth]: Couldn't read your auth config.\");\n console.log(\"\");\n logger.info(\n \"[#better-auth]: Make sure to default export your auth instance or to export as a variable named auth.\",\n );\n process.exit(1);\n }\n break;\n }\n } catch (e) {\n if (\n typeof e === \"object\" &&\n e &&\n \"message\" in e &&\n typeof e.message === \"string\" &&\n e.message.includes(\"This module cannot be imported from a Client Component module\")\n ) {\n if (shouldThrowOnError) {\n throw new Error(\n `Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`,\n );\n }\n logger.error(\n `Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`,\n );\n process.exit(1);\n }\n if (shouldThrowOnError) {\n throw e;\n }\n logger.error(\"[#better-auth]: Couldn't read your auth config.\", e);\n process.exit(1);\n }\n }\n }\n return configFile;\n } catch (e) {\n if (\n typeof e === \"object\" &&\n e &&\n \"message\" in e &&\n typeof e.message === \"string\" &&\n e.message.includes(\"This module cannot be imported from a Client Component module\")\n ) {\n if (shouldThrowOnError) {\n throw new Error(\n `Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`,\n );\n }\n logger.error(\n `Please remove import 'server-only' from your auth config file temporarily. The CLI cannot resolve the configuration with it included. You can re-add it after running the CLI.`,\n );\n process.exit(1);\n }\n if (shouldThrowOnError) {\n throw e;\n }\n\n logger.error(\"Couldn't read your auth config.\", e);\n process.exit(1);\n }\n}\n\nexport { possiblePaths };\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"get-tsconfig-info.js","sources":["../../../../src/better-auth-cli/utils/get-tsconfig-info.ts"],"sourcesContent":["import path from \"node:path\";\nimport fs from \"fs-extra\";\n\nexport function stripJsonComments(jsonString: string): string {\n return jsonString\n .replace(/\\\\\"|\"(?:\\\\\"|[^\"])*\"|(\\/\\/.*|\\/\\*[\\s\\S]*?\\*\\/)/g, (m, g) => (g ? \"\" : m))\n .replace(/,(?=\\s*[}\\]])/g, \"\");\n}\nexport function getTsconfigInfo(cwd?: string, flatPath?: string) {\n let tsConfigPath: string;\n if (flatPath) {\n tsConfigPath = flatPath;\n } else {\n tsConfigPath = cwd ? path.join(cwd, \"tsconfig.json\") : path.join(\"tsconfig.json\");\n }\n const text = fs.readFileSync(tsConfigPath, \"utf-8\");\n return JSON.parse(stripJsonComments(text));\n}\n"],"names":[],"mappings":";;AAGO,SAAS,kBAAkB,YAA4B;AAC5D,SAAO,WACJ,QAAQ,kDAAkD,CAAC,GAAG,MAAO,IAAI,KAAK,CAAE,EAChF,QAAQ,kBAAkB,EAAE;AACjC;AACgB,SAAA,gBAAgB,KAAc,UAAmB;AAC3D,MAAA;AAGG;AACU,mBAAA,MAAM,KAAK,KAAK,KAAK,eAAe,IAAI,KAAK,KAAK,eAAe;AAAA,EAAA;AAElF,QAAM,OAAO,GAAG,aAAa,cAAc,OAAO;AAClD,SAAO,KAAK,MAAM,kBAAkB,IAAI,CAAC;AAC3C;"}
1
+ {"version":3,"file":"get-tsconfig-info.js","sources":["../../../../src/better-auth-cli/utils/get-tsconfig-info.ts"],"sourcesContent":["import path from \"node:path\";\nimport fs from \"fs-extra\";\n\nexport function stripJsonComments(jsonString: string): string {\n return jsonString\n .replace(/\\\\\"|\"(?:\\\\\"|[^\"])*\"|(\\/\\/.*|\\/\\*[\\s\\S]*?\\*\\/)/g, (m, g) => (g ? \"\" : m))\n .replace(/,(?=\\s*[}\\]])/g, \"\");\n}\nexport function getTsconfigInfo(cwd?: string, flatPath?: string) {\n let tsConfigPath: string;\n if (flatPath) {\n tsConfigPath = flatPath;\n } else {\n tsConfigPath = cwd ? path.join(cwd, \"tsconfig.json\") : path.join(\"tsconfig.json\");\n }\n const text = fs.readFileSync(tsConfigPath, \"utf-8\");\n return JSON.parse(stripJsonComments(text));\n}\n"],"names":[],"mappings":";;AAGO,SAAS,kBAAkB,YAA4B;AAC5D,SAAO,WACJ,QAAQ,kDAAkD,CAAC,GAAG,MAAO,IAAI,KAAK,CAAE,EAChF,QAAQ,kBAAkB,EAAE;AACjC;AACO,SAAS,gBAAgB,KAAc,UAAmB;AAC/D,MAAI;AAGG;AACL,mBAAe,MAAM,KAAK,KAAK,KAAK,eAAe,IAAI,KAAK,KAAK,eAAe;AAAA,EAClF;AACA,QAAM,OAAO,GAAG,aAAa,cAAc,OAAO;AAClD,SAAO,KAAK,MAAM,kBAAkB,IAAI,CAAC;AAC3C;"}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node --no-warnings
2
2
  import { Command } from "@commander-js/extra-typings";
3
3
  import { logger } from "better-auth";
4
- import { getAdapter, getAuthTables } from "better-auth/db";
4
+ import { getAdapter, getSchema } from "better-auth/db";
5
5
  import chalk from "chalk";
6
6
  import fs from "fs-extra";
7
7
  import prompts from "prompts";
@@ -35,8 +35,8 @@ async function main() {
35
35
  logger.error("This generator is only compatible with the FileMaker adapter.");
36
36
  return;
37
37
  }
38
- const betterAuthSchema = getAuthTables(config);
39
- const adapterConfig = adapter.options.config;
38
+ const betterAuthSchema = getSchema(config);
39
+ const adapterConfig = adapter.filemakerConfig;
40
40
  const { fetch } = createRawFetch({
41
41
  ...adapterConfig.odata,
42
42
  auth: (
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node --no-warnings\nimport { Command } from \"@commander-js/extra-typings\";\nimport { logger } from \"better-auth\";\nimport { getAdapter, getAuthTables } from \"better-auth/db\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport prompts from \"prompts\";\nimport type { AdapterOptions } from \"../adapter\";\nimport { getConfig } from \"../better-auth-cli/utils/get-config\";\nimport { executeMigration, planMigration, prettyPrintMigrationPlan } from \"../migrate\";\nimport { createRawFetch } from \"../odata\";\nimport \"dotenv/config\";\n\nasync function main() {\n const program = new Command();\n\n program\n .command(\"migrate\", { isDefault: true })\n .option(\"--cwd <path>\", \"Path to the current working directory\", process.cwd())\n .option(\"--config <path>\", \"Path to the config file\")\n .option(\"-u, --username <username>\", \"Full Access Username\")\n .option(\"-p, --password <password>\", \"Full Access Password\")\n .option(\"-y, --yes\", \"Skip confirmation\", false)\n\n .action(async (options) => {\n const cwd = options.cwd;\n if (!fs.existsSync(cwd)) {\n logger.error(`The directory \"${cwd}\" does not exist.`);\n process.exit(1);\n }\n\n const config = await getConfig({\n cwd,\n configPath: options.config,\n });\n if (!config) {\n logger.error(\n \"No configuration file found. Add a `auth.ts` file to your project or pass the path to the configuration file using the `--config` flag.\",\n );\n return;\n }\n\n const adapter = await getAdapter(config).catch((e) => {\n logger.error(e.message);\n process.exit(1);\n });\n\n if (adapter.id !== \"filemaker\") {\n logger.error(\"This generator is only compatible with the FileMaker adapter.\");\n return;\n }\n\n const betterAuthSchema = getAuthTables(config);\n\n const adapterConfig = (adapter.options as AdapterOptions).config;\n const { fetch } = createRawFetch({\n ...adapterConfig.odata,\n auth:\n // If the username and password are provided in the CLI, use them to authenticate instead of what's in the config file.\n options.username && options.password\n ? {\n username: options.username,\n password: options.password,\n }\n : adapterConfig.odata.auth,\n logging: \"verbose\", // Enable logging for CLI operations\n });\n\n const migrationPlan = await planMigration(fetch, betterAuthSchema, adapterConfig.odata.database);\n\n if (migrationPlan.length === 0) {\n logger.info(\"No changes to apply. Database is up to date.\");\n return;\n }\n\n if (!options.yes) {\n prettyPrintMigrationPlan(migrationPlan);\n\n if (migrationPlan.length > 0) {\n console.log(chalk.gray(\"💡 Tip: You can use the --yes flag to skip this confirmation.\"));\n }\n\n const { confirm } = await prompts({\n type: \"confirm\",\n name: \"confirm\",\n message: \"Apply the above changes to your database?\",\n });\n if (!confirm) {\n logger.error(\"Schema changes not applied.\");\n return;\n }\n }\n\n await executeMigration(fetch, migrationPlan);\n\n logger.info(\"Migration applied successfully.\");\n });\n await program.parseAsync(process.argv);\n process.exit(0);\n}\n\nmain().catch(console.error);\n"],"names":[],"mappings":";;;;;;;;;;;AAaA,eAAe,OAAO;AACd,QAAA,UAAU,IAAI,QAAQ;AAE5B,UACG,QAAQ,WAAW,EAAE,WAAW,MAAM,EACtC,OAAO,gBAAgB,yCAAyC,QAAQ,IAAI,CAAC,EAC7E,OAAO,mBAAmB,yBAAyB,EACnD,OAAO,6BAA6B,sBAAsB,EAC1D,OAAO,6BAA6B,sBAAsB,EAC1D,OAAO,aAAa,qBAAqB,KAAK,EAE9C,OAAO,OAAO,YAAY;AACzB,UAAM,MAAM,QAAQ;AACpB,QAAI,CAAC,GAAG,WAAW,GAAG,GAAG;AAChB,aAAA,MAAM,kBAAkB,GAAG,mBAAmB;AACrD,cAAQ,KAAK,CAAC;AAAA,IAAA;AAGV,UAAA,SAAS,MAAM,UAAU;AAAA,MAC7B;AAAA,MACA,YAAY,QAAQ;AAAA,IAAA,CACrB;AACD,QAAI,CAAC,QAAQ;AACJ,aAAA;AAAA,QACL;AAAA,MACF;AACA;AAAA,IAAA;AAGF,UAAM,UAAU,MAAM,WAAW,MAAM,EAAE,MAAM,CAAC,MAAM;AAC7C,aAAA,MAAM,EAAE,OAAO;AACtB,cAAQ,KAAK,CAAC;AAAA,IAAA,CACf;AAEG,QAAA,QAAQ,OAAO,aAAa;AAC9B,aAAO,MAAM,+DAA+D;AAC5E;AAAA,IAAA;AAGI,UAAA,mBAAmB,cAAc,MAAM;AAEvC,UAAA,gBAAiB,QAAQ,QAA2B;AACpD,UAAA,EAAE,MAAM,IAAI,eAAe;AAAA,MAC/B,GAAG,cAAc;AAAA,MACjB;AAAA;AAAA,QAEE,QAAQ,YAAY,QAAQ,WACxB;AAAA,UACE,UAAU,QAAQ;AAAA,UAClB,UAAU,QAAQ;AAAA,QAAA,IAEpB,cAAc,MAAM;AAAA;AAAA,MAC1B,SAAS;AAAA;AAAA,IAAA,CACV;AAED,UAAM,gBAAgB,MAAM,cAAc,OAAO,kBAAkB,cAAc,MAAM,QAAQ;AAE3F,QAAA,cAAc,WAAW,GAAG;AAC9B,aAAO,KAAK,8CAA8C;AAC1D;AAAA,IAAA;AAGE,QAAA,CAAC,QAAQ,KAAK;AAChB,+BAAyB,aAAa;AAElC,UAAA,cAAc,SAAS,GAAG;AAC5B,gBAAQ,IAAI,MAAM,KAAK,+DAA+D,CAAC;AAAA,MAAA;AAGzF,YAAM,EAAE,YAAY,MAAM,QAAQ;AAAA,QAChC,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,MAAA,CACV;AACD,UAAI,CAAC,SAAS;AACZ,eAAO,MAAM,6BAA6B;AAC1C;AAAA,MAAA;AAAA,IACF;AAGI,UAAA,iBAAiB,OAAO,aAAa;AAE3C,WAAO,KAAK,iCAAiC;AAAA,EAAA,CAC9C;AACG,QAAA,QAAQ,WAAW,QAAQ,IAAI;AACrC,UAAQ,KAAK,CAAC;AAChB;AAEA,OAAO,MAAM,QAAQ,KAAK;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node --no-warnings\nimport { Command } from \"@commander-js/extra-typings\";\nimport { logger } from \"better-auth\";\nimport { getAdapter, getSchema } from \"better-auth/db\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport prompts from \"prompts\";\nimport type { FileMakerAdapterConfig } from \"../adapter\";\nimport { getConfig } from \"../better-auth-cli/utils/get-config\";\nimport { executeMigration, planMigration, prettyPrintMigrationPlan } from \"../migrate\";\nimport { createRawFetch } from \"../odata\";\nimport \"dotenv/config\";\n\nasync function main() {\n const program = new Command();\n\n program\n .command(\"migrate\", { isDefault: true })\n .option(\"--cwd <path>\", \"Path to the current working directory\", process.cwd())\n .option(\"--config <path>\", \"Path to the config file\")\n .option(\"-u, --username <username>\", \"Full Access Username\")\n .option(\"-p, --password <password>\", \"Full Access Password\")\n .option(\"-y, --yes\", \"Skip confirmation\", false)\n\n .action(async (options) => {\n const cwd = options.cwd;\n if (!fs.existsSync(cwd)) {\n logger.error(`The directory \"${cwd}\" does not exist.`);\n process.exit(1);\n }\n\n const config = await getConfig({\n cwd,\n configPath: options.config,\n });\n if (!config) {\n logger.error(\n \"No configuration file found. Add a `auth.ts` file to your project or pass the path to the configuration file using the `--config` flag.\",\n );\n return;\n }\n\n const adapter = await getAdapter(config).catch((e) => {\n logger.error(e.message);\n process.exit(1);\n });\n\n if (adapter.id !== \"filemaker\") {\n logger.error(\"This generator is only compatible with the FileMaker adapter.\");\n return;\n }\n\n const betterAuthSchema = getSchema(config);\n\n const adapterConfig = (adapter as unknown as { filemakerConfig: FileMakerAdapterConfig }).filemakerConfig;\n const { fetch } = createRawFetch({\n ...adapterConfig.odata,\n auth:\n // If the username and password are provided in the CLI, use them to authenticate instead of what's in the config file.\n options.username && options.password\n ? {\n username: options.username,\n password: options.password,\n }\n : adapterConfig.odata.auth,\n logging: \"verbose\", // Enable logging for CLI operations\n });\n\n const migrationPlan = await planMigration(fetch, betterAuthSchema, adapterConfig.odata.database);\n\n if (migrationPlan.length === 0) {\n logger.info(\"No changes to apply. Database is up to date.\");\n return;\n }\n\n if (!options.yes) {\n prettyPrintMigrationPlan(migrationPlan);\n\n if (migrationPlan.length > 0) {\n console.log(chalk.gray(\"💡 Tip: You can use the --yes flag to skip this confirmation.\"));\n }\n\n const { confirm } = await prompts({\n type: \"confirm\",\n name: \"confirm\",\n message: \"Apply the above changes to your database?\",\n });\n if (!confirm) {\n logger.error(\"Schema changes not applied.\");\n return;\n }\n }\n\n await executeMigration(fetch, migrationPlan);\n\n logger.info(\"Migration applied successfully.\");\n });\n await program.parseAsync(process.argv);\n process.exit(0);\n}\n\nmain().catch(console.error);\n"],"names":[],"mappings":";;;;;;;;;;;AAaA,eAAe,OAAO;AACpB,QAAM,UAAU,IAAI,QAAA;AAEpB,UACG,QAAQ,WAAW,EAAE,WAAW,MAAM,EACtC,OAAO,gBAAgB,yCAAyC,QAAQ,IAAA,CAAK,EAC7E,OAAO,mBAAmB,yBAAyB,EACnD,OAAO,6BAA6B,sBAAsB,EAC1D,OAAO,6BAA6B,sBAAsB,EAC1D,OAAO,aAAa,qBAAqB,KAAK,EAE9C,OAAO,OAAO,YAAY;AACzB,UAAM,MAAM,QAAQ;AACpB,QAAI,CAAC,GAAG,WAAW,GAAG,GAAG;AACvB,aAAO,MAAM,kBAAkB,GAAG,mBAAmB;AACrD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,UAAU;AAAA,MAC7B;AAAA,MACA,YAAY,QAAQ;AAAA,IAAA,CACrB;AACD,QAAI,CAAC,QAAQ;AACX,aAAO;AAAA,QACL;AAAA,MAAA;AAEF;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,WAAW,MAAM,EAAE,MAAM,CAAC,MAAM;AACpD,aAAO,MAAM,EAAE,OAAO;AACtB,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAED,QAAI,QAAQ,OAAO,aAAa;AAC9B,aAAO,MAAM,+DAA+D;AAC5E;AAAA,IACF;AAEA,UAAM,mBAAmB,UAAU,MAAM;AAEzC,UAAM,gBAAiB,QAAmE;AAC1F,UAAM,EAAE,MAAA,IAAU,eAAe;AAAA,MAC/B,GAAG,cAAc;AAAA,MACjB;AAAA;AAAA,QAEE,QAAQ,YAAY,QAAQ,WACxB;AAAA,UACE,UAAU,QAAQ;AAAA,UAClB,UAAU,QAAQ;AAAA,QAAA,IAEpB,cAAc,MAAM;AAAA;AAAA,MAC1B,SAAS;AAAA;AAAA,IAAA,CACV;AAED,UAAM,gBAAgB,MAAM,cAAc,OAAO,kBAAkB,cAAc,MAAM,QAAQ;AAE/F,QAAI,cAAc,WAAW,GAAG;AAC9B,aAAO,KAAK,8CAA8C;AAC1D;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,KAAK;AAChB,+BAAyB,aAAa;AAEtC,UAAI,cAAc,SAAS,GAAG;AAC5B,gBAAQ,IAAI,MAAM,KAAK,+DAA+D,CAAC;AAAA,MACzF;AAEA,YAAM,EAAE,YAAY,MAAM,QAAQ;AAAA,QAChC,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,MAAA,CACV;AACD,UAAI,CAAC,SAAS;AACZ,eAAO,MAAM,6BAA6B;AAC1C;AAAA,MACF;AAAA,IACF;AAEA,UAAM,iBAAiB,OAAO,aAAa;AAE3C,WAAO,KAAK,iCAAiC;AAAA,EAC/C,CAAC;AACH,QAAM,QAAQ,WAAW,QAAQ,IAAI;AACrC,UAAQ,KAAK,CAAC;AAChB;AAEA,OAAO,MAAM,QAAQ,KAAK;"}
@@ -1,15 +1,19 @@
1
- import { BetterAuthDbSchema } from 'better-auth/db';
1
+ import { DBFieldAttribute } from 'better-auth/db';
2
2
  import { Metadata } from 'fm-odata-client';
3
3
  import { default as z } from 'zod/v4';
4
4
  import { createRawFetch } from './odata.js';
5
+ type BetterAuthSchema = Record<string, {
6
+ fields: Record<string, DBFieldAttribute>;
7
+ order: number;
8
+ }>;
5
9
  export declare function getMetadata(fetch: ReturnType<typeof createRawFetch>["fetch"], databaseName: string): Promise<Metadata | null>;
6
- export declare function planMigration(fetch: ReturnType<typeof createRawFetch>["fetch"], betterAuthSchema: BetterAuthDbSchema, databaseName: string): Promise<MigrationPlan>;
10
+ export declare function planMigration(fetch: ReturnType<typeof createRawFetch>["fetch"], betterAuthSchema: BetterAuthSchema, databaseName: string): Promise<MigrationPlan>;
7
11
  export declare function executeMigration(fetch: ReturnType<typeof createRawFetch>["fetch"], migrationPlan: MigrationPlan): Promise<void>;
8
12
  declare const migrationPlanSchema: z.ZodArray<z.ZodObject<{
9
13
  tableName: z.ZodString;
10
14
  operation: z.ZodEnum<{
11
- update: "update";
12
15
  create: "create";
16
+ update: "update";
13
17
  }>;
14
18
  fields: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
15
19
  name: z.ZodString;
@@ -1,5 +1,14 @@
1
1
  import chalk from "chalk";
2
2
  import z from "zod/v4";
3
+ function normalizeBetterAuthFieldType(fieldType) {
4
+ if (typeof fieldType === "string") {
5
+ return fieldType;
6
+ }
7
+ if (Array.isArray(fieldType)) {
8
+ return fieldType.map(String).join("|");
9
+ }
10
+ return String(fieldType);
11
+ }
3
12
  async function getMetadata(fetch, databaseName) {
4
13
  var _a;
5
14
  console.log("getting metadata...");
@@ -57,15 +66,17 @@ async function planMigration(fetch, betterAuthSchema, databaseName) {
57
66
  ) : {};
58
67
  const baTables = Object.entries(betterAuthSchema).sort((a, b) => (a[1].order ?? 0) - (b[1].order ?? 0)).map(([key, value]) => ({
59
68
  ...value,
60
- keyName: key
69
+ modelName: key
70
+ // Use the key as modelName since getSchema uses table names as keys
61
71
  }));
62
72
  const migrationPlan = [];
63
73
  for (const baTable of baTables) {
64
74
  const fields = Object.entries(baTable.fields).map(([key, field]) => {
75
+ const t = normalizeBetterAuthFieldType(field.type);
65
76
  let type = "varchar";
66
- if (field.type === "boolean" || field.type.includes("number")) {
77
+ if (t.includes("boolean") || t.includes("number")) {
67
78
  type = "numeric";
68
- } else if (field.type === "date") {
79
+ } else if (t.includes("date")) {
69
80
  type = "timestamp";
70
81
  }
71
82
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.js","sources":["../../src/migrate.ts"],"sourcesContent":["import type { BetterAuthDbSchema } from \"better-auth/db\";\nimport chalk from \"chalk\";\nimport type { Metadata } from \"fm-odata-client\";\nimport z from \"zod/v4\";\nimport type { createRawFetch } from \"./odata\";\n\nexport async function getMetadata(fetch: ReturnType<typeof createRawFetch>[\"fetch\"], databaseName: string) {\n console.log(\"getting metadata...\");\n const result = await fetch(\"/$metadata\", {\n method: \"GET\",\n headers: { accept: \"application/json\" },\n output: z\n .looseObject({\n $Version: z.string(),\n \"@ServerVersion\": z.string(),\n })\n .or(z.null())\n .catch(null),\n });\n\n if (result.error) {\n console.error(\"Failed to get metadata:\", result.error);\n return null;\n }\n\n return (result.data?.[databaseName] ?? null) as Metadata | null;\n}\n\nexport async function planMigration(\n fetch: ReturnType<typeof createRawFetch>[\"fetch\"],\n betterAuthSchema: BetterAuthDbSchema,\n databaseName: string,\n): Promise<MigrationPlan> {\n const metadata = await getMetadata(fetch, databaseName);\n\n // Build a map from entity set name to entity type key\n const entitySetToType: Record<string, string> = {};\n if (metadata) {\n for (const [key, value] of Object.entries(metadata)) {\n if (value.$Kind === \"EntitySet\" && value.$Type) {\n // $Type is like 'betterauth_test.fmp12.proofkit_user_'\n const typeKey = value.$Type.split(\".\").pop(); // e.g., 'proofkit_user_'\n entitySetToType[key] = typeKey || key;\n }\n }\n }\n\n const existingTables = metadata\n ? Object.entries(entitySetToType).reduce(\n (acc, [entitySetName, entityTypeKey]) => {\n const entityType = metadata[entityTypeKey];\n if (!entityType) {\n return acc;\n }\n const fields = Object.entries(entityType)\n .filter(\n ([_fieldKey, fieldValue]) =>\n typeof fieldValue === \"object\" && fieldValue !== null && \"$Type\" in fieldValue,\n )\n .map(([fieldKey, fieldValue]) => {\n let type = \"varchar\";\n if (fieldValue.$Type === \"Edm.String\") {\n type = \"varchar\";\n } else if (fieldValue.$Type === \"Edm.DateTimeOffset\") {\n type = \"timestamp\";\n } else if (\n fieldValue.$Type === \"Edm.Decimal\" ||\n fieldValue.$Type === \"Edm.Int32\" ||\n fieldValue.$Type === \"Edm.Int64\"\n ) {\n type = \"numeric\";\n }\n return {\n name: fieldKey,\n type,\n };\n });\n acc[entitySetName] = fields;\n return acc;\n },\n {} as Record<string, { name: string; type: string }[]>,\n )\n : {};\n\n const baTables = Object.entries(betterAuthSchema)\n .sort((a, b) => (a[1].order ?? 0) - (b[1].order ?? 0))\n .map(([key, value]) => ({\n ...value,\n keyName: key,\n }));\n\n const migrationPlan: MigrationPlan = [];\n\n for (const baTable of baTables) {\n const fields: FmField[] = Object.entries(baTable.fields).map(([key, field]) => {\n let type: \"varchar\" | \"numeric\" | \"timestamp\" = \"varchar\";\n if (field.type === \"boolean\" || field.type.includes(\"number\")) {\n type = \"numeric\";\n } else if (field.type === \"date\") {\n type = \"timestamp\";\n }\n return {\n name: field.fieldName ?? key,\n type,\n };\n });\n\n // get existing table or create it\n const tableExists = baTable.modelName in existingTables;\n\n if (tableExists) {\n const existingFields = (existingTables[baTable.modelName] || []).map((f) => f.name);\n const existingFieldMap = (existingTables[baTable.modelName] || []).reduce(\n (acc, f) => {\n acc[f.name] = f.type;\n return acc;\n },\n {} as Record<string, string>,\n );\n // Warn about type mismatches (optional, not in plan)\n for (const field of fields) {\n if (existingFields.includes(field.name) && existingFieldMap[field.name] !== field.type) {\n console.warn(\n `⚠️ WARNING: Field '${field.name}' in table '${baTable.modelName}' exists but has type '${existingFieldMap[field.name]}' (expected '${field.type}'). Change the field type in FileMaker to avoid potential errors.`,\n );\n }\n }\n const fieldsToAdd = fields.filter((f) => !existingFields.includes(f.name));\n if (fieldsToAdd.length > 0) {\n migrationPlan.push({\n tableName: baTable.modelName,\n operation: \"update\",\n fields: fieldsToAdd,\n });\n }\n } else {\n migrationPlan.push({\n tableName: baTable.modelName,\n operation: \"create\",\n fields: [\n {\n name: \"id\",\n type: \"varchar\",\n primary: true,\n unique: true,\n },\n ...fields,\n ],\n });\n }\n }\n\n return migrationPlan;\n}\n\nexport async function executeMigration(\n fetch: ReturnType<typeof createRawFetch>[\"fetch\"],\n migrationPlan: MigrationPlan,\n) {\n for (const step of migrationPlan) {\n if (step.operation === \"create\") {\n console.log(\"Creating table:\", step.tableName);\n const result = await fetch(\"/FileMaker_Tables\", {\n method: \"POST\",\n body: {\n tableName: step.tableName,\n fields: step.fields,\n },\n });\n\n if (result.error) {\n console.error(`Failed to create table ${step.tableName}:`, result.error);\n throw new Error(`Migration failed: ${result.error}`);\n }\n } else if (step.operation === \"update\") {\n console.log(\"Adding fields to table:\", step.tableName);\n const result = await fetch(`/FileMaker_Tables/${step.tableName}`, {\n method: \"PATCH\",\n body: { fields: step.fields },\n });\n\n if (result.error) {\n console.error(`Failed to update table ${step.tableName}:`, result.error);\n throw new Error(`Migration failed: ${result.error}`);\n }\n }\n }\n}\n\nconst genericFieldSchema = z.object({\n name: z.string(),\n nullable: z.boolean().optional(),\n primary: z.boolean().optional(),\n unique: z.boolean().optional(),\n global: z.boolean().optional(),\n repetitions: z.number().optional(),\n});\n\nconst stringFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"varchar\"),\n maxLength: z.number().optional(),\n default: z.enum([\"USER\", \"USERNAME\", \"CURRENT_USER\"]).optional(),\n});\n\nconst numericFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"numeric\"),\n});\n\nconst dateFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"date\"),\n default: z.enum([\"CURRENT_DATE\", \"CURDATE\"]).optional(),\n});\n\nconst timeFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"time\"),\n default: z.enum([\"CURRENT_TIME\", \"CURTIME\"]).optional(),\n});\n\nconst timestampFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"timestamp\"),\n default: z.enum([\"CURRENT_TIMESTAMP\", \"CURTIMESTAMP\"]).optional(),\n});\n\nconst containerFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"container\"),\n externalSecurePath: z.string().optional(),\n});\n\nconst fieldSchema = z.discriminatedUnion(\"type\", [\n stringFieldSchema,\n numericFieldSchema,\n dateFieldSchema,\n timeFieldSchema,\n timestampFieldSchema,\n containerFieldSchema,\n]);\n\ntype FmField = z.infer<typeof fieldSchema>;\n\nconst migrationPlanSchema = z\n .object({\n tableName: z.string(),\n operation: z.enum([\"create\", \"update\"]),\n fields: z.array(fieldSchema),\n })\n .array();\n\nexport type MigrationPlan = z.infer<typeof migrationPlanSchema>;\n\nexport function prettyPrintMigrationPlan(migrationPlan: MigrationPlan) {\n if (!migrationPlan.length) {\n console.log(\"No changes to apply. Database is up to date.\");\n return;\n }\n console.log(chalk.bold.green(\"Migration plan:\"));\n for (const step of migrationPlan) {\n const emoji = step.operation === \"create\" ? \"✅\" : \"✏️\";\n console.log(\n `\\n${emoji} ${step.operation === \"create\" ? chalk.bold.green(\"Create table\") : chalk.bold.yellow(\"Update table\")}: ${step.tableName}`,\n );\n if (step.fields.length) {\n for (const field of step.fields) {\n let fieldDesc = ` - ${field.name} (${field.type}`;\n if (field.primary) {\n fieldDesc += \", primary\";\n }\n if (field.unique) {\n fieldDesc += \", unique\";\n }\n fieldDesc += \")\";\n console.log(fieldDesc);\n }\n } else {\n console.log(\" (No fields to add)\");\n }\n }\n console.log(\"\");\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"migrate.js","sources":["../../src/migrate.ts"],"sourcesContent":["import type { DBFieldAttribute } from \"better-auth/db\";\nimport chalk from \"chalk\";\nimport type { Metadata } from \"fm-odata-client\";\nimport z from \"zod/v4\";\nimport type { createRawFetch } from \"./odata\";\n\n/** Schema type returned by better-auth's getSchema function */\ntype BetterAuthSchema = Record<string, { fields: Record<string, DBFieldAttribute>; order: number }>;\n\nfunction normalizeBetterAuthFieldType(fieldType: unknown): string {\n if (typeof fieldType === \"string\") {\n return fieldType;\n }\n if (Array.isArray(fieldType)) {\n return fieldType.map(String).join(\"|\");\n }\n return String(fieldType);\n}\n\nexport async function getMetadata(fetch: ReturnType<typeof createRawFetch>[\"fetch\"], databaseName: string) {\n console.log(\"getting metadata...\");\n const result = await fetch(\"/$metadata\", {\n method: \"GET\",\n headers: { accept: \"application/json\" },\n output: z\n .looseObject({\n $Version: z.string(),\n \"@ServerVersion\": z.string(),\n })\n .or(z.null())\n .catch(null),\n });\n\n if (result.error) {\n console.error(\"Failed to get metadata:\", result.error);\n return null;\n }\n\n return (result.data?.[databaseName] ?? null) as Metadata | null;\n}\n\nexport async function planMigration(\n fetch: ReturnType<typeof createRawFetch>[\"fetch\"],\n betterAuthSchema: BetterAuthSchema,\n databaseName: string,\n): Promise<MigrationPlan> {\n const metadata = await getMetadata(fetch, databaseName);\n\n // Build a map from entity set name to entity type key\n const entitySetToType: Record<string, string> = {};\n if (metadata) {\n for (const [key, value] of Object.entries(metadata)) {\n if (value.$Kind === \"EntitySet\" && value.$Type) {\n // $Type is like 'betterauth_test.fmp12.proofkit_user_'\n const typeKey = value.$Type.split(\".\").pop(); // e.g., 'proofkit_user_'\n entitySetToType[key] = typeKey || key;\n }\n }\n }\n\n const existingTables = metadata\n ? Object.entries(entitySetToType).reduce(\n (acc, [entitySetName, entityTypeKey]) => {\n const entityType = metadata[entityTypeKey];\n if (!entityType) {\n return acc;\n }\n const fields = Object.entries(entityType)\n .filter(\n ([_fieldKey, fieldValue]) =>\n typeof fieldValue === \"object\" && fieldValue !== null && \"$Type\" in fieldValue,\n )\n .map(([fieldKey, fieldValue]) => {\n let type = \"varchar\";\n if (fieldValue.$Type === \"Edm.String\") {\n type = \"varchar\";\n } else if (fieldValue.$Type === \"Edm.DateTimeOffset\") {\n type = \"timestamp\";\n } else if (\n fieldValue.$Type === \"Edm.Decimal\" ||\n fieldValue.$Type === \"Edm.Int32\" ||\n fieldValue.$Type === \"Edm.Int64\"\n ) {\n type = \"numeric\";\n }\n return {\n name: fieldKey,\n type,\n };\n });\n acc[entitySetName] = fields;\n return acc;\n },\n {} as Record<string, { name: string; type: string }[]>,\n )\n : {};\n\n const baTables = Object.entries(betterAuthSchema)\n .sort((a, b) => (a[1].order ?? 0) - (b[1].order ?? 0))\n .map(([key, value]) => ({\n ...value,\n modelName: key, // Use the key as modelName since getSchema uses table names as keys\n }));\n\n const migrationPlan: MigrationPlan = [];\n\n for (const baTable of baTables) {\n const fields: FmField[] = Object.entries(baTable.fields).map(([key, field]) => {\n // Better Auth's FieldType can be a string literal union or arrays.\n // Normalize it to a string so our FM mapping logic remains stable.\n // Use .includes() for all checks to handle array types like [\"boolean\", \"null\"] → \"boolean|null\"\n const t = normalizeBetterAuthFieldType(field.type);\n let type: \"varchar\" | \"numeric\" | \"timestamp\" = \"varchar\";\n if (t.includes(\"boolean\") || t.includes(\"number\")) {\n type = \"numeric\";\n } else if (t.includes(\"date\")) {\n type = \"timestamp\";\n }\n return {\n name: field.fieldName ?? key,\n type,\n };\n });\n\n // get existing table or create it\n const tableExists = baTable.modelName in existingTables;\n\n if (tableExists) {\n const existingFields = (existingTables[baTable.modelName] || []).map((f) => f.name);\n const existingFieldMap = (existingTables[baTable.modelName] || []).reduce(\n (acc, f) => {\n acc[f.name] = f.type;\n return acc;\n },\n {} as Record<string, string>,\n );\n // Warn about type mismatches (optional, not in plan)\n for (const field of fields) {\n if (existingFields.includes(field.name) && existingFieldMap[field.name] !== field.type) {\n console.warn(\n `⚠️ WARNING: Field '${field.name}' in table '${baTable.modelName}' exists but has type '${existingFieldMap[field.name]}' (expected '${field.type}'). Change the field type in FileMaker to avoid potential errors.`,\n );\n }\n }\n const fieldsToAdd = fields.filter((f) => !existingFields.includes(f.name));\n if (fieldsToAdd.length > 0) {\n migrationPlan.push({\n tableName: baTable.modelName,\n operation: \"update\",\n fields: fieldsToAdd,\n });\n }\n } else {\n migrationPlan.push({\n tableName: baTable.modelName,\n operation: \"create\",\n fields: [\n {\n name: \"id\",\n type: \"varchar\",\n primary: true,\n unique: true,\n },\n ...fields,\n ],\n });\n }\n }\n\n return migrationPlan;\n}\n\nexport async function executeMigration(\n fetch: ReturnType<typeof createRawFetch>[\"fetch\"],\n migrationPlan: MigrationPlan,\n) {\n for (const step of migrationPlan) {\n if (step.operation === \"create\") {\n console.log(\"Creating table:\", step.tableName);\n const result = await fetch(\"/FileMaker_Tables\", {\n method: \"POST\",\n body: {\n tableName: step.tableName,\n fields: step.fields,\n },\n });\n\n if (result.error) {\n console.error(`Failed to create table ${step.tableName}:`, result.error);\n throw new Error(`Migration failed: ${result.error}`);\n }\n } else if (step.operation === \"update\") {\n console.log(\"Adding fields to table:\", step.tableName);\n const result = await fetch(`/FileMaker_Tables/${step.tableName}`, {\n method: \"PATCH\",\n body: { fields: step.fields },\n });\n\n if (result.error) {\n console.error(`Failed to update table ${step.tableName}:`, result.error);\n throw new Error(`Migration failed: ${result.error}`);\n }\n }\n }\n}\n\nconst genericFieldSchema = z.object({\n name: z.string(),\n nullable: z.boolean().optional(),\n primary: z.boolean().optional(),\n unique: z.boolean().optional(),\n global: z.boolean().optional(),\n repetitions: z.number().optional(),\n});\n\nconst stringFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"varchar\"),\n maxLength: z.number().optional(),\n default: z.enum([\"USER\", \"USERNAME\", \"CURRENT_USER\"]).optional(),\n});\n\nconst numericFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"numeric\"),\n});\n\nconst dateFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"date\"),\n default: z.enum([\"CURRENT_DATE\", \"CURDATE\"]).optional(),\n});\n\nconst timeFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"time\"),\n default: z.enum([\"CURRENT_TIME\", \"CURTIME\"]).optional(),\n});\n\nconst timestampFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"timestamp\"),\n default: z.enum([\"CURRENT_TIMESTAMP\", \"CURTIMESTAMP\"]).optional(),\n});\n\nconst containerFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"container\"),\n externalSecurePath: z.string().optional(),\n});\n\nconst fieldSchema = z.discriminatedUnion(\"type\", [\n stringFieldSchema,\n numericFieldSchema,\n dateFieldSchema,\n timeFieldSchema,\n timestampFieldSchema,\n containerFieldSchema,\n]);\n\ntype FmField = z.infer<typeof fieldSchema>;\n\nconst migrationPlanSchema = z\n .object({\n tableName: z.string(),\n operation: z.enum([\"create\", \"update\"]),\n fields: z.array(fieldSchema),\n })\n .array();\n\nexport type MigrationPlan = z.infer<typeof migrationPlanSchema>;\n\nexport function prettyPrintMigrationPlan(migrationPlan: MigrationPlan) {\n if (!migrationPlan.length) {\n console.log(\"No changes to apply. Database is up to date.\");\n return;\n }\n console.log(chalk.bold.green(\"Migration plan:\"));\n for (const step of migrationPlan) {\n const emoji = step.operation === \"create\" ? \"✅\" : \"✏️\";\n console.log(\n `\\n${emoji} ${step.operation === \"create\" ? chalk.bold.green(\"Create table\") : chalk.bold.yellow(\"Update table\")}: ${step.tableName}`,\n );\n if (step.fields.length) {\n for (const field of step.fields) {\n let fieldDesc = ` - ${field.name} (${field.type}`;\n if (field.primary) {\n fieldDesc += \", primary\";\n }\n if (field.unique) {\n fieldDesc += \", unique\";\n }\n fieldDesc += \")\";\n console.log(fieldDesc);\n }\n } else {\n console.log(\" (No fields to add)\");\n }\n }\n console.log(\"\");\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/odata/index.ts"],"sourcesContent":["/** biome-ignore-all lint/suspicious/noExplicitAny: library code */\nimport { logger as betterAuthLogger } from \"better-auth\";\nimport { err, ok, type Result } from \"neverthrow\";\nimport type { z } from \"zod/v4\";\n\ninterface BasicAuthCredentials {\n username: string;\n password: string;\n}\ninterface OttoAPIKeyAuth {\n apiKey: string;\n}\ntype ODataAuth = BasicAuthCredentials | OttoAPIKeyAuth;\n\nexport interface FmOdataConfig {\n serverUrl: string;\n auth: ODataAuth;\n database: string;\n logging?: true | \"verbose\" | \"none\";\n}\n\nexport function validateUrl(input: string): Result<URL, unknown> {\n try {\n const url = new URL(input);\n return ok(url);\n } catch (error) {\n return err(error);\n }\n}\n\nexport function createRawFetch(args: FmOdataConfig) {\n const result = validateUrl(args.serverUrl);\n\n if (result.isErr()) {\n throw new Error(\"Invalid server URL\");\n }\n\n let baseURL = result.value.origin;\n if (\"apiKey\" in args.auth) {\n baseURL += \"/otto\";\n }\n baseURL += `/fmi/odata/v4/${args.database}`;\n\n // Create authentication headers\n const authHeaders: Record<string, string> = {};\n if (\"apiKey\" in args.auth) {\n authHeaders.Authorization = `Bearer ${args.auth.apiKey}`;\n } else {\n const credentials = btoa(`${args.auth.username}:${args.auth.password}`);\n authHeaders.Authorization = `Basic ${credentials}`;\n }\n\n // Enhanced fetch function with body handling, validation, and structured responses\n const wrappedFetch = async <TOutput = any>(\n input: string | URL | Request,\n options?: Omit<RequestInit, \"body\"> & {\n body?: any; // Allow any type for body\n output?: z.ZodSchema<TOutput>; // Optional schema for validation\n },\n ): Promise<{ data?: TOutput; error?: string; response?: Response }> => {\n try {\n let url: string;\n\n // Handle different input types\n if (typeof input === \"string\") {\n // If it's already a full URL, use as-is, otherwise prepend baseURL\n url = input.startsWith(\"http\") ? input : `${baseURL}${input.startsWith(\"/\") ? input : `/${input}`}`;\n } else if (input instanceof URL) {\n url = input.toString();\n } else if (input instanceof Request) {\n url = input.url;\n } else {\n url = String(input);\n }\n\n // Handle body serialization\n let processedBody = options?.body;\n if (\n processedBody &&\n typeof processedBody === \"object\" &&\n !(processedBody instanceof FormData) &&\n !(processedBody instanceof URLSearchParams) &&\n !(processedBody instanceof ReadableStream)\n ) {\n processedBody = JSON.stringify(processedBody);\n }\n\n // Merge headers\n const headers = {\n \"Content-Type\": \"application/json\",\n ...authHeaders,\n ...(options?.headers || {}),\n };\n\n const requestInit: RequestInit = {\n ...options,\n headers,\n body: processedBody,\n };\n\n // Optional logging\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `${requestInit.method || \"GET\"} ${url}`);\n if (requestInit.body) {\n betterAuthLogger.info(\"raw-fetch\", \"Request body:\", requestInit.body);\n }\n }\n\n const response = await fetch(url, requestInit);\n\n // Optional logging for response details\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `Response status: ${response.status} ${response.statusText}`);\n betterAuthLogger.info(\"raw-fetch\", \"Response headers:\", Object.fromEntries(response.headers.entries()));\n }\n\n // Check if response is ok\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.error(\"raw-fetch\", `HTTP Error ${response.status}: ${errorText}`);\n }\n return {\n error: `HTTP ${response.status}: ${errorText}`,\n response,\n };\n }\n\n // Parse response based on content type\n let responseData: any;\n const contentType = response.headers.get(\"content-type\");\n\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `Response content-type: ${contentType || \"none\"}`);\n }\n\n if (contentType?.includes(\"application/json\")) {\n try {\n const responseText = await response.text();\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `Raw response text: \"${responseText}\"`);\n betterAuthLogger.info(\"raw-fetch\", `Response text length: ${responseText.length}`);\n }\n\n // Handle empty responses\n if (responseText.trim() === \"\") {\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", \"Empty JSON response, returning null\");\n }\n responseData = null;\n } else {\n responseData = JSON.parse(responseText);\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", \"Successfully parsed JSON response\");\n }\n }\n } catch (parseError) {\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.error(\"raw-fetch\", \"JSON parse error:\", parseError);\n }\n return {\n error: `Failed to parse JSON response: ${parseError instanceof Error ? parseError.message : \"Unknown parse error\"}`,\n response,\n };\n }\n } else if (contentType?.includes(\"text/\")) {\n // Handle text responses (text/plain, text/html, etc.)\n responseData = await response.text();\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `Text response: \"${responseData}\"`);\n }\n } else {\n // For other content types, try to get text but don't fail if it's binary\n try {\n responseData = await response.text();\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `Unknown content-type response as text: \"${responseData}\"`);\n }\n } catch {\n // If text parsing fails (e.g., binary data), return null\n responseData = null;\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", \"Could not parse response as text, returning null\");\n }\n }\n }\n\n // Validate output if schema provided\n if (options?.output) {\n const validation = options.output.safeParse(responseData);\n if (validation.success) {\n return {\n data: validation.data,\n response,\n };\n }\n return {\n error: `Validation failed: ${validation.error.message}`,\n response,\n };\n }\n\n // Return unvalidated data\n return {\n data: responseData as TOutput,\n response,\n };\n } catch (error) {\n return {\n error: error instanceof Error ? error.message : \"Unknown error occurred\",\n };\n }\n };\n\n return {\n baseURL,\n fetch: wrappedFetch,\n };\n}\n"],"names":["betterAuthLogger"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/odata/index.ts"],"sourcesContent":["/** biome-ignore-all lint/suspicious/noExplicitAny: library code */\nimport { logger as betterAuthLogger } from \"better-auth\";\nimport { err, ok, type Result } from \"neverthrow\";\nimport type { z } from \"zod/v4\";\n\ninterface BasicAuthCredentials {\n username: string;\n password: string;\n}\ninterface OttoAPIKeyAuth {\n apiKey: string;\n}\ntype ODataAuth = BasicAuthCredentials | OttoAPIKeyAuth;\n\nexport interface FmOdataConfig {\n serverUrl: string;\n auth: ODataAuth;\n database: string;\n logging?: true | \"verbose\" | \"none\";\n}\n\nexport function validateUrl(input: string): Result<URL, unknown> {\n try {\n const url = new URL(input);\n return ok(url);\n } catch (error) {\n return err(error);\n }\n}\n\nexport function createRawFetch(args: FmOdataConfig) {\n const result = validateUrl(args.serverUrl);\n\n if (result.isErr()) {\n throw new Error(\"Invalid server URL\");\n }\n\n let baseURL = result.value.origin;\n if (\"apiKey\" in args.auth) {\n baseURL += \"/otto\";\n }\n baseURL += `/fmi/odata/v4/${args.database}`;\n\n // Create authentication headers\n const authHeaders: Record<string, string> = {};\n if (\"apiKey\" in args.auth) {\n authHeaders.Authorization = `Bearer ${args.auth.apiKey}`;\n } else {\n const credentials = btoa(`${args.auth.username}:${args.auth.password}`);\n authHeaders.Authorization = `Basic ${credentials}`;\n }\n\n // Enhanced fetch function with body handling, validation, and structured responses\n const wrappedFetch = async <TOutput = any>(\n input: string | URL | Request,\n options?: Omit<RequestInit, \"body\"> & {\n body?: any; // Allow any type for body\n output?: z.ZodSchema<TOutput>; // Optional schema for validation\n },\n ): Promise<{ data?: TOutput; error?: string; response?: Response }> => {\n try {\n let url: string;\n\n // Handle different input types\n if (typeof input === \"string\") {\n // If it's already a full URL, use as-is, otherwise prepend baseURL\n url = input.startsWith(\"http\") ? input : `${baseURL}${input.startsWith(\"/\") ? input : `/${input}`}`;\n } else if (input instanceof URL) {\n url = input.toString();\n } else if (input instanceof Request) {\n url = input.url;\n } else {\n url = String(input);\n }\n\n // Handle body serialization\n let processedBody = options?.body;\n if (\n processedBody &&\n typeof processedBody === \"object\" &&\n !(processedBody instanceof FormData) &&\n !(processedBody instanceof URLSearchParams) &&\n !(processedBody instanceof ReadableStream)\n ) {\n processedBody = JSON.stringify(processedBody);\n }\n\n // Merge headers\n const headers = {\n \"Content-Type\": \"application/json\",\n ...authHeaders,\n ...(options?.headers || {}),\n };\n\n const requestInit: RequestInit = {\n ...options,\n headers,\n body: processedBody,\n };\n\n // Optional logging\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `${requestInit.method || \"GET\"} ${url}`);\n if (requestInit.body) {\n betterAuthLogger.info(\"raw-fetch\", \"Request body:\", requestInit.body);\n }\n }\n\n const response = await fetch(url, requestInit);\n\n // Optional logging for response details\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `Response status: ${response.status} ${response.statusText}`);\n betterAuthLogger.info(\"raw-fetch\", \"Response headers:\", Object.fromEntries(response.headers.entries()));\n }\n\n // Check if response is ok\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.error(\"raw-fetch\", `HTTP Error ${response.status}: ${errorText}`);\n }\n return {\n error: `HTTP ${response.status}: ${errorText}`,\n response,\n };\n }\n\n // Parse response based on content type\n let responseData: any;\n const contentType = response.headers.get(\"content-type\");\n\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `Response content-type: ${contentType || \"none\"}`);\n }\n\n if (contentType?.includes(\"application/json\")) {\n try {\n const responseText = await response.text();\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `Raw response text: \"${responseText}\"`);\n betterAuthLogger.info(\"raw-fetch\", `Response text length: ${responseText.length}`);\n }\n\n // Handle empty responses\n if (responseText.trim() === \"\") {\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", \"Empty JSON response, returning null\");\n }\n responseData = null;\n } else {\n responseData = JSON.parse(responseText);\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", \"Successfully parsed JSON response\");\n }\n }\n } catch (parseError) {\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.error(\"raw-fetch\", \"JSON parse error:\", parseError);\n }\n return {\n error: `Failed to parse JSON response: ${parseError instanceof Error ? parseError.message : \"Unknown parse error\"}`,\n response,\n };\n }\n } else if (contentType?.includes(\"text/\")) {\n // Handle text responses (text/plain, text/html, etc.)\n responseData = await response.text();\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `Text response: \"${responseData}\"`);\n }\n } else {\n // For other content types, try to get text but don't fail if it's binary\n try {\n responseData = await response.text();\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", `Unknown content-type response as text: \"${responseData}\"`);\n }\n } catch {\n // If text parsing fails (e.g., binary data), return null\n responseData = null;\n if (args.logging === \"verbose\" || args.logging === true) {\n betterAuthLogger.info(\"raw-fetch\", \"Could not parse response as text, returning null\");\n }\n }\n }\n\n // Validate output if schema provided\n if (options?.output) {\n const validation = options.output.safeParse(responseData);\n if (validation.success) {\n return {\n data: validation.data,\n response,\n };\n }\n return {\n error: `Validation failed: ${validation.error.message}`,\n response,\n };\n }\n\n // Return unvalidated data\n return {\n data: responseData as TOutput,\n response,\n };\n } catch (error) {\n return {\n error: error instanceof Error ? error.message : \"Unknown error occurred\",\n };\n }\n };\n\n return {\n baseURL,\n fetch: wrappedFetch,\n };\n}\n"],"names":["betterAuthLogger"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proofkit/better-auth",
3
- "version": "0.3.1-beta.0",
3
+ "version": "0.3.1-beta.1",
4
4
  "description": "FileMaker adapter for Better Auth",
5
5
  "type": "module",
6
6
  "main": "dist/esm/index.js",
@@ -36,34 +36,36 @@
36
36
  "url": "git+https://github.com/proofgeist/proofkit.git"
37
37
  },
38
38
  "dependencies": {
39
- "@babel/preset-react": "^7.27.1",
40
- "@babel/preset-typescript": "^7.27.1",
39
+ "@babel/preset-react": "^7.28.5",
40
+ "@babel/preset-typescript": "^7.28.5",
41
41
  "@commander-js/extra-typings": "^14.0.0",
42
- "@tanstack/vite-config": "^0.2.0",
43
- "better-auth": "^1.2.10",
44
- "c12": "^3.0.4",
42
+ "@tanstack/vite-config": "^0.2.1",
43
+ "better-auth": "^1.4.11",
44
+ "c12": "^3.3.3",
45
45
  "chalk": "5.4.1",
46
- "commander": "^14.0.0",
47
- "dotenv": "^16.5.0",
48
- "fs-extra": "^11.3.0",
46
+ "commander": "^14.0.2",
47
+ "dotenv": "^16.6.1",
48
+ "fs-extra": "^11.3.3",
49
49
  "neverthrow": "^8.2.0",
50
- "odata-query": "^8.0.4",
50
+ "odata-query": "^8.0.7",
51
51
  "prompts": "^2.4.2",
52
- "vite": "^6.3.4",
53
- "zod": "^4.1.13"
52
+ "vite": "^6.4.1",
53
+ "zod": "^4.3.5"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/fs-extra": "^11.0.4",
57
57
  "@types/prompts": "^2.4.9",
58
58
  "@vitest/ui": "^3.2.4",
59
- "fm-odata-client": "^3.0.1",
60
- "publint": "^0.3.12",
59
+ "fm-odata-client": "^3.0.2",
60
+ "publint": "^0.3.16",
61
61
  "typescript": "^5.9.3",
62
- "vitest": "^4.0.7"
62
+ "vitest": "^4.0.17"
63
63
  },
64
64
  "scripts": {
65
65
  "dev": "pnpm build:watch",
66
66
  "test": "vitest run",
67
+ "test:e2e": "doppler run -c test_betterauth -- vitest run tests/e2e",
68
+ "typecheck": "tsc --noEmit",
67
69
  "build": "vite build && publint --strict",
68
70
  "build:watch": "vite build --watch",
69
71
  "ci": "pnpm run build && pnpm run test",
package/src/adapter.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /** biome-ignore-all lint/suspicious/noExplicitAny: library code */
2
2
  import { logger } from "better-auth";
3
- import { type AdapterDebugLogs, type CleanedWhere, createAdapter } from "better-auth/adapters";
3
+ import { type CleanedWhere, createAdapter, type DBAdapterDebugLogOption } from "better-auth/adapters";
4
4
  import buildQuery from "odata-query";
5
5
  import { prettifyError, z } from "zod/v4";
6
6
  import { createRawFetch, type FmOdataConfig } from "./odata";
@@ -15,11 +15,11 @@ const configSchema = z.object({
15
15
  }),
16
16
  });
17
17
 
18
- interface FileMakerAdapterConfig {
18
+ export interface FileMakerAdapterConfig {
19
19
  /**
20
20
  * Helps you debug issues with the adapter.
21
21
  */
22
- debugLogs?: AdapterDebugLogs;
22
+ debugLogs?: DBAdapterDebugLogOption;
23
23
  /**
24
24
  * If the table names in the schema are plural.
25
25
  */
@@ -168,7 +168,7 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
168
168
  logging: config.debugLogs ? "verbose" : "none",
169
169
  });
170
170
 
171
- return createAdapter({
171
+ const adapterFactory = createAdapter({
172
172
  config: {
173
173
  adapterId: "filemaker",
174
174
  adapterName: "FileMaker",
@@ -382,4 +382,8 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
382
382
  };
383
383
  },
384
384
  });
385
+
386
+ // Expose the FileMaker config for CLI access
387
+ (adapterFactory as any).filemakerConfig = config as FileMakerAdapterConfig;
388
+ return adapterFactory;
385
389
  };
package/src/cli/index.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node --no-warnings
2
2
  import { Command } from "@commander-js/extra-typings";
3
3
  import { logger } from "better-auth";
4
- import { getAdapter, getAuthTables } from "better-auth/db";
4
+ import { getAdapter, getSchema } from "better-auth/db";
5
5
  import chalk from "chalk";
6
6
  import fs from "fs-extra";
7
7
  import prompts from "prompts";
8
- import type { AdapterOptions } from "../adapter";
8
+ import type { FileMakerAdapterConfig } from "../adapter";
9
9
  import { getConfig } from "../better-auth-cli/utils/get-config";
10
10
  import { executeMigration, planMigration, prettyPrintMigrationPlan } from "../migrate";
11
11
  import { createRawFetch } from "../odata";
@@ -50,9 +50,9 @@ async function main() {
50
50
  return;
51
51
  }
52
52
 
53
- const betterAuthSchema = getAuthTables(config);
53
+ const betterAuthSchema = getSchema(config);
54
54
 
55
- const adapterConfig = (adapter.options as AdapterOptions).config;
55
+ const adapterConfig = (adapter as unknown as { filemakerConfig: FileMakerAdapterConfig }).filemakerConfig;
56
56
  const { fetch } = createRawFetch({
57
57
  ...adapterConfig.odata,
58
58
  auth:
package/src/migrate.ts CHANGED
@@ -1,9 +1,22 @@
1
- import type { BetterAuthDbSchema } from "better-auth/db";
1
+ import type { DBFieldAttribute } from "better-auth/db";
2
2
  import chalk from "chalk";
3
3
  import type { Metadata } from "fm-odata-client";
4
4
  import z from "zod/v4";
5
5
  import type { createRawFetch } from "./odata";
6
6
 
7
+ /** Schema type returned by better-auth's getSchema function */
8
+ type BetterAuthSchema = Record<string, { fields: Record<string, DBFieldAttribute>; order: number }>;
9
+
10
+ function normalizeBetterAuthFieldType(fieldType: unknown): string {
11
+ if (typeof fieldType === "string") {
12
+ return fieldType;
13
+ }
14
+ if (Array.isArray(fieldType)) {
15
+ return fieldType.map(String).join("|");
16
+ }
17
+ return String(fieldType);
18
+ }
19
+
7
20
  export async function getMetadata(fetch: ReturnType<typeof createRawFetch>["fetch"], databaseName: string) {
8
21
  console.log("getting metadata...");
9
22
  const result = await fetch("/$metadata", {
@@ -28,7 +41,7 @@ export async function getMetadata(fetch: ReturnType<typeof createRawFetch>["fetc
28
41
 
29
42
  export async function planMigration(
30
43
  fetch: ReturnType<typeof createRawFetch>["fetch"],
31
- betterAuthSchema: BetterAuthDbSchema,
44
+ betterAuthSchema: BetterAuthSchema,
32
45
  databaseName: string,
33
46
  ): Promise<MigrationPlan> {
34
47
  const metadata = await getMetadata(fetch, databaseName);
@@ -86,17 +99,21 @@ export async function planMigration(
86
99
  .sort((a, b) => (a[1].order ?? 0) - (b[1].order ?? 0))
87
100
  .map(([key, value]) => ({
88
101
  ...value,
89
- keyName: key,
102
+ modelName: key, // Use the key as modelName since getSchema uses table names as keys
90
103
  }));
91
104
 
92
105
  const migrationPlan: MigrationPlan = [];
93
106
 
94
107
  for (const baTable of baTables) {
95
108
  const fields: FmField[] = Object.entries(baTable.fields).map(([key, field]) => {
109
+ // Better Auth's FieldType can be a string literal union or arrays.
110
+ // Normalize it to a string so our FM mapping logic remains stable.
111
+ // Use .includes() for all checks to handle array types like ["boolean", "null"] → "boolean|null"
112
+ const t = normalizeBetterAuthFieldType(field.type);
96
113
  let type: "varchar" | "numeric" | "timestamp" = "varchar";
97
- if (field.type === "boolean" || field.type.includes("number")) {
114
+ if (t.includes("boolean") || t.includes("number")) {
98
115
  type = "numeric";
99
- } else if (field.type === "date") {
116
+ } else if (t.includes("date")) {
100
117
  type = "timestamp";
101
118
  }
102
119
  return {