import { z } from 'zod'; import { GRANT_ROLES } from './permissions/schemas'; /** * Pond membership shared between api and web (issue #54). A "member" is a user * with a pond-scope allow grant (permissions.md §roles); the Members UI manages * these directly by role, on top of the general grant model (#51/#52). Only * pond-scope user grants are members — label/page-scope grants (#55) and the * `authenticated`/`public` subjects are managed elsewhere. */ /** The role a member holds on a pond, in descending order of capability. */ export type MemberRole = (typeof GRANT_ROLES)[number]; // 'pond_admin' | 'editor' | 'reader' /** Roles that consume a numbered seat quota (issue #22). Admins are unlimited. */ export const SEATED_MEMBER_ROLES = ['editor', 'reader'] as const; export type SeatedMemberRole = (typeof SEATED_MEMBER_ROLES)[number]; /** One pond member as shown in the management list. */ export interface MemberView { userId: string; username: string; displayName: string; role: MemberRole; /** The pond owner; their membership cannot be changed or removed here. */ isOwner: boolean; } /** Seat usage for a quota-limited role, for the "3 of 5 editor seats" display. */ export interface SeatUsage { used: number; limit: number; } /** Response of `GET /ponds/:id/members`. */ export interface PondMembersView { members: MemberView[]; seats: Record; pondType: 'personal' | 'shared'; /** Whether the requesting user may manage members (Pond Admin). */ canManage: boolean; } /** Non-empty username or e-mail; the server resolves it to exactly one user. */ const memberIdentifierSchema = z.string().trim().min(1, 'validation.required').max(320); export const addMemberInputSchema = z.object({ usernameOrEmail: memberIdentifierSchema, role: z.enum(GRANT_ROLES), }); export type AddMemberInput = z.infer; export const changeMemberRoleInputSchema = z.object({ role: z.enum(GRANT_ROLES), }); export type ChangeMemberRoleInput = z.infer;