@proofkit/better-auth 0.3.1-beta.1 → 0.4.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/intent.js +20 -0
- package/dist/esm/adapter.d.ts +3 -6
- package/dist/esm/adapter.js +58 -95
- package/dist/esm/adapter.js.map +1 -1
- package/dist/esm/cli/index.js +65 -22
- package/dist/esm/cli/index.js.map +1 -1
- package/dist/esm/migrate.d.ts +21 -83
- package/dist/esm/migrate.js +81 -100
- package/dist/esm/migrate.js.map +1 -1
- package/package.json +12 -9
- package/skills/better-auth-setup/SKILL.md +233 -0
- package/src/adapter.ts +80 -107
- package/src/cli/index.ts +86 -26
- package/src/migrate.ts +129 -160
- package/dist/esm/odata/index.d.ts +0 -29
- package/dist/esm/odata/index.js +0 -157
- package/dist/esm/odata/index.js.map +0 -1
- package/src/odata/index.ts +0 -219
package/bin/intent.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Auto-generated by @tanstack/intent setup
|
|
3
|
+
// Exposes the intent end-user CLI for consumers of this library.
|
|
4
|
+
// Commit this file, then add to your package.json:
|
|
5
|
+
// "bin": { "intent": "./bin/intent.js" }
|
|
6
|
+
try {
|
|
7
|
+
await import("@tanstack/intent/intent-library");
|
|
8
|
+
} catch (e) {
|
|
9
|
+
if (e?.code === "ERR_MODULE_NOT_FOUND" || e?.code === "MODULE_NOT_FOUND") {
|
|
10
|
+
console.error("@tanstack/intent is not installed.");
|
|
11
|
+
console.error("");
|
|
12
|
+
console.error("Install it as a dev dependency:");
|
|
13
|
+
console.error(" npm add -D @tanstack/intent");
|
|
14
|
+
console.error("");
|
|
15
|
+
console.error("Or run directly:");
|
|
16
|
+
console.error(" npx @tanstack/intent@latest list");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
throw e;
|
|
20
|
+
}
|
package/dist/esm/adapter.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
+
import { Database } from '../../fmodata/src.js';
|
|
1
2
|
import { CleanedWhere, DBAdapterDebugLogOption } from 'better-auth/adapters';
|
|
2
|
-
import { FmOdataConfig } from './odata.js';
|
|
3
3
|
export interface FileMakerAdapterConfig {
|
|
4
4
|
debugLogs?: DBAdapterDebugLogOption;
|
|
5
5
|
usePlural?: boolean;
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
export interface AdapterOptions {
|
|
9
|
-
config: FileMakerAdapterConfig;
|
|
6
|
+
database: Database;
|
|
10
7
|
}
|
|
11
8
|
export declare function parseWhere(where?: CleanedWhere[]): string;
|
|
12
|
-
export declare const FileMakerAdapter: (
|
|
9
|
+
export declare const FileMakerAdapter: (config: FileMakerAdapterConfig) => import('better-auth/adapters').AdapterFactory<import('better-auth').BetterAuthOptions>;
|
package/dist/esm/adapter.js
CHANGED
|
@@ -1,26 +1,5 @@
|
|
|
1
1
|
import { logger } from "better-auth";
|
|
2
|
-
import {
|
|
3
|
-
import buildQuery from "odata-query";
|
|
4
|
-
import { z, prettifyError } from "zod/v4";
|
|
5
|
-
import { createRawFetch } from "./odata/index.js";
|
|
6
|
-
const configSchema = z.object({
|
|
7
|
-
debugLogs: z.unknown().optional(),
|
|
8
|
-
usePlural: z.boolean().optional(),
|
|
9
|
-
odata: z.object({
|
|
10
|
-
serverUrl: z.url(),
|
|
11
|
-
auth: z.union([z.object({ username: z.string(), password: z.string() }), z.object({ apiKey: z.string() })]),
|
|
12
|
-
database: z.string().endsWith(".fmp12")
|
|
13
|
-
})
|
|
14
|
-
});
|
|
15
|
-
const defaultConfig = {
|
|
16
|
-
debugLogs: false,
|
|
17
|
-
usePlural: false,
|
|
18
|
-
odata: {
|
|
19
|
-
serverUrl: "",
|
|
20
|
-
auth: { username: "", password: "" },
|
|
21
|
-
database: ""
|
|
22
|
-
}
|
|
23
|
-
};
|
|
2
|
+
import { createAdapterFactory } from "better-auth/adapters";
|
|
24
3
|
const FIELD_SPECIAL_CHARS_REGEX = /[\s_]/;
|
|
25
4
|
const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
|
|
26
5
|
function parseWhere(where) {
|
|
@@ -107,43 +86,48 @@ function parseWhere(where) {
|
|
|
107
86
|
}
|
|
108
87
|
return clauses.join(" ");
|
|
109
88
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
89
|
+
function buildQueryString(params) {
|
|
90
|
+
var _a;
|
|
91
|
+
const parts = [];
|
|
92
|
+
if (params.top !== void 0) {
|
|
93
|
+
parts.push(`$top=${params.top}`);
|
|
114
94
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
95
|
+
if (params.skip !== void 0) {
|
|
96
|
+
parts.push(`$skip=${params.skip}`);
|
|
97
|
+
}
|
|
98
|
+
if (params.filter) {
|
|
99
|
+
parts.push(`$filter=${encodeURIComponent(params.filter)}`);
|
|
100
|
+
}
|
|
101
|
+
if (params.orderBy) {
|
|
102
|
+
parts.push(`$orderby=${encodeURIComponent(params.orderBy)}`);
|
|
103
|
+
}
|
|
104
|
+
if ((_a = params.select) == null ? void 0 : _a.length) {
|
|
105
|
+
parts.push(`$select=${params.select.map(encodeURIComponent).join(",")}`);
|
|
106
|
+
}
|
|
107
|
+
return parts.length > 0 ? `?${parts.join("&")}` : "";
|
|
108
|
+
}
|
|
109
|
+
const FileMakerAdapter = (config) => {
|
|
110
|
+
if (!config.database || typeof config.database !== "object") {
|
|
111
|
+
throw new Error("FileMakerAdapter requires a `database` (fmodata Database instance).");
|
|
112
|
+
}
|
|
113
|
+
const db = config.database;
|
|
114
|
+
const adapterFactory = createAdapterFactory({
|
|
121
115
|
config: {
|
|
122
116
|
adapterId: "filemaker",
|
|
123
117
|
adapterName: "FileMaker",
|
|
124
118
|
usePlural: config.usePlural ?? false,
|
|
125
|
-
// Whether the table names in the schema are plural.
|
|
126
119
|
debugLogs: config.debugLogs ?? false,
|
|
127
|
-
// Whether to enable debug logs.
|
|
128
120
|
supportsJSON: false,
|
|
129
|
-
// Whether the database supports JSON. (Default: false)
|
|
130
121
|
supportsDates: false,
|
|
131
|
-
// Whether the database supports dates. (Default: true)
|
|
132
122
|
supportsBooleans: false,
|
|
133
|
-
// Whether the database supports booleans. (Default: true)
|
|
134
123
|
supportsNumericIds: false
|
|
135
|
-
// Whether the database supports auto-incrementing numeric IDs. (Default: true)
|
|
136
124
|
},
|
|
137
125
|
adapter: () => {
|
|
138
126
|
return {
|
|
139
127
|
create: async ({ data, model }) => {
|
|
140
|
-
|
|
141
|
-
console.log("session", data);
|
|
142
|
-
}
|
|
143
|
-
const result = await fetch(`/${model}`, {
|
|
128
|
+
const result = await db._makeRequest(`/${model}`, {
|
|
144
129
|
method: "POST",
|
|
145
|
-
body: data
|
|
146
|
-
output: z.looseObject({ id: z.string() })
|
|
130
|
+
body: JSON.stringify(data)
|
|
147
131
|
});
|
|
148
132
|
if (result.error) {
|
|
149
133
|
throw new Error("Failed to create record");
|
|
@@ -154,14 +138,11 @@ const FileMakerAdapter = (_config = defaultConfig) => {
|
|
|
154
138
|
var _a;
|
|
155
139
|
const filter = parseWhere(where);
|
|
156
140
|
logger.debug("$filter", filter);
|
|
157
|
-
const query =
|
|
141
|
+
const query = buildQueryString({
|
|
158
142
|
filter: filter.length > 0 ? filter : void 0
|
|
159
143
|
});
|
|
160
|
-
const result = await
|
|
161
|
-
|
|
162
|
-
output: z.object({ value: z.number() })
|
|
163
|
-
});
|
|
164
|
-
if (!result.data) {
|
|
144
|
+
const result = await db._makeRequest(`/${model}/$count${query}`);
|
|
145
|
+
if (result.error) {
|
|
165
146
|
throw new Error("Failed to count records");
|
|
166
147
|
}
|
|
167
148
|
return ((_a = result.data) == null ? void 0 : _a.value) ?? 0;
|
|
@@ -170,14 +151,11 @@ const FileMakerAdapter = (_config = defaultConfig) => {
|
|
|
170
151
|
var _a, _b;
|
|
171
152
|
const filter = parseWhere(where);
|
|
172
153
|
logger.debug("$filter", filter);
|
|
173
|
-
const query =
|
|
154
|
+
const query = buildQueryString({
|
|
174
155
|
top: 1,
|
|
175
156
|
filter: filter.length > 0 ? filter : void 0
|
|
176
157
|
});
|
|
177
|
-
const result = await
|
|
178
|
-
method: "GET",
|
|
179
|
-
output: z.object({ value: z.array(z.any()) })
|
|
180
|
-
});
|
|
158
|
+
const result = await db._makeRequest(`/${model}${query}`);
|
|
181
159
|
if (result.error) {
|
|
182
160
|
throw new Error("Failed to find record");
|
|
183
161
|
}
|
|
@@ -187,17 +165,14 @@ const FileMakerAdapter = (_config = defaultConfig) => {
|
|
|
187
165
|
var _a;
|
|
188
166
|
const filter = parseWhere(where);
|
|
189
167
|
logger.debug("FIND MANY", { where, filter });
|
|
190
|
-
const query =
|
|
168
|
+
const query = buildQueryString({
|
|
191
169
|
top: limit,
|
|
192
170
|
skip: offset,
|
|
193
171
|
orderBy: sortBy ? `${sortBy.field} ${sortBy.direction ?? "asc"}` : void 0,
|
|
194
172
|
filter: filter.length > 0 ? filter : void 0
|
|
195
173
|
});
|
|
196
174
|
logger.debug("QUERY", query);
|
|
197
|
-
const result = await
|
|
198
|
-
method: "GET",
|
|
199
|
-
output: z.object({ value: z.array(z.any()) })
|
|
200
|
-
});
|
|
175
|
+
const result = await db._makeRequest(`/${model}${query}`);
|
|
201
176
|
logger.debug("RESULT", result);
|
|
202
177
|
if (result.error) {
|
|
203
178
|
throw new Error("Failed to find records");
|
|
@@ -207,45 +182,36 @@ const FileMakerAdapter = (_config = defaultConfig) => {
|
|
|
207
182
|
delete: async ({ model, where }) => {
|
|
208
183
|
var _a, _b, _c;
|
|
209
184
|
const filter = parseWhere(where);
|
|
210
|
-
console.log("DELETE", { model, where, filter });
|
|
211
185
|
logger.debug("$filter", filter);
|
|
212
|
-
const query =
|
|
186
|
+
const query = buildQueryString({
|
|
213
187
|
top: 1,
|
|
214
188
|
select: [`"id"`],
|
|
215
189
|
filter: filter.length > 0 ? filter : void 0
|
|
216
190
|
});
|
|
217
|
-
const toDelete = await
|
|
218
|
-
method: "GET",
|
|
219
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) })
|
|
220
|
-
});
|
|
191
|
+
const toDelete = await db._makeRequest(`/${model}${query}`);
|
|
221
192
|
const id = (_c = (_b = (_a = toDelete.data) == null ? void 0 : _a.value) == null ? void 0 : _b[0]) == null ? void 0 : _c.id;
|
|
222
193
|
if (!id) {
|
|
223
194
|
return;
|
|
224
195
|
}
|
|
225
|
-
const result = await
|
|
196
|
+
const result = await db._makeRequest(`/${model}('${id}')`, {
|
|
226
197
|
method: "DELETE"
|
|
227
198
|
});
|
|
228
199
|
if (result.error) {
|
|
229
|
-
console.log("DELETE ERROR", result.error);
|
|
230
200
|
throw new Error("Failed to delete record");
|
|
231
201
|
}
|
|
232
202
|
},
|
|
233
203
|
deleteMany: async ({ model, where }) => {
|
|
234
204
|
var _a, _b;
|
|
235
205
|
const filter = parseWhere(where);
|
|
236
|
-
|
|
237
|
-
const query = buildQuery({
|
|
206
|
+
const query = buildQueryString({
|
|
238
207
|
select: [`"id"`],
|
|
239
208
|
filter: filter.length > 0 ? filter : void 0
|
|
240
209
|
});
|
|
241
|
-
const rows = await
|
|
242
|
-
method: "GET",
|
|
243
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) })
|
|
244
|
-
});
|
|
210
|
+
const rows = await db._makeRequest(`/${model}${query}`);
|
|
245
211
|
const ids = ((_b = (_a = rows.data) == null ? void 0 : _a.value) == null ? void 0 : _b.map((r) => r.id)) ?? [];
|
|
246
212
|
let deleted = 0;
|
|
247
213
|
for (const id of ids) {
|
|
248
|
-
const res = await
|
|
214
|
+
const res = await db._makeRequest(`/${model}('${id}')`, {
|
|
249
215
|
method: "DELETE"
|
|
250
216
|
});
|
|
251
217
|
if (!res.error) {
|
|
@@ -259,51 +225,42 @@ const FileMakerAdapter = (_config = defaultConfig) => {
|
|
|
259
225
|
const filter = parseWhere(where);
|
|
260
226
|
logger.debug("UPDATE", { model, where, update });
|
|
261
227
|
logger.debug("$filter", filter);
|
|
262
|
-
const query =
|
|
228
|
+
const query = buildQueryString({
|
|
263
229
|
select: [`"id"`],
|
|
264
230
|
filter: filter.length > 0 ? filter : void 0
|
|
265
231
|
});
|
|
266
|
-
const existing = await
|
|
267
|
-
method: "GET",
|
|
268
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) })
|
|
269
|
-
});
|
|
232
|
+
const existing = await db._makeRequest(`/${model}${query}`);
|
|
270
233
|
logger.debug("EXISTING", existing.data);
|
|
271
234
|
const id = (_c = (_b = (_a = existing.data) == null ? void 0 : _a.value) == null ? void 0 : _b[0]) == null ? void 0 : _c.id;
|
|
272
235
|
if (!id) {
|
|
273
236
|
return null;
|
|
274
237
|
}
|
|
275
|
-
const patchRes = await
|
|
238
|
+
const patchRes = await db._makeRequest(`/${model}('${id}')`, {
|
|
276
239
|
method: "PATCH",
|
|
277
|
-
body: update
|
|
240
|
+
body: JSON.stringify(update)
|
|
278
241
|
});
|
|
279
242
|
logger.debug("PATCH RES", patchRes.data);
|
|
280
243
|
if (patchRes.error) {
|
|
281
244
|
return null;
|
|
282
245
|
}
|
|
283
|
-
const readBack = await
|
|
284
|
-
method: "GET",
|
|
285
|
-
output: z.record(z.string(), z.unknown())
|
|
286
|
-
});
|
|
246
|
+
const readBack = await db._makeRequest(`/${model}('${id}')`);
|
|
287
247
|
logger.debug("READ BACK", readBack.data);
|
|
288
248
|
return readBack.data ?? null;
|
|
289
249
|
},
|
|
290
250
|
updateMany: async ({ model, where, update }) => {
|
|
291
251
|
var _a, _b;
|
|
292
252
|
const filter = parseWhere(where);
|
|
293
|
-
const query =
|
|
253
|
+
const query = buildQueryString({
|
|
294
254
|
select: [`"id"`],
|
|
295
255
|
filter: filter.length > 0 ? filter : void 0
|
|
296
256
|
});
|
|
297
|
-
const rows = await
|
|
298
|
-
method: "GET",
|
|
299
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) })
|
|
300
|
-
});
|
|
257
|
+
const rows = await db._makeRequest(`/${model}${query}`);
|
|
301
258
|
const ids = ((_b = (_a = rows.data) == null ? void 0 : _a.value) == null ? void 0 : _b.map((r) => r.id)) ?? [];
|
|
302
259
|
let updated = 0;
|
|
303
260
|
for (const id of ids) {
|
|
304
|
-
const res = await
|
|
261
|
+
const res = await db._makeRequest(`/${model}('${id}')`, {
|
|
305
262
|
method: "PATCH",
|
|
306
|
-
body: update
|
|
263
|
+
body: JSON.stringify(update)
|
|
307
264
|
});
|
|
308
265
|
if (!res.error) {
|
|
309
266
|
updated++;
|
|
@@ -314,8 +271,14 @@ const FileMakerAdapter = (_config = defaultConfig) => {
|
|
|
314
271
|
};
|
|
315
272
|
}
|
|
316
273
|
});
|
|
317
|
-
|
|
318
|
-
|
|
274
|
+
const originalFactory = adapterFactory;
|
|
275
|
+
const wrappedFactory = ((options) => {
|
|
276
|
+
const adapter = originalFactory(options);
|
|
277
|
+
adapter.database = db;
|
|
278
|
+
return adapter;
|
|
279
|
+
});
|
|
280
|
+
wrappedFactory.database = db;
|
|
281
|
+
return wrappedFactory;
|
|
319
282
|
};
|
|
320
283
|
export {
|
|
321
284
|
FileMakerAdapter,
|
package/dist/esm/adapter.js.map
CHANGED
|
@@ -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 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
|
+
{"version":3,"file":"adapter.js","sources":["../../src/adapter.ts"],"sourcesContent":["/** biome-ignore-all lint/suspicious/noExplicitAny: library code */\nimport type { Database } from \"@proofkit/fmodata\";\nimport { logger } from \"better-auth\";\nimport { type CleanedWhere, createAdapterFactory, type DBAdapterDebugLogOption } from \"better-auth/adapters\";\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 * The fmodata Database instance to use for all OData requests.\n */\n database: Database;\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\n/**\n * Build an OData query string from parameters.\n */\nfunction buildQueryString(params: {\n top?: number;\n skip?: number;\n filter?: string;\n orderBy?: string;\n select?: string[];\n}): string {\n const parts: string[] = [];\n if (params.top !== undefined) {\n parts.push(`$top=${params.top}`);\n }\n if (params.skip !== undefined) {\n parts.push(`$skip=${params.skip}`);\n }\n if (params.filter) {\n parts.push(`$filter=${encodeURIComponent(params.filter)}`);\n }\n if (params.orderBy) {\n parts.push(`$orderby=${encodeURIComponent(params.orderBy)}`);\n }\n if (params.select?.length) {\n parts.push(`$select=${params.select.map(encodeURIComponent).join(\",\")}`);\n }\n return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\n}\n\nexport const FileMakerAdapter = (config: FileMakerAdapterConfig) => {\n if (!config.database || typeof config.database !== \"object\") {\n throw new Error(\"FileMakerAdapter requires a `database` (fmodata Database instance).\");\n }\n\n const db = config.database;\n\n const adapterFactory = createAdapterFactory({\n config: {\n adapterId: \"filemaker\",\n adapterName: \"FileMaker\",\n usePlural: config.usePlural ?? false,\n debugLogs: config.debugLogs ?? false,\n supportsJSON: false,\n supportsDates: false,\n supportsBooleans: false,\n supportsNumericIds: false,\n },\n adapter: () => {\n return {\n create: async ({ data, model }) => {\n const result = await db._makeRequest<Record<string, any>>(`/${model}`, {\n method: \"POST\",\n body: JSON.stringify(data),\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 = buildQueryString({\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const result = await db._makeRequest<{ value: number }>(`/${model}/$count${query}`);\n if (result.error) {\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 = buildQueryString({\n top: 1,\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const result = await db._makeRequest<{ value: any[] }>(`/${model}${query}`);\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 = buildQueryString({\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 db._makeRequest<{ value: any[] }>(`/${model}${query}`);\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 logger.debug(\"$filter\", filter);\n\n // Find a single id matching the filter\n const query = buildQueryString({\n top: 1,\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const toDelete = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);\n\n const id = toDelete.data?.value?.[0]?.id;\n if (!id) {\n return;\n }\n\n const result = await db._makeRequest(`/${model}('${id}')`, {\n method: \"DELETE\",\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\n // Find all ids matching the filter\n const query = buildQueryString({\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const rows = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);\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 db._makeRequest(`/${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\n // Find one id to update\n const query = buildQueryString({\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const existing = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);\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 db._makeRequest(`/${model}('${id}')`, {\n method: \"PATCH\",\n body: JSON.stringify(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 db._makeRequest<Record<string, unknown>>(`/${model}('${id}')`);\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\n // Find all ids matching the filter\n const query = buildQueryString({\n select: [`\"id\"`],\n filter: filter.length > 0 ? filter : undefined,\n });\n\n const rows = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);\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 db._makeRequest(`/${model}('${id}')`, {\n method: \"PATCH\",\n body: JSON.stringify(update),\n });\n if (!res.error) {\n updated++;\n }\n }\n return updated as any;\n },\n };\n },\n });\n\n // Expose the Database instance for CLI access.\n // Set on both the factory function (for pre-getAdapter extraction)\n // and the returned adapter (for post-getAdapter extraction).\n const originalFactory = adapterFactory;\n const wrappedFactory = ((options: unknown) => {\n const adapter = (originalFactory as (opts: unknown) => Record<string, unknown>)(options);\n adapter.database = db;\n return adapter;\n }) as typeof adapterFactory;\n (wrappedFactory as unknown as { database: Database }).database = db;\n return wrappedFactory;\n};\n"],"names":[],"mappings":"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"}
|
package/dist/esm/cli/index.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node --no-warnings
|
|
2
2
|
import { Command } from "@commander-js/extra-typings";
|
|
3
|
+
import { FMServerConnection } from "@proofkit/fmodata";
|
|
3
4
|
import { logger } from "better-auth";
|
|
4
|
-
import {
|
|
5
|
+
import { getSchema } from "better-auth/db";
|
|
5
6
|
import chalk from "chalk";
|
|
6
7
|
import fs from "fs-extra";
|
|
7
8
|
import prompts from "prompts";
|
|
8
9
|
import { getConfig } from "../better-auth-cli/utils/get-config.js";
|
|
9
10
|
import { planMigration, prettyPrintMigrationPlan, executeMigration } from "../migrate.js";
|
|
10
|
-
import { createRawFetch } from "../odata/index.js";
|
|
11
11
|
import "dotenv/config";
|
|
12
12
|
async function main() {
|
|
13
13
|
const program = new Command();
|
|
14
14
|
program.command("migrate", { isDefault: true }).option("--cwd <path>", "Path to the current working directory", process.cwd()).option("--config <path>", "Path to the config file").option("-u, --username <username>", "Full Access Username").option("-p, --password <password>", "Full Access Password").option("-y, --yes", "Skip confirmation", false).action(async (options) => {
|
|
15
|
+
var _a, _b, _c, _d;
|
|
15
16
|
const cwd = options.cwd;
|
|
16
17
|
if (!fs.existsSync(cwd)) {
|
|
17
18
|
logger.error(`The directory "${cwd}" does not exist.`);
|
|
@@ -27,35 +28,70 @@ async function main() {
|
|
|
27
28
|
);
|
|
28
29
|
return;
|
|
29
30
|
}
|
|
30
|
-
const
|
|
31
|
-
|
|
31
|
+
const databaseFactory = config.database;
|
|
32
|
+
if (!databaseFactory || typeof databaseFactory !== "function") {
|
|
33
|
+
logger.error("No database adapter found in auth config.");
|
|
32
34
|
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
+
}
|
|
36
|
+
let adapter;
|
|
37
|
+
try {
|
|
38
|
+
adapter = databaseFactory(config);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
logger.error(e instanceof Error ? e.message : String(e));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
if ((adapter == null ? void 0 : adapter.id) !== "filemaker") {
|
|
35
44
|
logger.error("This generator is only compatible with the FileMaker adapter.");
|
|
36
45
|
return;
|
|
37
46
|
}
|
|
38
47
|
const betterAuthSchema = getSchema(config);
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
const configDb = adapter.database ?? ((_a = config.database) == null ? void 0 : _a.database);
|
|
49
|
+
if (!configDb || typeof configDb !== "object" || !("schema" in configDb)) {
|
|
50
|
+
logger.error(
|
|
51
|
+
"Could not extract Database instance from adapter. Ensure your auth.ts uses FileMakerAdapter with an fmodata Database."
|
|
52
|
+
);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
let db = configDb;
|
|
56
|
+
const dbObj = configDb;
|
|
57
|
+
const dbName = dbObj._getDatabaseName ?? dbObj.databaseName ?? "";
|
|
58
|
+
const baseUrl = (_c = (_b = dbObj.context) == null ? void 0 : _b._getBaseUrl) == null ? void 0 : _c.call(_b);
|
|
59
|
+
const serverUrl = baseUrl ? new URL(baseUrl).origin : void 0;
|
|
60
|
+
if (options.username && options.password) {
|
|
61
|
+
if (!dbName) {
|
|
62
|
+
logger.error("Could not determine database filename from adapter config.");
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
if (!baseUrl) {
|
|
66
|
+
logger.error(
|
|
67
|
+
"Could not determine server URL from adapter config. Ensure your auth.ts uses FMServerConnection."
|
|
68
|
+
);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
const fetchClientOptions = (_d = dbObj.context) == null ? void 0 : _d._fetchClientOptions;
|
|
72
|
+
const connection = new FMServerConnection({
|
|
73
|
+
serverUrl,
|
|
74
|
+
auth: {
|
|
45
75
|
username: options.username,
|
|
46
76
|
password: options.password
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
77
|
+
},
|
|
78
|
+
fetchClientOptions
|
|
79
|
+
});
|
|
80
|
+
db = connection.database(dbName);
|
|
81
|
+
}
|
|
82
|
+
let migrationPlan;
|
|
83
|
+
try {
|
|
84
|
+
migrationPlan = await planMigration(db, betterAuthSchema);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
logger.error(`Failed to read database schema: ${err instanceof Error ? err.message : err}`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
53
89
|
if (migrationPlan.length === 0) {
|
|
54
90
|
logger.info("No changes to apply. Database is up to date.");
|
|
55
91
|
return;
|
|
56
92
|
}
|
|
57
93
|
if (!options.yes) {
|
|
58
|
-
prettyPrintMigrationPlan(migrationPlan);
|
|
94
|
+
prettyPrintMigrationPlan(migrationPlan, { serverUrl, fileName: dbName });
|
|
59
95
|
if (migrationPlan.length > 0) {
|
|
60
96
|
console.log(chalk.gray("💡 Tip: You can use the --yes flag to skip this confirmation."));
|
|
61
97
|
}
|
|
@@ -69,11 +105,18 @@ async function main() {
|
|
|
69
105
|
return;
|
|
70
106
|
}
|
|
71
107
|
}
|
|
72
|
-
|
|
73
|
-
|
|
108
|
+
try {
|
|
109
|
+
await executeMigration(db, migrationPlan);
|
|
110
|
+
logger.info("Migration applied successfully.");
|
|
111
|
+
} catch {
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
74
114
|
});
|
|
75
115
|
await program.parseAsync(process.argv);
|
|
76
116
|
process.exit(0);
|
|
77
117
|
}
|
|
78
|
-
main().catch(
|
|
118
|
+
main().catch((err) => {
|
|
119
|
+
logger.error(err.message ?? err);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
});
|
|
79
122
|
//# sourceMappingURL=index.js.map
|
|
@@ -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 {
|
|
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 type { Database, FFetchOptions } from \"@proofkit/fmodata\";\nimport { FMServerConnection } from \"@proofkit/fmodata\";\nimport { logger } from \"better-auth\";\nimport { getSchema } from \"better-auth/db\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport prompts from \"prompts\";\nimport { getConfig } from \"../better-auth-cli/utils/get-config\";\nimport { executeMigration, planMigration, prettyPrintMigrationPlan } from \"../migrate\";\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 // Resolve adapter directly (getAdapter removed in Better Auth 1.5)\n const databaseFactory = config.database;\n if (!databaseFactory || typeof databaseFactory !== \"function\") {\n logger.error(\"No database adapter found in auth config.\");\n process.exit(1);\n }\n let adapter: { id?: string; database?: unknown };\n try {\n adapter = (databaseFactory as (opts: unknown) => { id?: string; database?: unknown })(config);\n } catch (e) {\n logger.error(e instanceof Error ? e.message : String(e));\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 // Extract Database from the adapter factory or resolved adapter.\n // config.database is the FileMakerAdapter factory function (has .database set on it).\n // adapter is the resolved adapter after getAdapter() calls the factory (also has .database).\n // Try both: adapter first (post-call), then config.database (pre-call / factory function).\n const configDb =\n (adapter as unknown as { database?: Database }).database ??\n (config.database as unknown as { database?: Database } | undefined)?.database;\n if (!configDb || typeof configDb !== \"object\" || !(\"schema\" in configDb)) {\n logger.error(\n \"Could not extract Database instance from adapter. Ensure your auth.ts uses FileMakerAdapter with an fmodata Database.\",\n );\n process.exit(1);\n }\n let db: Database = configDb;\n\n // Extract database name and server URL for display.\n // Try the public getter first (_getDatabaseName), fall back to the private field (databaseName).\n const dbObj = configDb as unknown as {\n _getDatabaseName?: string;\n databaseName?: string;\n context?: { _getBaseUrl?: () => string; _fetchClientOptions?: unknown };\n };\n const dbName: string = dbObj._getDatabaseName ?? dbObj.databaseName ?? \"\";\n const baseUrl: string | undefined = dbObj.context?._getBaseUrl?.();\n const serverUrl = baseUrl ? new URL(baseUrl).origin : undefined;\n\n // If CLI credential overrides are provided, construct a new connection\n if (options.username && options.password) {\n if (!dbName) {\n logger.error(\"Could not determine database filename from adapter config.\");\n process.exit(1);\n }\n\n if (!baseUrl) {\n logger.error(\n \"Could not determine server URL from adapter config. Ensure your auth.ts uses FMServerConnection.\",\n );\n process.exit(1);\n }\n\n const fetchClientOptions = dbObj.context?._fetchClientOptions as FFetchOptions | undefined;\n const connection = new FMServerConnection({\n serverUrl: serverUrl as string,\n auth: {\n username: options.username,\n password: options.password,\n },\n fetchClientOptions,\n });\n\n db = connection.database(dbName);\n }\n\n let migrationPlan: Awaited<ReturnType<typeof planMigration>>;\n try {\n migrationPlan = await planMigration(db, betterAuthSchema);\n } catch (err) {\n logger.error(`Failed to read database schema: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\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, { serverUrl, fileName: dbName });\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 try {\n await executeMigration(db, migrationPlan);\n logger.info(\"Migration applied successfully.\");\n } catch {\n process.exit(1);\n }\n });\n await program.parseAsync(process.argv);\n process.exit(0);\n}\n\nmain().catch((err) => {\n logger.error(err.message ?? err);\n process.exit(1);\n});\n"],"names":[],"mappings":"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"}
|