UNPKG

@beignet/core

Version:

Core framework primitives for Beignet

234 lines 7.86 kB
/** * @beignet/core/entitlements * * Provider-neutral product access primitives for Beignet applications. */ type MaybePromise<T> = T | Promise<T>; /** * Stable app-defined capability key, such as "issues.create". */ export type EntitlementKey = string; /** * Principal whose product access is being checked. */ export type EntitlementSubject = { /** * App-defined subject type, commonly "tenant" or "account". */ type: string; /** * Stable subject identifier. */ id: string; }; /** * Input for checking whether a subject has access to a capability. */ export type EntitlementCheckInput<TKey extends EntitlementKey = EntitlementKey> = { /** * Capability being checked. */ entitlement: TKey; /** * Tenant, account, or other app-owned subject whose product access is checked. */ subject: EntitlementSubject; }; /** * An entitlement decision that allows the capability. */ export type EntitlementAllowedDecision = { allowed: true; }; /** * An entitlement decision that denies the capability. */ export type EntitlementDeniedDecision = { allowed: false; reason?: string; code?: string; details?: unknown; }; /** * Normalized entitlement decision. */ export type EntitlementDecision = EntitlementAllowedDecision | EntitlementDeniedDecision; /** * Value an entitlement resolver may return. */ export type EntitlementResolverResult = boolean | EntitlementDecision; /** * Entitlement port method that produced a decision. */ export type EntitlementDecisionSource = "can" | "inspect" | "require"; /** * Optional context for entitlement checks. */ export type EntitlementCheckOptions<TContext = unknown> = { /** * Application context used for the check. Observers can use this to copy * request correlation fields without coupling the resolver to app context. */ ctx?: TContext; /** * Source to report to observers. Port methods set this automatically. */ source?: EntitlementDecisionSource; }; /** * App-facing product access port. */ export type EntitlementsPort<TKey extends EntitlementKey = EntitlementKey, TContext = unknown> = { /** * Return only whether the capability is allowed. */ can(input: EntitlementCheckInput<TKey>, options?: EntitlementCheckOptions<TContext>): Promise<boolean>; /** * Return the full allow/deny decision without throwing. */ inspect(input: EntitlementCheckInput<TKey>, options?: EntitlementCheckOptions<TContext>): Promise<EntitlementDecision>; }; /** * Function that decides whether an entitlement is granted. */ export type EntitlementResolver<TKey extends EntitlementKey = EntitlementKey> = (input: EntitlementCheckInput<TKey>) => MaybePromise<EntitlementResolverResult>; /** * Best-effort entitlement decision observation emitted by an entitlements port. * * Observers are diagnostic only. They do not participate in entitlement * control flow and thrown/rejected observer errors are ignored. */ export type EntitlementDecisionObservation<TContext = unknown, TKey extends EntitlementKey = EntitlementKey> = { /** * Port helper that produced the decision. */ source: EntitlementDecisionSource; /** * Application context passed by the caller, when available. */ ctx?: TContext; /** * Capability being evaluated. */ entitlement: TKey; /** * Subject whose product access is checked. */ subject: EntitlementSubject; /** * Normalized decision, when resolver evaluation returned normally. */ decision?: EntitlementDecision; /** * Error thrown by the resolver, when evaluation failed. */ error?: unknown; /** * Resolver duration, excluding observer work. */ durationMs: number; /** * Correlation fields copied from the context when present. */ requestId?: string; traceId?: string; spanId?: string; parentSpanId?: string; traceparent?: string; }; /** * Best-effort observer called after each entitlement decision or resolver * error. */ export type EntitlementDecisionObserver<TContext = unknown, TKey extends EntitlementKey = EntitlementKey> = (observation: EntitlementDecisionObservation<TContext, TKey>) => MaybePromise<void>; /** * Options for `createEntitlements(...)`. */ export type CreateEntitlementsOptions<TKey extends EntitlementKey = EntitlementKey, TContext = unknown> = { /** * Resolver that maps app-owned product state to an entitlement decision. */ inspect: EntitlementResolver<TKey>; /** * Optional best-effort observer for entitlement decisions. * * This hook is diagnostic only: it cannot change decisions or thrown errors. */ onDecision?: EntitlementDecisionObserver<TContext, TKey>; }; /** * Static grant map keyed by subject key. */ export type StaticEntitlementGrantMap<TKey extends EntitlementKey = EntitlementKey> = Record<string, readonly TKey[]>; /** * Options for `createStaticEntitlements(...)`. */ export type CreateStaticEntitlementsOptions<TKey extends EntitlementKey = EntitlementKey, TContext = unknown> = { /** * Granted capabilities keyed by subject. The default key is * `${subject.type}:${subject.id}`. */ grants: StaticEntitlementGrantMap<TKey>; /** * Optional subject key mapper. */ subjectKey?: (subject: EntitlementSubject) => string; /** * Default denial decision fields. */ denied?: Omit<EntitlementDeniedDecision, "allowed">; /** * Optional best-effort observer for entitlement decisions. */ onDecision?: EntitlementDecisionObserver<TContext, TKey>; }; /** * Options accepted by `requireEntitlement(...)`. */ export type RequireEntitlementOptions<TKey extends EntitlementKey = EntitlementKey> = { /** * Create the error to throw instead of the framework default. */ error?: (decision: EntitlementDeniedDecision, input: EntitlementCheckInput<TKey>) => unknown; }; /** * Context shape consumed by `requireEntitlement(...)`. */ export type EntitlementsContext<TKey extends EntitlementKey = EntitlementKey> = { ports: { entitlements: EntitlementsPort<TKey>; }; }; /** * Error thrown by `requireEntitlement(...)` when product access is denied. */ export declare class EntitlementRequiredError extends Error { readonly code: string; readonly status = 403; readonly details?: unknown; readonly entitlement: string; readonly subject: EntitlementSubject; constructor(input: EntitlementCheckInput, decision?: EntitlementDeniedDecision); } /** * Create an explicit allow decision. */ export declare function allowEntitlement(): EntitlementAllowedDecision; /** * Create an explicit deny decision. */ export declare function denyEntitlement(reasonOrDecision?: string | Omit<EntitlementDeniedDecision, "allowed">): EntitlementDeniedDecision; /** * Create an entitlement port from an app-owned resolver. */ export declare function createEntitlements<TKey extends EntitlementKey = EntitlementKey, TContext = unknown>(options: CreateEntitlementsOptions<TKey, TContext>): EntitlementsPort<TKey, TContext>; /** * Create a static entitlement port for tests, starters, and simple apps. */ export declare function createStaticEntitlements<TKey extends EntitlementKey = EntitlementKey, TContext = unknown>(options: CreateStaticEntitlementsOptions<TKey, TContext>): EntitlementsPort<TKey, TContext>; /** * Require an entitlement or throw a framework-owned 403 error. */ export declare function requireEntitlement<TKey extends EntitlementKey = EntitlementKey>(ctx: EntitlementsContext<TKey>, input: EntitlementCheckInput<TKey>, options?: RequireEntitlementOptions<TKey>): Promise<EntitlementAllowedDecision>; export {}; //# sourceMappingURL=index.d.ts.map