@proofkit/better-auth 0.2.0 → 0.2.2

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,6 +1,7 @@
1
1
  import { createAdapter } from "better-auth/adapters";
2
2
  import { createFmOdataFetch } from "./odata/index.js";
3
3
  import { z, prettifyError } from "zod/v4";
4
+ import { logger } from "better-auth";
4
5
  const configSchema = z.object({
5
6
  debugLogs: z.unknown().optional(),
6
7
  usePlural: z.boolean().optional(),
@@ -31,10 +32,16 @@ function parseWhere(where) {
31
32
  }
32
33
  function formatValue(value) {
33
34
  if (value === null) return "null";
34
- if (typeof value === "string") return `'${value.replace(/'/g, "''")}'`;
35
35
  if (typeof value === "boolean") return value ? "true" : "false";
36
- if (value instanceof Date) return `'${value.toISOString()}'`;
36
+ if (value instanceof Date) return value.toISOString();
37
37
  if (Array.isArray(value)) return `(${value.map(formatValue).join(",")})`;
38
+ if (typeof value === "string") {
39
+ const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
40
+ if (isoDateRegex.test(value)) {
41
+ return value;
42
+ }
43
+ return `'${value.replace(/'/g, "''")}'`;
44
+ }
38
45
  return (value == null ? void 0 : value.toString()) ?? "";
39
46
  }
40
47
  const opMap = {
@@ -92,8 +99,8 @@ const FileMakerAdapter = (_config = defaultConfig) => {
92
99
  }
93
100
  const config = parsed.data;
94
101
  const fetch = createFmOdataFetch({
95
- ...config.odata
96
- // logging: config.debugLogs ? true : "none",
102
+ ...config.odata,
103
+ logging: config.debugLogs ? "verbose" : "none"
97
104
  });
98
105
  return createAdapter({
99
106
  config: {
@@ -111,11 +118,28 @@ const FileMakerAdapter = (_config = defaultConfig) => {
111
118
  // Whether the database supports booleans. (Default: true)
112
119
  supportsNumericIds: false
113
120
  // Whether the database supports auto-incrementing numeric IDs. (Default: true)
121
+ // customTransformInput: ({ data, fieldAttributes }) => {
122
+ // if (fieldAttributes.type === "date") {
123
+ // const rawData = data as string;
124
+ // const fmDate = dayjs(rawData).format("YYYY+MM+DD");
125
+ // const isoDate = dayjs(rawData).format("YYYY-MM-DD");
126
+ // const newData = rawData
127
+ // .replace(isoDate, fmDate)
128
+ // .replace("T", " ")
129
+ // .replace("Z", "");
130
+ // console.log(`replaced ${isoDate} with ${fmDate}`);
131
+ // return newData;
132
+ // }
133
+ // return data;
134
+ // },
114
135
  },
115
136
  adapter: ({ options }) => {
116
137
  return {
117
138
  options: { config },
118
139
  create: async ({ data, model, select }) => {
140
+ if (model === "session") {
141
+ console.log("session", data);
142
+ }
119
143
  const result = await fetch(`/${model}`, {
120
144
  method: "POST",
121
145
  body: data,
@@ -128,10 +152,12 @@ const FileMakerAdapter = (_config = defaultConfig) => {
128
152
  },
129
153
  count: async ({ model, where }) => {
130
154
  var _a;
155
+ const filter = parseWhere(where);
156
+ logger.debug("$filter", filter);
131
157
  const result = await fetch(`/${model}/$count`, {
132
158
  method: "GET",
133
159
  query: {
134
- $filter: parseWhere(where)
160
+ $filter: filter
135
161
  },
136
162
  output: z.object({ value: z.number() })
137
163
  });
@@ -142,10 +168,12 @@ const FileMakerAdapter = (_config = defaultConfig) => {
142
168
  },
143
169
  findOne: async ({ model, where }) => {
144
170
  var _a, _b;
171
+ const filter = parseWhere(where);
172
+ logger.debug("$filter", filter);
145
173
  const result = await fetch(`/${model}`, {
146
174
  method: "GET",
147
175
  query: {
148
- ...where.length > 0 ? { $filter: parseWhere(where) } : {},
176
+ ...filter.length > 0 ? { $filter: filter } : {},
149
177
  $top: 1
150
178
  },
151
179
  output: z.object({ value: z.array(z.any()) })
@@ -158,6 +186,7 @@ const FileMakerAdapter = (_config = defaultConfig) => {
158
186
  findMany: async ({ model, where, limit, offset, sortBy }) => {
159
187
  var _a;
160
188
  const filter = parseWhere(where);
189
+ logger.debug("$filter", filter);
161
190
  const rows = await fetch(`/${model}`, {
162
191
  method: "GET",
163
192
  query: {
@@ -174,12 +203,14 @@ const FileMakerAdapter = (_config = defaultConfig) => {
174
203
  return ((_a = rows.data) == null ? void 0 : _a.value) ?? [];
175
204
  },
176
205
  delete: async ({ model, where }) => {
206
+ const filter = parseWhere(where);
207
+ logger.debug("$filter", filter);
208
+ console.log("delete", model, where, filter);
177
209
  const result = await fetch(`/${model}`, {
178
210
  method: "DELETE",
179
211
  query: {
180
- ...where.length > 0 ? { $filter: parseWhere(where) } : {},
181
- $top: 1,
182
- $select: [`"id"`]
212
+ ...where.length > 0 ? { $filter: filter } : {},
213
+ $top: 1
183
214
  }
184
215
  });
185
216
  if (result.error) {
@@ -187,12 +218,15 @@ const FileMakerAdapter = (_config = defaultConfig) => {
187
218
  }
188
219
  },
189
220
  deleteMany: async ({ model, where }) => {
221
+ const filter = parseWhere(where);
222
+ logger.debug(
223
+ where.map((o) => `typeof ${o.value} is ${typeof o.value}`).join("\n")
224
+ );
225
+ logger.debug("$filter", filter);
190
226
  const result = await fetch(`/${model}/$count`, {
191
227
  method: "DELETE",
192
228
  query: {
193
- ...where.length > 0 ? { $filter: parseWhere(where) } : {},
194
- $top: 1,
195
- $select: [`"id"`]
229
+ ...where.length > 0 ? { $filter: filter } : {}
196
230
  },
197
231
  output: z.coerce.number()
198
232
  });
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","sources":["../../src/adapter.ts"],"sourcesContent":["import {\n CleanedWhere,\n createAdapter,\n type AdapterDebugLogs,\n} from \"better-auth/adapters\";\nimport { createFmOdataFetch, type FmOdataConfig } from \"./odata\";\nimport { prettifyError, z } from \"zod/v4\";\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([\n z.object({ username: z.string(), password: z.string() }),\n z.object({ apiKey: z.string() }),\n ]),\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: z.infer<typeof configSchema>[\"odata\"];\n}\n\nexport type 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/**\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) return \"\";\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) return field;\n // Always quote if field is 'id' or has space or underscore\n if (field === \"id\" || /[\\s_]/.test(field)) return `\"${field}\"`;\n return field;\n }\n\n // Helper to format values for OData\n function formatValue(value: any): string {\n if (value === null) return \"null\";\n if (typeof value === \"string\") return `'${value.replace(/'/g, \"''\")}'`;\n if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n if (value instanceof Date) return `'${value.toISOString()}'`;\n if (Array.isArray(value)) return `(${value.map(formatValue).join(\",\")})`;\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) continue;\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\n .map((v) => `${field} eq ${formatValue(v)}`)\n .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 = (\n _config: FileMakerAdapterConfig = defaultConfig,\n) => {\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 = createFmOdataFetch({\n ...config.odata,\n // logging: config.debugLogs ? true : \"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: ({ options }) => {\n return {\n options: { config },\n create: async ({ data, model, select }) => {\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 result = await fetch(`/${model}/$count`, {\n method: \"GET\",\n query: {\n $filter: parseWhere(where),\n },\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 ?? 0;\n },\n findOne: async ({ model, where }) => {\n const result = await fetch(`/${model}`, {\n method: \"GET\",\n query: {\n ...(where.length > 0 ? { $filter: parseWhere(where) } : {}),\n $top: 1,\n },\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] ?? null;\n },\n findMany: async ({ model, where, limit, offset, sortBy }) => {\n const filter = parseWhere(where);\n\n const rows = await fetch(`/${model}`, {\n method: \"GET\",\n query: {\n ...(filter.length > 0 ? { $filter: filter } : {}),\n $top: limit,\n $skip: offset,\n ...(sortBy\n ? { $orderby: `\"${sortBy.field}\" ${sortBy.direction ?? \"asc\"}` }\n : {}),\n },\n output: z.object({ value: z.array(z.any()) }),\n });\n if (rows.error) {\n throw new Error(\"Failed to find records\");\n }\n return rows.data?.value ?? [];\n },\n delete: async ({ model, where }) => {\n const result = await fetch(`/${model}`, {\n method: \"DELETE\",\n query: {\n ...(where.length > 0 ? { $filter: parseWhere(where) } : {}),\n $top: 1,\n $select: [`\"id\"`],\n },\n });\n if (result.error) {\n throw new Error(\"Failed to delete record\");\n }\n },\n deleteMany: async ({ model, where }) => {\n const result = await fetch(`/${model}/$count`, {\n method: \"DELETE\",\n query: {\n ...(where.length > 0 ? { $filter: parseWhere(where) } : {}),\n $top: 1,\n $select: [`\"id\"`],\n },\n output: z.coerce.number(),\n });\n if (result.error) {\n throw new Error(\"Failed to delete record\");\n }\n return result.data ?? 0;\n },\n update: async ({ model, where, update }) => {\n const result = await fetch(`/${model}`, {\n method: \"PATCH\",\n query: {\n ...(where.length > 0 ? { $filter: parseWhere(where) } : {}),\n $top: 1,\n $select: [`\"id\"`],\n },\n body: update,\n output: z.object({ value: z.array(z.any()) }),\n });\n return result.data?.value?.[0] ?? null;\n },\n updateMany: async ({ model, where, update }) => {\n const filter = parseWhere(where);\n const result = await fetch(`/${model}`, {\n method: \"PATCH\",\n query: {\n ...(where.length > 0 ? { $filter: filter } : {}),\n },\n body: update,\n });\n return result.data 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":["import {\n CleanedWhere,\n createAdapter,\n type AdapterDebugLogs,\n} from \"better-auth/adapters\";\nimport { createFmOdataFetch, type FmOdataConfig } from \"./odata\";\nimport { prettifyError, z } from \"zod/v4\";\nimport dayjs from \"dayjs\";\nimport { logger } from \"better-auth\";\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([\n z.object({ username: z.string(), password: z.string() }),\n z.object({ apiKey: z.string() }),\n ]),\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: z.infer<typeof configSchema>[\"odata\"];\n}\n\nexport type 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/**\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) return \"\";\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) return field;\n // Always quote if field is 'id' or has space or underscore\n if (field === \"id\" || /[\\s_]/.test(field)) return `\"${field}\"`;\n return field;\n }\n\n // Helper to format values for OData\n function formatValue(value: any): string {\n if (value === null) return \"null\";\n if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n if (value instanceof Date) return value.toISOString();\n if (Array.isArray(value)) return `(${value.map(formatValue).join(\",\")})`;\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 const isoDateRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z?$/;\n if (isoDateRegex.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) continue;\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\n .map((v) => `${field} eq ${formatValue(v)}`)\n .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 = (\n _config: FileMakerAdapterConfig = defaultConfig,\n) => {\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 = createFmOdataFetch({\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 // customTransformInput: ({ data, fieldAttributes }) => {\n // if (fieldAttributes.type === \"date\") {\n // const rawData = data as string;\n // const fmDate = dayjs(rawData).format(\"YYYY+MM+DD\");\n // const isoDate = dayjs(rawData).format(\"YYYY-MM-DD\");\n\n // const newData = rawData\n // .replace(isoDate, fmDate)\n // .replace(\"T\", \" \")\n // .replace(\"Z\", \"\");\n\n // console.log(`replaced ${isoDate} with ${fmDate}`);\n // return newData;\n // }\n\n // return data;\n // },\n },\n adapter: ({ options }) => {\n return {\n options: { config },\n create: async ({ data, model, select }) => {\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 const result = await fetch(`/${model}/$count`, {\n method: \"GET\",\n query: {\n $filter: filter,\n },\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 ?? 0;\n },\n findOne: async ({ model, where }) => {\n const filter = parseWhere(where);\n logger.debug(\"$filter\", filter);\n const result = await fetch(`/${model}`, {\n method: \"GET\",\n query: {\n ...(filter.length > 0 ? { $filter: filter } : {}),\n $top: 1,\n },\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] ?? null;\n },\n findMany: async ({ model, where, limit, offset, sortBy }) => {\n const filter = parseWhere(where);\n logger.debug(\"$filter\", filter);\n\n const rows = await fetch(`/${model}`, {\n method: \"GET\",\n query: {\n ...(filter.length > 0 ? { $filter: filter } : {}),\n $top: limit,\n $skip: offset,\n ...(sortBy\n ? { $orderby: `\"${sortBy.field}\" ${sortBy.direction ?? \"asc\"}` }\n : {}),\n },\n output: z.object({ value: z.array(z.any()) }),\n });\n if (rows.error) {\n throw new Error(\"Failed to find records\");\n }\n return rows.data?.value ?? [];\n },\n delete: async ({ model, where }) => {\n const filter = parseWhere(where);\n logger.debug(\"$filter\", filter);\n console.log(\"delete\", model, where, filter);\n const result = await fetch(`/${model}`, {\n method: \"DELETE\",\n query: {\n ...(where.length > 0 ? { $filter: filter } : {}),\n $top: 1,\n },\n });\n if (result.error) {\n throw new Error(\"Failed to delete record\");\n }\n },\n deleteMany: async ({ model, where }) => {\n const filter = parseWhere(where);\n logger.debug(\n where\n .map((o) => `typeof ${o.value} is ${typeof o.value}`)\n .join(\"\\n\"),\n );\n logger.debug(\"$filter\", filter);\n\n const result = await fetch(`/${model}/$count`, {\n method: \"DELETE\",\n query: {\n ...(where.length > 0 ? { $filter: filter } : {}),\n },\n output: z.coerce.number(),\n });\n if (result.error) {\n throw new Error(\"Failed to delete record\");\n }\n return result.data ?? 0;\n },\n update: async ({ model, where, update }) => {\n const result = await fetch(`/${model}`, {\n method: \"PATCH\",\n query: {\n ...(where.length > 0 ? { $filter: parseWhere(where) } : {}),\n $top: 1,\n $select: [`\"id\"`],\n },\n body: update,\n output: z.object({ value: z.array(z.any()) }),\n });\n return result.data?.value?.[0] ?? null;\n },\n updateMany: async ({ model, where, update }) => {\n const filter = parseWhere(where);\n const result = await fetch(`/${model}`, {\n method: \"PATCH\",\n query: {\n ...(where.length > 0 ? { $filter: filter } : {}),\n },\n body: update,\n });\n return result.data as any;\n },\n };\n },\n });\n};\n"],"names":[],"mappings":"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"}
@@ -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 fs from \"fs-extra\";\n\nimport {\n executeMigration,\n planMigration,\n prettyPrintMigrationPlan,\n} from \"../migrate\";\nimport { getAdapter, getAuthTables } from \"better-auth/db\";\nimport { getConfig } from \"../better-auth-cli/utils/get-config\";\nimport { logger } from \"better-auth\";\nimport { BasicAuth, Connection, Database } from \"fm-odata-client\";\nimport prompts from \"prompts\";\nimport chalk from \"chalk\";\nimport { AdapterOptions } from \"../adapter\";\nimport { createFmOdataFetch } from \"../odata\";\n\nasync function main() {\n const program = new Command();\n\n program\n .command(\"migrate\", { isDefault: true })\n .option(\n \"--cwd <path>\",\n \"Path to the current working directory\",\n process.cwd(),\n )\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(\n \"This generator is only compatible with the FileMaker adapter.\",\n );\n return;\n }\n\n const betterAuthSchema = getAuthTables(config);\n\n const adapterConfig = (adapter.options as AdapterOptions).config;\n const fetch = createFmOdataFetch({\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 });\n\n const migrationPlan = await planMigration(\n fetch,\n betterAuthSchema,\n adapterConfig.odata.database,\n );\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(\n chalk.gray(\n \"💡 Tip: You can use the --yes flag to skip this confirmation.\",\n ),\n );\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":";;;;;;;;;;AAkBA,eAAe,OAAO;AACd,QAAA,UAAU,IAAI,QAAQ;AAE5B,UACG,QAAQ,WAAW,EAAE,WAAW,KAAM,CAAA,EACtC;AAAA,IACC;AAAA,IACA;AAAA,IACA,QAAQ,IAAI;AAAA,EAAA,EAEb,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;AACvB,aAAA;AAAA,QACL;AAAA,MACF;AACA;AAAA,IAAA;AAGI,UAAA,mBAAmB,cAAc,MAAM;AAEvC,UAAA,gBAAiB,QAAQ,QAA2B;AAC1D,UAAM,QAAQ,mBAAmB;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,IAAA,CAC3B;AAED,UAAM,gBAAgB,MAAM;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,cAAc,MAAM;AAAA,IACtB;AAEI,QAAA,cAAc,WAAW,GAAG;AAC9B,aAAO,KAAK,8CAA8C;AAC1D;AAAA,IAAA;AAGE,QAAA,CAAC,QAAQ,KAAK;AAChB,+BAAyB,aAAa;AAElC,UAAA,cAAc,SAAS,GAAG;AACpB,gBAAA;AAAA,UACN,MAAM;AAAA,YACJ;AAAA,UAAA;AAAA,QAEJ;AAAA,MAAA;AAGF,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 fs from \"fs-extra\";\n\nimport {\n executeMigration,\n planMigration,\n prettyPrintMigrationPlan,\n} from \"../migrate\";\nimport { getAdapter, getAuthTables } from \"better-auth/db\";\nimport { getConfig } from \"../better-auth-cli/utils/get-config\";\nimport { logger } from \"better-auth\";\nimport prompts from \"prompts\";\nimport chalk from \"chalk\";\nimport { AdapterOptions } from \"../adapter\";\nimport { createFmOdataFetch } from \"../odata\";\n\nasync function main() {\n const program = new Command();\n\n program\n .command(\"migrate\", { isDefault: true })\n .option(\n \"--cwd <path>\",\n \"Path to the current working directory\",\n process.cwd(),\n )\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(\n \"This generator is only compatible with the FileMaker adapter.\",\n );\n return;\n }\n\n const betterAuthSchema = getAuthTables(config);\n\n const adapterConfig = (adapter.options as AdapterOptions).config;\n const fetch = createFmOdataFetch({\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 });\n\n const migrationPlan = await planMigration(\n fetch,\n betterAuthSchema,\n adapterConfig.odata.database,\n );\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(\n chalk.gray(\n \"💡 Tip: You can use the --yes flag to skip this confirmation.\",\n ),\n );\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":";;;;;;;;;;AAiBA,eAAe,OAAO;AACd,QAAA,UAAU,IAAI,QAAQ;AAE5B,UACG,QAAQ,WAAW,EAAE,WAAW,KAAM,CAAA,EACtC;AAAA,IACC;AAAA,IACA;AAAA,IACA,QAAQ,IAAI;AAAA,EAAA,EAEb,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;AACvB,aAAA;AAAA,QACL;AAAA,MACF;AACA;AAAA,IAAA;AAGI,UAAA,mBAAmB,cAAc,MAAM;AAEvC,UAAA,gBAAiB,QAAQ,QAA2B;AAC1D,UAAM,QAAQ,mBAAmB;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,IAAA,CAC3B;AAED,UAAM,gBAAgB,MAAM;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,cAAc,MAAM;AAAA,IACtB;AAEI,QAAA,cAAc,WAAW,GAAG;AAC9B,aAAO,KAAK,8CAA8C;AAC1D;AAAA,IAAA;AAGE,QAAA,CAAC,QAAQ,KAAK;AAChB,+BAAyB,aAAa;AAElC,UAAA,cAAc,SAAS,GAAG;AACpB,gBAAA;AAAA,UACN,MAAM;AAAA,YACJ;AAAA,UAAA;AAAA,QAEJ;AAAA,MAAA;AAGF,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;"}
@@ -18,7 +18,7 @@ export declare const migrationPlanSchema: z.ZodArray<z.ZodObject<{
18
18
  unique: z.ZodOptional<z.ZodBoolean>;
19
19
  global: z.ZodOptional<z.ZodBoolean>;
20
20
  repetitions: z.ZodOptional<z.ZodNumber>;
21
- type: z.ZodLiteral<"string">;
21
+ type: z.ZodLiteral<"varchar">;
22
22
  maxLength: z.ZodOptional<z.ZodNumber>;
23
23
  default: z.ZodOptional<z.ZodEnum<{
24
24
  USER: "USER";
@@ -31,7 +31,7 @@ async function planMigration(fetch, betterAuthSchema, databaseName) {
31
31
  ([fieldKey, fieldValue]) => typeof fieldValue === "object" && fieldValue !== null && "$Type" in fieldValue
32
32
  ).map(([fieldKey, fieldValue]) => ({
33
33
  name: fieldKey,
34
- type: fieldValue.$Type === "Edm.String" ? "string" : fieldValue.$Type === "Edm.DateTimeOffset" ? "timestamp" : fieldValue.$Type === "Edm.Decimal" || fieldValue.$Type === "Edm.Int32" || fieldValue.$Type === "Edm.Int64" ? "numeric" : "string"
34
+ type: fieldValue.$Type === "Edm.String" ? "varchar" : fieldValue.$Type === "Edm.DateTimeOffset" ? "timestamp" : fieldValue.$Type === "Edm.Decimal" || fieldValue.$Type === "Edm.Int32" || fieldValue.$Type === "Edm.Int64" ? "numeric" : "varchar"
35
35
  }));
36
36
  acc[entitySetName] = fields;
37
37
  return acc;
@@ -47,7 +47,7 @@ async function planMigration(fetch, betterAuthSchema, databaseName) {
47
47
  const fields = Object.entries(baTable.fields).map(
48
48
  ([key, field]) => ({
49
49
  name: field.fieldName ?? key,
50
- type: field.type === "boolean" || field.type.includes("number") ? "numeric" : field.type === "date" ? "timestamp" : "string"
50
+ type: field.type === "boolean" || field.type.includes("number") ? "numeric" : field.type === "date" ? "timestamp" : "varchar"
51
51
  })
52
52
  );
53
53
  const tableExists = Object.prototype.hasOwnProperty.call(
@@ -61,7 +61,7 @@ async function planMigration(fetch, betterAuthSchema, databaseName) {
61
61
  fields: [
62
62
  {
63
63
  name: "id",
64
- type: "string",
64
+ type: "varchar",
65
65
  primary: true,
66
66
  unique: true
67
67
  },
@@ -128,7 +128,7 @@ const genericFieldSchema = z.object({
128
128
  repetitions: z.number().optional()
129
129
  });
130
130
  const stringFieldSchema = genericFieldSchema.extend({
131
- type: z.literal("string"),
131
+ type: z.literal("varchar"),
132
132
  maxLength: z.number().optional(),
133
133
  default: z.enum(["USER", "USERNAME", "CURRENT_USER"]).optional()
134
134
  });
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.js","sources":["../../src/migrate.ts"],"sourcesContent":["import { type BetterAuthDbSchema } from \"better-auth/db\";\nimport { type Metadata, type Field as FmField } from \"fm-odata-client\";\nimport chalk from \"chalk\";\nimport z from \"zod/v4\";\nimport { createFmOdataFetch } from \"./odata\";\n\nexport async function getMetadata(\n fetch: ReturnType<typeof createFmOdataFetch>,\n databaseName: string,\n) {\n const result = await fetch(\"/$metadata\", {\n method: \"GET\",\n headers: { accept: \"application/json\" },\n output: z.looseObject({\n $Version: z.string(),\n \"@ServerVersion\": z.string(),\n }),\n });\n return result.data?.[databaseName] as Metadata;\n}\n\nexport async function planMigration(\n fetch: ReturnType<typeof createFmOdataFetch>,\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 let 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) return acc;\n const fields = Object.entries(entityType)\n .filter(\n ([fieldKey, fieldValue]) =>\n typeof fieldValue === \"object\" &&\n fieldValue !== null &&\n \"$Type\" in fieldValue,\n )\n .map(([fieldKey, fieldValue]) => ({\n name: fieldKey,\n type:\n fieldValue.$Type === \"Edm.String\"\n ? \"string\"\n : fieldValue.$Type === \"Edm.DateTimeOffset\"\n ? \"timestamp\"\n : fieldValue.$Type === \"Edm.Decimal\" ||\n fieldValue.$Type === \"Edm.Int32\" ||\n fieldValue.$Type === \"Edm.Int64\"\n ? \"numeric\"\n : \"string\",\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(\n ([key, field]) => ({\n name: field.fieldName ?? key,\n type:\n field.type === \"boolean\" || field.type.includes(\"number\")\n ? \"numeric\"\n : field.type === \"date\"\n ? \"timestamp\"\n : \"string\",\n }),\n );\n\n // get existing table or create it\n const tableExists = Object.prototype.hasOwnProperty.call(\n existingTables,\n baTable.modelName,\n );\n\n if (!tableExists) {\n migrationPlan.push({\n tableName: baTable.modelName,\n operation: \"create\",\n fields: [\n {\n name: \"id\",\n type: \"string\",\n primary: true,\n unique: true,\n },\n ...fields,\n ],\n });\n } else {\n const existingFields = (existingTables[baTable.modelName] || []).map(\n (f) => f.name,\n );\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 fields.forEach((field) => {\n if (\n existingFields.includes(field.name) &&\n existingFieldMap[field.name] !== field.type\n ) {\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(\n (f) => !existingFields.includes(f.name),\n );\n if (fieldsToAdd.length > 0) {\n migrationPlan.push({\n tableName: baTable.modelName,\n operation: \"update\",\n fields: fieldsToAdd,\n });\n }\n }\n }\n\n return migrationPlan;\n}\n\nexport async function executeMigration(\n fetch: ReturnType<typeof createFmOdataFetch>,\n migrationPlan: MigrationPlan,\n) {\n for (const step of migrationPlan) {\n if (step.operation === \"create\") {\n console.log(\"Creating table:\", step.tableName);\n await fetch(\"@post/FileMaker_Tables\", {\n body: {\n tableName: step.tableName,\n fields: step.fields,\n },\n });\n } else if (step.operation === \"update\") {\n console.log(\"Adding fields to table:\", step.tableName);\n await fetch(\"@post/FileMaker_Tables/:tableName\", {\n params: { tableName: step.tableName },\n body: { fields: step.fields },\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(\"string\"),\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\nexport const 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) fieldDesc += \", primary\";\n if (field.unique) fieldDesc += \", unique\";\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 BetterAuthDbSchema } from \"better-auth/db\";\nimport { type Metadata } from \"fm-odata-client\";\nimport chalk from \"chalk\";\nimport z from \"zod/v4\";\nimport { createFmOdataFetch } from \"./odata\";\n\nexport async function getMetadata(\n fetch: ReturnType<typeof createFmOdataFetch>,\n databaseName: string,\n) {\n const result = await fetch(\"/$metadata\", {\n method: \"GET\",\n headers: { accept: \"application/json\" },\n output: z.looseObject({\n $Version: z.string(),\n \"@ServerVersion\": z.string(),\n }),\n });\n return result.data?.[databaseName] as Metadata;\n}\n\nexport async function planMigration(\n fetch: ReturnType<typeof createFmOdataFetch>,\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 let 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) return acc;\n const fields = Object.entries(entityType)\n .filter(\n ([fieldKey, fieldValue]) =>\n typeof fieldValue === \"object\" &&\n fieldValue !== null &&\n \"$Type\" in fieldValue,\n )\n .map(([fieldKey, fieldValue]) => ({\n name: fieldKey,\n type:\n fieldValue.$Type === \"Edm.String\"\n ? \"varchar\"\n : fieldValue.$Type === \"Edm.DateTimeOffset\"\n ? \"timestamp\"\n : fieldValue.$Type === \"Edm.Decimal\" ||\n fieldValue.$Type === \"Edm.Int32\" ||\n fieldValue.$Type === \"Edm.Int64\"\n ? \"numeric\"\n : \"varchar\",\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(\n ([key, field]) => ({\n name: field.fieldName ?? key,\n type:\n field.type === \"boolean\" || field.type.includes(\"number\")\n ? \"numeric\"\n : field.type === \"date\"\n ? \"timestamp\"\n : \"varchar\",\n }),\n );\n\n // get existing table or create it\n const tableExists = Object.prototype.hasOwnProperty.call(\n existingTables,\n baTable.modelName,\n );\n\n if (!tableExists) {\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 } else {\n const existingFields = (existingTables[baTable.modelName] || []).map(\n (f) => f.name,\n );\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 fields.forEach((field) => {\n if (\n existingFields.includes(field.name) &&\n existingFieldMap[field.name] !== field.type\n ) {\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(\n (f) => !existingFields.includes(f.name),\n );\n if (fieldsToAdd.length > 0) {\n migrationPlan.push({\n tableName: baTable.modelName,\n operation: \"update\",\n fields: fieldsToAdd,\n });\n }\n }\n }\n\n return migrationPlan;\n}\n\nexport async function executeMigration(\n fetch: ReturnType<typeof createFmOdataFetch>,\n migrationPlan: MigrationPlan,\n) {\n for (const step of migrationPlan) {\n if (step.operation === \"create\") {\n console.log(\"Creating table:\", step.tableName);\n await fetch(\"@post/FileMaker_Tables\", {\n body: {\n tableName: step.tableName,\n fields: step.fields,\n },\n });\n } else if (step.operation === \"update\") {\n console.log(\"Adding fields to table:\", step.tableName);\n await fetch(\"@post/FileMaker_Tables/:tableName\", {\n params: { tableName: step.tableName },\n body: { fields: step.fields },\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\nexport const 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) fieldDesc += \", primary\";\n if (field.unique) fieldDesc += \", unique\";\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,5 +1,6 @@
1
1
  import { createSchema, createFetch } from "@better-fetch/fetch";
2
2
  import { logger } from "@better-fetch/logger";
3
+ import { logger as logger$1 } from "better-auth";
3
4
  import { ok, err } from "neverthrow";
4
5
  import { z } from "zod/v4";
5
6
  const schema = createSchema({
@@ -57,11 +58,11 @@ function createFmOdataFetch(args) {
57
58
  verbose: args.logging === "verbose",
58
59
  enabled: args.logging === "verbose" || !!args.logging,
59
60
  console: {
60
- fail: (...args2) => console.error(...args2),
61
- success: (...args2) => console.log(...args2),
62
- log: (...args2) => console.log(...args2),
63
- error: (...args2) => console.error(...args2),
64
- warn: (...args2) => console.warn(...args2)
61
+ fail: (...args2) => logger$1.error("better-fetch", ...args2),
62
+ success: (...args2) => logger$1.info("better-fetch", ...args2),
63
+ log: (...args2) => logger$1.info("better-fetch", ...args2),
64
+ error: (...args2) => logger$1.error("better-fetch", ...args2),
65
+ warn: (...args2) => logger$1.warn("better-fetch", ...args2)
65
66
  }
66
67
  })
67
68
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/odata/index.ts"],"sourcesContent":["import { createFetch, createSchema } from \"@better-fetch/fetch\";\nimport { logger } from \"@better-fetch/logger\";\nimport { err, ok, Result } from \"neverthrow\";\nimport { z } from \"zod/v4\";\n\nexport type BasicAuthCredentials = {\n username: string;\n password: string;\n};\nexport type OttoAPIKeyAuth = {\n apiKey: string;\n};\nexport type ODataAuth = BasicAuthCredentials | OttoAPIKeyAuth;\n\nexport function isBasicAuth(auth: ODataAuth): auth is BasicAuthCredentials {\n return (\n typeof (auth as BasicAuthCredentials).username === \"string\" &&\n typeof (auth as BasicAuthCredentials).password === \"string\"\n );\n}\n\nexport function isOttoAPIKeyAuth(auth: ODataAuth): auth is OttoAPIKeyAuth {\n return typeof (auth as OttoAPIKeyAuth).apiKey === \"string\";\n}\n\nexport type FmOdataConfig = {\n serverUrl: string;\n auth: ODataAuth;\n database: string;\n logging?: true | \"verbose\" | \"none\";\n};\n\nconst schema = createSchema({\n /**\n * Create a new table\n */\n \"@post/FileMaker_Tables\": {\n input: z.object({ tableName: z.string(), fields: z.array(z.any()) }),\n },\n /**\n * Add fields to a table\n */\n \"@patch/FileMaker_Tables/:tableName\": {\n params: z.object({ tableName: z.string() }),\n input: z.object({ fields: z.array(z.any()) }),\n },\n /**\n * Delete a table\n */\n \"@delete/FileMaker_Tables/:tableName\": {\n params: z.object({ tableName: z.string() }),\n },\n /**\n * Delete a field from a table\n */\n \"@delete/FileMaker_Tables/:tableName/:fieldName\": {\n params: z.object({ tableName: z.string(), fieldName: z.string() }),\n },\n});\n\nexport function createFmOdataFetch(args: FmOdataConfig) {\n const result = validateUrl(args.serverUrl);\n\n if (result.isErr()) {\n throw new Error(\"Invalid server URL\");\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 return createFetch({\n baseURL,\n auth:\n \"apiKey\" in args.auth\n ? { type: \"Bearer\", token: args.auth.apiKey }\n : {\n type: \"Basic\",\n username: args.auth.username,\n password: args.auth.password,\n },\n onError: (error) => {\n console.error(\"url\", error.request.url.toString());\n console.log(error.error);\n console.log(\"error.request.body\", JSON.stringify(error.request.body));\n },\n schema,\n plugins: [\n logger({\n verbose: args.logging === \"verbose\",\n enabled: args.logging === \"verbose\" || !!args.logging,\n console: {\n fail: (...args) => console.error(...args),\n success: (...args) => console.log(...args),\n log: (...args) => console.log(...args),\n error: (...args) => console.error(...args),\n warn: (...args) => console.warn(...args),\n },\n }),\n ],\n });\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"],"names":["args"],"mappings":";;;;AAgCA,MAAM,SAAS,aAAa;AAAA;AAAA;AAAA;AAAA,EAI1B,0BAA0B;AAAA,IACxB,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,QAAQ,EAAE,MAAM,EAAE,IAAK,CAAA,EAAG,CAAA;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAIA,sCAAsC;AAAA,IACpC,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,OAAA,GAAU;AAAA,IAC1C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAA,CAAK,EAAG,CAAA;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAIA,uCAAuC;AAAA,IACrC,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,SAAU,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAIA,kDAAkD;AAAA,IAChD,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,GAAG,WAAW,EAAE,SAAU,CAAA;AAAA,EAAA;AAErE,CAAC;AAEM,SAAS,mBAAmB,MAAqB;AAChD,QAAA,SAAS,YAAY,KAAK,SAAS;AAErC,MAAA,OAAO,SAAS;AACZ,UAAA,IAAI,MAAM,oBAAoB;AAAA,EAAA;AAElC,MAAA,UAAU,OAAO,MAAM;AACvB,MAAA,YAAY,KAAK,MAAM;AACd,eAAA;AAAA,EAAA;AAEF,aAAA,iBAAiB,KAAK,QAAQ;AAEzC,SAAO,YAAY;AAAA,IACjB;AAAA,IACA,MACE,YAAY,KAAK,OACb,EAAE,MAAM,UAAU,OAAO,KAAK,KAAK,OAAA,IACnC;AAAA,MACE,MAAM;AAAA,MACN,UAAU,KAAK,KAAK;AAAA,MACpB,UAAU,KAAK,KAAK;AAAA,IACtB;AAAA,IACN,SAAS,CAAC,UAAU;AAClB,cAAQ,MAAM,OAAO,MAAM,QAAQ,IAAI,UAAU;AACzC,cAAA,IAAI,MAAM,KAAK;AACvB,cAAQ,IAAI,sBAAsB,KAAK,UAAU,MAAM,QAAQ,IAAI,CAAC;AAAA,IACtE;AAAA,IACA;AAAA,IACA,SAAS;AAAA,MACP,OAAO;AAAA,QACL,SAAS,KAAK,YAAY;AAAA,QAC1B,SAAS,KAAK,YAAY,aAAa,CAAC,CAAC,KAAK;AAAA,QAC9C,SAAS;AAAA,UACP,MAAM,IAAIA,UAAS,QAAQ,MAAM,GAAGA,KAAI;AAAA,UACxC,SAAS,IAAIA,UAAS,QAAQ,IAAI,GAAGA,KAAI;AAAA,UACzC,KAAK,IAAIA,UAAS,QAAQ,IAAI,GAAGA,KAAI;AAAA,UACrC,OAAO,IAAIA,UAAS,QAAQ,MAAM,GAAGA,KAAI;AAAA,UACzC,MAAM,IAAIA,UAAS,QAAQ,KAAK,GAAGA,KAAI;AAAA,QAAA;AAAA,MAE1C,CAAA;AAAA,IAAA;AAAA,EACH,CACD;AACH;AAEO,SAAS,YAAY,OAAqC;AAC3D,MAAA;AACI,UAAA,MAAM,IAAI,IAAI,KAAK;AACzB,WAAO,GAAG,GAAG;AAAA,WACN,OAAO;AACd,WAAO,IAAI,KAAK;AAAA,EAAA;AAEpB;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/odata/index.ts"],"sourcesContent":["import { createFetch, createSchema } from \"@better-fetch/fetch\";\nimport { logger } from \"@better-fetch/logger\";\nimport { logger as betterAuthLogger } from \"better-auth\";\nimport { err, ok, Result } from \"neverthrow\";\nimport { z } from \"zod/v4\";\n\nexport type BasicAuthCredentials = {\n username: string;\n password: string;\n};\nexport type OttoAPIKeyAuth = {\n apiKey: string;\n};\nexport type ODataAuth = BasicAuthCredentials | OttoAPIKeyAuth;\n\nexport function isBasicAuth(auth: ODataAuth): auth is BasicAuthCredentials {\n return (\n typeof (auth as BasicAuthCredentials).username === \"string\" &&\n typeof (auth as BasicAuthCredentials).password === \"string\"\n );\n}\n\nexport function isOttoAPIKeyAuth(auth: ODataAuth): auth is OttoAPIKeyAuth {\n return typeof (auth as OttoAPIKeyAuth).apiKey === \"string\";\n}\n\nexport type FmOdataConfig = {\n serverUrl: string;\n auth: ODataAuth;\n database: string;\n logging?: true | \"verbose\" | \"none\";\n};\n\nconst schema = createSchema({\n /**\n * Create a new table\n */\n \"@post/FileMaker_Tables\": {\n input: z.object({ tableName: z.string(), fields: z.array(z.any()) }),\n },\n /**\n * Add fields to a table\n */\n \"@patch/FileMaker_Tables/:tableName\": {\n params: z.object({ tableName: z.string() }),\n input: z.object({ fields: z.array(z.any()) }),\n },\n /**\n * Delete a table\n */\n \"@delete/FileMaker_Tables/:tableName\": {\n params: z.object({ tableName: z.string() }),\n },\n /**\n * Delete a field from a table\n */\n \"@delete/FileMaker_Tables/:tableName/:fieldName\": {\n params: z.object({ tableName: z.string(), fieldName: z.string() }),\n },\n});\n\nexport function createFmOdataFetch(args: FmOdataConfig) {\n const result = validateUrl(args.serverUrl);\n\n if (result.isErr()) {\n throw new Error(\"Invalid server URL\");\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 return createFetch({\n baseURL,\n auth:\n \"apiKey\" in args.auth\n ? { type: \"Bearer\", token: args.auth.apiKey }\n : {\n type: \"Basic\",\n username: args.auth.username,\n password: args.auth.password,\n },\n onError: (error) => {\n console.error(\"url\", error.request.url.toString());\n console.log(error.error);\n console.log(\"error.request.body\", JSON.stringify(error.request.body));\n },\n schema,\n plugins: [\n logger({\n verbose: args.logging === \"verbose\",\n enabled: args.logging === \"verbose\" || !!args.logging,\n console: {\n fail: (...args) => betterAuthLogger.error(\"better-fetch\", ...args),\n success: (...args) => betterAuthLogger.info(\"better-fetch\", ...args),\n log: (...args) => betterAuthLogger.info(\"better-fetch\", ...args),\n error: (...args) => betterAuthLogger.error(\"better-fetch\", ...args),\n warn: (...args) => betterAuthLogger.warn(\"better-fetch\", ...args),\n },\n }),\n ],\n });\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"],"names":["args","betterAuthLogger"],"mappings":";;;;;AAiCA,MAAM,SAAS,aAAa;AAAA;AAAA;AAAA;AAAA,EAI1B,0BAA0B;AAAA,IACxB,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,QAAQ,EAAE,MAAM,EAAE,IAAK,CAAA,EAAG,CAAA;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAIA,sCAAsC;AAAA,IACpC,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,OAAA,GAAU;AAAA,IAC1C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAA,CAAK,EAAG,CAAA;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAIA,uCAAuC;AAAA,IACrC,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,SAAU,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAIA,kDAAkD;AAAA,IAChD,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,GAAG,WAAW,EAAE,SAAU,CAAA;AAAA,EAAA;AAErE,CAAC;AAEM,SAAS,mBAAmB,MAAqB;AAChD,QAAA,SAAS,YAAY,KAAK,SAAS;AAErC,MAAA,OAAO,SAAS;AACZ,UAAA,IAAI,MAAM,oBAAoB;AAAA,EAAA;AAElC,MAAA,UAAU,OAAO,MAAM;AACvB,MAAA,YAAY,KAAK,MAAM;AACd,eAAA;AAAA,EAAA;AAEF,aAAA,iBAAiB,KAAK,QAAQ;AAEzC,SAAO,YAAY;AAAA,IACjB;AAAA,IACA,MACE,YAAY,KAAK,OACb,EAAE,MAAM,UAAU,OAAO,KAAK,KAAK,OAAA,IACnC;AAAA,MACE,MAAM;AAAA,MACN,UAAU,KAAK,KAAK;AAAA,MACpB,UAAU,KAAK,KAAK;AAAA,IACtB;AAAA,IACN,SAAS,CAAC,UAAU;AAClB,cAAQ,MAAM,OAAO,MAAM,QAAQ,IAAI,UAAU;AACzC,cAAA,IAAI,MAAM,KAAK;AACvB,cAAQ,IAAI,sBAAsB,KAAK,UAAU,MAAM,QAAQ,IAAI,CAAC;AAAA,IACtE;AAAA,IACA;AAAA,IACA,SAAS;AAAA,MACP,OAAO;AAAA,QACL,SAAS,KAAK,YAAY;AAAA,QAC1B,SAAS,KAAK,YAAY,aAAa,CAAC,CAAC,KAAK;AAAA,QAC9C,SAAS;AAAA,UACP,MAAM,IAAIA,UAASC,SAAiB,MAAM,gBAAgB,GAAGD,KAAI;AAAA,UACjE,SAAS,IAAIA,UAASC,SAAiB,KAAK,gBAAgB,GAAGD,KAAI;AAAA,UACnE,KAAK,IAAIA,UAASC,SAAiB,KAAK,gBAAgB,GAAGD,KAAI;AAAA,UAC/D,OAAO,IAAIA,UAASC,SAAiB,MAAM,gBAAgB,GAAGD,KAAI;AAAA,UAClE,MAAM,IAAIA,UAASC,SAAiB,KAAK,gBAAgB,GAAGD,KAAI;AAAA,QAAA;AAAA,MAEnE,CAAA;AAAA,IAAA;AAAA,EACH,CACD;AACH;AAEO,SAAS,YAAY,OAAqC;AAC3D,MAAA;AACI,UAAA,MAAM,IAAI,IAAI,KAAK;AACzB,WAAO,GAAG,GAAG;AAAA,WACN,OAAO;AACd,WAAO,IAAI,KAAK;AAAA,EAAA;AAEpB;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proofkit/better-auth",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "FileMaker adapter for Better Auth",
5
5
  "type": "module",
6
6
  "main": "dist/esm/index.js",
@@ -46,6 +46,7 @@
46
46
  "c12": "^3.0.4",
47
47
  "chalk": "5.4.1",
48
48
  "commander": "^14.0.0",
49
+ "dayjs": "^1.11.13",
49
50
  "dotenv": "^16.5.0",
50
51
  "fs-extra": "^11.3.0",
51
52
  "neverthrow": "^8.2.0",
package/src/adapter.ts CHANGED
@@ -5,6 +5,8 @@ import {
5
5
  } from "better-auth/adapters";
6
6
  import { createFmOdataFetch, type FmOdataConfig } from "./odata";
7
7
  import { prettifyError, z } from "zod/v4";
8
+ import dayjs from "dayjs";
9
+ import { logger } from "better-auth";
8
10
 
9
11
  const configSchema = z.object({
10
12
  debugLogs: z.unknown().optional(),
@@ -70,10 +72,20 @@ export function parseWhere(where?: CleanedWhere[]): string {
70
72
  // Helper to format values for OData
71
73
  function formatValue(value: any): string {
72
74
  if (value === null) return "null";
73
- if (typeof value === "string") return `'${value.replace(/'/g, "''")}'`;
74
75
  if (typeof value === "boolean") return value ? "true" : "false";
75
- if (value instanceof Date) return `'${value.toISOString()}'`;
76
+ if (value instanceof Date) return value.toISOString();
76
77
  if (Array.isArray(value)) return `(${value.map(formatValue).join(",")})`;
78
+
79
+ // Handle strings - check if it's an ISO date string first
80
+ if (typeof value === "string") {
81
+ // Check if it's an ISO date string (YYYY-MM-DDTHH:mm:ss.sssZ format)
82
+ const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
83
+ if (isoDateRegex.test(value)) {
84
+ return value; // Return ISO date strings without quotes
85
+ }
86
+ return `'${value.replace(/'/g, "''")}'`; // Regular strings get quotes
87
+ }
88
+
77
89
  return value?.toString() ?? "";
78
90
  }
79
91
 
@@ -144,7 +156,7 @@ export const FileMakerAdapter = (
144
156
 
145
157
  const fetch = createFmOdataFetch({
146
158
  ...config.odata,
147
- // logging: config.debugLogs ? true : "none",
159
+ logging: config.debugLogs ? "verbose" : "none",
148
160
  });
149
161
 
150
162
  return createAdapter({
@@ -157,11 +169,32 @@ export const FileMakerAdapter = (
157
169
  supportsDates: false, // Whether the database supports dates. (Default: true)
158
170
  supportsBooleans: false, // Whether the database supports booleans. (Default: true)
159
171
  supportsNumericIds: false, // Whether the database supports auto-incrementing numeric IDs. (Default: true)
172
+ // customTransformInput: ({ data, fieldAttributes }) => {
173
+ // if (fieldAttributes.type === "date") {
174
+ // const rawData = data as string;
175
+ // const fmDate = dayjs(rawData).format("YYYY+MM+DD");
176
+ // const isoDate = dayjs(rawData).format("YYYY-MM-DD");
177
+
178
+ // const newData = rawData
179
+ // .replace(isoDate, fmDate)
180
+ // .replace("T", " ")
181
+ // .replace("Z", "");
182
+
183
+ // console.log(`replaced ${isoDate} with ${fmDate}`);
184
+ // return newData;
185
+ // }
186
+
187
+ // return data;
188
+ // },
160
189
  },
161
190
  adapter: ({ options }) => {
162
191
  return {
163
192
  options: { config },
164
193
  create: async ({ data, model, select }) => {
194
+ if (model === "session") {
195
+ console.log("session", data);
196
+ }
197
+
165
198
  const result = await fetch(`/${model}`, {
166
199
  method: "POST",
167
200
  body: data,
@@ -175,10 +208,12 @@ export const FileMakerAdapter = (
175
208
  return result.data as any;
176
209
  },
177
210
  count: async ({ model, where }) => {
211
+ const filter = parseWhere(where);
212
+ logger.debug("$filter", filter);
178
213
  const result = await fetch(`/${model}/$count`, {
179
214
  method: "GET",
180
215
  query: {
181
- $filter: parseWhere(where),
216
+ $filter: filter,
182
217
  },
183
218
  output: z.object({ value: z.number() }),
184
219
  });
@@ -188,10 +223,12 @@ export const FileMakerAdapter = (
188
223
  return result.data?.value ?? 0;
189
224
  },
190
225
  findOne: async ({ model, where }) => {
226
+ const filter = parseWhere(where);
227
+ logger.debug("$filter", filter);
191
228
  const result = await fetch(`/${model}`, {
192
229
  method: "GET",
193
230
  query: {
194
- ...(where.length > 0 ? { $filter: parseWhere(where) } : {}),
231
+ ...(filter.length > 0 ? { $filter: filter } : {}),
195
232
  $top: 1,
196
233
  },
197
234
  output: z.object({ value: z.array(z.any()) }),
@@ -203,6 +240,7 @@ export const FileMakerAdapter = (
203
240
  },
204
241
  findMany: async ({ model, where, limit, offset, sortBy }) => {
205
242
  const filter = parseWhere(where);
243
+ logger.debug("$filter", filter);
206
244
 
207
245
  const rows = await fetch(`/${model}`, {
208
246
  method: "GET",
@@ -222,12 +260,14 @@ export const FileMakerAdapter = (
222
260
  return rows.data?.value ?? [];
223
261
  },
224
262
  delete: async ({ model, where }) => {
263
+ const filter = parseWhere(where);
264
+ logger.debug("$filter", filter);
265
+ console.log("delete", model, where, filter);
225
266
  const result = await fetch(`/${model}`, {
226
267
  method: "DELETE",
227
268
  query: {
228
- ...(where.length > 0 ? { $filter: parseWhere(where) } : {}),
269
+ ...(where.length > 0 ? { $filter: filter } : {}),
229
270
  $top: 1,
230
- $select: [`"id"`],
231
271
  },
232
272
  });
233
273
  if (result.error) {
@@ -235,12 +275,18 @@ export const FileMakerAdapter = (
235
275
  }
236
276
  },
237
277
  deleteMany: async ({ model, where }) => {
278
+ const filter = parseWhere(where);
279
+ logger.debug(
280
+ where
281
+ .map((o) => `typeof ${o.value} is ${typeof o.value}`)
282
+ .join("\n"),
283
+ );
284
+ logger.debug("$filter", filter);
285
+
238
286
  const result = await fetch(`/${model}/$count`, {
239
287
  method: "DELETE",
240
288
  query: {
241
- ...(where.length > 0 ? { $filter: parseWhere(where) } : {}),
242
- $top: 1,
243
- $select: [`"id"`],
289
+ ...(where.length > 0 ? { $filter: filter } : {}),
244
290
  },
245
291
  output: z.coerce.number(),
246
292
  });
package/src/cli/index.ts CHANGED
@@ -10,7 +10,6 @@ import {
10
10
  import { getAdapter, getAuthTables } from "better-auth/db";
11
11
  import { getConfig } from "../better-auth-cli/utils/get-config";
12
12
  import { logger } from "better-auth";
13
- import { BasicAuth, Connection, Database } from "fm-odata-client";
14
13
  import prompts from "prompts";
15
14
  import chalk from "chalk";
16
15
  import { AdapterOptions } from "../adapter";
package/src/migrate.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type BetterAuthDbSchema } from "better-auth/db";
2
- import { type Metadata, type Field as FmField } from "fm-odata-client";
2
+ import { type Metadata } from "fm-odata-client";
3
3
  import chalk from "chalk";
4
4
  import z from "zod/v4";
5
5
  import { createFmOdataFetch } from "./odata";
@@ -54,14 +54,14 @@ export async function planMigration(
54
54
  name: fieldKey,
55
55
  type:
56
56
  fieldValue.$Type === "Edm.String"
57
- ? "string"
57
+ ? "varchar"
58
58
  : fieldValue.$Type === "Edm.DateTimeOffset"
59
59
  ? "timestamp"
60
60
  : fieldValue.$Type === "Edm.Decimal" ||
61
61
  fieldValue.$Type === "Edm.Int32" ||
62
62
  fieldValue.$Type === "Edm.Int64"
63
63
  ? "numeric"
64
- : "string",
64
+ : "varchar",
65
65
  }));
66
66
  acc[entitySetName] = fields;
67
67
  return acc;
@@ -88,7 +88,7 @@ export async function planMigration(
88
88
  ? "numeric"
89
89
  : field.type === "date"
90
90
  ? "timestamp"
91
- : "string",
91
+ : "varchar",
92
92
  }),
93
93
  );
94
94
 
@@ -105,7 +105,7 @@ export async function planMigration(
105
105
  fields: [
106
106
  {
107
107
  name: "id",
108
- type: "string",
108
+ type: "varchar",
109
109
  primary: true,
110
110
  unique: true,
111
111
  },
@@ -183,7 +183,7 @@ const genericFieldSchema = z.object({
183
183
  });
184
184
 
185
185
  const stringFieldSchema = genericFieldSchema.extend({
186
- type: z.literal("string"),
186
+ type: z.literal("varchar"),
187
187
  maxLength: z.number().optional(),
188
188
  default: z.enum(["USER", "USERNAME", "CURRENT_USER"]).optional(),
189
189
  });
@@ -221,6 +221,8 @@ const fieldSchema = z.discriminatedUnion("type", [
221
221
  containerFieldSchema,
222
222
  ]);
223
223
 
224
+ type FmField = z.infer<typeof fieldSchema>;
225
+
224
226
  export const migrationPlanSchema = z
225
227
  .object({
226
228
  tableName: z.string(),
@@ -1,5 +1,6 @@
1
1
  import { createFetch, createSchema } from "@better-fetch/fetch";
2
2
  import { logger } from "@better-fetch/logger";
3
+ import { logger as betterAuthLogger } from "better-auth";
3
4
  import { err, ok, Result } from "neverthrow";
4
5
  import { z } from "zod/v4";
5
6
 
@@ -91,11 +92,11 @@ export function createFmOdataFetch(args: FmOdataConfig) {
91
92
  verbose: args.logging === "verbose",
92
93
  enabled: args.logging === "verbose" || !!args.logging,
93
94
  console: {
94
- fail: (...args) => console.error(...args),
95
- success: (...args) => console.log(...args),
96
- log: (...args) => console.log(...args),
97
- error: (...args) => console.error(...args),
98
- warn: (...args) => console.warn(...args),
95
+ fail: (...args) => betterAuthLogger.error("better-fetch", ...args),
96
+ success: (...args) => betterAuthLogger.info("better-fetch", ...args),
97
+ log: (...args) => betterAuthLogger.info("better-fetch", ...args),
98
+ error: (...args) => betterAuthLogger.error("better-fetch", ...args),
99
+ warn: (...args) => betterAuthLogger.warn("better-fetch", ...args),
99
100
  },
100
101
  }),
101
102
  ],