All files / src/server/dal capabilities.ts

100% Statements 7/7
100% Branches 6/6
100% Functions 2/2
100% Lines 5/5

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59                                              79x                                     362x                       473x 171x 112x    
// DAL capability map: role model core (purses-89g).
// ownerId is the SOLE owner source; membership.role is a display label only.
 
export type Role = "owner" | "curator";
 
export type Operation =
  | "collection:read"
  | "collection:manage"
  | "collection:purge"
  | "collection:audit"
  | "members:read"
  | "members:manage"
  | "invitations:read"
  | "invitations:manage"
  | "items:read"
  | "items:write"
  | "items:purge"
  | "photos:write"
  | "sessions:write"
  | "preferences:write";
 
// Compile-exhaustive map: every Operation has a minimum required Role.
// owner satisfies all operations; curator satisfies only curator-level ones.
export const CAPABILITIES: Record<Operation, Role> = {
  "collection:read": "curator",
  "members:read": "curator",
  "items:read": "curator",
  "items:write": "curator",
  "photos:write": "curator",
  "sessions:write": "curator",
  "preferences:write": "curator",
  "invitations:read": "owner",
  "collection:manage": "owner",
  "collection:purge": "owner",
  "collection:audit": "owner",
  "members:manage": "owner",
  "invitations:manage": "owner",
  "items:purge": "owner",
};
 
// owner satisfies every role (including owner); curator satisfies curator only.
export function roleSatisfies(effective: Role, required: Role): boolean {
  return effective === "owner" || effective === required;
}
 
// Resolve the caller's effective role for a collection.
// collection.ownerId is canonical and sole — membership.role is never read here.
// Returns "owner" if the caller is the collection owner, "curator" if they have
// a membership row, null if they are not a member at all.
export function resolveEffectiveRole(
  collection: { ownerId: string },
  membership: { id: string } | null,
  userId: string,
): Role | null {
  if (collection.ownerId === userId) return "owner";
  if (membership) return "curator";
  return null;
}