@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/src/adapter.ts
CHANGED
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
/** biome-ignore-all lint/suspicious/noExplicitAny: library code */
|
|
2
|
+
import type { Database } from "@proofkit/fmodata";
|
|
2
3
|
import { logger } from "better-auth";
|
|
3
4
|
import { type CleanedWhere, createAdapter, type DBAdapterDebugLogOption } from "better-auth/adapters";
|
|
4
|
-
import buildQuery from "odata-query";
|
|
5
|
-
import { prettifyError, z } from "zod/v4";
|
|
6
|
-
import { createRawFetch, type FmOdataConfig } from "./odata";
|
|
7
|
-
|
|
8
|
-
const configSchema = z.object({
|
|
9
|
-
debugLogs: z.unknown().optional(),
|
|
10
|
-
usePlural: z.boolean().optional(),
|
|
11
|
-
odata: z.object({
|
|
12
|
-
serverUrl: z.url(),
|
|
13
|
-
auth: z.union([z.object({ username: z.string(), password: z.string() }), z.object({ apiKey: z.string() })]),
|
|
14
|
-
database: z.string().endsWith(".fmp12"),
|
|
15
|
-
}),
|
|
16
|
-
});
|
|
17
5
|
|
|
18
6
|
export interface FileMakerAdapterConfig {
|
|
19
7
|
/**
|
|
@@ -24,27 +12,12 @@ export interface FileMakerAdapterConfig {
|
|
|
24
12
|
* If the table names in the schema are plural.
|
|
25
13
|
*/
|
|
26
14
|
usePlural?: boolean;
|
|
27
|
-
|
|
28
15
|
/**
|
|
29
|
-
*
|
|
16
|
+
* The fmodata Database instance to use for all OData requests.
|
|
30
17
|
*/
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface AdapterOptions {
|
|
35
|
-
config: FileMakerAdapterConfig;
|
|
18
|
+
database: Database;
|
|
36
19
|
}
|
|
37
20
|
|
|
38
|
-
const defaultConfig: Required<FileMakerAdapterConfig> = {
|
|
39
|
-
debugLogs: false,
|
|
40
|
-
usePlural: false,
|
|
41
|
-
odata: {
|
|
42
|
-
serverUrl: "",
|
|
43
|
-
auth: { username: "", password: "" },
|
|
44
|
-
database: "",
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
21
|
// Regex patterns for field validation and ISO date detection
|
|
49
22
|
const FIELD_SPECIAL_CHARS_REGEX = /[\s_]/;
|
|
50
23
|
const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
|
|
@@ -155,41 +128,59 @@ export function parseWhere(where?: CleanedWhere[]): string {
|
|
|
155
128
|
return clauses.join(" ");
|
|
156
129
|
}
|
|
157
130
|
|
|
158
|
-
|
|
159
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Build an OData query string from parameters.
|
|
133
|
+
*/
|
|
134
|
+
function buildQueryString(params: {
|
|
135
|
+
top?: number;
|
|
136
|
+
skip?: number;
|
|
137
|
+
filter?: string;
|
|
138
|
+
orderBy?: string;
|
|
139
|
+
select?: string[];
|
|
140
|
+
}): string {
|
|
141
|
+
const parts: string[] = [];
|
|
142
|
+
if (params.top !== undefined) {
|
|
143
|
+
parts.push(`$top=${params.top}`);
|
|
144
|
+
}
|
|
145
|
+
if (params.skip !== undefined) {
|
|
146
|
+
parts.push(`$skip=${params.skip}`);
|
|
147
|
+
}
|
|
148
|
+
if (params.filter) {
|
|
149
|
+
parts.push(`$filter=${encodeURIComponent(params.filter)}`);
|
|
150
|
+
}
|
|
151
|
+
if (params.orderBy) {
|
|
152
|
+
parts.push(`$orderby=${encodeURIComponent(params.orderBy)}`);
|
|
153
|
+
}
|
|
154
|
+
if (params.select?.length) {
|
|
155
|
+
parts.push(`$select=${params.select.map(encodeURIComponent).join(",")}`);
|
|
156
|
+
}
|
|
157
|
+
return parts.length > 0 ? `?${parts.join("&")}` : "";
|
|
158
|
+
}
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
export const FileMakerAdapter = (config: FileMakerAdapterConfig) => {
|
|
161
|
+
if (!config.database || typeof config.database !== "object") {
|
|
162
|
+
throw new Error("FileMakerAdapter requires a `database` (fmodata Database instance).");
|
|
163
163
|
}
|
|
164
|
-
const config = parsed.data;
|
|
165
164
|
|
|
166
|
-
const
|
|
167
|
-
...config.odata,
|
|
168
|
-
logging: config.debugLogs ? "verbose" : "none",
|
|
169
|
-
});
|
|
165
|
+
const db = config.database;
|
|
170
166
|
|
|
171
167
|
const adapterFactory = createAdapter({
|
|
172
168
|
config: {
|
|
173
169
|
adapterId: "filemaker",
|
|
174
170
|
adapterName: "FileMaker",
|
|
175
|
-
usePlural: config.usePlural ?? false,
|
|
176
|
-
debugLogs: config.debugLogs ?? false,
|
|
177
|
-
supportsJSON: false,
|
|
178
|
-
supportsDates: false,
|
|
179
|
-
supportsBooleans: false,
|
|
180
|
-
supportsNumericIds: false,
|
|
171
|
+
usePlural: config.usePlural ?? false,
|
|
172
|
+
debugLogs: config.debugLogs ?? false,
|
|
173
|
+
supportsJSON: false,
|
|
174
|
+
supportsDates: false,
|
|
175
|
+
supportsBooleans: false,
|
|
176
|
+
supportsNumericIds: false,
|
|
181
177
|
},
|
|
182
178
|
adapter: () => {
|
|
183
179
|
return {
|
|
184
180
|
create: async ({ data, model }) => {
|
|
185
|
-
|
|
186
|
-
console.log("session", data);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const result = await fetch(`/${model}`, {
|
|
181
|
+
const result = await db._makeRequest<Record<string, any>>(`/${model}`, {
|
|
190
182
|
method: "POST",
|
|
191
|
-
body: data,
|
|
192
|
-
output: z.looseObject({ id: z.string() }),
|
|
183
|
+
body: JSON.stringify(data),
|
|
193
184
|
});
|
|
194
185
|
|
|
195
186
|
if (result.error) {
|
|
@@ -202,15 +193,12 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
202
193
|
const filter = parseWhere(where);
|
|
203
194
|
logger.debug("$filter", filter);
|
|
204
195
|
|
|
205
|
-
const query =
|
|
196
|
+
const query = buildQueryString({
|
|
206
197
|
filter: filter.length > 0 ? filter : undefined,
|
|
207
198
|
});
|
|
208
199
|
|
|
209
|
-
const result = await
|
|
210
|
-
|
|
211
|
-
output: z.object({ value: z.number() }),
|
|
212
|
-
});
|
|
213
|
-
if (!result.data) {
|
|
200
|
+
const result = await db._makeRequest<{ value: number }>(`/${model}/$count${query}`);
|
|
201
|
+
if (result.error) {
|
|
214
202
|
throw new Error("Failed to count records");
|
|
215
203
|
}
|
|
216
204
|
return (result.data?.value as any) ?? 0;
|
|
@@ -219,15 +207,12 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
219
207
|
const filter = parseWhere(where);
|
|
220
208
|
logger.debug("$filter", filter);
|
|
221
209
|
|
|
222
|
-
const query =
|
|
210
|
+
const query = buildQueryString({
|
|
223
211
|
top: 1,
|
|
224
212
|
filter: filter.length > 0 ? filter : undefined,
|
|
225
213
|
});
|
|
226
214
|
|
|
227
|
-
const result = await
|
|
228
|
-
method: "GET",
|
|
229
|
-
output: z.object({ value: z.array(z.any()) }),
|
|
230
|
-
});
|
|
215
|
+
const result = await db._makeRequest<{ value: any[] }>(`/${model}${query}`);
|
|
231
216
|
if (result.error) {
|
|
232
217
|
throw new Error("Failed to find record");
|
|
233
218
|
}
|
|
@@ -237,7 +222,7 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
237
222
|
const filter = parseWhere(where);
|
|
238
223
|
logger.debug("FIND MANY", { where, filter });
|
|
239
224
|
|
|
240
|
-
const query =
|
|
225
|
+
const query = buildQueryString({
|
|
241
226
|
top: limit,
|
|
242
227
|
skip: offset,
|
|
243
228
|
orderBy: sortBy ? `${sortBy.field} ${sortBy.direction ?? "asc"}` : undefined,
|
|
@@ -245,10 +230,7 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
245
230
|
});
|
|
246
231
|
logger.debug("QUERY", query);
|
|
247
232
|
|
|
248
|
-
const result = await
|
|
249
|
-
method: "GET",
|
|
250
|
-
output: z.object({ value: z.array(z.any()) }),
|
|
251
|
-
});
|
|
233
|
+
const result = await db._makeRequest<{ value: any[] }>(`/${model}${query}`);
|
|
252
234
|
logger.debug("RESULT", result);
|
|
253
235
|
|
|
254
236
|
if (result.error) {
|
|
@@ -259,54 +241,44 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
259
241
|
},
|
|
260
242
|
delete: async ({ model, where }) => {
|
|
261
243
|
const filter = parseWhere(where);
|
|
262
|
-
console.log("DELETE", { model, where, filter });
|
|
263
244
|
logger.debug("$filter", filter);
|
|
264
245
|
|
|
265
246
|
// Find a single id matching the filter
|
|
266
|
-
const query =
|
|
247
|
+
const query = buildQueryString({
|
|
267
248
|
top: 1,
|
|
268
249
|
select: [`"id"`],
|
|
269
250
|
filter: filter.length > 0 ? filter : undefined,
|
|
270
251
|
});
|
|
271
252
|
|
|
272
|
-
const toDelete = await
|
|
273
|
-
method: "GET",
|
|
274
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) }),
|
|
275
|
-
});
|
|
253
|
+
const toDelete = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);
|
|
276
254
|
|
|
277
255
|
const id = toDelete.data?.value?.[0]?.id;
|
|
278
256
|
if (!id) {
|
|
279
|
-
// Nothing to delete
|
|
280
257
|
return;
|
|
281
258
|
}
|
|
282
259
|
|
|
283
|
-
const result = await
|
|
260
|
+
const result = await db._makeRequest(`/${model}('${id}')`, {
|
|
284
261
|
method: "DELETE",
|
|
285
262
|
});
|
|
286
263
|
if (result.error) {
|
|
287
|
-
console.log("DELETE ERROR", result.error);
|
|
288
264
|
throw new Error("Failed to delete record");
|
|
289
265
|
}
|
|
290
266
|
},
|
|
291
267
|
deleteMany: async ({ model, where }) => {
|
|
292
268
|
const filter = parseWhere(where);
|
|
293
|
-
console.log("DELETE MANY", { model, where, filter });
|
|
294
269
|
|
|
295
270
|
// Find all ids matching the filter
|
|
296
|
-
const query =
|
|
271
|
+
const query = buildQueryString({
|
|
297
272
|
select: [`"id"`],
|
|
298
273
|
filter: filter.length > 0 ? filter : undefined,
|
|
299
274
|
});
|
|
300
275
|
|
|
301
|
-
const rows = await
|
|
302
|
-
method: "GET",
|
|
303
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) }),
|
|
304
|
-
});
|
|
276
|
+
const rows = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);
|
|
305
277
|
|
|
306
278
|
const ids = rows.data?.value?.map((r: any) => r.id) ?? [];
|
|
307
279
|
let deleted = 0;
|
|
308
280
|
for (const id of ids) {
|
|
309
|
-
const res = await
|
|
281
|
+
const res = await db._makeRequest(`/${model}('${id}')`, {
|
|
310
282
|
method: "DELETE",
|
|
311
283
|
});
|
|
312
284
|
if (!res.error) {
|
|
@@ -319,16 +291,14 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
319
291
|
const filter = parseWhere(where);
|
|
320
292
|
logger.debug("UPDATE", { model, where, update });
|
|
321
293
|
logger.debug("$filter", filter);
|
|
294
|
+
|
|
322
295
|
// Find one id to update
|
|
323
|
-
const query =
|
|
296
|
+
const query = buildQueryString({
|
|
324
297
|
select: [`"id"`],
|
|
325
298
|
filter: filter.length > 0 ? filter : undefined,
|
|
326
299
|
});
|
|
327
300
|
|
|
328
|
-
const existing = await
|
|
329
|
-
method: "GET",
|
|
330
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) }),
|
|
331
|
-
});
|
|
301
|
+
const existing = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);
|
|
332
302
|
logger.debug("EXISTING", existing.data);
|
|
333
303
|
|
|
334
304
|
const id = existing.data?.value?.[0]?.id;
|
|
@@ -336,9 +306,9 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
336
306
|
return null;
|
|
337
307
|
}
|
|
338
308
|
|
|
339
|
-
const patchRes = await
|
|
309
|
+
const patchRes = await db._makeRequest(`/${model}('${id}')`, {
|
|
340
310
|
method: "PATCH",
|
|
341
|
-
body: update,
|
|
311
|
+
body: JSON.stringify(update),
|
|
342
312
|
});
|
|
343
313
|
logger.debug("PATCH RES", patchRes.data);
|
|
344
314
|
if (patchRes.error) {
|
|
@@ -346,32 +316,27 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
346
316
|
}
|
|
347
317
|
|
|
348
318
|
// Read back the updated record
|
|
349
|
-
const readBack = await
|
|
350
|
-
method: "GET",
|
|
351
|
-
output: z.record(z.string(), z.unknown()),
|
|
352
|
-
});
|
|
319
|
+
const readBack = await db._makeRequest<Record<string, unknown>>(`/${model}('${id}')`);
|
|
353
320
|
logger.debug("READ BACK", readBack.data);
|
|
354
321
|
return (readBack.data as any) ?? null;
|
|
355
322
|
},
|
|
356
323
|
updateMany: async ({ model, where, update }) => {
|
|
357
324
|
const filter = parseWhere(where);
|
|
325
|
+
|
|
358
326
|
// Find all ids matching the filter
|
|
359
|
-
const query =
|
|
327
|
+
const query = buildQueryString({
|
|
360
328
|
select: [`"id"`],
|
|
361
329
|
filter: filter.length > 0 ? filter : undefined,
|
|
362
330
|
});
|
|
363
331
|
|
|
364
|
-
const rows = await
|
|
365
|
-
method: "GET",
|
|
366
|
-
output: z.object({ value: z.array(z.object({ id: z.string() })) }),
|
|
367
|
-
});
|
|
332
|
+
const rows = await db._makeRequest<{ value: { id: string }[] }>(`/${model}${query}`);
|
|
368
333
|
|
|
369
334
|
const ids = rows.data?.value?.map((r: any) => r.id) ?? [];
|
|
370
335
|
let updated = 0;
|
|
371
336
|
for (const id of ids) {
|
|
372
|
-
const res = await
|
|
337
|
+
const res = await db._makeRequest(`/${model}('${id}')`, {
|
|
373
338
|
method: "PATCH",
|
|
374
|
-
body: update,
|
|
339
|
+
body: JSON.stringify(update),
|
|
375
340
|
});
|
|
376
341
|
if (!res.error) {
|
|
377
342
|
updated++;
|
|
@@ -383,7 +348,15 @@ export const FileMakerAdapter = (_config: FileMakerAdapterConfig = defaultConfig
|
|
|
383
348
|
},
|
|
384
349
|
});
|
|
385
350
|
|
|
386
|
-
// Expose the
|
|
387
|
-
|
|
388
|
-
|
|
351
|
+
// Expose the Database instance for CLI access.
|
|
352
|
+
// Set on both the factory function (for pre-getAdapter extraction)
|
|
353
|
+
// and the returned adapter (for post-getAdapter extraction).
|
|
354
|
+
const originalFactory = adapterFactory;
|
|
355
|
+
const wrappedFactory = ((options: unknown) => {
|
|
356
|
+
const adapter = (originalFactory as (opts: unknown) => Record<string, unknown>)(options);
|
|
357
|
+
adapter.database = db;
|
|
358
|
+
return adapter;
|
|
359
|
+
}) as typeof adapterFactory;
|
|
360
|
+
(wrappedFactory as unknown as { database: Database }).database = db;
|
|
361
|
+
return wrappedFactory;
|
|
389
362
|
};
|
package/src/cli/index.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node --no-warnings
|
|
2
2
|
import { Command } from "@commander-js/extra-typings";
|
|
3
|
+
import type { Database, FFetchOptions } from "@proofkit/fmodata";
|
|
4
|
+
import { FMServerConnection } from "@proofkit/fmodata";
|
|
3
5
|
import { logger } from "better-auth";
|
|
4
6
|
import { getAdapter, getSchema } from "better-auth/db";
|
|
5
7
|
import chalk from "chalk";
|
|
6
8
|
import fs from "fs-extra";
|
|
7
9
|
import prompts from "prompts";
|
|
8
|
-
import type { FileMakerAdapterConfig } from "../adapter";
|
|
9
10
|
import { getConfig } from "../better-auth-cli/utils/get-config";
|
|
10
11
|
import { executeMigration, planMigration, prettyPrintMigrationPlan } from "../migrate";
|
|
11
|
-
import { createRawFetch } from "../odata";
|
|
12
12
|
import "dotenv/config";
|
|
13
13
|
|
|
14
14
|
async function main() {
|
|
@@ -52,21 +52,66 @@ async function main() {
|
|
|
52
52
|
|
|
53
53
|
const betterAuthSchema = getSchema(config);
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
55
|
+
// Extract Database from the adapter factory or resolved adapter.
|
|
56
|
+
// config.database is the FileMakerAdapter factory function (has .database set on it).
|
|
57
|
+
// adapter is the resolved adapter after getAdapter() calls the factory (also has .database).
|
|
58
|
+
// Try both: adapter first (post-call), then config.database (pre-call / factory function).
|
|
59
|
+
const configDb =
|
|
60
|
+
(adapter as unknown as { database?: Database }).database ??
|
|
61
|
+
(config.database as unknown as { database?: Database } | undefined)?.database;
|
|
62
|
+
if (!configDb || typeof configDb !== "object" || !("schema" in configDb)) {
|
|
63
|
+
logger.error(
|
|
64
|
+
"Could not extract Database instance from adapter. Ensure your auth.ts uses FileMakerAdapter with an fmodata Database.",
|
|
65
|
+
);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
let db: Database = configDb;
|
|
69
|
+
|
|
70
|
+
// Extract database name and server URL for display.
|
|
71
|
+
// Try the public getter first (_getDatabaseName), fall back to the private field (databaseName).
|
|
72
|
+
const dbObj = configDb as unknown as {
|
|
73
|
+
_getDatabaseName?: string;
|
|
74
|
+
databaseName?: string;
|
|
75
|
+
context?: { _getBaseUrl?: () => string; _fetchClientOptions?: unknown };
|
|
76
|
+
};
|
|
77
|
+
const dbName: string = dbObj._getDatabaseName ?? dbObj.databaseName ?? "";
|
|
78
|
+
const baseUrl: string | undefined = dbObj.context?._getBaseUrl?.();
|
|
79
|
+
const serverUrl = baseUrl ? new URL(baseUrl).origin : undefined;
|
|
80
|
+
|
|
81
|
+
// If CLI credential overrides are provided, construct a new connection
|
|
82
|
+
if (options.username && options.password) {
|
|
83
|
+
if (!dbName) {
|
|
84
|
+
logger.error("Could not determine database filename from adapter config.");
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!baseUrl) {
|
|
89
|
+
logger.error(
|
|
90
|
+
"Could not determine server URL from adapter config. Ensure your auth.ts uses FMServerConnection.",
|
|
91
|
+
);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
68
94
|
|
|
69
|
-
|
|
95
|
+
const fetchClientOptions = dbObj.context?._fetchClientOptions as FFetchOptions | undefined;
|
|
96
|
+
const connection = new FMServerConnection({
|
|
97
|
+
serverUrl: serverUrl as string,
|
|
98
|
+
auth: {
|
|
99
|
+
username: options.username,
|
|
100
|
+
password: options.password,
|
|
101
|
+
},
|
|
102
|
+
fetchClientOptions,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
db = connection.database(dbName);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let migrationPlan: Awaited<ReturnType<typeof planMigration>>;
|
|
109
|
+
try {
|
|
110
|
+
migrationPlan = await planMigration(db, betterAuthSchema);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
logger.error(`Failed to read database schema: ${err instanceof Error ? err.message : err}`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
70
115
|
|
|
71
116
|
if (migrationPlan.length === 0) {
|
|
72
117
|
logger.info("No changes to apply. Database is up to date.");
|
|
@@ -74,7 +119,7 @@ async function main() {
|
|
|
74
119
|
}
|
|
75
120
|
|
|
76
121
|
if (!options.yes) {
|
|
77
|
-
prettyPrintMigrationPlan(migrationPlan);
|
|
122
|
+
prettyPrintMigrationPlan(migrationPlan, { serverUrl, fileName: dbName });
|
|
78
123
|
|
|
79
124
|
if (migrationPlan.length > 0) {
|
|
80
125
|
console.log(chalk.gray("💡 Tip: You can use the --yes flag to skip this confirmation."));
|
|
@@ -91,12 +136,18 @@ async function main() {
|
|
|
91
136
|
}
|
|
92
137
|
}
|
|
93
138
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
139
|
+
try {
|
|
140
|
+
await executeMigration(db, migrationPlan);
|
|
141
|
+
logger.info("Migration applied successfully.");
|
|
142
|
+
} catch {
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
97
145
|
});
|
|
98
146
|
await program.parseAsync(process.argv);
|
|
99
147
|
process.exit(0);
|
|
100
148
|
}
|
|
101
149
|
|
|
102
|
-
main().catch(
|
|
150
|
+
main().catch((err) => {
|
|
151
|
+
logger.error(err.message ?? err);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
});
|