db/cell_grant_queries.ts

Raw queries against the cell_grant table.

Resource-side ACL for cells: each row admits a principal at a level (viewer | editor). Principal is discriminated by which columns are set — actor_id (single actor) xor (role, scope_id?) (any holder of a matching role_grant). Owner is implicit on cell.created_by and never appears in this table.

Convention: deps: QueryDeps first, no audit side effects, mutations return the affected row (or null for not-found).

query_cell_grant_create upserts on the relevant partial unique index so re-granting the same principal updates level rather than producing duplicate rows. The two principal shapes use different indexes:

  • Actor-shaped: idx_cell_grant_unique_actor on (cell_id, actor_id).
  • Role-shaped: idx_cell_grant_unique_role_scope on (cell_id, role, scope_id) with NULLS NOT DISTINCT so two (role, NULL) grants on the same cell collide.
view source

Declarations
#

9 declarations

CellGrantCreateQueryInput
#

CellGrantPrincipalQueryInput
#

db/cell_grant_queries.ts view source

CellGrantPrincipalQueryInput import type {CellGrantPrincipalQueryInput} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Discriminated principal input for query_cell_grant_create. Wire and query shapes are aligned — actor-shaped principals carry a pre-resolved actor_id; pickers run actor_search to convert a typed name to an id upstream of the handler.

CellGrantRow
#

db/cell_grant_queries.ts view source

CellGrantRow import type {CellGrantRow} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Row shape returned by cell_grant SELECTs.

id

type Uuid

cell_id

type Uuid

level

type CellGrantLevel

actor_id

type Uuid | null

role

type string | null

scope_id

type Uuid | null

granted_by

type Uuid | null

created_at

type Date

query_cell_grant_create
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, input: CellGrantCreateQueryInput): Promise<CellGrantRow> import {query_cell_grant_create} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Insert a grant, or update the existing row's level + granted_by when one already exists for the same (cell_id, principal) pair.

Idempotent re-share: caller doesn't need to check existence first. The UPSERT path runs even when the existing row's level matches — handlers reading the row's prior state for audit ("create vs. update") must do so before this call.

deps

query deps

input

cell, level, principal, grantor

returns

Promise<CellGrantRow>

the inserted-or-updated row

query_cell_grant_delete
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, grant_id: string & $brand<"Uuid">): Promise<CellGrantRow | null> import {query_cell_grant_delete} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Delete a grant by id, returning the deleted row.

Returning the row lets the caller audit the principal + level after the delete and (for self-revoke) recompute still_admitted against the remaining grants on the cell without a second fetch.

deps

query deps

grant_id

grant id

type string & $brand<"Uuid">

returns

Promise<CellGrantRow | null>

the deleted row or null when no row matched

query_cell_grant_get
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, grant_id: string & $brand<"Uuid">): Promise<CellGrantRow | null> import {query_cell_grant_get} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Fetch a grant by id.

deps

query deps

grant_id

grant id

type string & $brand<"Uuid">

returns

Promise<CellGrantRow | null>

the row or null when not found

query_cell_grant_list_for_cell
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, cell_id: string & $brand<"Uuid">): Promise<CellGrantRow[]> import {query_cell_grant_list_for_cell} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

List all grants on a cell, oldest first.

Used by cell_grant_list (RPC) and by handlers that need grants alongside the cell row for the authorize predicate.

deps

query deps

cell_id

cell id

type string & $brand<"Uuid">

returns

Promise<CellGrantRow[]>

matching rows

query_cell_grant_list_for_cells
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, cell_ids: readonly (string & $brand<"Uuid">)[]): Promise<CellGrantRow[]> import {query_cell_grant_list_for_cells} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

List all grants across a set of cells, ordered by cell then creation. Used by the strict relation-read filter to test can_view_cell per target in memory — the caller groups the flat result by cell_id. Returns every grant on each cell (not caller-filtered), because can_view_cell needs the full grant list to decide admission.

deps

query deps

cell_ids

cells to fetch grants for (duplicates are harmless)

type readonly (string & $brand<"Uuid">)[]

returns

Promise<CellGrantRow[]>

matching grant rows (group by cell_id caller-side)

query_cell_grants_for_caller_in_cells
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, cell_ids: (string & $brand<"Uuid">)[], caller_actor_id: (string & $brand<"Uuid">) | null, role_grant_roles: string[], role_grant_scope_ids: ((string & $brand<...>) | null)[]): Promise<...> import {query_cell_grants_for_caller_in_cells} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Load grants that admit the caller (by actor or role-scoped role_grants) across multiple cells. Used to enrich cell_list responses with context about what granted access. Returns grants for the given cells that match the caller's identity or role_grant set.

deps

cell_ids

cells to fetch grants for

type (string & $brand<"Uuid">)[]

caller_actor_id

actor id of the caller (null for unauth)

type (string & $brand<"Uuid">) | null

role_grant_roles

active role_grant roles (parallel array)

type string[]

role_grant_scope_ids

active role_grant scope ids (parallel array, parallel to roles)

type ((string & $brand<"Uuid">) | null)[]

returns

Promise<CellGrantRow[]>

matching grants (may include grants the caller doesn't match; caller's list handler must filter when returning to the API)

Depends on
#

Imported by
#