@payload-auth-link/migrate 0.0.0-stage → 0.2.1

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.
Files changed (72) hide show
  1. package/README.md +49 -2
  2. package/dist/cli.d.ts +7 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +257 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/doctor/bands.d.ts +16 -0
  7. package/dist/doctor/bands.d.ts.map +1 -0
  8. package/dist/doctor/bands.js +49 -0
  9. package/dist/doctor/bands.js.map +1 -0
  10. package/dist/doctor/database.d.ts +3 -0
  11. package/dist/doctor/database.d.ts.map +1 -0
  12. package/dist/doctor/database.js +110 -0
  13. package/dist/doctor/database.js.map +1 -0
  14. package/dist/doctor/plugins.d.ts +12 -0
  15. package/dist/doctor/plugins.d.ts.map +1 -0
  16. package/dist/doctor/plugins.js +202 -0
  17. package/dist/doctor/plugins.js.map +1 -0
  18. package/dist/doctor/run-doctor.d.ts +48 -0
  19. package/dist/doctor/run-doctor.d.ts.map +1 -0
  20. package/dist/doctor/run-doctor.js +236 -0
  21. package/dist/doctor/run-doctor.js.map +1 -0
  22. package/dist/doctor/source-scan.d.ts +10 -0
  23. package/dist/doctor/source-scan.d.ts.map +1 -0
  24. package/dist/doctor/source-scan.js +142 -0
  25. package/dist/doctor/source-scan.js.map +1 -0
  26. package/dist/doctor/types.d.ts +86 -0
  27. package/dist/doctor/types.d.ts.map +1 -0
  28. package/dist/doctor/types.js +7 -0
  29. package/dist/doctor/types.js.map +1 -0
  30. package/dist/doctor/users.d.ts +10 -0
  31. package/dist/doctor/users.d.ts.map +1 -0
  32. package/dist/doctor/users.js +176 -0
  33. package/dist/doctor/users.js.map +1 -0
  34. package/dist/importer/apply.d.ts +39 -0
  35. package/dist/importer/apply.d.ts.map +1 -0
  36. package/dist/importer/apply.js +588 -0
  37. package/dist/importer/apply.js.map +1 -0
  38. package/dist/importer/lifecycle.d.ts +52 -0
  39. package/dist/importer/lifecycle.d.ts.map +1 -0
  40. package/dist/importer/lifecycle.js +265 -0
  41. package/dist/importer/lifecycle.js.map +1 -0
  42. package/dist/importer/run-import.d.ts +40 -0
  43. package/dist/importer/run-import.d.ts.map +1 -0
  44. package/dist/importer/run-import.js +176 -0
  45. package/dist/importer/run-import.js.map +1 -0
  46. package/dist/importer/source.d.ts +49 -0
  47. package/dist/importer/source.d.ts.map +1 -0
  48. package/dist/importer/source.js +147 -0
  49. package/dist/importer/source.js.map +1 -0
  50. package/dist/importer/types.d.ts +69 -0
  51. package/dist/importer/types.d.ts.map +1 -0
  52. package/dist/importer/types.js +13 -0
  53. package/dist/importer/types.js.map +1 -0
  54. package/dist/index.d.ts +19 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +17 -0
  57. package/dist/index.js.map +1 -0
  58. package/package.json +70 -4
  59. package/src/cli.ts +287 -0
  60. package/src/doctor/bands.ts +66 -0
  61. package/src/doctor/database.ts +130 -0
  62. package/src/doctor/plugins.ts +216 -0
  63. package/src/doctor/run-doctor.ts +278 -0
  64. package/src/doctor/source-scan.ts +148 -0
  65. package/src/doctor/types.ts +92 -0
  66. package/src/doctor/users.ts +207 -0
  67. package/src/importer/apply.ts +731 -0
  68. package/src/importer/lifecycle.ts +328 -0
  69. package/src/importer/run-import.ts +237 -0
  70. package/src/importer/source.ts +211 -0
  71. package/src/importer/types.ts +96 -0
  72. package/src/index.ts +53 -0
