Skip to the content

Capsid menu

app/lib/roles.ts

32 lines and 1 functions in DrDustinEdwards/carrel, at commit d5d5e82 on 2026-10-24. This file on GitHub.

Functions

The functions of roles.ts
FunctionLinesComplexityCoveredCRAPMutation
can34 to 362100%OkCRAP 2No data: mutation testing has not run on this repo yet

Imports

It imports no other module of this repo. Imported by app/lib/ai.server.ts, app/lib/binder.server.ts, app/lib/book-history.server.ts, app/lib/books.server.ts, app/lib/bulk.server.ts, app/lib/content.server.ts, app/lib/google/drive.server.ts, app/lib/history.server.ts, app/lib/legal.server.ts, app/lib/mcp/tools.ts, app/lib/media.server.ts, app/lib/mentions.server.ts, app/lib/people-admin.server.ts, app/lib/people.server.ts, app/lib/projects.server.ts, app/lib/voice.server.ts, app/lib/waiting.server.ts, app/lib/writing.server.ts, app/routes/ai-draft.tsx, app/routes/book.chapter.tsx, app/routes/book.corkboard.tsx, app/routes/book.file.tsx, app/routes/book.outliner.tsx, app/routes/book.tsx, app/routes/editor.tsx, app/routes/legal.tsx, app/routes/media.tsx, app/routes/project.flags.tsx, app/routes/project.tsx, app/routes/project.voice.tsx.

Source

// Who may do what on a project. Pure, so every caller (UI loaders now, MCP tools later) asks the
// same question the same way.

export type Role = "reader" | "editor" | "owner";

/**
 * `delete_media` removes a file from a site's storage (stage 3). It is the Owner's alone: a file can
 * be in use on pages the site's reference check cannot see, such as another site linking it.
 * `delete_content` removes a post from a site. Also the Owner's alone: unlike unpublish, it takes the
 * post off the site's list altogether.
 * `read_mentions` and `decide_mention` are the Owner's alone: Dustin ruled (2026-09-26) that the inbox belongs to
 * Dustin alone (a mention is text a stranger sent, and the site reserves deleting one to its admin).
 * They are two actions so the page can be opened to Editors and Readers by changing one line here.
 */
export type Action =
  | "read"
  | "comment"
  | "edit"
  | "publish"
  | "send_external"
  | "manage"
  | "delete_media"
  | "delete_content"
  | "read_mentions"
  | "decide_mention";

const ALLOWED: Record<Role, ReadonlySet<Action>> = {
  reader: new Set(["read", "comment"]),
  editor: new Set(["read", "comment", "edit"]),
  owner: new Set(["read", "comment", "edit", "publish", "send_external", "manage", "delete_media", "delete_content", "read_mentions", "decide_mention"]),
};

/** A person with no role on a project may do nothing on it, including read it. */
export function can(role: Role | null, action: Action): boolean {
  return role !== null && ALLOWED[role].has(action);
}