|
7 | 7 | */ |
8 | 8 |
|
9 | 9 | /** |
10 | | - * Organization membership roles — the ONE place the console names them. |
| 10 | + * Organization membership roles — the console's ONE entry point for them. |
11 | 11 | * |
12 | 12 | * These are better-auth's organization roles as the ObjectStack framework |
13 | 13 | * registers them, stored in `sys_member.role` / `sys_invitation.role`. Before |
14 | 14 | * this module the vocabulary was inlined as `type Role = 'owner' | 'admin' | |
15 | 15 | * 'member'` in both `MembersPage` and `InviteMemberDialog`, so a role the |
16 | 16 | * server had learned about was still unreachable from either screen. |
17 | 17 | * |
18 | | - * ## The vocabulary is CLOSED — this mirror is complete by construction |
| 18 | + * ## The vocabulary is CLOSED — and derived, not mirrored |
19 | 19 | * |
20 | 20 | * [framework ADR-0108 / objectstack#3723] These four names are the WHOLE list, |
21 | 21 | * and the framework owns them. An application cannot add a fifth: the server |
22 | 22 | * used to register every declared `position` / `permission` name as an |
23 | 23 | * organization role, and that was retired, because every value stored in |
24 | 24 | * `sys_member.role` is projected into `current_user.positions` — so a business |
25 | 25 | * role handed out this way was capability with none of the position system's |
26 | | - * controls (no `granted_by`, no validity window, no scope check). |
| 26 | + * controls (no `granted_by`, no validity window, no scope check). A membership |
| 27 | + * role is an organization GRADE (what you may reach); what a person may DO is |
| 28 | + * a position, granted through `sys_user_position` or an invitation's placement |
| 29 | + * (framework ADR-0105 D8). |
27 | 30 | * |
28 | | - * So the standing instruction that used to live here — *"a role added |
29 | | - * server-side must be added HERE too"* — no longer applies. There are no |
30 | | - * server-side additions to chase. A membership role is an organization GRADE |
31 | | - * (what you may reach); what a person may DO is a position, granted through |
32 | | - * `sys_user_position` or an invitation's placement (framework ADR-0105 D8). |
| 31 | + * The names below are re-exports of `BUILTIN_MEMBERSHIP_ROLES` from |
| 32 | + * `@objectstack/spec` — the same constant the enforced `sys_member.role` / |
| 33 | + * `sys_invitation.role` selects are built from — so this list cannot drift |
| 34 | + * from what the server accepts, by construction. |
33 | 35 | * |
34 | | - * ⚠️ Still a mirror, not a derivation — but now only for a packaging reason, |
35 | | - * not a design one. The names live in `@objectstack/spec` as |
36 | | - * `BUILTIN_MEMBERSHIP_ROLES` / `BUILTIN_MEMBERSHIP_ROLE_OPTIONS`, which |
37 | | - * `@object-ui/auth` cannot import yet: this package does not depend on |
38 | | - * `@objectstack/spec`, and the constants ship in the first release carrying |
39 | | - * ADR-0108 (absent from the published 16.1.0). Once this package takes that |
40 | | - * dependency at a version that has them, the four `export const`s below become |
41 | | - * a re-export and `ORG_ROLES` becomes `[...BUILTIN_MEMBERSHIP_ROLES]` — the |
42 | | - * labels and grade ladder stay here, since they are console concerns. |
43 | | - * `orgRolesMatchFramework` in the tests pins the list until then. |
| 36 | + * What stays LOCAL, deliberately: `ORG_ROLE_LABELS` and the grade ladder |
| 37 | + * (`orgRoleGrade` / `invitableOrgRoles` / `assignableOrgRoles`). They are |
| 38 | + * console concerns — i18n keys and screen-narrowing rules — and folding them |
| 39 | + * into the name list would be the modeling error ADR-0108 D4 warns about: |
| 40 | + * *what names exist* is a list; *which names mean authority* and *how a name |
| 41 | + * projects* are rules that belong next to what they govern. |
44 | 42 | */ |
45 | 43 |
|
46 | | -export const ORG_ROLE_OWNER = 'owner'; |
47 | | -export const ORG_ROLE_ADMIN = 'admin'; |
48 | | -/** |
49 | | - * [framework ADR-0105 D8] The delegated issuer grade. May reach |
50 | | - * `/organization/invite-member` WITHOUT being an org admin, which is what gives |
51 | | - * D8's scope-bounded issuance gate a caller. Carries no authority of its own: |
52 | | - * what a delegate may actually *place* comes from a separately-granted |
53 | | - * `adminScope`, surfaced to this console by `describeDelegableScope()`. |
54 | | - */ |
55 | | -export const ORG_ROLE_DELEGATED_ADMIN = 'delegated_admin'; |
56 | | -export const ORG_ROLE_MEMBER = 'member'; |
| 44 | +import { |
| 45 | + BUILTIN_MEMBERSHIP_ROLES, |
| 46 | + MEMBERSHIP_ROLE_OWNER as ORG_ROLE_OWNER, |
| 47 | + MEMBERSHIP_ROLE_ADMIN as ORG_ROLE_ADMIN, |
| 48 | + MEMBERSHIP_ROLE_DELEGATED_ADMIN as ORG_ROLE_DELEGATED_ADMIN, |
| 49 | + MEMBERSHIP_ROLE_MEMBER as ORG_ROLE_MEMBER, |
| 50 | + type BuiltinMembershipRole, |
| 51 | +} from '@objectstack/spec'; |
| 52 | + |
| 53 | +// Kept under the console's own names so call sites read as org-screen |
| 54 | +// vocabulary. `ORG_ROLE_DELEGATED_ADMIN` is ADR-0105 D8's delegated issuer |
| 55 | +// grade: it may reach `/organization/invite-member` WITHOUT being an org |
| 56 | +// admin, and carries no authority of its own — what a delegate may actually |
| 57 | +// *place* comes from a separately-granted `adminScope`, surfaced to this |
| 58 | +// console by `describeDelegableScope()`. |
| 59 | +export { ORG_ROLE_OWNER, ORG_ROLE_ADMIN, ORG_ROLE_DELEGATED_ADMIN, ORG_ROLE_MEMBER }; |
57 | 60 |
|
58 | | -export type OrgRole = |
59 | | - | typeof ORG_ROLE_OWNER |
60 | | - | typeof ORG_ROLE_ADMIN |
61 | | - | typeof ORG_ROLE_DELEGATED_ADMIN |
62 | | - | typeof ORG_ROLE_MEMBER; |
| 61 | +export type OrgRole = BuiltinMembershipRole; |
63 | 62 |
|
64 | | -/** Display order: most privileged first, as the screens list them. */ |
65 | | -export const ORG_ROLES: readonly OrgRole[] = [ |
66 | | - ORG_ROLE_OWNER, |
67 | | - ORG_ROLE_ADMIN, |
68 | | - ORG_ROLE_DELEGATED_ADMIN, |
69 | | - ORG_ROLE_MEMBER, |
70 | | -] as const; |
| 63 | +/** Display order: most privileged first, as the spec ships it and the screens list it. */ |
| 64 | +export const ORG_ROLES: readonly OrgRole[] = [...BUILTIN_MEMBERSHIP_ROLES]; |
71 | 65 |
|
72 | 66 | /** i18n key + English fallback for a role, so every screen labels it identically. */ |
73 | 67 | export const ORG_ROLE_LABELS: Record<OrgRole, { key: string; defaultValue: string }> = { |
|
0 commit comments