63 lines
1.8 KiB
TypeScript
63 lines
1.8 KiB
TypeScript
/**
|
|
* Object Visibility Membrane — per-object access filtering.
|
|
*
|
|
* Provides a soft membrane where individual items (tasks, docs, events, etc.)
|
|
* can be restricted to specific role tiers. Items default to 'viewer' (visible
|
|
* to everyone) when no visibility is set.
|
|
*
|
|
* Importable by both server and client code.
|
|
*/
|
|
|
|
export type ObjectVisibility = 'viewer' | 'member' | 'moderator' | 'admin';
|
|
export const DEFAULT_VISIBILITY: ObjectVisibility = 'viewer';
|
|
|
|
const ROLE_LEVELS: Record<string, number> = {
|
|
viewer: 0,
|
|
member: 1,
|
|
moderator: 2,
|
|
admin: 3,
|
|
};
|
|
|
|
/** Check if a caller with `callerRole` can see an item with `visibility`. */
|
|
export function isVisibleTo(
|
|
visibility: ObjectVisibility | null | undefined,
|
|
callerRole: string,
|
|
): boolean {
|
|
const vis = visibility ?? DEFAULT_VISIBILITY;
|
|
const required = ROLE_LEVELS[vis] ?? 0;
|
|
const actual = ROLE_LEVELS[callerRole] ?? 0;
|
|
return actual >= required;
|
|
}
|
|
|
|
/**
|
|
* Filter a Record of items by visibility.
|
|
* @param getVis - accessor for the visibility field (defaults to `item.visibility`)
|
|
*/
|
|
export function filterByVisibility<T>(
|
|
items: Record<string, T>,
|
|
callerRole: string,
|
|
getVis: (item: T) => ObjectVisibility | null | undefined = (item) =>
|
|
(item as any).visibility,
|
|
): Record<string, T> {
|
|
const result: Record<string, T> = {};
|
|
for (const [key, item] of Object.entries(items)) {
|
|
if (isVisibleTo(getVis(item), callerRole)) {
|
|
result[key] = item;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Filter an array of items by visibility.
|
|
* @param getVis - accessor for the visibility field (defaults to `item.visibility`)
|
|
*/
|
|
export function filterArrayByVisibility<T>(
|
|
items: T[],
|
|
callerRole: string,
|
|
getVis: (item: T) => ObjectVisibility | null | undefined = (item) =>
|
|
(item as any).visibility,
|
|
): T[] {
|
|
return items.filter((item) => isVisibleTo(getVis(item), callerRole));
|
|
}
|