package/README.md CHANGED
@@ -1,3 +1,50 @@
1
- # Temporary Holding Version
1
+ # @payload-auth-link/migrate
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The payload-auth → payload-auth-link migration kit
4
+ ([issues #49–#59](https://github.com/jellologic/payload-auth-link/issues/59)):
5
+ a read-only **doctor/preflight** today, with the runnable importer and
6
+ exporters riding the same CLI as they land (#50, #58).
7
+
8
+ ## Doctor (read-only preflight)
9
+
10
+ ```sh
11
+ bunx @payload-auth-link/migrate doctor --config ./src/payload.config.ts --src ./src
12
+ ```
13
+
14
+ Reports, without writing anything:
15
+
16
+ - **Version bands** — payload, better-auth, next against the supported
17
+ bands (better-auth 1.6 → 1.7 removed `oidcProvider`, so apps using it
18
+ are flagged).
19
+ - **Better Auth plugins** — supported, auto-declarable, unprobed, or
20
+ removed upstream, derived from the collections payload-auth built and
21
+ the plugin calls in the config source.
22
+ - **The users collection** — field collisions with the plugin's
23
+ injected fields, the existing role field and its values
24
+ (non-canonical values such as `frontPlugin`), and whether Payload's
25
+ local strategy is on.
26
+ - **0.7.0 behaviour changes that will bite** — provider-owned
27
+ documents, suspension on every strategy, access defaults, the
28
+ `req.user` projection.
29
+ - **Source scan** — `payload-auth/*` imports, `payload.betterAuth` /
30
+ `getPayloadAuth` call sites, `$context` internals, and
31
+ `req.user.<field>` reads outside the session projection.
32
+ - **Database** (with `--database-url` or `DATABASE_URL`) — row counts
33
+ per auth table, unverified users (who will not JIT-link), and
34
+ orphans.
35
+
36
+ Output: human-readable text, or `--json` for tools. Exit codes:
37
+ `0` clean, `1` blockers, `2` warnings only, `3` usage errors.
38
+
39
+ Every finding carries a stable machine `code`, so CI (and the
40
+ migration guide) can assert exact blocker lists. The authoritative
41
+ exercise of the doctor lives in the repo's payload-auth fixture
42
+ (`fixtures/payload-auth-app`), where the CI job pins the exact
43
+ blockers it must report — and none it must not.
44
+
45
+ Requires Bun (`bunx`): the doctor loads the app's config module with
46
+ Bun's module loader, which tolerates the directory imports
47
+ payload-auth's dist uses.
48
+
49
+ Peers: `payload` ^3.83.0 || 4.0.0-beta.0 (the import commands boot the
50
+ app's own Payload instance; the doctor itself only reads).
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bun
2
+ type StdSink = {
3
+ readonly write: (chunk: string) => void;
4
+ };
5
+ declare function main(argv: readonly string[], out?: StdSink, err?: StdSink): Promise<number>;
6
+ export { main as cliMain };
7
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AA6KA,KAAK,OAAO,GAAG;IACb,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC,CAAA;AAED,iBAAe,IAAI,CACjB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,GAAE,OAAwB,EAC7B,GAAG,GAAE,OAAwB,GAC5B,OAAO,CAAC,MAAM,CAAC,CAwFjB;AAiBD,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,CAAA"}
package/dist/cli.js ADDED
@@ -0,0 +1,257 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * The `@payload-auth-link/migrate` CLI (issues #49–#59). Today: the
4
+ * read-only `doctor` (#52). The importer and exporters ride the same
5
+ * entrypoint as they land (#50, #58).
6
+ *
7
+ * Exit codes: 0 clean, 1 blockers, 2 warnings only, 3 usage errors.
8
+ * Never logs credential material — connection strings print only in
9
+ * the `--json` echo of what the caller passed, and secrets inside them
10
+ * are the caller's own input.
11
+ */
12
+ import { runDoctor } from './doctor/run-doctor.js';
13
+ import { runFinalize, runFromPayloadAuth, runRollback, runVerify, } from './importer/run-import.js';
14
+ function parseArgs(argv) {
15
+ const positional = [];
16
+ const flags = new Map();
17
+ for (let index = 0; index < argv.length; index += 1) {
18
+ const arg = argv[index];
19
+ if (arg === undefined) {
20
+ continue;
21
+ }
22
+ if (arg.startsWith('--')) {
23
+ const eq = arg.indexOf('=');
24
+ if (eq > 0) {
25
+ flags.set(arg.slice(2, eq), arg.slice(eq + 1));
26
+ }
27
+ else {
28
+ const name = arg.slice(2);
29
+ const next = argv[index + 1];
30
+ if (next !== undefined && !next.startsWith('--')) {
31
+ flags.set(name, next);
32
+ index += 1;
33
+ }
34
+ else {
35
+ flags.set(name, true);
36
+ }
37
+ }
38
+ }
39
+ else {
40
+ positional.push(arg);
41
+ }
42
+ }
43
+ return { command: positional[0] ?? null, flags };
44
+ }
45
+ function usage() {
46
+ return `@payload-auth-link/migrate — the payload-auth → payload-auth-link migration kit
47
+
48
+ Commands:
49
+ doctor --config ./src/payload.config.ts [--src ./src] [--json] [--database-url URL]
50
+ from-payload-auth --config ./src/payload.config.ts [--apply] [--sessions=carry|drop]
51
+ [--manifest ./payload-auth-link-migration.json] [--database-url URL] [--json]
52
+ verify --config ./src/payload.config.ts [--database-url URL] [--json]
53
+ rollback --config ./src/payload.config.ts [--manifest ...]
54
+ finalize [--manifest ...]
55
+
56
+ doctor is read-only: it loads the config, inspects the users
57
+ collection and Better Auth plugins, scans sources for call sites
58
+ that must migrate, and (with a database URL) reports row counts,
59
+ unverified users, and orphans.
60
+
61
+ from-payload-auth is a DRY RUN by default: it prints planned row
62
+ counts per model, collisions, and anything unmappable. --apply
63
+ writes the payload-auth-link collections through the Payload Local
64
+ API, records every created row and field change in the manifest,
65
+ and never modifies the source tables. verify checks both sides;
66
+ rollback undoes exactly the manifest; finalize marks it read-only
67
+ so the legacy tables may be dropped later, in their own migration.
68
+
69
+ Exit codes: 0 clean, 1 blockers/mismatch, 2 warnings only, 3 usage errors.
70
+ `;
71
+ }
72
+ function formatReport(report) {
73
+ const lines = [];
74
+ lines.push('== versions ==');
75
+ for (const finding of report.versions) {
76
+ lines.push(`${finding.status === 'ok' ? ' ok ' : finding.status === 'unknown' ? ' ? ' : ' !! '} ${finding.name}: ${finding.installed ?? 'unresolved'} (band ${finding.band})`);
77
+ }
78
+ lines.push('');
79
+ lines.push('== Better Auth plugins ==');
80
+ if (report.plugins.length === 0) {
81
+ lines.push(' none declared');
82
+ }
83
+ for (const finding of report.plugins) {
84
+ lines.push(` - ${finding.id}: ${finding.status} — ${finding.detail}`);
85
+ }
86
+ lines.push('');
87
+ lines.push('== users collection ==');
88
+ if (report.users === null) {
89
+ lines.push(' not found');
90
+ }
91
+ else {
92
+ lines.push(` slug: ${report.users.slug}`);
93
+ const role = report.users.roleField;
94
+ if (role !== null) {
95
+ lines.push(` role field '${role.name}': ${role.type}${role.hasMany ? ' (hasMany)' : ''} values [${role.values.join(', ')}]${role.nonCanonical.length > 0 ? ` — NON-CANONICAL: ${role.nonCanonical.join(', ')}` : ''}`);
96
+ }
97
+ else {
98
+ lines.push(' role field: none found');
99
+ }
100
+ lines.push(` local strategy: ${report.users.localStrategy ? 'ON' : 'off'}`);
101
+ if (report.users.collisions.length > 0) {
102
+ lines.push(` collisions with injected fields: ${report.users.collisions.join(', ')}`);
103
+ }
104
+ for (const note of report.users.notes) {
105
+ lines.push(` note: ${note}`);
106
+ }
107
+ }
108
+ lines.push('');
109
+ lines.push('== source scan ==');
110
+ if (report.source.length === 0) {
111
+ lines.push(' no migration call sites found');
112
+ }
113
+ for (const finding of report.source) {
114
+ lines.push(` - ${finding.kind}: ${finding.files.length} file(s)${finding.fields.length > 0 ? ` (fields: ${finding.fields.join(', ')})` : ''}`);
115
+ for (const file of finding.files.slice(0, 8)) {
116
+ lines.push(` ${file}`);
117
+ }
118
+ }
119
+ lines.push('');
120
+ lines.push('== database ==');
121
+ if (report.database === null) {
122
+ lines.push(' not inspected');
123
+ }
124
+ else {
125
+ for (const table of report.database.tables) {
126
+ lines.push(` ${table.name}: ${table.rows}`);
127
+ }
128
+ if (report.database.unverifiedUsers !== null) {
129
+ lines.push(` unverified users: ${report.database.unverifiedUsers}`);
130
+ }
131
+ for (const orphan of report.database.orphans) {
132
+ lines.push(` ORPHANS ${orphan.kind}: ${orphan.count}`);
133
+ }
134
+ }
135
+ lines.push('');
136
+ lines.push('== behaviour changes that will bite (0.7.0) ==');
137
+ for (const change of report.behaviourChanges) {
138
+ lines.push(` * ${change.title}: ${change.detail}`);
139
+ }
140
+ lines.push('');
141
+ lines.push('== blockers ==');
142
+ if (report.blockers.length === 0) {
143
+ lines.push(' none');
144
+ }
145
+ for (const issue of report.blockers) {
146
+ lines.push(` [${issue.code}] ${issue.detail}`);
147
+ }
148
+ lines.push('');
149
+ lines.push('== warnings ==');
150
+ if (report.warnings.length === 0) {
151
+ lines.push(' none');
152
+ }
153
+ for (const issue of report.warnings) {
154
+ lines.push(` [${issue.code}] ${issue.detail}`);
155
+ }
156
+ return lines.join('\n');
157
+ }
158
+ async function main(argv, out = process.stdout, err = process.stderr) {
159
+ const { command, flags } = parseArgs(argv);
160
+ if (command === null || command === 'help' || flags.get('help') === true) {
161
+ out.write(usage());
162
+ return command === null && argv.length === 0 ? 3 : 0;
163
+ }
164
+ const importCommands = ['from-payload-auth', 'verify', 'rollback', 'finalize'];
165
+ if (command !== 'doctor' && !importCommands.includes(command)) {
166
+ err.write(`unknown command '${command}'\n\n${usage()}`);
167
+ return 3;
168
+ }
169
+ if (importCommands.includes(command)) {
170
+ const configFlag = flags.get('config');
171
+ if (typeof configFlag !== 'string' || configFlag === '') {
172
+ err.write(`${command} requires --config <path to payload.config.ts>\n`);
173
+ return 3;
174
+ }
175
+ const databaseUrl = typeof flags.get('database-url') === 'string'
176
+ ? flags.get('database-url')
177
+ : (process.env.DATABASE_URL ?? '');
178
+ if ((command === 'from-payload-auth' || command === 'verify') && databaseUrl === '') {
179
+ err.write(`${command} requires --database-url or DATABASE_URL\n`);
180
+ return 3;
181
+ }
182
+ const sessionsFlag = flags.get('sessions');
183
+ const sessions = sessionsFlag === 'drop' ? 'drop' : 'carry';
184
+ const importOptions = {
185
+ configPath: new URL(configFlag, `file://${process.cwd()}/`).pathname,
186
+ databaseUrl,
187
+ apply: flags.get('apply') === true,
188
+ sessions,
189
+ ...(typeof flags.get('manifest') === 'string'
190
+ ? { manifestPath: flags.get('manifest') }
191
+ : {}),
192
+ ...(typeof flags.get('slugs') === 'string'
193
+ ? { slugsJson: flags.get('slugs') }
194
+ : {}),
195
+ json: flags.get('json') === true,
196
+ };
197
+ if (command === 'from-payload-auth') {
198
+ const outcome = await runFromPayloadAuth(importOptions);
199
+ if (outcome.plan.unmappable.length > 0) {
200
+ err.write(`ABORT: ${outcome.plan.unmappable.length} unmappable row(s); nothing was silently skipped\n`);
201
+ return 1;
202
+ }
203
+ return 0;
204
+ }
205
+ if (command === 'verify') {
206
+ const result = await runVerify(importOptions);
207
+ return result.ok ? 0 : 1;
208
+ }
209
+ if (command === 'rollback') {
210
+ await runRollback(importOptions);
211
+ return 0;
212
+ }
213
+ await runFinalize(importOptions);
214
+ return 0;
215
+ }
216
+ const configPath = flags.get('config');
217
+ if (typeof configPath !== 'string' || configPath === '') {
218
+ err.write('doctor requires --config <path to payload.config.ts>\n');
219
+ return 3;
220
+ }
221
+ const resolvedConfig = new URL(configPath, `file://${process.cwd()}/`).pathname;
222
+ const srcFlag = flags.get('src');
223
+ const dbFlag = flags.get('database-url');
224
+ const report = await runDoctor({
225
+ configPath: resolvedConfig,
226
+ ...(typeof srcFlag === 'string'
227
+ ? { srcDir: new URL(srcFlag, `file://${process.cwd()}/`).pathname }
228
+ : {}),
229
+ ...(typeof dbFlag === 'string' ? { databaseUrl: dbFlag } : {}),
230
+ });
231
+ if (flags.get('json') === true) {
232
+ out.write(`${JSON.stringify(report, null, 2)}\n`);
233
+ }
234
+ else {
235
+ out.write(`${formatReport(report)}\n`);
236
+ }
237
+ if (report.blockers.length > 0) {
238
+ return 1;
239
+ }
240
+ if (report.warnings.length > 0) {
241
+ return 2;
242
+ }
243
+ return 0;
244
+ }
245
+ const invokedDirectly = process.argv[1]?.endsWith('cli.js') === true || process.argv[1]?.endsWith('cli.ts') === true;
246
+ if (invokedDirectly) {
247
+ main(process.argv.slice(2))
248
+ .then((code) => {
249
+ process.exitCode = code;
250
+ })
251
+ .catch((error) => {
252
+ process.stderr.write(`doctor failed: ${error instanceof Error ? error.message : String(error)}\n`);
253
+ process.exitCode = 1;
254
+ });
255
+ }
256
+ export { main as cliMain };
257
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type VersionRange } from '@payload-auth-link/core';
2
+ import type { VersionFinding } from './types.js';
3
+ /**
4
+ * The bands the doctor checks against. These mirror the authoritative
5
+ * constants — `supportedPayloadBand` (packages/plugin) and
6
+ * `supportedBetterAuthBand` (packages/provider-better-auth) — and a
7
+ * unit test asserts the copies never drift from them, so the doctor
8
+ * cannot promise a band the runtime would refuse.
9
+ */
10
+ export declare const payloadBand: readonly VersionRange[];
11
+ export declare const betterAuthBand: readonly VersionRange[];
12
+ /** Next is a peer of the Next adapter; the doctor reports it informationally. */
13
+ export declare const nextBand: readonly VersionRange[];
14
+ /** Band checks for the three upstreams that gate a migration. */
15
+ export declare function checkVersions(fromUrl: string): VersionFinding[];
16
+ //# sourceMappingURL=bands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bands.d.ts","sourceRoot":"","sources":["../../src/doctor/bands.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,yBAAyB,CAAA;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,YAAY,EAG9C,CAAA;AACD,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAA8C,CAAA;AAChG,iFAAiF;AACjF,eAAO,MAAM,QAAQ,EAAE,SAAS,YAAY,EAAgD,CAAA;AAgC5F,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAW/D"}
@@ -0,0 +1,49 @@
1
+ import { describeBand, isSupportedVersion, readInstalledVersion, } from '@payload-auth-link/core';
2
+ /**
3
+ * The bands the doctor checks against. These mirror the authoritative
4
+ * constants — `supportedPayloadBand` (packages/plugin) and
5
+ * `supportedBetterAuthBand` (packages/provider-better-auth) — and a
6
+ * unit test asserts the copies never drift from them, so the doctor
7
+ * cannot promise a band the runtime would refuse.
8
+ */
9
+ export const payloadBand = [
10
+ { min: '3.83.0', maxExclusive: '4.0.0' },
11
+ { exact: '4.0.0-beta.0' },
12
+ ];
13
+ export const betterAuthBand = [{ min: '1.7.6', maxExclusive: '1.8.0' }];
14
+ /** Next is a peer of the Next adapter; the doctor reports it informationally. */
15
+ export const nextBand = [{ min: '15.0.0', maxExclusive: '17.0.0' }];
16
+ /**
17
+ * Better Auth minors this doctor knows the payload-auth app must move
18
+ * off: 1.6 removed `oidcProvider` in 1.7 and payload-auth's peer range
19
+ * still admits 1.4+, so an app can sit far below the supported band
20
+ * without anything telling it.
21
+ */
22
+ function versionFinding(name, band, fromUrl, note) {
23
+ const installed = readInstalledVersion(name, fromUrl);
24
+ const status = installed === null ? 'unknown' : isSupportedVersion(installed, band) ? 'ok' : 'blocker';
25
+ const base = installed === null
26
+ ? 'could not read the installed manifest; resolve the version manually'
27
+ : isSupportedVersion(installed, band)
28
+ ? `installed ${installed} is inside the supported band`
29
+ : `installed ${installed} is OUTSIDE the supported band; upgrade before migrating`;
30
+ return {
31
+ name,
32
+ installed,
33
+ band: describeBand(band),
34
+ status,
35
+ detail: [base, note?.(installed)].filter((part) => part !== undefined).join(' — '),
36
+ };
37
+ }
38
+ /** Band checks for the three upstreams that gate a migration. */
39
+ export function checkVersions(fromUrl) {
40
+ return [
41
+ versionFinding('payload', payloadBand, fromUrl),
42
+ versionFinding('better-auth', betterAuthBand, fromUrl, (installed) => installed !== null &&
43
+ (installed.startsWith('1.4.') || installed.startsWith('1.5.') || installed.startsWith('1.6.'))
44
+ ? 'better-auth 1.7 removed `oidcProvider` and the `mcp` plugin; an OAuth 2.1 server moves to @better-auth/oauth-provider (a schema migration — see the journey notes in issue #60)'
45
+ : undefined),
46
+ versionFinding('next', nextBand, fromUrl),
47
+ ];
48
+ }
49
+ //# sourceMappingURL=bands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bands.js","sourceRoot":"","sources":["../../src/doctor/bands.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,GAErB,MAAM,yBAAyB,CAAA;AAGhC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAA4B;IAClD,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE;IACxC,EAAE,KAAK,EAAE,cAAc,EAAE;CAC1B,CAAA;AACD,MAAM,CAAC,MAAM,cAAc,GAA4B,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAA;AAChG,iFAAiF;AACjF,MAAM,CAAC,MAAM,QAAQ,GAA4B,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAA;AAE5F;;;;;GAKG;AACH,SAAS,cAAc,CACrB,IAAY,EACZ,IAA6B,EAC7B,OAAe,EACf,IAAuD;IAEvD,MAAM,SAAS,GAAG,oBAAoB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACrD,MAAM,MAAM,GACV,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;IACzF,MAAM,IAAI,GACR,SAAS,KAAK,IAAI;QAChB,CAAC,CAAC,qEAAqE;QACvE,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC;YACnC,CAAC,CAAC,aAAa,SAAS,+BAA+B;YACvD,CAAC,CAAC,aAAa,SAAS,0DAA0D,CAAA;IACxF,OAAO;QACL,IAAI;QACJ,SAAS;QACT,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC;QACxB,MAAM;QACN,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;KACnF,CAAA;AACH,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,OAAO;QACL,cAAc,CAAC,SAAS,EAAE,WAAW,EAAE,OAAO,CAAC;QAC/C,cAAc,CAAC,aAAa,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,SAAS,EAAE,EAAE,CACnE,SAAS,KAAK,IAAI;YAClB,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAC5F,CAAC,CAAC,iLAAiL;YACnL,CAAC,CAAC,SAAS,CACd;QACD,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC;KAC1C,CAAA;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DatabaseFinding } from './types.js';
2
+ export declare function introspectDatabase(connectionString: string): Promise<DatabaseFinding>;
3
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/doctor/database.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AA6DjD,wBAAsB,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAmE3F"}
@@ -0,0 +1,110 @@
1
+ import { Client } from 'pg';
2
+ /**
3
+ * Read-only database introspection (issue #52): row counts for the
4
+ * payload-auth layout's auth tables, unverified users (who will not
5
+ * JIT-link), and orphans. Only an honest SELECT — the doctor never
6
+ * writes.
7
+ */
8
+ /**
9
+ * The payload-auth 1.9.x Postgres layout (snake_case columns; Payload's
10
+ * drizzle adapter stores hasMany selects as child tables like
11
+ * `users_role`). Tables that don't exist are simply absent from the
12
+ * report — apps customize slugs and drop plugins.
13
+ */
14
+ const knownTables = [
15
+ 'users',
16
+ 'accounts',
17
+ 'sessions',
18
+ 'verifications',
19
+ 'api_keys',
20
+ 'organizations',
21
+ 'members',
22
+ 'invitations',
23
+ 'jwks',
24
+ 'admin_invitations',
25
+ 'users_role',
26
+ 'two_factors',
27
+ 'passkeys',
28
+ 'oauth_applications',
29
+ 'oauth_access_tokens',
30
+ 'oauth_consents',
31
+ 'teams',
32
+ 'team_members',
33
+ 'subscriptions',
34
+ 'device_code',
35
+ 'sso_providers',
36
+ ];
37
+ async function tableExists(client, table) {
38
+ const result = await client.query(`SELECT 1 FROM information_schema.tables
39
+ WHERE table_schema = 'public' AND table_name = $1`, [table]);
40
+ return result.rows.length > 0;
41
+ }
42
+ /**
43
+ * Discover the columns a table actually has, so checks adapt to
44
+ * customized layouts instead of assuming.
45
+ */
46
+ async function columnsOf(client, table) {
47
+ const result = await client.query(`SELECT column_name FROM information_schema.columns
48
+ WHERE table_schema = 'public' AND table_name = $1`, [table]);
49
+ return new Set(result.rows.map((row) => String(row.column_name)));
50
+ }
51
+ export async function introspectDatabase(connectionString) {
52
+ const client = new Client({ connectionString });
53
+ await client.connect();
54
+ try {
55
+ const tables = [];
56
+ for (const table of knownTables) {
57
+ if (!(await tableExists(client, table))) {
58
+ continue;
59
+ }
60
+ const counted = await client.query(`SELECT count(*)::int AS n FROM ${table}`);
61
+ const first = counted.rows[0];
62
+ tables.push({ name: table, rows: first?.n ?? 0 });
63
+ }
64
+ let unverifiedUsers = null;
65
+ if (await tableExists(client, 'users')) {
66
+ const columns = await columnsOf(client, 'users');
67
+ if (columns.has('email_verified')) {
68
+ const counted = await client.query('SELECT count(*)::int AS n FROM users WHERE email_verified = false');
69
+ const first = counted.rows[0];
70
+ unverifiedUsers = first?.n ?? null;
71
+ }
72
+ }
73
+ const orphans = [];
74
+ const orphanChecks = [
75
+ { kind: 'accounts-without-user', table: 'accounts', column: 'user_id', target: 'users' },
76
+ {
77
+ kind: 'members-without-org',
78
+ table: 'members',
79
+ column: 'organization_id',
80
+ target: 'organizations',
81
+ },
82
+ { kind: 'members-without-user', table: 'members', column: 'user_id', target: 'users' },
83
+ {
84
+ kind: 'invitations-without-org',
85
+ table: 'invitations',
86
+ column: 'organization_id',
87
+ target: 'organizations',
88
+ },
89
+ ];
90
+ for (const check of orphanChecks) {
91
+ if ((await tableExists(client, check.table)) &&
92
+ (await tableExists(client, check.target)) &&
93
+ (await columnsOf(client, check.table)).has(check.column)) {
94
+ const counted = await client.query(`SELECT count(*)::int AS n FROM ${check.table}
95
+ WHERE ${check.column} IS NOT NULL
96
+ AND NOT EXISTS (SELECT 1 FROM ${check.target} t WHERE t.id = ${check.table}.${check.column})`);
97
+ const first = counted.rows[0];
98
+ const count = first?.n ?? 0;
99
+ if (count > 0) {
100
+ orphans.push({ kind: check.kind, count });
101
+ }
102
+ }
103
+ }
104
+ return { tables, unverifiedUsers, orphans };
105
+ }
106
+ finally {
107
+ await client.end();
108
+ }
109
+ }
110
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/doctor/database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { PluginFinding } from './types.js';
2
+ export declare function classifyPlugins(ids: readonly string[]): PluginFinding[];
3
+ /** Plugin ids proven live by the collections payload-auth built. */
4
+ export declare function pluginsFromCollectionSlugs(slugs: readonly string[]): string[];
5
+ /**
6
+ * Plugin ids found by scanning the config source for plugin factory
7
+ * calls. Regex on purpose: the loaded config cannot reveal these
8
+ * (closure-held options), and the doctor must not execute app code
9
+ * beyond loading its config module.
10
+ */
11
+ export declare function pluginsFromConfigSource(configSource: string): string[];
12
+ //# sourceMappingURL=plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../../src/doctor/plugins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AA2E/C,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,aAAa,EAAE,CA8DvE;AA+BD,oEAAoE;AACpE,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAS7E;AAsBD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAStE"}