import type { EmailCta, EmailLinkBlock } from "../server/email-template.js"; import type { UserProfile } from "../user-profile/shared.js"; export interface ShareEmailExtras { paragraphs?: string[]; secondaryCta?: EmailCta; linkBlock?: EmailLinkBlock; closingParagraphs?: string[]; } export interface ShareableResourceRegistration { type: string; resourceTable: any; sharesTable: any; displayName: string; titleColumn?: string; getResourcePath?: (resource: any) => string | undefined; getLogoUrl?: (resource: any) => string | undefined | Promise; getBrandName?: (resource: any) => string | undefined | Promise; getSender?: (resource: any, ctx: { sender: UserProfile; }) => { fromName?: string; replyTo?: string; } | undefined | Promise<{ fromName?: string; replyTo?: string; } | undefined>; getHeroHtml?: (resource: any, ctx: { href: string; alt?: string; }) => string | undefined | Promise; getShareEmailExtras?: (resource: any, ctx: { href: string; sender: UserProfile; recipientEmail: string; }) => ShareEmailExtras | undefined | Promise; getDb: () => any; /** * Optional resource-owned persistence hook for visibility changes. Use this * when changing visibility must share a transaction with another invariant, * such as reserving a human-readable name before the row becomes visible. * The generic action falls back to a direct update when this is omitted. */ persistVisibilityChange?: (args: { resource: any; resourceId: string; visibility: "private" | "org" | "public"; update: Record; userEmail?: string; orgId?: string; }) => void | Promise; /** * Runs before a sharing change is persisted. Throw to refuse the change. * It receives the resource row, so a registration can refuse per-row states * (for example, a resource that holds private content). Runs only for a * visibility change that differs from the current value, and for every * `share-resource` grant. It runs again after that change is written, and a * refusal then undoes the write, so one change can call it twice: keep it * free of side effects. */ assertSharingChange?: (args: { resource: any; change: { kind: "visibility"; visibility: "private" | "org" | "public"; } | { kind: "grant"; }; }) => void | Promise; allowPublic?: boolean; publicAccessRole?: "viewer" | "commenter" | "editor" | "admin" | ((resource: any, ctx: { userEmail?: string; orgId?: string; authCapability?: string; }) => "viewer" | "commenter" | "editor" | "admin" | Promise<"viewer" | "commenter" | "editor" | "admin">); requireOrgMemberForUserShares?: boolean; supportsGroupShares?: boolean; /** * Grant manager access to registration-specific identities. When supplied, * a resource-level admin share does not bypass this policy; non-managers * resolve at editor. */ canManageAccess?: (resource: any, ctx: { userEmail?: string; orgId?: string; }) => boolean | Promise; resolveAccessContext?: (ctx: { userEmail?: string; orgId?: string; authCapability?: string; }) => { userEmail?: string; orgId?: string; authCapability?: string; }; ownerAccessIgnoresOrg?: boolean; /** * Whether a row still counts as an openable resource, for example not in * the trash. `columns` are the resource-table keys `isAvailable` reads; * they join the lightweight access projection. A row that fails the rule * reads as missing to anyone who can't open it, and as trashed to anyone * who can. Omit it when every row is available. */ availability?: { columns: readonly string[]; isAvailable: (resource: any) => boolean; }; /** * Lets signed-in people who can't open a resource of this type ask its * owner and admins for access. Off by default, so an app only starts * emailing owners once its access screen offers the request. */ accessRequests?: boolean; /** * A context that can open the resource when the viewer's own can't, such * as the authority a Content space lends its members. Only a link's status * reads it, so such a viewer hears that a page is in the trash instead of * missing; `resolveAccess` and the share actions use the viewer's own * context, and the app applies the grant in its own access helpers. * `columns` are the resource-table keys `resolve` reads; they join the * lightweight access projection, so `resolve` gets the row already loaded. */ fallbackAccessContext?: { columns: readonly string[]; resolve: (resource: any, ctx: { userEmail?: string; orgId?: string; }) => Promise<{ userEmail?: string; orgId?: string; } | null>; }; agentReadable?: false | { resourceKind: string; getContextPath: (resource: any) => string | undefined; getPagePath?: (resource: any) => string | undefined; ttlSeconds?: number; }; } export declare function registerShareableResource(entry: ShareableResourceRegistration): void; export declare function getShareableResource(type: string): ShareableResourceRegistration | undefined; export declare function requireShareableResource(type: string): ShareableResourceRegistration; export declare function listShareableResources(): ShareableResourceRegistration[];