@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-scan.js","sourceRoot":"","sources":["../../src/doctor/source-scan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAGnD;;;;;GAKG;AAEH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AAC9E,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC;IACvB,cAAc;IACd,MAAM;IACN,OAAO;IACP,QAAQ;IACR,UAAU;IACV,cAAc;IACd,mBAAmB;CACpB,CAAC,CAAA;AAEF,MAAM,iBAAiB,GACrB,+GAA+G,CAAA;AACjH,MAAM,kBAAkB,GAAG,8CAA8C,CAAA;AACzE,MAAM,cAAc,GAAG,wBAAwB,CAAA;AAC/C,qEAAqE;AACrE,uEAAuE;AACvE,qDAAqD;AACrD,MAAM,WAAW,GAAG,kCAAkC,CAAA;AACtD,0DAA0D;AAC1D,MAAM,YAAY,GAAG,sDAAsD,CAAA;AAE3E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAwB,IAAI,GAAG,CAAC;IAC9D,IAAI;IACJ,YAAY;IACZ,OAAO;IACP,OAAO;IACP,aAAa;IACb,WAAW;IACX,OAAO;IACP,gBAAgB;CACjB,CAAC,CAAA;AAEF,SAAS,IAAI,CAAC,GAAW,EAAE,IAAc;IACvC,IAAI,OAAiB,CAAA;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAM;IACR,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACjD,SAAQ;QACV,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAC7B,IAAI,KAAK,CAAA;QACT,IAAI,CAAC;YACH,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ;QACV,CAAC;QACD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAClB,CAAC;aAAM,IAAI,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YAC9C,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjB,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IACnB,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAA;IACrC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAA;IACvC,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAU,CAAA;IAC7C,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAA;IACxC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAA;IACtC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAA;IACvC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,OAAe,CAAA;QACnB,IAAI,CAAC;YACH,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ;QACV,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,OAAe,EAAE,IAAiB,EAAQ,EAAE;YACzD,OAAO,CAAC,SAAS,GAAG,CAAC,CAAA;YACrB,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAChB,CAAC;QACH,CAAC,CAAA;QACD,KAAK,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAA;QACrC,KAAK,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAA;QACxC,KAAK,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAA;QAC1C,KAAK,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;QAClC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACnD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;YACtB,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3D,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;gBACxB,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACxB,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,GAAG,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC5D,MAAM,QAAQ,GAAoB;QAChC;YACE,IAAI,EAAE,qBAAqB;YAC3B,KAAK,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;YACvC,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,mFAAmF;SAC5F;QACD;YACE,IAAI,EAAE,kBAAkB;YACxB,KAAK,EAAE,CAAC,GAAG,mBAAmB,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;YAC/C,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,iFAAiF;SAC1F;QACD;YACE,IAAI,EAAE,sBAAsB;YAC5B,KAAK,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;YACzC,MAAM,EAAE,EAAE;YACV,MAAM,EACJ,iHAAiH;SACpH;QACD;YACE,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;YAC1C,MAAM,EAAE,EAAE;YACV,MAAM,EACJ,wIAAwI;SAC3I;QACD;YACE,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;YACxC,MAAM,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,IAAI,EAAE;YACjC,MAAM,EACJ,4JAA4J;SAC/J;KACF,CAAA;IACD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;AAC/D,CAAC"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The doctor's report shape (issue #52): read-only findings, every
3
+ * one carrying a stable machine code so tests (and the guide) assert
4
+ * exact blocker lists instead of prose.
5
+ */
6
+ /** A finding the doctor is confident about: fix it before migrating. */
7
+ export type DoctorIssue = {
8
+ /** Stable machine code — e.g. `better-auth-out-of-band`. */
9
+ readonly code: string;
10
+ /** Human detail: what was found, what closes it. */
11
+ readonly detail: string;
12
+ };
13
+ /** One upstream dependency against its supported band. */
14
+ export type VersionFinding = {
15
+ readonly name: string;
16
+ /** `null` when no manifest resolved (exotic bundling). */
17
+ readonly installed: string | null;
18
+ /** The supported band, human-readable. */
19
+ readonly band: string;
20
+ readonly status: 'ok' | 'warning' | 'blocker' | 'unknown';
21
+ readonly detail: string;
22
+ };
23
+ /** One Better Auth plugin's migration posture. */
24
+ export type PluginFinding = {
25
+ /** The plugin's `id` (or the satellite package name). */
26
+ readonly id: string;
27
+ readonly status: 'supported' | 'auto-declarable' | 'unprobed' | 'unsupported' | 'removed-upstream';
28
+ readonly detail: string;
29
+ };
30
+ /** The users collection, as the migration will meet it. */
31
+ export type UsersFinding = {
32
+ /** Collection slug (payload-auth's `users` by default). */
33
+ readonly slug: string;
34
+ readonly roleField: {
35
+ readonly name: string;
36
+ readonly type: string;
37
+ readonly hasMany: boolean;
38
+ /** Declared select values, when the field is a select. */
39
+ readonly values: readonly string[];
40
+ /** Values that are not canonical lowercase slugs. */
41
+ readonly nonCanonical: readonly string[];
42
+ } | null;
43
+ /** Payload's own email+password local strategy is active. */
44
+ readonly localStrategy: boolean;
45
+ /** Host fields colliding with the plugin's injected field names. */
46
+ readonly collisions: readonly string[];
47
+ readonly notes: readonly string[];
48
+ };
49
+ /** One source-scan category with the files that matched. */
50
+ export type SourceFinding = {
51
+ readonly kind: 'payload-auth-import' | 'better-auth-accessor' | 'get-payload-auth' | 'ba-internals' | 'req-user-field';
52
+ readonly files: readonly string[];
53
+ /** For `req-user-field`: every field read off `req.user`. */
54
+ readonly fields: readonly string[];
55
+ readonly detail: string;
56
+ };
57
+ /** Database state, as far as a read-only connection can see. */
58
+ export type DatabaseFinding = {
59
+ readonly tables: readonly {
60
+ readonly name: string;
61
+ readonly rows: number;
62
+ }[];
63
+ /** Users with `emailVerified = false` — they will not JIT-link. */
64
+ readonly unverifiedUsers: number | null;
65
+ readonly orphans: readonly {
66
+ readonly kind: string;
67
+ readonly count: number;
68
+ }[];
69
+ };
70
+ /** Default-behaviour changes in payload-auth-link 0.7.0 that bite. */
71
+ export type BehaviourChange = {
72
+ readonly title: string;
73
+ readonly detail: string;
74
+ };
75
+ /** The full read-only doctor report (`--json` prints this object). */
76
+ export type DoctorReport = {
77
+ readonly versions: readonly VersionFinding[];
78
+ readonly plugins: readonly PluginFinding[];
79
+ readonly users: UsersFinding | null;
80
+ readonly source: readonly SourceFinding[];
81
+ readonly database: DatabaseFinding | null;
82
+ readonly behaviourChanges: readonly BehaviourChange[];
83
+ readonly blockers: readonly DoctorIssue[];
84
+ readonly warnings: readonly DoctorIssue[];
85
+ };
86
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/doctor/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG;IACxB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,0DAA0D;AAC1D,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;IACzD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,kDAAkD;AAClD,MAAM,MAAM,aAAa,GAAG;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,iBAAiB,GAAG,UAAU,GAAG,aAAa,GAAG,kBAAkB,CAAA;IAClG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,2DAA2D;AAC3D,MAAM,MAAM,YAAY,GAAG;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE;QAClB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;QACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;QACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;QACzB,0DAA0D;QAC1D,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;QAClC,qDAAqD;QACrD,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;KACzC,GAAG,IAAI,CAAA;IACR,6DAA6D;IAC7D,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC,CAAA;AAED,4DAA4D;AAC5D,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EACT,qBAAqB,GACrB,sBAAsB,GACtB,kBAAkB,GAClB,cAAc,GACd,gBAAgB,CAAA;IACpB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,gEAAgE;AAChE,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC5E,mEAAmE;IACnE,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC/E,CAAA;AAED,sEAAsE;AACtE,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,sEAAsE;AACtE,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAA;IAC5C,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAA;IAC1C,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAA;IACnC,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAA;IACzC,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAA;IACzC,QAAQ,CAAC,gBAAgB,EAAE,SAAS,eAAe,EAAE,CAAA;IACrD,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;IACzC,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;CAC1C,CAAA"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The doctor's report shape (issue #52): read-only findings, every
3
+ * one carrying a stable machine code so tests (and the guide) assert
4
+ * exact blocker lists instead of prose.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/doctor/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,10 @@
1
+ import type { DoctorIssue, UsersFinding } from './types.js';
2
+ /** Find the payload-auth-shaped users collection (or the app's own users slug). */
3
+ export declare function findUsersCollection(config: unknown, usersSlug: string): Record<string, unknown> | null;
4
+ export declare function localStrategyOn(collection: Record<string, unknown>): boolean;
5
+ export declare function analyzeUsersCollection(collection: Record<string, unknown> | null, rolesFieldName: string): {
6
+ finding: UsersFinding | null;
7
+ issues: DoctorIssue[];
8
+ };
9
+ export declare function injectedFieldNamesForDocs(): readonly string[];
10
+ //# sourceMappingURL=users.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/doctor/users.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAuD3D,mFAAmF;AACnF,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAUhC;AAmBD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAS5E;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAC1C,cAAc,EAAE,MAAM,GACrB;IAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,CAmGzD;AAED,wBAAgB,yBAAyB,IAAI,SAAS,MAAM,EAAE,CAE7D"}
@@ -0,0 +1,176 @@
1
+ import { isRecord } from '@payload-auth-link/core';
2
+ /**
3
+ * The users collection analysis (issue #52): collisions with the
4
+ * plugin's injected fields, the existing role field and its values,
5
+ * and whether Payload's local strategy is on — the three things that
6
+ * decide whether a payload-auth app's user table can be adopted as-is.
7
+ */
8
+ /**
9
+ * The plugin's injected users-collection field names (see
10
+ * `packages/plugin/src/collections.ts`). The `roles` field is special:
11
+ * the plugin ADOPTS a compatible host select instead of throwing, so a
12
+ * host `roles`/`role` field is analyzed as the role field, not
13
+ * reported as a collision.
14
+ */
15
+ const injectedFieldNames = new Set([
16
+ 'identities',
17
+ 'phone',
18
+ 'suspendedUntil',
19
+ 'suspendReason',
20
+ 'linkable',
21
+ 'linkableEmail',
22
+ ]);
23
+ /** Canonical roles are lowercase slugs (`staff`, `front-plugin`). */
24
+ const canonicalRole = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
25
+ /** Narrow a config field list; configs are untyped at this boundary. */
26
+ function fieldList(collection) {
27
+ const fields = collection.fields;
28
+ if (!Array.isArray(fields)) {
29
+ return [];
30
+ }
31
+ return fields.filter(isRecord);
32
+ }
33
+ function fieldName(field) {
34
+ return typeof field.name === 'string' && field.name !== '' ? field.name : null;
35
+ }
36
+ function fieldType(field) {
37
+ return typeof field.type === 'string' ? field.type : 'unknown';
38
+ }
39
+ function selectValues(field) {
40
+ const options = field.options;
41
+ if (!Array.isArray(options)) {
42
+ return [];
43
+ }
44
+ return options
45
+ .map((option) => (typeof option === 'string' ? option : isRecord(option) ? option.value : null))
46
+ .filter((value) => typeof value === 'string');
47
+ }
48
+ /** Find the payload-auth-shaped users collection (or the app's own users slug). */
49
+ export function findUsersCollection(config, usersSlug) {
50
+ if (!isRecord(config) || !Array.isArray(config.collections)) {
51
+ return null;
52
+ }
53
+ for (const collection of config.collections) {
54
+ if (isRecord(collection) && collection.slug === usersSlug) {
55
+ return collection;
56
+ }
57
+ }
58
+ return null;
59
+ }
60
+ /**
61
+ * Payload's local email+password strategy for a collection. payload-auth
62
+ * leaves it on (its own Better Auth strategy rides alongside), which is
63
+ * exactly the surface 0.7.0 closes for provider-linked documents.
64
+ */
65
+ /** Names of the custom auth strategies a collection declares. */
66
+ function strategiesOf(collection) {
67
+ const auth = collection.auth;
68
+ if (!isRecord(auth) || !Array.isArray(auth.strategies)) {
69
+ return [];
70
+ }
71
+ return auth.strategies
72
+ .filter(isRecord)
73
+ .map((strategy) => strategy.name)
74
+ .filter((name) => typeof name === 'string');
75
+ }
76
+ export function localStrategyOn(collection) {
77
+ const auth = collection.auth;
78
+ if (auth === undefined || auth === null) {
79
+ return false;
80
+ }
81
+ if (auth === true) {
82
+ return true;
83
+ }
84
+ return isRecord(auth) ? auth.disableLocalStrategy !== true : false;
85
+ }
86
+ export function analyzeUsersCollection(collection, rolesFieldName) {
87
+ if (collection === null) {
88
+ return {
89
+ finding: null,
90
+ issues: [
91
+ { code: 'users-collection-missing', detail: 'no users collection found in the config' },
92
+ ],
93
+ };
94
+ }
95
+ const fields = fieldList(collection);
96
+ const issues = [];
97
+ const notes = [];
98
+ const collisions = fields
99
+ .map(fieldName)
100
+ .filter((name) => name !== null)
101
+ .filter((name) => injectedFieldNames.has(name));
102
+ let roleField = null;
103
+ const roleCandidates = fields.filter((field) => {
104
+ const name = fieldName(field);
105
+ return name === rolesFieldName || name === 'role';
106
+ });
107
+ for (const field of roleCandidates) {
108
+ const name = fieldName(field) ?? 'role';
109
+ if (fieldType(field) === 'select') {
110
+ const values = selectValues(field);
111
+ const nonCanonical = values.filter((value) => !canonicalRole.test(value));
112
+ roleField = {
113
+ name,
114
+ type: fieldType(field),
115
+ hasMany: field.hasMany === true,
116
+ values,
117
+ nonCanonical,
118
+ };
119
+ if (nonCanonical.length > 0) {
120
+ issues.push({
121
+ code: 'non-canonical-role-value',
122
+ detail: `the '${name}' field stores non-canonical value(s) ${nonCanonical
123
+ .map((value) => `'${value}'`)
124
+ .join(', ')}; the role value map (#56) keeps them stored as-is while roles stay canonical inside the plugin`,
125
+ });
126
+ }
127
+ if (field.hasMany !== true) {
128
+ issues.push({
129
+ code: 'roles-field-single-select',
130
+ detail: `the '${name}' field is a single-select; the plugin can only adopt a multi-select (hasMany) covering every configured role`,
131
+ });
132
+ }
133
+ }
134
+ else if (name === rolesFieldName) {
135
+ issues.push({
136
+ code: 'roles-field-not-select',
137
+ detail: `the '${rolesFieldName}' field is a '${fieldType(field)}', not a select; rename it and let the plugin inject its governed field`,
138
+ });
139
+ }
140
+ }
141
+ if (collisions.length > 0) {
142
+ issues.push({
143
+ code: 'injected-field-collision',
144
+ detail: `field(s) ${collisions
145
+ .map((name) => `'${name}'`)
146
+ .join(', ')} collide with the plugin's injected fields; rename them before booting payload-auth-link (its config throws on collisions)`,
147
+ });
148
+ }
149
+ const local = localStrategyOn(collection);
150
+ if (local) {
151
+ notes.push('Payload local strategy is ON (payload-auth leaves it active): since 0.7.0, provider-linked documents refuse local sign-in and forgot/reset by default — break-glass operator accounts keep theirs; allowLocalCredentialsForLinkedDocs: true reopens it only for a migration window');
152
+ }
153
+ else {
154
+ const strategies = Array.isArray(collection.auth) ? [] : strategiesOf(collection);
155
+ notes.push(strategies.length > 0
156
+ ? `Payload local strategy is OFF; authentication rides custom strategy/ies: ${strategies.join(', ')}`
157
+ : 'Payload local strategy is OFF for this collection');
158
+ }
159
+ if (isRecord(collection.auth) && collection.auth.useAPIKey === true) {
160
+ notes.push('useAPIKey is enabled: API keys are refused for suspended users at request time since 0.7.0 and never carry MFA-gated roles');
161
+ }
162
+ return {
163
+ finding: {
164
+ slug: typeof collection.slug === 'string' ? collection.slug : 'users',
165
+ roleField,
166
+ localStrategy: local,
167
+ collisions,
168
+ notes,
169
+ },
170
+ issues,
171
+ };
172
+ }
173
+ export function injectedFieldNamesForDocs() {
174
+ return [...injectedFieldNames, 'roles', 'email'];
175
+ }
176
+ //# sourceMappingURL=users.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/doctor/users.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { Payload } from 'payload';
2
+ import { type ImportManifest, type ImportResult, type PlanRow } from './types.js';
3
+ /**
4
+ * Apply side (issue #50): copy payload-auth rows into the
5
+ * payload-auth-link collections through the Payload Local API.
6
+ *
7
+ * - Password hashes, OAuth tokens, API-key hashes, and jwks carry over
8
+ * VERBATIM (payload-auth used Better Auth's own hashing).
9
+ * - `linkable` + `linkableEmail` are granted from VERIFIED emails only;
10
+ * unverified users are reported by the plan, never linked.
11
+ * - Sessions default to `carry` so nobody is logged out: the carried
12
+ * rows keep their tokens, and with the same `advanced.cookiePrefix`
13
+ * the existing cookies keep working.
14
+ * - Idempotent and resumable: every target row carries a deterministic
15
+ * `baId` derived from the source id (`acct-<source id>`), so a
16
+ * re-run after a crash finds its own earlier rows and skips them.
17
+ * - Every created row and every field write lands in the manifest, so
18
+ * `rollback` is exact and the source tables stay untouched.
19
+ */
20
+ export type ApplyOptions = {
21
+ readonly payload: Payload;
22
+ readonly sourceConnectionString: string;
23
+ readonly apply: boolean;
24
+ readonly sessions: 'carry' | 'drop';
25
+ readonly manifest: ImportManifest | null;
26
+ /** Written (merged) on `apply`; required when `apply` is true. */
27
+ readonly manifestPath: string;
28
+ readonly sourceCounts: Readonly<Record<string, number>>;
29
+ readonly sourceDatabase: string;
30
+ /** Target collection slugs; defaults must match the engine wiring. */
31
+ readonly slugs?: Readonly<Record<string, string>> | undefined;
32
+ /** Injectable clock for deterministic `linkedAt` stamps. */
33
+ readonly now?: () => Date;
34
+ readonly log?: (message: string) => void;
35
+ };
36
+ export declare function applyImport(options: ApplyOptions): Promise<ImportResult>;
37
+ /** Collapse per-row plan entries into one line per model (dry-run print). */
38
+ export declare function summarizePlanRows(rows: readonly PlanRow[]): PlanRow[];
39
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/importer/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEtC,OAAO,EAKL,KAAK,cAAc,EAEnB,KAAK,YAAY,EACjB,KAAK,OAAO,EACb,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAA;IACvC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAA;IACxC,kEAAkE;IAClE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACvD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAAA;IAC7D,4DAA4D;IAC5D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACzC,CAAA;AAkGD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAwjB9E;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,EAAE,CAerE"}