@absolutejs/auth 0.41.0 → 0.43.0
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/cli/import/auth0.d.ts +2 -0
- package/dist/cli/import/clerk.d.ts +2 -0
- package/dist/cli/import/index.d.ts +10 -0
- package/dist/cli/import/lucia.d.ts +2 -0
- package/dist/cli/import/nextauth.d.ts +2 -0
- package/dist/cli/import/supabase.d.ts +2 -0
- package/dist/cli/import/types.d.ts +26 -0
- package/dist/cli/migrate.js +392 -27
- package/dist/cli/migrate.js.map +10 -4
- package/dist/index.js +10 -3
- package/dist/index.js.map +4 -4
- package/dist/oidc/config.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ImportResult, Importer } from './types';
|
|
2
|
+
export declare const importers: Record<string, Importer>;
|
|
3
|
+
export type ImportOptions = {
|
|
4
|
+
commit: boolean;
|
|
5
|
+
databaseUrl: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const runImport: (result: ImportResult, options: ImportOptions) => Promise<{
|
|
8
|
+
identityCount: number;
|
|
9
|
+
userCount: number;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type ImportedUser = {
|
|
2
|
+
createdAtMs: number;
|
|
3
|
+
email: string;
|
|
4
|
+
emailVerified: boolean;
|
|
5
|
+
externalId: string;
|
|
6
|
+
familyName?: string;
|
|
7
|
+
givenName?: string;
|
|
8
|
+
passwordHash?: string;
|
|
9
|
+
passwordHashAlgo?: 'argon2id' | 'bcrypt' | 'scrypt' | 'pbkdf2';
|
|
10
|
+
};
|
|
11
|
+
export type ImportedIdentity = {
|
|
12
|
+
authProvider: string;
|
|
13
|
+
createdAtMs: number;
|
|
14
|
+
metadata?: Record<string, unknown>;
|
|
15
|
+
providerSubject: string;
|
|
16
|
+
userExternalId: string;
|
|
17
|
+
};
|
|
18
|
+
export type ImportResult = {
|
|
19
|
+
identities: ImportedIdentity[];
|
|
20
|
+
source: string;
|
|
21
|
+
users: ImportedUser[];
|
|
22
|
+
};
|
|
23
|
+
export type Importer = {
|
|
24
|
+
parse: (path: string) => Promise<ImportResult>;
|
|
25
|
+
source: string;
|
|
26
|
+
};
|
package/dist/cli/migrate.js
CHANGED
|
@@ -1,6 +1,270 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
|
+
// src/cli/import/index.ts
|
|
5
|
+
import { neon } from "@neondatabase/serverless";
|
|
6
|
+
|
|
7
|
+
// src/cli/import/auth0.ts
|
|
8
|
+
import { readFile } from "fs/promises";
|
|
9
|
+
var parseUsersFromText = (text) => {
|
|
10
|
+
const trimmed = text.trim();
|
|
11
|
+
if (trimmed.startsWith("[")) {
|
|
12
|
+
return JSON.parse(trimmed);
|
|
13
|
+
}
|
|
14
|
+
return trimmed.split(`
|
|
15
|
+
`).filter((line) => line.length > 0).map((line) => JSON.parse(line));
|
|
16
|
+
};
|
|
17
|
+
var auth0Importer = {
|
|
18
|
+
source: "auth0",
|
|
19
|
+
parse: async (path) => {
|
|
20
|
+
const text = await readFile(path, "utf-8");
|
|
21
|
+
const auth0Users = parseUsersFromText(text);
|
|
22
|
+
const users = auth0Users.map((raw) => ({
|
|
23
|
+
createdAtMs: raw.created_at === undefined ? Date.now() : Date.parse(raw.created_at),
|
|
24
|
+
email: raw.email,
|
|
25
|
+
emailVerified: raw.email_verified ?? false,
|
|
26
|
+
externalId: raw.user_id,
|
|
27
|
+
familyName: raw.family_name,
|
|
28
|
+
givenName: raw.given_name,
|
|
29
|
+
passwordHash: raw.password_hash,
|
|
30
|
+
passwordHashAlgo: raw.password_hash?.startsWith("$2b$") || raw.password_hash?.startsWith("$2a$") || raw.password_hash?.startsWith("$2y$") ? "bcrypt" : undefined
|
|
31
|
+
}));
|
|
32
|
+
const identities = auth0Users.flatMap((raw) => (raw.identities ?? []).filter((identity) => identity.provider !== "auth0").map((identity) => ({
|
|
33
|
+
authProvider: identity.provider.replace("-oauth2", ""),
|
|
34
|
+
createdAtMs: raw.created_at === undefined ? Date.now() : Date.parse(raw.created_at),
|
|
35
|
+
metadata: identity.connection === undefined ? undefined : { connection: identity.connection },
|
|
36
|
+
providerSubject: identity.user_id,
|
|
37
|
+
userExternalId: raw.user_id
|
|
38
|
+
})));
|
|
39
|
+
return { identities, source: "auth0", users };
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// src/cli/import/clerk.ts
|
|
44
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
45
|
+
var stripOauthPrefix = (provider) => provider.startsWith("oauth_") ? provider.slice("oauth_".length) : provider;
|
|
46
|
+
var clerkImporter = {
|
|
47
|
+
source: "clerk",
|
|
48
|
+
parse: async (path) => {
|
|
49
|
+
const text = await readFile2(path, "utf-8");
|
|
50
|
+
const clerkUsers = JSON.parse(text);
|
|
51
|
+
const users = clerkUsers.filter((raw) => raw.email_addresses !== undefined && raw.email_addresses.length > 0).map((raw) => {
|
|
52
|
+
const primary = raw.email_addresses?.find((e) => raw.primary_email_address_id === undefined || e.email_address.length > 0) ?? raw.email_addresses?.[0];
|
|
53
|
+
return {
|
|
54
|
+
createdAtMs: raw.created_at ?? Date.now(),
|
|
55
|
+
email: primary?.email_address ?? "",
|
|
56
|
+
emailVerified: primary?.verification?.status === "verified",
|
|
57
|
+
externalId: raw.id,
|
|
58
|
+
familyName: raw.last_name,
|
|
59
|
+
givenName: raw.first_name,
|
|
60
|
+
passwordHash: raw.password_digest,
|
|
61
|
+
passwordHashAlgo: raw.password_digest?.startsWith("$argon2id") ? "argon2id" : raw.password_digest?.startsWith("$2") ? "bcrypt" : undefined
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
const identities = clerkUsers.flatMap((raw) => (raw.external_accounts ?? []).map((account) => ({
|
|
65
|
+
authProvider: stripOauthPrefix(account.provider),
|
|
66
|
+
createdAtMs: raw.created_at ?? Date.now(),
|
|
67
|
+
providerSubject: account.provider_user_id ?? account.external_id ?? "",
|
|
68
|
+
userExternalId: raw.id
|
|
69
|
+
})));
|
|
70
|
+
return { identities, source: "clerk", users };
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/cli/import/lucia.ts
|
|
75
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
76
|
+
var toMs = (value) => {
|
|
77
|
+
if (value === undefined)
|
|
78
|
+
return Date.now();
|
|
79
|
+
if (typeof value === "number")
|
|
80
|
+
return value < 1000000000000 ? value * 1000 : value;
|
|
81
|
+
return Date.parse(value);
|
|
82
|
+
};
|
|
83
|
+
var luciaImporter = {
|
|
84
|
+
source: "lucia",
|
|
85
|
+
parse: async (path) => {
|
|
86
|
+
const text = await readFile3(path, "utf-8");
|
|
87
|
+
const parsed = JSON.parse(text);
|
|
88
|
+
const passwordByUserId = new Map;
|
|
89
|
+
const oauthKeys = [];
|
|
90
|
+
for (const key of parsed.keys ?? []) {
|
|
91
|
+
const separator = key.id.indexOf(":");
|
|
92
|
+
if (separator < 0)
|
|
93
|
+
continue;
|
|
94
|
+
const provider = key.id.slice(0, separator);
|
|
95
|
+
if (provider === "email" || provider === "username") {
|
|
96
|
+
passwordByUserId.set(key.user_id, key.hashed_password);
|
|
97
|
+
} else {
|
|
98
|
+
oauthKeys.push(key);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const users = parsed.users.map((raw) => ({
|
|
102
|
+
createdAtMs: toMs(raw.created_at),
|
|
103
|
+
email: raw.email ?? "",
|
|
104
|
+
emailVerified: true,
|
|
105
|
+
externalId: raw.id,
|
|
106
|
+
passwordHash: passwordByUserId.get(raw.id),
|
|
107
|
+
passwordHashAlgo: passwordByUserId.get(raw.id)?.startsWith("$argon2id") ? "argon2id" : passwordByUserId.get(raw.id)?.startsWith("$2") ? "bcrypt" : undefined
|
|
108
|
+
}));
|
|
109
|
+
const identities = oauthKeys.map((key) => {
|
|
110
|
+
const separator = key.id.indexOf(":");
|
|
111
|
+
return {
|
|
112
|
+
authProvider: key.id.slice(0, separator),
|
|
113
|
+
createdAtMs: Date.now(),
|
|
114
|
+
providerSubject: key.id.slice(separator + 1),
|
|
115
|
+
userExternalId: key.user_id
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
return { identities, source: "lucia", users };
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// src/cli/import/nextauth.ts
|
|
123
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
124
|
+
var splitName = (full) => {
|
|
125
|
+
if (full === undefined)
|
|
126
|
+
return { familyName: undefined, givenName: undefined };
|
|
127
|
+
const parts = full.split(/\s+/);
|
|
128
|
+
if (parts.length === 1)
|
|
129
|
+
return { familyName: undefined, givenName: parts[0] };
|
|
130
|
+
return {
|
|
131
|
+
familyName: parts.slice(1).join(" "),
|
|
132
|
+
givenName: parts[0]
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
var nextauthImporter = {
|
|
136
|
+
source: "nextauth",
|
|
137
|
+
parse: async (path) => {
|
|
138
|
+
const text = await readFile4(path, "utf-8");
|
|
139
|
+
const parsed = JSON.parse(text);
|
|
140
|
+
const users = parsed.users.map((raw) => {
|
|
141
|
+
const split = splitName(raw.name);
|
|
142
|
+
const passwordHash = parsed.passwordsByUserId?.[raw.id];
|
|
143
|
+
return {
|
|
144
|
+
createdAtMs: Date.now(),
|
|
145
|
+
email: raw.email,
|
|
146
|
+
emailVerified: raw.emailVerified !== null && raw.emailVerified !== undefined,
|
|
147
|
+
externalId: raw.id,
|
|
148
|
+
familyName: split.familyName,
|
|
149
|
+
givenName: split.givenName,
|
|
150
|
+
passwordHash,
|
|
151
|
+
passwordHashAlgo: passwordHash?.startsWith("$argon2id") ? "argon2id" : passwordHash?.startsWith("$2") ? "bcrypt" : undefined
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
const identities = (parsed.accounts ?? []).filter((account) => account.type === undefined || account.type === "oauth").map((account) => ({
|
|
155
|
+
authProvider: account.provider,
|
|
156
|
+
createdAtMs: Date.now(),
|
|
157
|
+
providerSubject: account.providerAccountId,
|
|
158
|
+
userExternalId: account.userId
|
|
159
|
+
}));
|
|
160
|
+
return { identities, source: "nextauth", users };
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// src/cli/import/supabase.ts
|
|
165
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
166
|
+
var splitFullName = (full) => {
|
|
167
|
+
if (full === undefined)
|
|
168
|
+
return { familyName: undefined, givenName: undefined };
|
|
169
|
+
const parts = full.split(/\s+/);
|
|
170
|
+
if (parts.length === 1)
|
|
171
|
+
return { familyName: undefined, givenName: parts[0] };
|
|
172
|
+
return {
|
|
173
|
+
familyName: parts.slice(1).join(" "),
|
|
174
|
+
givenName: parts[0]
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
var supabaseImporter = {
|
|
178
|
+
source: "supabase",
|
|
179
|
+
parse: async (path) => {
|
|
180
|
+
const text = await readFile5(path, "utf-8");
|
|
181
|
+
const parsed = JSON.parse(text);
|
|
182
|
+
const users = parsed.users.map((raw) => {
|
|
183
|
+
const meta = raw.raw_user_meta_data ?? {};
|
|
184
|
+
const split = splitFullName(meta.full_name);
|
|
185
|
+
return {
|
|
186
|
+
createdAtMs: raw.created_at === undefined ? Date.now() : Date.parse(raw.created_at),
|
|
187
|
+
email: raw.email,
|
|
188
|
+
emailVerified: raw.email_confirmed_at !== undefined,
|
|
189
|
+
externalId: raw.id,
|
|
190
|
+
familyName: meta.family_name ?? split.familyName,
|
|
191
|
+
givenName: meta.given_name ?? split.givenName,
|
|
192
|
+
passwordHash: raw.encrypted_password,
|
|
193
|
+
passwordHashAlgo: raw.encrypted_password?.startsWith("$2") ? "bcrypt" : undefined
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
const identities = (parsed.identities ?? []).map((identity) => ({
|
|
197
|
+
authProvider: identity.provider,
|
|
198
|
+
createdAtMs: identity.created_at === undefined ? Date.now() : Date.parse(identity.created_at),
|
|
199
|
+
providerSubject: identity.provider_id,
|
|
200
|
+
userExternalId: identity.user_id
|
|
201
|
+
}));
|
|
202
|
+
return { identities, source: "supabase", users };
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// src/cli/import/index.ts
|
|
207
|
+
var importers = {
|
|
208
|
+
auth0: auth0Importer,
|
|
209
|
+
clerk: clerkImporter,
|
|
210
|
+
lucia: luciaImporter,
|
|
211
|
+
nextauth: nextauthImporter,
|
|
212
|
+
supabase: supabaseImporter
|
|
213
|
+
};
|
|
214
|
+
var runImport = async (result, options) => {
|
|
215
|
+
const sql = neon(options.databaseUrl);
|
|
216
|
+
const subByExternalId = new Map;
|
|
217
|
+
for (const user of result.users) {
|
|
218
|
+
subByExternalId.set(user.externalId, crypto.randomUUID());
|
|
219
|
+
}
|
|
220
|
+
if (!options.commit) {
|
|
221
|
+
return {
|
|
222
|
+
identityCount: result.identities.length,
|
|
223
|
+
userCount: result.users.length
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
for (const user of result.users) {
|
|
227
|
+
const sub = subByExternalId.get(user.externalId);
|
|
228
|
+
await sql`
|
|
229
|
+
INSERT INTO users (sub, email, password, family_name, given_name, email_verified, created_at)
|
|
230
|
+
VALUES (
|
|
231
|
+
${sub},
|
|
232
|
+
${user.email.toLowerCase().trim()},
|
|
233
|
+
${user.passwordHash ?? null},
|
|
234
|
+
${user.familyName ?? null},
|
|
235
|
+
${user.givenName ?? null},
|
|
236
|
+
${user.emailVerified},
|
|
237
|
+
to_timestamp(${user.createdAtMs} / 1000.0)
|
|
238
|
+
)
|
|
239
|
+
ON CONFLICT (email) DO NOTHING
|
|
240
|
+
`;
|
|
241
|
+
}
|
|
242
|
+
let insertedIdentities = 0;
|
|
243
|
+
for (const identity of result.identities) {
|
|
244
|
+
const sub = subByExternalId.get(identity.userExternalId);
|
|
245
|
+
if (sub === undefined)
|
|
246
|
+
continue;
|
|
247
|
+
const inserted = await sql`
|
|
248
|
+
INSERT INTO auth_identities (id, auth_provider, provider_subject, user_sub, metadata)
|
|
249
|
+
VALUES (
|
|
250
|
+
${`${identity.authProvider}:${identity.providerSubject}`},
|
|
251
|
+
${identity.authProvider},
|
|
252
|
+
${identity.providerSubject},
|
|
253
|
+
${sub},
|
|
254
|
+
${identity.metadata ?? {}}
|
|
255
|
+
)
|
|
256
|
+
ON CONFLICT (auth_provider, provider_subject) DO NOTHING
|
|
257
|
+
RETURNING id
|
|
258
|
+
`;
|
|
259
|
+
if (Array.isArray(inserted) && inserted.length > 0)
|
|
260
|
+
insertedIdentities++;
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
identityCount: insertedIdentities,
|
|
264
|
+
userCount: result.users.length
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
|
|
4
268
|
// src/adaptive/postgresStores.ts
|
|
5
269
|
import { and, desc, eq } from "drizzle-orm";
|
|
6
270
|
import {
|
|
@@ -13,7 +277,7 @@ import {
|
|
|
13
277
|
} from "drizzle-orm/pg-core";
|
|
14
278
|
|
|
15
279
|
// src/stores/postgres.ts
|
|
16
|
-
import { neon } from "@neondatabase/serverless";
|
|
280
|
+
import { neon as neon2 } from "@neondatabase/serverless";
|
|
17
281
|
import { drizzle } from "drizzle-orm/neon-http";
|
|
18
282
|
|
|
19
283
|
// src/adaptive/postgresStores.ts
|
|
@@ -133,7 +397,7 @@ var warrantsTable = pgTable5("auth_fga_warrants", {
|
|
|
133
397
|
});
|
|
134
398
|
|
|
135
399
|
// src/linkedProviders/neonStores.ts
|
|
136
|
-
import { neon as
|
|
400
|
+
import { neon as neon3 } from "@neondatabase/serverless";
|
|
137
401
|
import { desc as desc4, eq as eq6 } from "drizzle-orm";
|
|
138
402
|
import { drizzle as drizzle2 } from "drizzle-orm/neon-http";
|
|
139
403
|
import {
|
|
@@ -2386,7 +2650,7 @@ var scimTokensTable = pgTable14("auth_scim_tokens", {
|
|
|
2386
2650
|
});
|
|
2387
2651
|
|
|
2388
2652
|
// src/session/neonStore.ts
|
|
2389
|
-
import { neon as
|
|
2653
|
+
import { neon as neon4 } from "@neondatabase/serverless";
|
|
2390
2654
|
import { eq as eq15 } from "drizzle-orm";
|
|
2391
2655
|
import { drizzle as drizzle3 } from "drizzle-orm/neon-http";
|
|
2392
2656
|
import {
|
|
@@ -2703,7 +2967,18 @@ var blockMigrations = {
|
|
|
2703
2967
|
};
|
|
2704
2968
|
|
|
2705
2969
|
// src/cli/migrate.ts
|
|
2706
|
-
var
|
|
2970
|
+
var TOP_USAGE = `Usage:
|
|
2971
|
+
bunx absolute-auth <command> [options]
|
|
2972
|
+
|
|
2973
|
+
Commands:
|
|
2974
|
+
migrate Apply the package's Drizzle migrations
|
|
2975
|
+
import <source> <file> Import a user export from another auth library
|
|
2976
|
+
<source> is one of: ${Object.keys(importers).sort().join(", ")}
|
|
2977
|
+
help Print this message
|
|
2978
|
+
|
|
2979
|
+
Run 'bunx absolute-auth <command> --help' for command-specific options.
|
|
2980
|
+
`;
|
|
2981
|
+
var MIGRATE_USAGE = `Usage:
|
|
2707
2982
|
bunx absolute-auth migrate --db <url> [--blocks block1,block2,...]
|
|
2708
2983
|
|
|
2709
2984
|
Options:
|
|
@@ -2713,17 +2988,19 @@ Options:
|
|
|
2713
2988
|
|
|
2714
2989
|
Available blocks: ${Object.keys(blockMigrations).sort().join(", ")}
|
|
2715
2990
|
`;
|
|
2716
|
-
var
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2991
|
+
var IMPORT_USAGE = `Usage:
|
|
2992
|
+
bunx absolute-auth import <source> <file> --db <url> [--commit]
|
|
2993
|
+
|
|
2994
|
+
Arguments:
|
|
2995
|
+
source one of: ${Object.keys(importers).sort().join(", ")}
|
|
2996
|
+
file path to the export JSON (see docs/MIGRATE-FROM-*.md per source)
|
|
2997
|
+
|
|
2998
|
+
Options:
|
|
2999
|
+
--db, --database-url Postgres connection string (falls back to DATABASE_URL env)
|
|
3000
|
+
--commit Without this, the run is a dry-run (counts only, no inserts)
|
|
3001
|
+
--help Print this message
|
|
3002
|
+
`;
|
|
3003
|
+
var parseMigrateArgs = (argv) => {
|
|
2727
3004
|
const parsed = {
|
|
2728
3005
|
blocks: undefined,
|
|
2729
3006
|
databaseUrl: undefined,
|
|
@@ -2731,34 +3008,67 @@ var parseArgs = (argv) => {
|
|
|
2731
3008
|
};
|
|
2732
3009
|
const args = [...argv];
|
|
2733
3010
|
while (args.length > 0) {
|
|
2734
|
-
|
|
3011
|
+
const flag = args.shift();
|
|
3012
|
+
if (flag === "--help" || flag === "-h") {
|
|
3013
|
+
parsed.help = true;
|
|
3014
|
+
} else if (flag === "--db" || flag === "--database-url") {
|
|
3015
|
+
parsed.databaseUrl = args.shift();
|
|
3016
|
+
} else if (flag === "--blocks") {
|
|
3017
|
+
const list = args.shift() ?? "";
|
|
3018
|
+
parsed.blocks = list.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) => entry);
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
return parsed;
|
|
3022
|
+
};
|
|
3023
|
+
var parseImportArgs = (argv) => {
|
|
3024
|
+
const parsed = {
|
|
3025
|
+
commit: false,
|
|
3026
|
+
databaseUrl: undefined,
|
|
3027
|
+
file: undefined,
|
|
3028
|
+
help: false,
|
|
3029
|
+
source: undefined
|
|
3030
|
+
};
|
|
3031
|
+
const args = [...argv];
|
|
3032
|
+
const positionals = [];
|
|
3033
|
+
while (args.length > 0) {
|
|
3034
|
+
const next = args.shift();
|
|
3035
|
+
if (next === undefined)
|
|
3036
|
+
break;
|
|
3037
|
+
if (next === "--help" || next === "-h") {
|
|
3038
|
+
parsed.help = true;
|
|
3039
|
+
} else if (next === "--db" || next === "--database-url") {
|
|
3040
|
+
parsed.databaseUrl = args.shift();
|
|
3041
|
+
} else if (next === "--commit") {
|
|
3042
|
+
parsed.commit = true;
|
|
3043
|
+
} else if (!next.startsWith("--")) {
|
|
3044
|
+
positionals.push(next);
|
|
3045
|
+
}
|
|
2735
3046
|
}
|
|
3047
|
+
parsed.source = positionals[0];
|
|
3048
|
+
parsed.file = positionals[1];
|
|
2736
3049
|
return parsed;
|
|
2737
3050
|
};
|
|
2738
|
-
var die = (message) => {
|
|
3051
|
+
var die = (message, usage) => {
|
|
2739
3052
|
process.stderr.write(`error: ${message}
|
|
2740
3053
|
|
|
2741
3054
|
`);
|
|
2742
|
-
process.stderr.write(
|
|
3055
|
+
process.stderr.write(usage);
|
|
2743
3056
|
process.exit(1);
|
|
2744
3057
|
};
|
|
2745
|
-
var
|
|
2746
|
-
const
|
|
2747
|
-
if (positional[0] === "migrate")
|
|
2748
|
-
positional.shift();
|
|
2749
|
-
const { blocks, databaseUrl, help } = parseArgs(positional);
|
|
3058
|
+
var runMigrate = async (argv) => {
|
|
3059
|
+
const { blocks, databaseUrl, help } = parseMigrateArgs(argv);
|
|
2750
3060
|
if (help) {
|
|
2751
|
-
process.stdout.write(
|
|
3061
|
+
process.stdout.write(MIGRATE_USAGE);
|
|
2752
3062
|
return;
|
|
2753
3063
|
}
|
|
2754
3064
|
const resolved = databaseUrl ?? process.env["DATABASE_URL"];
|
|
2755
3065
|
if (resolved === undefined || resolved.length === 0) {
|
|
2756
|
-
die("a Postgres URL is required (--db or DATABASE_URL)");
|
|
3066
|
+
die("a Postgres URL is required (--db or DATABASE_URL)", MIGRATE_USAGE);
|
|
2757
3067
|
return;
|
|
2758
3068
|
}
|
|
2759
3069
|
const unknown = blocks?.filter((block) => !(block in blockMigrations)) ?? [];
|
|
2760
3070
|
if (unknown.length > 0) {
|
|
2761
|
-
die(`unknown block(s): ${unknown.join(", ")}
|
|
3071
|
+
die(`unknown block(s): ${unknown.join(", ")}`, MIGRATE_USAGE);
|
|
2762
3072
|
return;
|
|
2763
3073
|
}
|
|
2764
3074
|
const result = await runMigrations({ blocks, databaseUrl: resolved });
|
|
@@ -2766,7 +3076,62 @@ var main = async () => {
|
|
|
2766
3076
|
${result.applied.length} migration(s) applied, ${result.skipped.length} skipped.
|
|
2767
3077
|
`);
|
|
2768
3078
|
};
|
|
3079
|
+
var runImportCommand = async (argv) => {
|
|
3080
|
+
const { commit, databaseUrl, file, help, source } = parseImportArgs(argv);
|
|
3081
|
+
if (help) {
|
|
3082
|
+
process.stdout.write(IMPORT_USAGE);
|
|
3083
|
+
return;
|
|
3084
|
+
}
|
|
3085
|
+
if (source === undefined) {
|
|
3086
|
+
die("source is required", IMPORT_USAGE);
|
|
3087
|
+
return;
|
|
3088
|
+
}
|
|
3089
|
+
const importer = importers[source];
|
|
3090
|
+
if (importer === undefined) {
|
|
3091
|
+
die(`unknown source "${source}"`, IMPORT_USAGE);
|
|
3092
|
+
return;
|
|
3093
|
+
}
|
|
3094
|
+
if (file === undefined) {
|
|
3095
|
+
die("file path is required", IMPORT_USAGE);
|
|
3096
|
+
return;
|
|
3097
|
+
}
|
|
3098
|
+
const resolved = databaseUrl ?? process.env["DATABASE_URL"];
|
|
3099
|
+
if (resolved === undefined || resolved.length === 0) {
|
|
3100
|
+
die("a Postgres URL is required (--db or DATABASE_URL)", IMPORT_USAGE);
|
|
3101
|
+
return;
|
|
3102
|
+
}
|
|
3103
|
+
process.stdout.write(`[${source}] parsing ${file}\u2026
|
|
3104
|
+
`);
|
|
3105
|
+
const result = await importer.parse(file);
|
|
3106
|
+
process.stdout.write(`[${source}] parsed ${result.users.length} user(s), ${result.identities.length} identity row(s).
|
|
3107
|
+
`);
|
|
3108
|
+
const counts = await runImport(result, { commit, databaseUrl: resolved });
|
|
3109
|
+
if (commit) {
|
|
3110
|
+
process.stdout.write(`[${source}] inserted ${counts.userCount} user(s), ${counts.identityCount} new identity row(s). \u2713
|
|
3111
|
+
`);
|
|
3112
|
+
} else {
|
|
3113
|
+
process.stdout.write(`[${source}] dry-run \u2014 pass --commit to insert.
|
|
3114
|
+
`);
|
|
3115
|
+
}
|
|
3116
|
+
};
|
|
3117
|
+
var main = async () => {
|
|
3118
|
+
const argv = process.argv.slice(2);
|
|
3119
|
+
const command = argv.shift();
|
|
3120
|
+
if (command === undefined || command === "help" || command === "--help") {
|
|
3121
|
+
process.stdout.write(TOP_USAGE);
|
|
3122
|
+
return;
|
|
3123
|
+
}
|
|
3124
|
+
if (command === "migrate") {
|
|
3125
|
+
await runMigrate(argv);
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
if (command === "import") {
|
|
3129
|
+
await runImportCommand(argv);
|
|
3130
|
+
return;
|
|
3131
|
+
}
|
|
3132
|
+
die(`unknown command "${command}"`, TOP_USAGE);
|
|
3133
|
+
};
|
|
2769
3134
|
await main();
|
|
2770
3135
|
|
|
2771
|
-
//# debugId=
|
|
3136
|
+
//# debugId=6E4C57DBFBD5A3C964756E2164756E21
|
|
2772
3137
|
//# sourceMappingURL=migrate.js.map
|