import type { BillingAdapter } from "./types.js"; import type { PlanModel } from "./plan-model.js"; type Assignments = Record; /** Assign a member to a seat type (pass null/"" to clear → default seat). */ export declare function assignSeatType(adapter: BillingAdapter, orgId: string, memberId: string, seatType: string | null): Promise; /** * The full member → seat-type map for the org. * * A per-member store can be asked about a member but not who the members are, so * enumerating needs `adapter.listMemberIds`. Without it this returns what the * legacy org map holds — which is everything an adapter with no per-member store * has anyway. N reads for N members: an admin screen, not the hot path, and the * same shape `memberUsage` already documents. */ export declare function listSeatAssignments(adapter: BillingAdapter, orgId: string): Promise; /** A single member's assigned seat type, or null if unassigned. */ export declare function getSeatType(adapter: BillingAdapter, orgId: string, memberId: string): Promise; /** * Drop a workspace's entries from each member's own metadata, and report how many were. * * Called when a workspace closes. Both per-member stores are keyed by org * (`{ [orgId]: … }`) precisely so one workspace cannot read or overwrite another's — the same * keying means a closed workspace's entries would otherwise sit in every ex-member's record * for ever, spending a budget measured in characters (10 keys, 600 chars per value) that their * REMAINING workspaces still need. A person who has passed through a few dead workspaces would * eventually be unable to be assigned a seat anywhere. * * A `""` tombstone is NOT written here, unlike a cleared seat: there is no legacy org map left * to fall back to, because the org is going away with it. */ export declare function clearMemberRecords(adapter: BillingAdapter, orgId: string, memberIds: readonly string[]): Promise; /** * Whether this member can be put on `seatType` — the guardrail that stops a seat being * given away. * * `assignSeatType` is a metadata write: it changes which pack a person draws and touches the * subscription not at all. So an owner could move everybody onto the most expensive seat and * the invoice would never notice — and since a member can now ASK for a bigger seat and an * owner can grant it in one click, that is one click from handing out a €105/month seat for * nothing. Two ceilings answer it: * * PURCHASED — `getSubscription().seatCounts[seatType]`, what the workspace is paying for. * Assigning more of a type than were bought is selling at zero. * MAX — the plan's own `seatTypes[t].max`, a product rule ("one shared agent seat"). * * The DEFAULT seat counts unassigned members too, because they draw it whether or not * anybody said so — counting only explicit assignments would let a workspace with one * purchased Standard seat quietly seat ten people on it. * * Unknown means ALLOW, deliberately, and there is a lot of unknown: no subscription record, * no `seatCounts` (a plan that sells no seats, a free plan, a record written before seat * counts existed), no `listMemberIds`. Refusing on a number the library cannot read would * break every deployment whose adapter does not report one, and the failure would be an * owner unable to seat their own team — worse than the giveaway being prevented. */ export declare function seatAssignable(adapter: BillingAdapter, orgId: string, model: PlanModel | null, memberId: string, seatType: string | null): Promise<{ ok: true; } | { ok: false; reason: "not_purchased" | "at_max"; purchased?: number; assigned: number; }>; /** * How much room is left on a seat type — the same counting `seatAssignable` does, as a READ. * * The guard answers "may I put THIS member here", one candidate at a time, and returns a * reason when the answer is no. That is the wrong shape for the two things a caller actually * wants before it offers a control: how many are left, and whether to grey the option out at * all. Asking the guard N times to draw one picker is N × (assignments + members + * subscription) reads, so consumers stopped asking and offered every seat — which is how a * picker comes to show a Premium seat that the write then refuses. * * `remaining: null` means UNKNOWN, not zero, and it is the common case: no subscription * record, no `seatCounts`, no `max`. Unknown allows, exactly as the guard does — a UI must * render an unknown as available, never as full. */ export declare function seatCapacity(adapter: BillingAdapter, orgId: string, model: PlanModel | null, seatType: string): Promise<{ seatType: string; assigned: number; purchased: number | null; max: number | null; remaining: number | null; }>; export {}; //# sourceMappingURL=seats.d.ts.map