UNPKG

@furystack/repository

Version:

Repository implementation for FuryStack

88 lines 3.73 kB
import type { FindOptions, PhysicalStore, WithOptionalId } from '@furystack/core'; import type { Injector } from '@furystack/inject'; /** Discriminated union returned by authorizer callbacks. */ export type AuthorizationResult = SuccessfullyValidationResult | FailedValidationResult; export interface SuccessfullyValidationResult { isAllowed: true; } export interface FailedValidationResult { isAllowed: false; /** Surfaced as the message of the thrown {@link AuthorizationError}. */ message: string; } /** * Configuration for a {@link DataSet}: backing store + authorization + * modification hooks + filter post-processing. Every callback is optional — * an empty `DataSetSettings` performs no checks and forwards calls straight * to the physical store. * * Authorization callbacks come in two flavours: * * - `authorizeX` — runs **before** the entity is loaded. Use for cheap, * identity-only checks ("is the user logged in?", "is the user an admin?"). * When omitted, no pre-check is performed. * - `authorizeXEntity` — runs **after** the entity is loaded, with the * loaded entity in scope. Use for ownership / row-level checks. Missing * entities skip the per-entity check entirely. * * A callback returning `{ isAllowed: false }` makes the operation throw * {@link AuthorizationError}. */ export interface DataSetSettings<T, TPrimaryKey extends keyof T, TWritableData = WithOptionalId<T, TPrimaryKey>> { physicalStore: PhysicalStore<T, TPrimaryKey, TWritableData>; /** Pre-persist authorization for `add`. */ authorizeAdd?: (options: { injector: Injector; entity: TWritableData; }) => Promise<AuthorizationResult>; /** Pre-persist mutation for `add` (default fields, hashing, normalisation). */ modifyOnAdd?: (options: { injector: Injector; entity: TWritableData; }) => Promise<TWritableData>; /** Pre-load authorization for `update`. Sees the change, not the entity. */ authorizeUpdate?: (options: { injector: Injector; change: Partial<T>; }) => Promise<AuthorizationResult>; /** Post-load authorization for `update`. Sees both the loaded entity and the change. */ authorizeUpdateEntity?: (options: { injector: Injector; entity: T; change: Partial<T>; }) => Promise<AuthorizationResult>; /** Pre-persist mutation for `update`. */ modifyOnUpdate?: (options: { injector: Injector; id: T[keyof T]; entity: Partial<T>; }) => Promise<Partial<T>>; /** Pre-load authorization for `remove`. */ authorizeRemove?: (options: { injector: Injector; }) => Promise<AuthorizationResult>; /** Post-load authorization for `remove`, per loaded entity. */ authorizeRemoveEntity?: (options: { injector: Injector; entity: T; }) => Promise<AuthorizationResult>; /** Pre-load authorization for `get` / `find` / `count`. No entity in scope. */ authorizeGet?: (options: { injector: Injector; }) => Promise<AuthorizationResult>; /** Post-load authorization for `get`, per loaded entity. */ authorizeGetEntity?: (options: { injector: Injector; entity: T; }) => Promise<AuthorizationResult>; /** * Filter post-processor. Receives the caller's {@link FindOptions} and * returns a (potentially augmented) one. Use to inject mandatory clauses * (tenant scoping, soft-delete masks) the caller cannot bypass. */ addFilter?: <TFields extends Array<keyof T>>(options: { injector: Injector; filter: FindOptions<T, TFields>; }) => Promise<FindOptions<T, TFields>>; } //# sourceMappingURL=data-set-setting.d.ts.map