@proofkit/better-auth 0.3.1-beta.1 → 0.4.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/adapter.d.ts +3 -6
- package/dist/esm/adapter.js +56 -93
- package/dist/esm/adapter.js.map +1 -1
- package/dist/esm/cli/index.js +52 -17
- 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 +2 -5
- package/src/adapter.ts +78 -105
- package/src/cli/index.ts +72 -21
- 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/dist/esm/migrate.d.ts
CHANGED
|
@@ -1,89 +1,27 @@
|
|
|
1
|
+
import { Database, Metadata } from '../../fmodata/src.js';
|
|
1
2
|
import { DBFieldAttribute } from 'better-auth/db';
|
|
2
|
-
import { Metadata } from 'fm-odata-client';
|
|
3
|
-
import { default as z } from 'zod/v4';
|
|
4
|
-
import { createRawFetch } from './odata.js';
|
|
5
3
|
type BetterAuthSchema = Record<string, {
|
|
6
4
|
fields: Record<string, DBFieldAttribute>;
|
|
7
5
|
order: number;
|
|
8
6
|
}>;
|
|
9
|
-
export declare function getMetadata(
|
|
10
|
-
export declare function planMigration(
|
|
11
|
-
export declare function executeMigration(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
USERNAME: "USERNAME";
|
|
30
|
-
CURRENT_USER: "CURRENT_USER";
|
|
31
|
-
}>>;
|
|
32
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
33
|
-
name: z.ZodString;
|
|
34
|
-
nullable: z.ZodOptional<z.ZodBoolean>;
|
|
35
|
-
primary: z.ZodOptional<z.ZodBoolean>;
|
|
36
|
-
unique: z.ZodOptional<z.ZodBoolean>;
|
|
37
|
-
global: z.ZodOptional<z.ZodBoolean>;
|
|
38
|
-
repetitions: z.ZodOptional<z.ZodNumber>;
|
|
39
|
-
type: z.ZodLiteral<"numeric">;
|
|
40
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
41
|
-
name: z.ZodString;
|
|
42
|
-
nullable: z.ZodOptional<z.ZodBoolean>;
|
|
43
|
-
primary: z.ZodOptional<z.ZodBoolean>;
|
|
44
|
-
unique: z.ZodOptional<z.ZodBoolean>;
|
|
45
|
-
global: z.ZodOptional<z.ZodBoolean>;
|
|
46
|
-
repetitions: z.ZodOptional<z.ZodNumber>;
|
|
47
|
-
type: z.ZodLiteral<"date">;
|
|
48
|
-
default: z.ZodOptional<z.ZodEnum<{
|
|
49
|
-
CURRENT_DATE: "CURRENT_DATE";
|
|
50
|
-
CURDATE: "CURDATE";
|
|
51
|
-
}>>;
|
|
52
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
53
|
-
name: z.ZodString;
|
|
54
|
-
nullable: z.ZodOptional<z.ZodBoolean>;
|
|
55
|
-
primary: z.ZodOptional<z.ZodBoolean>;
|
|
56
|
-
unique: z.ZodOptional<z.ZodBoolean>;
|
|
57
|
-
global: z.ZodOptional<z.ZodBoolean>;
|
|
58
|
-
repetitions: z.ZodOptional<z.ZodNumber>;
|
|
59
|
-
type: z.ZodLiteral<"time">;
|
|
60
|
-
default: z.ZodOptional<z.ZodEnum<{
|
|
61
|
-
CURRENT_TIME: "CURRENT_TIME";
|
|
62
|
-
CURTIME: "CURTIME";
|
|
63
|
-
}>>;
|
|
64
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
65
|
-
name: z.ZodString;
|
|
66
|
-
nullable: z.ZodOptional<z.ZodBoolean>;
|
|
67
|
-
primary: z.ZodOptional<z.ZodBoolean>;
|
|
68
|
-
unique: z.ZodOptional<z.ZodBoolean>;
|
|
69
|
-
global: z.ZodOptional<z.ZodBoolean>;
|
|
70
|
-
repetitions: z.ZodOptional<z.ZodNumber>;
|
|
71
|
-
type: z.ZodLiteral<"timestamp">;
|
|
72
|
-
default: z.ZodOptional<z.ZodEnum<{
|
|
73
|
-
CURRENT_TIMESTAMP: "CURRENT_TIMESTAMP";
|
|
74
|
-
CURTIMESTAMP: "CURTIMESTAMP";
|
|
75
|
-
}>>;
|
|
76
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
77
|
-
name: z.ZodString;
|
|
78
|
-
nullable: z.ZodOptional<z.ZodBoolean>;
|
|
79
|
-
primary: z.ZodOptional<z.ZodBoolean>;
|
|
80
|
-
unique: z.ZodOptional<z.ZodBoolean>;
|
|
81
|
-
global: z.ZodOptional<z.ZodBoolean>;
|
|
82
|
-
repetitions: z.ZodOptional<z.ZodNumber>;
|
|
83
|
-
type: z.ZodLiteral<"container">;
|
|
84
|
-
externalSecurePath: z.ZodOptional<z.ZodString>;
|
|
85
|
-
}, z.core.$strip>], "type">>;
|
|
86
|
-
}, z.core.$strip>>;
|
|
87
|
-
export type MigrationPlan = z.infer<typeof migrationPlanSchema>;
|
|
88
|
-
export declare function prettyPrintMigrationPlan(migrationPlan: MigrationPlan): void;
|
|
7
|
+
export declare function getMetadata(db: Database): Promise<Metadata>;
|
|
8
|
+
export declare function planMigration(db: Database, betterAuthSchema: BetterAuthSchema): Promise<MigrationPlan>;
|
|
9
|
+
export declare function executeMigration(db: Database, migrationPlan: MigrationPlan): Promise<void>;
|
|
10
|
+
interface FmField {
|
|
11
|
+
name: string;
|
|
12
|
+
type: "string" | "numeric" | "timestamp";
|
|
13
|
+
primary?: boolean;
|
|
14
|
+
unique?: boolean;
|
|
15
|
+
}
|
|
16
|
+
declare const migrationStepTypes: readonly ["create", "update"];
|
|
17
|
+
interface MigrationStep {
|
|
18
|
+
tableName: string;
|
|
19
|
+
operation: (typeof migrationStepTypes)[number];
|
|
20
|
+
fields: FmField[];
|
|
21
|
+
}
|
|
22
|
+
export type MigrationPlan = MigrationStep[];
|
|
23
|
+
export declare function prettyPrintMigrationPlan(migrationPlan: MigrationPlan, target?: {
|
|
24
|
+
serverUrl?: string;
|
|
25
|
+
fileName?: string;
|
|
26
|
+
}): void;
|
|
89
27
|
export {};
|
package/dist/esm/migrate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { isODataError, isFMODataError } from "@proofkit/fmodata";
|
|
1
2
|
import chalk from "chalk";
|
|
2
|
-
import z from "zod/v4";
|
|
3
3
|
function normalizeBetterAuthFieldType(fieldType) {
|
|
4
4
|
if (typeof fieldType === "string") {
|
|
5
5
|
return fieldType;
|
|
@@ -9,35 +9,29 @@ function normalizeBetterAuthFieldType(fieldType) {
|
|
|
9
9
|
}
|
|
10
10
|
return String(fieldType);
|
|
11
11
|
}
|
|
12
|
-
async function getMetadata(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
});
|
|
23
|
-
if (result.error) {
|
|
24
|
-
console.error("Failed to get metadata:", result.error);
|
|
25
|
-
return null;
|
|
12
|
+
async function getMetadata(db) {
|
|
13
|
+
const metadata = await db.getMetadata({ format: "json" });
|
|
14
|
+
return metadata;
|
|
15
|
+
}
|
|
16
|
+
function mapFieldType(t) {
|
|
17
|
+
if (t.includes("boolean") || t.includes("number")) {
|
|
18
|
+
return "numeric";
|
|
19
|
+
}
|
|
20
|
+
if (t.includes("date")) {
|
|
21
|
+
return "timestamp";
|
|
26
22
|
}
|
|
27
|
-
return
|
|
23
|
+
return "string";
|
|
28
24
|
}
|
|
29
|
-
async function planMigration(
|
|
30
|
-
const metadata = await getMetadata(
|
|
25
|
+
async function planMigration(db, betterAuthSchema) {
|
|
26
|
+
const metadata = await getMetadata(db);
|
|
31
27
|
const entitySetToType = {};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
entitySetToType[key] = typeKey || key;
|
|
37
|
-
}
|
|
28
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
29
|
+
if (value.$Kind === "EntitySet" && value.$Type) {
|
|
30
|
+
const typeKey = value.$Type.split(".").pop();
|
|
31
|
+
entitySetToType[key] = typeKey || key;
|
|
38
32
|
}
|
|
39
33
|
}
|
|
40
|
-
const existingTables =
|
|
34
|
+
const existingTables = Object.entries(entitySetToType).reduce(
|
|
41
35
|
(acc, [entitySetName, entityTypeKey]) => {
|
|
42
36
|
const entityType = metadata[entityTypeKey];
|
|
43
37
|
if (!entityType) {
|
|
@@ -46,9 +40,9 @@ async function planMigration(fetch, betterAuthSchema, databaseName) {
|
|
|
46
40
|
const fields = Object.entries(entityType).filter(
|
|
47
41
|
([_fieldKey, fieldValue]) => typeof fieldValue === "object" && fieldValue !== null && "$Type" in fieldValue
|
|
48
42
|
).map(([fieldKey, fieldValue]) => {
|
|
49
|
-
let type = "
|
|
43
|
+
let type = "string";
|
|
50
44
|
if (fieldValue.$Type === "Edm.String") {
|
|
51
|
-
type = "
|
|
45
|
+
type = "string";
|
|
52
46
|
} else if (fieldValue.$Type === "Edm.DateTimeOffset") {
|
|
53
47
|
type = "timestamp";
|
|
54
48
|
} else if (fieldValue.$Type === "Edm.Decimal" || fieldValue.$Type === "Edm.Int32" || fieldValue.$Type === "Edm.Int64") {
|
|
@@ -63,22 +57,16 @@ async function planMigration(fetch, betterAuthSchema, databaseName) {
|
|
|
63
57
|
return acc;
|
|
64
58
|
},
|
|
65
59
|
{}
|
|
66
|
-
)
|
|
60
|
+
);
|
|
67
61
|
const baTables = Object.entries(betterAuthSchema).sort((a, b) => (a[1].order ?? 0) - (b[1].order ?? 0)).map(([key, value]) => ({
|
|
68
62
|
...value,
|
|
69
63
|
modelName: key
|
|
70
|
-
// Use the key as modelName since getSchema uses table names as keys
|
|
71
64
|
}));
|
|
72
65
|
const migrationPlan = [];
|
|
73
66
|
for (const baTable of baTables) {
|
|
74
67
|
const fields = Object.entries(baTable.fields).map(([key, field]) => {
|
|
75
68
|
const t = normalizeBetterAuthFieldType(field.type);
|
|
76
|
-
|
|
77
|
-
if (t.includes("boolean") || t.includes("number")) {
|
|
78
|
-
type = "numeric";
|
|
79
|
-
} else if (t.includes("date")) {
|
|
80
|
-
type = "timestamp";
|
|
81
|
-
}
|
|
69
|
+
const type = mapFieldType(t);
|
|
82
70
|
return {
|
|
83
71
|
name: field.fieldName ?? key,
|
|
84
72
|
type
|
|
@@ -116,7 +104,7 @@ async function planMigration(fetch, betterAuthSchema, databaseName) {
|
|
|
116
104
|
fields: [
|
|
117
105
|
{
|
|
118
106
|
name: "id",
|
|
119
|
-
type: "
|
|
107
|
+
type: "string",
|
|
120
108
|
primary: true,
|
|
121
109
|
unique: true
|
|
122
110
|
},
|
|
@@ -127,85 +115,78 @@ async function planMigration(fetch, betterAuthSchema, databaseName) {
|
|
|
127
115
|
}
|
|
128
116
|
return migrationPlan;
|
|
129
117
|
}
|
|
130
|
-
async function executeMigration(
|
|
118
|
+
async function executeMigration(db, migrationPlan) {
|
|
131
119
|
for (const step of migrationPlan) {
|
|
120
|
+
const fmodataFields = step.fields.map((f) => ({
|
|
121
|
+
name: f.name,
|
|
122
|
+
type: f.type,
|
|
123
|
+
...f.primary ? { primary: true } : {},
|
|
124
|
+
...f.unique ? { unique: true } : {}
|
|
125
|
+
}));
|
|
132
126
|
if (step.operation === "create") {
|
|
133
127
|
console.log("Creating table:", step.tableName);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
fields: step.fields
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
if (result.error) {
|
|
142
|
-
console.error(`Failed to create table ${step.tableName}:`, result.error);
|
|
143
|
-
throw new Error(`Migration failed: ${result.error}`);
|
|
128
|
+
try {
|
|
129
|
+
await db.schema.createTable(step.tableName, fmodataFields);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
throw migrationError("create", step.tableName, error);
|
|
144
132
|
}
|
|
145
133
|
} else if (step.operation === "update") {
|
|
146
134
|
console.log("Adding fields to table:", step.tableName);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (result.error) {
|
|
152
|
-
console.error(`Failed to update table ${step.tableName}:`, result.error);
|
|
153
|
-
throw new Error(`Migration failed: ${result.error}`);
|
|
135
|
+
try {
|
|
136
|
+
await db.schema.addFields(step.tableName, fmodataFields);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
throw migrationError("update", step.tableName, error);
|
|
154
139
|
}
|
|
155
140
|
}
|
|
156
141
|
}
|
|
157
142
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const fieldSchema = z.discriminatedUnion("type", [
|
|
191
|
-
stringFieldSchema,
|
|
192
|
-
numericFieldSchema,
|
|
193
|
-
dateFieldSchema,
|
|
194
|
-
timeFieldSchema,
|
|
195
|
-
timestampFieldSchema,
|
|
196
|
-
containerFieldSchema
|
|
197
|
-
]);
|
|
198
|
-
z.object({
|
|
199
|
-
tableName: z.string(),
|
|
200
|
-
operation: z.enum(["create", "update"]),
|
|
201
|
-
fields: z.array(fieldSchema)
|
|
202
|
-
}).array();
|
|
203
|
-
function prettyPrintMigrationPlan(migrationPlan) {
|
|
143
|
+
function formatError(error) {
|
|
144
|
+
if (isODataError(error)) {
|
|
145
|
+
const code = error.code ? ` (${error.code})` : "";
|
|
146
|
+
return `${error.message}${code}`;
|
|
147
|
+
}
|
|
148
|
+
if (isFMODataError(error)) {
|
|
149
|
+
return error.message;
|
|
150
|
+
}
|
|
151
|
+
if (error instanceof Error) {
|
|
152
|
+
return error.message;
|
|
153
|
+
}
|
|
154
|
+
return String(error);
|
|
155
|
+
}
|
|
156
|
+
function migrationError(operation, tableName, error) {
|
|
157
|
+
const action = operation === "create" ? "create table" : "update table";
|
|
158
|
+
const base = `Failed to ${action} "${tableName}"`;
|
|
159
|
+
if (isODataError(error) && error.code === "207") {
|
|
160
|
+
console.error(
|
|
161
|
+
chalk.red(`
|
|
162
|
+
${base}: Cannot modify schema.`),
|
|
163
|
+
chalk.yellow("\nThe account used does not have schema modification privileges."),
|
|
164
|
+
chalk.gray(
|
|
165
|
+
"\nUse --username and --password to provide Full Access credentials, or grant schema modification privileges to the current account."
|
|
166
|
+
)
|
|
167
|
+
);
|
|
168
|
+
} else {
|
|
169
|
+
console.error(chalk.red(`
|
|
170
|
+
${base}:`), formatError(error));
|
|
171
|
+
}
|
|
172
|
+
return new Error(`Migration failed: ${formatError(error)}`);
|
|
173
|
+
}
|
|
174
|
+
function prettyPrintMigrationPlan(migrationPlan, target) {
|
|
204
175
|
if (!migrationPlan.length) {
|
|
205
176
|
console.log("No changes to apply. Database is up to date.");
|
|
206
177
|
return;
|
|
207
178
|
}
|
|
208
179
|
console.log(chalk.bold.green("Migration plan:"));
|
|
180
|
+
if ((target == null ? void 0 : target.serverUrl) || (target == null ? void 0 : target.fileName)) {
|
|
181
|
+
const parts = [];
|
|
182
|
+
if (target.fileName) {
|
|
183
|
+
parts.push(chalk.cyan(target.fileName));
|
|
184
|
+
}
|
|
185
|
+
if (target.serverUrl) {
|
|
186
|
+
parts.push(chalk.gray(target.serverUrl));
|
|
187
|
+
}
|
|
188
|
+
console.log(` Target: ${parts.join(" @ ")}`);
|
|
189
|
+
}
|
|
209
190
|
for (const step of migrationPlan) {
|
|
210
191
|
const emoji = step.operation === "create" ? "✅" : "✏️";
|
|
211
192
|
console.log(
|
package/dist/esm/migrate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.js","sources":["../../src/migrate.ts"],"sourcesContent":["import type { DBFieldAttribute } from \"better-auth/db\";\nimport chalk from \"chalk\";\nimport type { Metadata } from \"fm-odata-client\";\nimport z from \"zod/v4\";\nimport type { createRawFetch } from \"./odata\";\n\n/** Schema type returned by better-auth's getSchema function */\ntype BetterAuthSchema = Record<string, { fields: Record<string, DBFieldAttribute>; order: number }>;\n\nfunction normalizeBetterAuthFieldType(fieldType: unknown): string {\n if (typeof fieldType === \"string\") {\n return fieldType;\n }\n if (Array.isArray(fieldType)) {\n return fieldType.map(String).join(\"|\");\n }\n return String(fieldType);\n}\n\nexport async function getMetadata(fetch: ReturnType<typeof createRawFetch>[\"fetch\"], databaseName: string) {\n console.log(\"getting metadata...\");\n const result = await fetch(\"/$metadata\", {\n method: \"GET\",\n headers: { accept: \"application/json\" },\n output: z\n .looseObject({\n $Version: z.string(),\n \"@ServerVersion\": z.string(),\n })\n .or(z.null())\n .catch(null),\n });\n\n if (result.error) {\n console.error(\"Failed to get metadata:\", result.error);\n return null;\n }\n\n return (result.data?.[databaseName] ?? null) as Metadata | null;\n}\n\nexport async function planMigration(\n fetch: ReturnType<typeof createRawFetch>[\"fetch\"],\n betterAuthSchema: BetterAuthSchema,\n databaseName: string,\n): Promise<MigrationPlan> {\n const metadata = await getMetadata(fetch, databaseName);\n\n // Build a map from entity set name to entity type key\n const entitySetToType: Record<string, string> = {};\n if (metadata) {\n for (const [key, value] of Object.entries(metadata)) {\n if (value.$Kind === \"EntitySet\" && value.$Type) {\n // $Type is like 'betterauth_test.fmp12.proofkit_user_'\n const typeKey = value.$Type.split(\".\").pop(); // e.g., 'proofkit_user_'\n entitySetToType[key] = typeKey || key;\n }\n }\n }\n\n const existingTables = metadata\n ? Object.entries(entitySetToType).reduce(\n (acc, [entitySetName, entityTypeKey]) => {\n const entityType = metadata[entityTypeKey];\n if (!entityType) {\n return acc;\n }\n const fields = Object.entries(entityType)\n .filter(\n ([_fieldKey, fieldValue]) =>\n typeof fieldValue === \"object\" && fieldValue !== null && \"$Type\" in fieldValue,\n )\n .map(([fieldKey, fieldValue]) => {\n let type = \"varchar\";\n if (fieldValue.$Type === \"Edm.String\") {\n type = \"varchar\";\n } else if (fieldValue.$Type === \"Edm.DateTimeOffset\") {\n type = \"timestamp\";\n } else if (\n fieldValue.$Type === \"Edm.Decimal\" ||\n fieldValue.$Type === \"Edm.Int32\" ||\n fieldValue.$Type === \"Edm.Int64\"\n ) {\n type = \"numeric\";\n }\n return {\n name: fieldKey,\n type,\n };\n });\n acc[entitySetName] = fields;\n return acc;\n },\n {} as Record<string, { name: string; type: string }[]>,\n )\n : {};\n\n const baTables = Object.entries(betterAuthSchema)\n .sort((a, b) => (a[1].order ?? 0) - (b[1].order ?? 0))\n .map(([key, value]) => ({\n ...value,\n modelName: key, // Use the key as modelName since getSchema uses table names as keys\n }));\n\n const migrationPlan: MigrationPlan = [];\n\n for (const baTable of baTables) {\n const fields: FmField[] = Object.entries(baTable.fields).map(([key, field]) => {\n // Better Auth's FieldType can be a string literal union or arrays.\n // Normalize it to a string so our FM mapping logic remains stable.\n // Use .includes() for all checks to handle array types like [\"boolean\", \"null\"] → \"boolean|null\"\n const t = normalizeBetterAuthFieldType(field.type);\n let type: \"varchar\" | \"numeric\" | \"timestamp\" = \"varchar\";\n if (t.includes(\"boolean\") || t.includes(\"number\")) {\n type = \"numeric\";\n } else if (t.includes(\"date\")) {\n type = \"timestamp\";\n }\n return {\n name: field.fieldName ?? key,\n type,\n };\n });\n\n // get existing table or create it\n const tableExists = baTable.modelName in existingTables;\n\n if (tableExists) {\n const existingFields = (existingTables[baTable.modelName] || []).map((f) => f.name);\n const existingFieldMap = (existingTables[baTable.modelName] || []).reduce(\n (acc, f) => {\n acc[f.name] = f.type;\n return acc;\n },\n {} as Record<string, string>,\n );\n // Warn about type mismatches (optional, not in plan)\n for (const field of fields) {\n if (existingFields.includes(field.name) && existingFieldMap[field.name] !== field.type) {\n console.warn(\n `⚠️ WARNING: Field '${field.name}' in table '${baTable.modelName}' exists but has type '${existingFieldMap[field.name]}' (expected '${field.type}'). Change the field type in FileMaker to avoid potential errors.`,\n );\n }\n }\n const fieldsToAdd = fields.filter((f) => !existingFields.includes(f.name));\n if (fieldsToAdd.length > 0) {\n migrationPlan.push({\n tableName: baTable.modelName,\n operation: \"update\",\n fields: fieldsToAdd,\n });\n }\n } else {\n migrationPlan.push({\n tableName: baTable.modelName,\n operation: \"create\",\n fields: [\n {\n name: \"id\",\n type: \"varchar\",\n primary: true,\n unique: true,\n },\n ...fields,\n ],\n });\n }\n }\n\n return migrationPlan;\n}\n\nexport async function executeMigration(\n fetch: ReturnType<typeof createRawFetch>[\"fetch\"],\n migrationPlan: MigrationPlan,\n) {\n for (const step of migrationPlan) {\n if (step.operation === \"create\") {\n console.log(\"Creating table:\", step.tableName);\n const result = await fetch(\"/FileMaker_Tables\", {\n method: \"POST\",\n body: {\n tableName: step.tableName,\n fields: step.fields,\n },\n });\n\n if (result.error) {\n console.error(`Failed to create table ${step.tableName}:`, result.error);\n throw new Error(`Migration failed: ${result.error}`);\n }\n } else if (step.operation === \"update\") {\n console.log(\"Adding fields to table:\", step.tableName);\n const result = await fetch(`/FileMaker_Tables/${step.tableName}`, {\n method: \"PATCH\",\n body: { fields: step.fields },\n });\n\n if (result.error) {\n console.error(`Failed to update table ${step.tableName}:`, result.error);\n throw new Error(`Migration failed: ${result.error}`);\n }\n }\n }\n}\n\nconst genericFieldSchema = z.object({\n name: z.string(),\n nullable: z.boolean().optional(),\n primary: z.boolean().optional(),\n unique: z.boolean().optional(),\n global: z.boolean().optional(),\n repetitions: z.number().optional(),\n});\n\nconst stringFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"varchar\"),\n maxLength: z.number().optional(),\n default: z.enum([\"USER\", \"USERNAME\", \"CURRENT_USER\"]).optional(),\n});\n\nconst numericFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"numeric\"),\n});\n\nconst dateFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"date\"),\n default: z.enum([\"CURRENT_DATE\", \"CURDATE\"]).optional(),\n});\n\nconst timeFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"time\"),\n default: z.enum([\"CURRENT_TIME\", \"CURTIME\"]).optional(),\n});\n\nconst timestampFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"timestamp\"),\n default: z.enum([\"CURRENT_TIMESTAMP\", \"CURTIMESTAMP\"]).optional(),\n});\n\nconst containerFieldSchema = genericFieldSchema.extend({\n type: z.literal(\"container\"),\n externalSecurePath: z.string().optional(),\n});\n\nconst fieldSchema = z.discriminatedUnion(\"type\", [\n stringFieldSchema,\n numericFieldSchema,\n dateFieldSchema,\n timeFieldSchema,\n timestampFieldSchema,\n containerFieldSchema,\n]);\n\ntype FmField = z.infer<typeof fieldSchema>;\n\nconst migrationPlanSchema = z\n .object({\n tableName: z.string(),\n operation: z.enum([\"create\", \"update\"]),\n fields: z.array(fieldSchema),\n })\n .array();\n\nexport type MigrationPlan = z.infer<typeof migrationPlanSchema>;\n\nexport function prettyPrintMigrationPlan(migrationPlan: MigrationPlan) {\n if (!migrationPlan.length) {\n console.log(\"No changes to apply. Database is up to date.\");\n return;\n }\n console.log(chalk.bold.green(\"Migration plan:\"));\n for (const step of migrationPlan) {\n const emoji = step.operation === \"create\" ? \"✅\" : \"✏️\";\n console.log(\n `\\n${emoji} ${step.operation === \"create\" ? chalk.bold.green(\"Create table\") : chalk.bold.yellow(\"Update table\")}: ${step.tableName}`,\n );\n if (step.fields.length) {\n for (const field of step.fields) {\n let fieldDesc = ` - ${field.name} (${field.type}`;\n if (field.primary) {\n fieldDesc += \", primary\";\n }\n if (field.unique) {\n fieldDesc += \", unique\";\n }\n fieldDesc += \")\";\n console.log(fieldDesc);\n }\n } else {\n console.log(\" (No fields to add)\");\n }\n }\n console.log(\"\");\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"migrate.js","sources":["../../src/migrate.ts"],"sourcesContent":["import type { Database, Field, Metadata } from \"@proofkit/fmodata\";\nimport { isFMODataError, isODataError } from \"@proofkit/fmodata\";\nimport type { DBFieldAttribute } from \"better-auth/db\";\nimport chalk from \"chalk\";\n\n/** Schema type returned by better-auth's getSchema function */\ntype BetterAuthSchema = Record<string, { fields: Record<string, DBFieldAttribute>; order: number }>;\n\nfunction normalizeBetterAuthFieldType(fieldType: unknown): string {\n if (typeof fieldType === \"string\") {\n return fieldType;\n }\n if (Array.isArray(fieldType)) {\n return fieldType.map(String).join(\"|\");\n }\n return String(fieldType);\n}\n\nexport async function getMetadata(db: Database): Promise<Metadata> {\n const metadata = await db.getMetadata({ format: \"json\" });\n return metadata;\n}\n\n/** Map a better-auth field type string to an fmodata Field type */\nfunction mapFieldType(t: string): \"string\" | \"numeric\" | \"timestamp\" {\n if (t.includes(\"boolean\") || t.includes(\"number\")) {\n return \"numeric\";\n }\n if (t.includes(\"date\")) {\n return \"timestamp\";\n }\n return \"string\";\n}\n\nexport async function planMigration(db: Database, betterAuthSchema: BetterAuthSchema): Promise<MigrationPlan> {\n const metadata = await getMetadata(db);\n\n // Build a map from entity set name to entity type key\n const entitySetToType: Record<string, string> = {};\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 const existingTables = Object.entries(entitySetToType).reduce(\n (acc, [entitySetName, entityTypeKey]) => {\n const entityType = metadata[entityTypeKey];\n if (!entityType) {\n return acc;\n }\n const fields = Object.entries(entityType)\n .filter(\n ([_fieldKey, fieldValue]) => typeof fieldValue === \"object\" && fieldValue !== null && \"$Type\" in fieldValue,\n )\n .map(([fieldKey, fieldValue]) => {\n let type = \"string\";\n if (fieldValue.$Type === \"Edm.String\") {\n type = \"string\";\n } else if (fieldValue.$Type === \"Edm.DateTimeOffset\") {\n type = \"timestamp\";\n } else if (\n fieldValue.$Type === \"Edm.Decimal\" ||\n fieldValue.$Type === \"Edm.Int32\" ||\n fieldValue.$Type === \"Edm.Int64\"\n ) {\n type = \"numeric\";\n }\n return {\n name: fieldKey,\n type,\n };\n });\n acc[entitySetName] = fields;\n return acc;\n },\n {} as Record<string, { name: string; type: string }[]>,\n );\n\n const baTables = Object.entries(betterAuthSchema)\n .sort((a, b) => (a[1].order ?? 0) - (b[1].order ?? 0))\n .map(([key, value]) => ({\n ...value,\n modelName: key,\n }));\n\n const migrationPlan: MigrationPlan = [];\n\n for (const baTable of baTables) {\n const fields: FmField[] = Object.entries(baTable.fields).map(([key, field]) => {\n const t = normalizeBetterAuthFieldType(field.type);\n const type = mapFieldType(t);\n return {\n name: field.fieldName ?? key,\n type,\n };\n });\n\n const tableExists = baTable.modelName in existingTables;\n\n if (tableExists) {\n const existingFields = (existingTables[baTable.modelName] || []).map((f) => f.name);\n const existingFieldMap = (existingTables[baTable.modelName] || []).reduce(\n (acc, f) => {\n acc[f.name] = f.type;\n return acc;\n },\n {} as Record<string, string>,\n );\n for (const field of fields) {\n if (existingFields.includes(field.name) && existingFieldMap[field.name] !== field.type) {\n console.warn(\n `⚠️ WARNING: Field '${field.name}' in table '${baTable.modelName}' exists but has type '${existingFieldMap[field.name]}' (expected '${field.type}'). Change the field type in FileMaker to avoid potential errors.`,\n );\n }\n }\n const fieldsToAdd = fields.filter((f) => !existingFields.includes(f.name));\n if (fieldsToAdd.length > 0) {\n migrationPlan.push({\n tableName: baTable.modelName,\n operation: \"update\",\n fields: fieldsToAdd,\n });\n }\n } else {\n migrationPlan.push({\n tableName: baTable.modelName,\n operation: \"create\",\n fields: [\n {\n name: \"id\",\n type: \"string\",\n primary: true,\n unique: true,\n },\n ...fields,\n ],\n });\n }\n }\n\n return migrationPlan;\n}\n\nexport async function executeMigration(db: Database, migrationPlan: MigrationPlan) {\n for (const step of migrationPlan) {\n // Convert plan fields to fmodata Field type\n const fmodataFields: Field[] = step.fields.map((f) => ({\n name: f.name,\n type: f.type,\n ...(f.primary ? { primary: true } : {}),\n ...(f.unique ? { unique: true } : {}),\n }));\n\n if (step.operation === \"create\") {\n console.log(\"Creating table:\", step.tableName);\n try {\n await db.schema.createTable(step.tableName, fmodataFields);\n } catch (error) {\n throw migrationError(\"create\", step.tableName, error);\n }\n } else if (step.operation === \"update\") {\n console.log(\"Adding fields to table:\", step.tableName);\n try {\n await db.schema.addFields(step.tableName, fmodataFields);\n } catch (error) {\n throw migrationError(\"update\", step.tableName, error);\n }\n }\n }\n}\n\ninterface FmField {\n name: string;\n type: \"string\" | \"numeric\" | \"timestamp\";\n primary?: boolean;\n unique?: boolean;\n}\n\nconst migrationStepTypes = [\"create\", \"update\"] as const;\ninterface MigrationStep {\n tableName: string;\n operation: (typeof migrationStepTypes)[number];\n fields: FmField[];\n}\n\nexport type MigrationPlan = MigrationStep[];\n\nfunction formatError(error: unknown): string {\n if (isODataError(error)) {\n const code = error.code ? ` (${error.code})` : \"\";\n return `${error.message}${code}`;\n }\n if (isFMODataError(error)) {\n return error.message;\n }\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n\nfunction migrationError(operation: string, tableName: string, error: unknown): Error {\n const action = operation === \"create\" ? \"create table\" : \"update table\";\n const base = `Failed to ${action} \"${tableName}\"`;\n\n if (isODataError(error) && error.code === \"207\") {\n console.error(\n chalk.red(`\\n${base}: Cannot modify schema.`),\n chalk.yellow(\"\\nThe account used does not have schema modification privileges.\"),\n chalk.gray(\n \"\\nUse --username and --password to provide Full Access credentials, or grant schema modification privileges to the current account.\",\n ),\n );\n } else {\n console.error(chalk.red(`\\n${base}:`), formatError(error));\n }\n return new Error(`Migration failed: ${formatError(error)}`);\n}\n\nexport function prettyPrintMigrationPlan(\n migrationPlan: MigrationPlan,\n target?: { serverUrl?: string; fileName?: string },\n) {\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 if (target?.serverUrl || target?.fileName) {\n const parts: string[] = [];\n if (target.fileName) {\n parts.push(chalk.cyan(target.fileName));\n }\n if (target.serverUrl) {\n parts.push(chalk.gray(target.serverUrl));\n }\n console.log(` Target: ${parts.join(\" @ \")}`);\n }\n for (const step of migrationPlan) {\n const emoji = step.operation === \"create\" ? \"✅\" : \"✏️\";\n console.log(\n `\\n${emoji} ${step.operation === \"create\" ? chalk.bold.green(\"Create table\") : chalk.bold.yellow(\"Update table\")}: ${step.tableName}`,\n );\n if (step.fields.length) {\n for (const field of step.fields) {\n let fieldDesc = ` - ${field.name} (${field.type}`;\n if (field.primary) {\n fieldDesc += \", primary\";\n }\n if (field.unique) {\n fieldDesc += \", unique\";\n }\n fieldDesc += \")\";\n console.log(fieldDesc);\n }\n } else {\n console.log(\" (No fields to add)\");\n }\n }\n console.log(\"\");\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proofkit/better-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-beta.3",
|
|
4
4
|
"description": "FileMaker adapter for Better Auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
@@ -46,17 +46,14 @@
|
|
|
46
46
|
"commander": "^14.0.2",
|
|
47
47
|
"dotenv": "^16.6.1",
|
|
48
48
|
"fs-extra": "^11.3.3",
|
|
49
|
-
"neverthrow": "^8.2.0",
|
|
50
|
-
"odata-query": "^8.0.7",
|
|
51
49
|
"prompts": "^2.4.2",
|
|
52
50
|
"vite": "^6.4.1",
|
|
53
|
-
"
|
|
51
|
+
"@proofkit/fmodata": "0.1.0-beta.26"
|
|
54
52
|
},
|
|
55
53
|
"devDependencies": {
|
|
56
54
|
"@types/fs-extra": "^11.0.4",
|
|
57
55
|
"@types/prompts": "^2.4.9",
|
|
58
56
|
"@vitest/ui": "^3.2.4",
|
|
59
|
-
"fm-odata-client": "^3.0.2",
|
|
60
57
|
"publint": "^0.3.16",
|
|
61
58
|
"typescript": "^5.9.3",
|
|
62
59
|
"vitest": "^4.0.17"
|