import { n as IamPrimitives, t as AccessControl } from "./access-control-CxeWQI64.js"; //#region src/core/types/dot-path.d.ts /** Dot-path type machinery: context-wide paths (`DotPaths`, `PathValue`, `DollarPaths`) and attribute-bag paths. */ declare namespace DotPath { /** * String-literal union of every reachable path through `T`; arrays are leaves; index-signatures bail to `never`. * * @template T - The object type to extract paths from. * @template Prefix - Internal accumulator for the current path prefix (do not set manually). * * @example * ```ts * type Ctx = { subject: { id: string; attributes: { status: string } } } * type Paths = DotPath.DotPaths * // = 'subject' | 'subject.id' | 'subject.attributes' | 'subject.attributes.status' * ``` */ export type DotPaths = string extends keyof T ? never : { [K in keyof T & string]: T[K] extends readonly any[] ? `${Prefix}${K}` : T[K] extends ((...args: any[]) => any) ? never : T[K] extends object ? `${Prefix}${K}` | DotPaths : `${Prefix}${K}` }[keyof T & string]; /** * Value type at a context-wide dot path within `T`; `never` if the path does not exist. * * @template T - The object type to resolve within. * @template P - A dot-separated path string (e.g. `'subject.attributes.status'`). * * @example * ```ts * type Ctx = { subject: { attributes: { status: 'active' | 'banned' } } } * type V = DotPath.PathValue * // = 'active' | 'banned' * ``` */ export type PathValue = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? PathValue : never : P extends keyof T ? T[P] : never; /** * Context-wide path: typed paths only when closed; accepts `string` when an open attribute bag is present. * * @template T - The context type to extract paths from. * * @example * ```ts * type ClosedCtx = { subject: { id: string } } * type Paths1 = DotPath.FlexibleDotPaths // = 'subject' | 'subject.id' * * type OpenCtx = { subject: { attributes: DotPath.IAnyAttributes } } * type Paths2 = DotPath.FlexibleDotPaths // accepts any string too * ``` */ export type FlexibleDotPaths = true extends HasOpenIndex ? DotPaths | (string & {}) : DotPaths; /** * `$`-prefixed context paths for cross-references; e.g. `'$subject.id'`. * * @template TContext - The full evaluation context type. * * @example * ```ts * type Ctx = { subject: { id: string; roles: string[] } } * type Refs = DotPath.DollarPaths * // = '$subject' | '$subject.id' | '$subject.roles' * ``` */ export type DollarPaths = `$${DotPaths}`; /** * Smart `$`-prefixed path. Preserves known-path autocomplete plus accepts * arbitrary `$`-strings. Must be used at the method-signature site (not * nested in computed types) so the IDE renders the literal suggestions. * * @template TContext - The full evaluation context type. */ export type FlexibleDollarPaths = DollarPaths | (string & {}); /** * Adapts an attribute value type for builder inputs while preserving `$` * references. Non-string values pass through unchanged; string-capable * values gain {@link DollarPaths} so a comparison can reference another * request field. * * @template TContext - The full evaluation context type. * @template TValue - The attribute-compatible value type accepted by the builder. */ export type ConditionValue = Exclude | (Extract extends never ? never : StringConditionValue>); /** * Value at a context dot-path; falls back to {@link IamPrimitives.AttributeValue} on mismatch. * * @template TContext - The full evaluation context type. * @template P - A dot-separated path string. */ export type FieldValue = PathValue extends IamPrimitives.AttributeValue ? ConditionValue> : ConditionValue; /** * Subject attribute-bag from a context; `never` when missing. * * @template TContext - The full evaluation context type. */ export type SubjectAttrShape = TContext extends { subject: { attributes: infer A; }; } ? A : never; /** * Resource attribute-bag from a context; `never` when missing. * * @template TContext - The full evaluation context type. */ export type ResourceAttrShape = TContext extends { resource: { attributes: infer A; }; } ? A : never; /** * Environment bag from a context; `never` when missing. * * @template TContext - The full evaluation context type. */ export type EnvAttrShape = TContext extends { environment: infer E; } ? E : never; /** * Dot-paths into subject attribute bag (used by `When.attr()`). * * @template TContext - The full evaluation context type. * * @example * ```ts * type Ctx = { subject: { attributes: { profile: { tier: string } } } } * type Keys = DotPath.SubjectAttrs // = 'profile' | 'profile.tier' * ``` */ export type SubjectAttrs = AttrPaths>; /** * Dot-paths into resource attribute bag (used by `When.resourceAttr()`); see {@link ResolvedResourceAttrPaths}. * * @template TContext - The full evaluation context type. * * @example * ```ts * type Ctx = { resource: { attributes: { ownerId: string; status: 'draft' | 'live' } } } * type Keys = DotPath.ResourceAttrs // = 'ownerId' | 'status' * ``` */ export type ResourceAttrs = AttrPaths>; /** * Dot-paths into environment (used by `When.env()`). * * @template TContext - The full evaluation context type. */ export type EnvAttrs = AttrPaths>; /** * Per-resource attribute map declared via `resourceAttributes`; `never` when absent. * * @template TContext - The full evaluation context type. */ export type ResourceAttrMap = TContext extends { resourceAttributes: infer M extends Record; } ? M : never; /** * Resource attribute shape narrowed to `TResource`; falls back to merged union for `'*'` / unknown. * * @template TContext - The full evaluation context type. * @template TResource - The resource type string (or `'*'` for all resources). */ export type ResolvedResourceAttrs = ResourceAttrMap extends never ? ResourceAttrShape : TResource extends keyof ResourceAttrMap ? ResourceAttrMap[TResource] : MergedResourceAttrs>; /** * Dot-paths into {@link ResolvedResourceAttrs}; typed `key` for `When.resourceAttr()`. * * @template TContext - The full evaluation context type. * @template TResource - The resource type string (or `'*'` for all resources). */ export type ResolvedResourceAttrPaths = AttrPaths>; /** * Value at a dot-path inside an attribute-bag; `never` on invalid path. * * @template T - The attribute-bag object type. * @template P - The dot-separated path string. */ export type AttrValueAt = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? AttrValueAt : never : P extends keyof T ? T[P] : never; /** * Constrained value lookup at `P` in attribute bag `T`; falls back to {@link IamPrimitives.AttributeValue}. * * @template T - The attribute-bag object type. * @template P - The dot-separated path string. * * @example * ```ts * type Bag = { profile: { tier: 'gold' | 'silver' } } * type V = DotPath.AttrValue // = 'gold' | 'silver' * ``` */ export type AttrValue = T extends Record ? AttrValueAt extends IamPrimitives.AttributeValue ? Exclude, undefined> : IamPrimitives.AttributeValue : IamPrimitives.AttributeValue; /** Marker for open-ended attribute bags; index signature returns {@link IamPrimitives.AttributeValue}. */ export interface IAnyAttributes { [key: string]: IamPrimitives.AttributeValue; } /** * Default evaluation context shape with open attribute bags. * * @example * ```ts * const ctx: DotPath.IDefaultContext = { * action: 'read', * subject: { id: 'u-1', roles: ['editor'], attributes: { tier: 'gold' } }, * resource: { type: 'post', id: 'p-42', attributes: { ownerId: 'u-1' } }, * environment: { hour: 14 }, * scope: 'org-acme', * } * ``` */ export interface IDefaultContext { /** The action being performed (e.g. `'read'`, `'update'`). */ action: string; /** The authenticated subject making the request. */ subject: { /** Unique subject identifier. */id: string; /** Flat list of effective role IDs. */ roles: string[]; /** Subject attribute bag (e.g. `{ department: 'engineering', status: 'active' }`). */ attributes: IAnyAttributes; }; /** The target resource being accessed. */ resource: { /** Resource type string (e.g. `'post'`, `'comment'`). */type: string; /** Optional resource instance ID. */ id?: string; /** Resource attribute bag (e.g. `{ ownerId: 'user-1', status: 'published' }`). */ attributes: IAnyAttributes; }; /** Environment attribute bag (e.g. `{ hour: 14, maintenanceMode: false }`). */ environment: IAnyAttributes; /** Authorization scope for multi-tenant applications (e.g. `'org-acme'`). */ scope: string; } /** * Dot-path string union into attribute-bag `T`; widens to `string` for open bags. * * @template T - The attribute-bag object type. */ type AttrPaths = T extends Record ? string extends keyof T ? string : { [K in keyof T & string]: IsPlainObject extends true ? K | `${K}.${AttrPaths}` : K }[keyof T & string] : never; /** * Detects whether `T` is a plain user-defined object (and therefore worth * recursing into for dot-paths). Arrays, functions, `Date`, `Map`, and * `Set` are treated as leaves. */ type IsPlainObject = T extends object ? T extends readonly unknown[] ? false : T extends ((...args: unknown[]) => unknown) ? false : T extends Date ? false : T extends Map ? false : T extends Set ? false : true : false; /** * Detects whether any branch of `T` contains a string index signature. * Used by {@link FlexibleDotPaths} to decide whether to add the * `(string & {})` fallback for loose path acceptance. */ type HasOpenIndex = string extends keyof T ? true : true extends { [K in keyof T & string]: T[K] extends object ? HasOpenIndex : false }[keyof T & string] ? true : false; /** * Keeps string-based condition inputs `$`-aware without widening narrow * string unions. If `TValue` is already `string`, only `$`-paths are added; * if `TValue` is a literal union, both the literals and `$`-paths are accepted. */ type StringConditionValue = string extends TValue ? DollarPaths : TValue | DollarPaths; /** * Collects every attribute key declared across the per-resource map values. * Internal helper for {@link MergedResourceAttrs}. */ type AllResourceKeys = M[keyof M] extends infer U ? U extends Record ? keyof U & string : never : never; /** * For a given attribute key, unions the value type from every resource that * declares it. Internal helper for {@link MergedResourceAttrs}. */ type ResourceKeyValue = { [R in keyof M]: K extends keyof M[R] ? M[R][K] : never }[keyof M]; /** * Merges every per-resource attribute object into a single shape so the * `'*'` wildcard case in {@link ResolvedResourceAttrs} accepts any * attribute defined on any resource. */ type MergedResourceAttrs = { [K in AllResourceKeys]: ResourceKeyValue }; export {}; } //#endregion //#region src/core/builder/when.d.ts /** * Fluent condition builder for duck-iam rules and role permissions. * * `When` accumulates a list of {@link AccessControl.ICondition} and nested {@link AccessControl.IConditionGroup} * items and then emits them as an `all` (AND), `any` (OR), or `none` (NOT) group * via the terminal build methods. It is used as the callback argument in * {@link RuleBuilder.when}, {@link RuleBuilder.whenAny}, and * {@link RoleBuilder.grantWhen}. * * @example * ```ts * // Inside a rule * defineRule('expense.approve') * .allow() * .on('approve').of('expense') * .when(w => w * .attr('department', 'eq', 'engineering') * .resourceAttr('amount', 'lte', 10_000) * ) * * // Nested OR inside an AND * defineRule('post.edit') * .allow() * .on('update').of('post') * .when(w => w * .or(o => o.isOwner().role('admin')) * .env('time', 'gte', 9) * ) * ``` * * @template TAction - Union of valid action strings * @template TResource - Union of valid resource strings * @template TRole - Union of valid role ID strings * @template TScope - Union of valid scope strings * @template TContext - Shape of the full evaluation context for typed dot-paths * @template TActiveResource - Resource narrowed by the parent `RuleBuilder.of()` (typed `resourceAttr`) */ declare class When { private _items; /** * Appends a raw {@link AccessControl.ICondition} to the builder with fully typed dot-path * field access. The `field` parameter is constrained to valid paths within * `TContext`, and `value` is inferred from the type at that path. * * @param field - Dot-path to the attribute being tested (e.g. `'subject.attributes.tier'`) * @param op - The {@link AccessControl.Operator} to apply * @param value - The right-hand side value (omit for `exists`) * @returns `this` for chaining * * @example * ```ts * w.check('subject.attributes.status', 'eq', 'banned') // OK * w.check('environment.hour', 'gte', 9) // OK * w.check('resource.attributes.status', 'eq', 'deleted') // ERROR if 'deleted' not in type * w.check('subject.attributes.age', 'eq', 30) // ERROR if 'age' not in type * ``` * @returns `this` for chaining. */ check

>(field: P, op: AccessControl.Operator, value?: DotPath.FieldValue | DotPath.FlexibleDollarPaths): this; /** * Asserts `field == value`. * * @param field - Typed dot-path attribute path * @param value - Expected value (inferred from path type) * @returns `this` for chaining */ eq

>(field: P, value: DotPath.FieldValue | DotPath.FlexibleDollarPaths): this; /** * Asserts `field != value`. * * @param field - Typed dot-path attribute path * @param value - Value the field must not equal * @returns `this` for chaining */ neq

>(field: P, value: DotPath.FieldValue | DotPath.FlexibleDollarPaths): this; /** * Asserts `field` is one of the given `values`. * * @param field - Typed dot-path attribute path * @param values - Array of acceptable values * @returns `this` for chaining */ in

>(field: P, values: Array | DotPath.FlexibleDollarPaths>): this; /** * Asserts that the array at `field` contains `value`. * * Commonly used to check role membership: * `w.contains('subject.roles', 'admin')`. * * @param field - Typed dot-path attribute path pointing to an array * @param value - The value that must be present in the array * @returns `this` for chaining */ contains

>(field: P, value: string): this; /** * Asserts that `field` exists (is defined and non-null). * * @param field - Typed dot-path attribute path to check for existence * @returns `this` for chaining */ exists

>(field: P): this; /** * Asserts `field > value`. * * @param field - Typed dot-path attribute path * @param value - Numeric lower bound (exclusive) * @returns `this` for chaining */ gt

>(field: P, value: number): this; /** * Asserts `field >= value`. * * @param field - Typed dot-path attribute path * @param value - Numeric lower bound (inclusive) * @returns `this` for chaining */ gte

>(field: P, value: number): this; /** * Asserts `field < value`. * * @param field - Typed dot-path attribute path * @param value - Numeric upper bound (exclusive) * @returns `this` for chaining */ lt

>(field: P, value: number): this; /** * Asserts `field <= value`. * * @param field - Typed dot-path attribute path * @param value - Numeric upper bound (inclusive) * @returns `this` for chaining */ lte

>(field: P, value: number): this; /** * Asserts that `field` matches the given regular expression string. * * @param field - Typed dot-path attribute path * @param regex - Regular expression pattern (as a string) * @returns `this` for chaining */ matches

>(field: P, regex: string): this; /** * Asserts the subject holds the given role. * * Equivalent to `w.contains('subject.roles', roleId)`. * * @param roleId - The role ID that must be present in `subject.roles` * @returns `this` for chaining */ role(roleId: TRole): this; /** * Asserts the subject holds at least one of the given roles. * * Equivalent to `w.check('subject.roles', 'in', roleIds)`. * * @param roleIds - Role IDs to test membership against * @returns `this` for chaining */ roles(...roleIds: TRole[]): this; /** * Asserts the request is made within a specific scope. * * Equivalent to `w.check('scope', 'eq', id)`. * * @param id - The scope the request must be in * @returns `this` for chaining */ scope(id: TScope): this; /** * Asserts the request is made within one of the given scopes. * * Equivalent to `w.check('scope', 'in', ids)`. * * @param ids - Acceptable scope IDs * @returns `this` for chaining */ scopes(...ids: TScope[]): this; /** * Asserts the subject is the owner of the resource. * * Checks that `ownerField` equals the special variable `'$subject.id'`, * which the engine resolves to the current subject's ID at evaluation time. * * @example * ```ts * // Using the default owner field * w.isOwner() * * // Custom owner field * w.isOwner('resource.attributes.createdBy') * ``` * * @param ownerField - Dot-path to the owner attribute on the resource. * Defaults to `'resource.attributes.ownerId'`. * @returns `this` for chaining */ isOwner(ownerField?: DotPath.FlexibleDotPaths): this; /** * Asserts the resource's type is one of the given values. * * Equivalent to `w.check('resource.type', 'in', types)`. * * @param types - Acceptable resource type strings * @returns `this` for chaining */ resourceType(...types: (TResource | '*')[]): this; /** * Asserts a subject attribute at the given path. * * Prefixes `path` with `'subject.attributes.'` automatically. * * @example * ```ts * w.attr('department', 'eq', 'engineering') * // evaluates: subject.attributes.department == 'engineering' * ``` * * @param path - Typed attribute key under `subject.attributes` * @param op - The {@link AccessControl.Operator} to apply * @param value - Right-hand side value (inferred from type) * @returns `this` for chaining */ attr & string>(path: K, op: AccessControl.Operator, value?: DotPath.ConditionValue, K>> | DotPath.FlexibleDollarPaths): this; /** * Asserts a resource attribute at the given path. * * Prefixes `path` with `'resource.attributes.'` automatically. * * @example * ```ts * w.resourceAttr('status', 'eq', 'published') * // evaluates: resource.attributes.status == 'published' * ``` * * @param path - Typed attribute key under `resource.attributes` * @param op - The {@link AccessControl.Operator} to apply * @param value - Right-hand side value (inferred from type) * @returns `this` for chaining */ resourceAttr & string>(path: K, op: AccessControl.Operator, value?: DotPath.ConditionValue, K>> | DotPath.FlexibleDollarPaths): this; /** * Asserts an environment attribute at the given path. * * Prefixes `path` with `'environment.'` automatically. Useful for * time-based or context-based conditions. * * @example * ```ts * w.env('hour', 'gte', 9).env('hour', 'lte', 17) * // evaluates: environment.hour >= 9 AND environment.hour <= 17 * ``` * * @param path - Typed attribute key under `environment` * @param op - The {@link AccessControl.Operator} to apply * @param value - Right-hand side value (inferred from type) * @returns `this` for chaining */ env & string>(path: K, op: AccessControl.Operator, value?: DotPath.ConditionValue, K>> | DotPath.FlexibleDollarPaths): this; /** * Appends a nested ALL-of (AND) condition group. * * Every condition added inside the callback must hold. The nested group is * treated as a single item within the outer builder's condition list. * * @example * ```ts * w.and(a => a.attr('tier', 'eq', 'premium').env('region', 'eq', 'us')) * ``` * * @param fn - Callback that receives a nested {@link When} and returns it * @returns `this` for chaining */ and(fn: (w: When) => When): this; /** * Appends a nested ANY-of (OR) condition group. * * At least one condition inside the callback must hold. * * @example * ```ts * w.or(o => o.isOwner().role('admin')) * // passes if subject is owner OR has the admin role * ``` * * @param fn - Callback that receives a nested {@link When} and returns it * @returns `this` for chaining */ or(fn: (w: When) => When): this; /** * Appends a nested NONE-of (NOT) condition group. * * None of the conditions inside the callback may hold. Equivalent to * negating an OR group. * * @example * ```ts * w.not(n => n.attr('status', 'eq', 'banned')) * // passes if subject.attributes.status is NOT 'banned' * ``` * * @param fn - Callback that receives a nested {@link When} and returns it * @returns `this` for chaining */ not(fn: (w: When) => When): this; /** * Emits the accumulated conditions as an ALL-of (`{ all: [...] }`) group. * * Every condition in the list must hold. This is the default used by * {@link RuleBuilder.when} and {@link RoleBuilder.grantWhen}. * * @returns A readonly `all` condition group */ buildAll(): { readonly all: ReadonlyArray; }; /** * Emits the accumulated conditions as an ANY-of (`{ any: [...] }`) group. * * At least one condition in the list must hold. Used by * {@link RuleBuilder.whenAny}. * * @returns A readonly `any` condition group */ buildAny(): { readonly any: ReadonlyArray; }; /** * Emits the accumulated conditions as a NONE-of (`{ none: [...] }`) group. * * None of the conditions in the list may hold. Produced by the {@link not} * nesting helper. * * @returns A readonly `none` condition group */ buildNone(): { readonly none: ReadonlyArray; }; } /** * Creates a standalone {@link When} condition builder. * * Useful when you need to construct a {@link AccessControl.IConditionGroup} outside of a * rule or role builder - for example, to build a reusable condition and * spread it across multiple rules. * * @example * ```ts * import { when } from '@gentleduck/iam' * * const ownerOrAdmin = when() * .or(o => o.isOwner().role('admin')) * .buildAll() * ``` * * @returns A new {@link When} instance * * @template TAction - Union of valid action strings * @template TResource - Union of valid resource strings * @template TRole - Union of valid role ID strings * @template TScope - Union of valid scope strings * @template TContext - Shape of the full evaluation context for typed dot-paths * @template TActiveResource - Resource narrowed by the parent `RuleBuilder.of()` (typed `resourceAttr`) */ declare const when: () => When; //#endregion //#region src/core/builder/rule.d.ts /** * Fluent builder for constructing {@link AccessControl.IRule} objects in duck-iam. * * Rules are the atomic unit of an ABAC policy. Each rule declares an effect * (`allow` or `deny`), the actions and resources it covers, an optional scope * restriction, and an optional condition tree that must hold for the rule to * fire. * * Rules are collected into a {@link PolicyBuilder} and evaluated by the engine * using the policy's chosen conflict-resolution algorithm * (`allow-overrides`, `deny-overrides`, `first-match`, or `highest-priority`). * * @example * ```ts * import { defineRule } from '@gentleduck/iam' * * const rule = defineRule('post.update.owner') * .allow() * .desc('Authors may update their own posts') * .priority(20) * .on('update') * .of('post') * .when(w => w.isOwner()) * .build() * ``` * * @template TAction - Union of valid action strings (e.g. `'read' | 'write'`) * @template TResource - Union of valid resource strings (e.g. `'post' | 'comment'`) * @template TScope - Union of valid scope strings (e.g. `'org-1' | 'org-2'`) * @template TRole - Union of valid role ID strings (e.g. `'viewer' | 'admin'`) * @template TContext - Shape of the full evaluation context for typed dot-paths * @template TActiveResource - The narrowed resource selected via `.of()` (used by typed `resourceAttr`) */ declare class RuleBuilder { private _id; private _effect; private _description?; private _priority; private _actions; private _resources; private _conditions; private _metadata?; private _scopeCondition?; constructor(id: string); /** * Sets the rule effect to `allow`. * * This is the default effect - you only need to call this explicitly when * overriding a previous `.deny()` call on the same builder instance. * * @returns `this` for chaining */ allow(): this; /** * Sets the rule effect to `deny`. * * Deny rules take precedence over allow rules when the policy algorithm is * `deny-overrides`. Under `allow-overrides` a deny only wins if no allow * rule matches. * * @returns `this` for chaining */ deny(): this; /** * Attaches a human-readable description to the rule. * * Descriptions are stored on the {@link AccessControl.IRule} object and surfaced by the * engine's explain/debug output. They have no effect on evaluation. * * @param d - Description text * @returns `this` for chaining */ desc(d: string): this; /** * Sets the rule's evaluation priority. * * Higher numbers are evaluated first. The default priority is `10`. * Priority matters when the policy algorithm is `highest-priority` - the * matching rule with the highest priority number wins. * * @param p - Priority value (higher = evaluated earlier) * @returns `this` for chaining */ priority(p: number): this; /** * Declares the actions this rule applies to. * * Pass `'*'` to match all actions. Accepts multiple arguments. * * @example * ```ts * defineRule('post.read-write') * .on('read', 'update') * .of('post') * ``` * * @param actions - One or more action strings, or `'*'` for all actions * @returns `this` for chaining */ on(...actions: (TAction | '*')[]): this; /** * Declares the resources this rule applies to. * * Pass `'*'` to match all resources. Accepts multiple arguments. * * @example * ```ts * defineRule('content.read') * .on('read') * .of('post', 'comment') * ``` * * @param resources - One or more resource strings, or `'*'` for all resources * @returns `this` for chaining */ of(...resources: R[]): RuleBuilder; /** * Restricts this rule to one or more scopes. * * A scope typically represents a tenant, organization, or workspace. * When a scope is set, the engine only fires the rule when the request's * scope matches. Passing `'*'` is a no-op - use no scope restriction for * global rules instead. * * Scope conditions compose correctly with `.when()` and `.whenAny()`. * * @example * ```ts * defineRule('org1.post.update') * .allow() * .on('update') * .of('post') * .forScope('org-1') * ``` * * @param scopes - One or more scope strings to restrict this rule to * @returns `this` for chaining */ forScope(...scopes: (TScope | '*')[]): this; /** * Attaches an ALL-of condition group to the rule using a {@link When} builder. * * Every condition added inside the callback must hold (`AND` semantics) for * the rule to match. Composes with `.forScope()` - the scope check is * prepended to the condition list automatically at build time. * * @example * ```ts * defineRule('expense.approve') * .allow() * .on('approve') * .of('expense') * .when(w => w * .attr('department', 'eq', 'engineering') * .resourceAttr('amount', 'lte', 10000) * ) * ``` * * @param fn - Callback that receives a {@link When} builder and returns it after chaining conditions * @returns `this` for chaining */ when(fn: (w: When) => When): this; /** * Attaches an ANY-of condition group to the rule using a {@link When} builder. * * At least one condition added inside the callback must hold (`OR` semantics) * for the rule to match. * * @example * ```ts * defineRule('post.manage') * .allow() * .on('update', 'delete') * .of('post') * .whenAny(w => w * .isOwner() * .attr('role', 'eq', 'admin') * ) * ``` * * @param fn - Callback that receives a {@link When} builder and returns it after chaining conditions * @returns `this` for chaining */ whenAny(fn: (w: When) => When): this; /** * Attaches arbitrary metadata to the rule. * * Metadata is stored on the {@link AccessControl.IRule} object but is never used during * policy evaluation. Use it for audit logs, admin dashboards, or any * application-level bookkeeping. * * @param m - Key-value map of metadata attributes * @returns `this` for chaining */ meta(m: IamPrimitives.Attributes): this; /** * Finalises the builder and returns a plain {@link AccessControl.IRule} object. * * Any scope condition set via `.forScope()` is merged into the condition * group here so that `.forScope()` and `.when()` / `.whenAny()` always * compose correctly regardless of call order. * * @returns A fully constructed, immutable {@link AccessControl.IRule} */ build(): AccessControl.IRule; } /** * Creates a new {@link RuleBuilder} for the given rule ID. * * Prefer this factory over instantiating `RuleBuilder` directly. When using * `createIam`, use `access.defineRule()` instead to get type-safe * action, resource, and scope constraints. * * @example * ```ts * import { defineRule } from '@gentleduck/iam' * * const rule = defineRule('post.read') * .allow() * .on('read') * .of('post') * .build() * ``` * * @param id - Unique identifier for this rule within its policy * @returns A new {@link RuleBuilder} instance * * @template TAction - Union of valid action strings * @template TResource - Union of valid resource strings * @template TScope - Union of valid scope strings * @template TRole - Union of valid role ID strings * @template TContext - Shape of the full evaluation context for typed dot-paths */ declare const defineRule: (id: string) => RuleBuilder; //#endregion //#region src/core/builder/policy.d.ts /** * Fluent builder for constructing ABAC {@link AccessControl.IPolicy} objects. * * Policies define attribute-based access control rules that go beyond simple * role-permission mappings. Use them for time-based restrictions, IP/geo-fencing, * cross-attribute checks, dynamic deny rules, and maintenance-mode guards. * * The combining algorithm chosen via `.algorithm(...)` controls *intra*-policy * rule conflicts. Decisions across multiple policies are merged by the engine's * configured `policyCombine` (defaults to `'and'`; see {@link AccessControl.PolicyCombine}). * * @template TAction - Union of valid action strings. * @template TResource - Union of valid resource strings. * @template TRole - Union of valid role strings. * @template TScope - Union of valid scope strings. * @template TContext - Shape of the full evaluation context for typed dot-paths. * * @example * ```typescript * import { definePolicy } from '@gentleduck/iam' * * const weekendDeny = definePolicy('deny-weekends') * .name('Deny on Weekends') * .desc('Block all write operations on weekends') * .version(1) * .algorithm('deny-overrides') * .rule('r-deny-weekends', r => r * .deny() * .on('create', 'update', 'delete') * .of('*') * .when(w => w.env('dayOfWeek', 'in', [0, 6])) * ) * .build() * ``` */ declare class PolicyBuilder { private _id; private _name; private _description?; private _algorithm; private _rules; private _targets?; private _version?; constructor(id: string); /** * Sets a human-readable name for the policy. * * Defaults to the policy `id` if not called. * * @param n - Display name. * @returns `this` for chaining. */ name(n: string): this; /** * Sets an optional description for the policy. * * @param d - Description text. * @returns `this` for chaining. */ desc(d: string): this; /** * Sets a version number for tracking policy changes over time. * * @param v - Version number. * @returns `this` for chaining. */ version(v: number): this; /** * Sets the combining algorithm used to resolve conflicts between rules * within this policy. * * | Algorithm | Behavior | * |---|---| * | `deny-overrides` | Any deny wins. Default. Best for restriction policies. | * | `allow-overrides` | Any allow wins. Best for RBAC / permissive rules. | * | `first-match` | First matching rule wins. Best for firewall-style ordered lists. | * | `highest-priority` | Highest priority number wins. Best for emergency overrides. | * * Defaults to `'deny-overrides'`. * * @param a - Combining algorithm. * @returns `this` for chaining. */ algorithm(a: AccessControl.CombiningAlgorithm): this; /** * Scopes this policy to specific actions, resources, or roles. * * If an incoming request does not match all specified targets, the policy is * skipped entirely - its rules are not evaluated. This is useful for * restriction policies that only apply to a subset of operations. * * @param t - Target constraints to match against. * * @example * ```typescript * definePolicy('write-restrictions') * .target({ * actions: ['create', 'update', 'delete'], * resources: ['post', 'comment'], * }) * ``` * @returns `this` for chaining. */ target(t: NonNullable['targets']>): this; /** * Adds a rule to the policy using an inline {@link RuleBuilder} callback. * * Rules are the individual allow/deny statements inside a policy. Each rule * specifies an effect, the actions and resources it applies to, an optional * priority, and attribute-based conditions. * * @param id - Unique identifier for the rule within this policy. * @param fn - Builder callback that configures and returns the rule. * * @example * ```typescript * definePolicy('ip-guard') * .rule('block-bad-ips', r => r * .deny() * .on('*') * .of('*') * .when(w => w.env('ip', 'in', ['10.0.0.99', '10.0.0.100'])) * ) * ``` * @returns `this` for chaining. */ rule(id: string, fn: (r: RuleBuilder) => RuleBuilder): this; /** * Adds a pre-built {@link AccessControl.IRule} object directly to the policy. * * Use this when you have rules defined separately via `defineRule` and want * to compose them into a policy without the inline callback form. * * @param rule - A fully constructed `Rule` object. * * @example * ```typescript * import { defineRule } from '@gentleduck/iam' * * const denyDrafts = defineRule('deny-drafts') * .deny() * .on('read') * .of('post') * .when(w => w.resourceAttr('status', 'eq', 'draft')) * .build() * * definePolicy('post-access').addRule(denyDrafts) * ``` * @returns `this` for chaining. */ addRule(rule: AccessControl.IRule): this; /** * Produces the final {@link AccessControl.IPolicy} object. * * Call this after all builder methods have been chained. The resulting object * can be passed to an adapter or registered with the engine directly. * * @returns The constructed `Policy`. */ build(): AccessControl.IPolicy; } /** * Creates a new {@link PolicyBuilder} for the given policy ID. * * This is the primary entry point for defining ABAC policies. Prefer this * factory over constructing `PolicyBuilder` directly. When using * `createIam`, use `access.definePolicy()` instead to get type-safe * action, resource, and role constraints. * * @template TAction - Union of valid action strings. * @template TResource - Union of valid resource strings. * @template TRole - Union of valid role strings. * @template TScope - Union of valid scope strings. * @template TContext - Shape of the full evaluation context for typed dot-paths. * * @param id - Unique identifier for the policy. Also used as the default name. * @returns A new `PolicyBuilder` instance. * * @example * ```typescript * import { definePolicy } from '@gentleduck/iam' * * const maintenanceMode = definePolicy('maintenance-mode') * .name('Maintenance Mode') * .desc('Deny all writes when the maintenance flag is active') * .algorithm('deny-overrides') * .rule('deny-writes', r => r * .deny() * .on('create', 'update', 'delete') * .of('*') * .when(w => w.env('maintenanceMode', 'eq', true)) * ) * .build() * ``` */ declare const definePolicy: (id: string) => PolicyBuilder; //#endregion //#region src/core/builder/role.d.ts /** * Fluent builder for constructing {@link AccessControl.IRole} objects in duck-iam. * * Roles are the RBAC side of duck-iam. Each role holds a set of * action/resource permissions and an optional inheritance chain. At evaluation * time, `rolesToPolicy()` converts every role into ABAC rules that flow through * the same engine as hand-written policies, so RBAC and ABAC compose. * * Prefer the {@link defineRole} factory (or `access.defineRole()` for type-safe * variants) over instantiating `RoleBuilder` directly. * * @example * ```ts * import { defineRole } from '@gentleduck/iam' * * const editor = defineRole('editor') * .name('Editor') * .desc('Full write access to posts and comments') * .inherits('viewer') * .grant('create', 'post') * .grant('update', 'post') * .grant('delete', 'post') * .grantCRUD('comment') * .build() * ``` * * @template TAction - Union of valid action strings (e.g. `'read' | 'write'`) * @template TResource - Union of valid resource strings (e.g. `'post' | 'comment'`) * @template TRole - Literal string type of the role ID (inferred by {@link defineRole}) * @template TScope - Union of valid scope strings (e.g. `'org-1' | 'org-2'`) * @template TContext - Shape of the full evaluation context for typed dot-paths */ declare class RoleBuilder { private _id; private _name; private _description?; private _permissions; private _inherits; private _scope?; private _metadata?; constructor(id: TRole); /** * Sets a human-readable display name for the role. * * Defaults to the role ID if not called. Used in admin dashboards, * audit logs, and the engine's explain output. * * @param n - Display name (e.g. `'Content Editor'`) * @returns `this` for chaining */ name(n: string): this; /** * Attaches a human-readable description to the role. * * Stored on the {@link AccessControl.IRole} object for documentation purposes. * Not used during policy evaluation. * * @param d - Description text * @returns `this` for chaining */ desc(d: string): this; /** * Declares parent roles this role inherits from. * * The role receives all permissions from every listed parent, resolved * recursively. Multiple parents are supported. Inheritance cycles are * handled safely via a visited set - cycles are skipped rather than * causing infinite recursion. * * Note: inherited permissions cannot be selectively removed. To restrict * access below what a parent grants, use an ABAC deny policy instead. * * @example * ```ts * // Single parent * defineRole('editor').inherits('viewer') * * // Multiple parents * defineRole('moderator').inherits('viewer', 'commenter') * ``` * * @param roleIds - IDs of the parent roles to inherit from * @returns `this` for chaining */ inherits(...roleIds: (TRole | (string & {}))[]): this; /** * Sets a default scope that applies to every permission in this role. * * When `rolesToPolicy()` converts this role, each generated rule gets an * additional condition `scope eq ""`. The permission only fires when the * request's scope matches. * * To scope individual permissions rather than the entire role, use * {@link grantScoped} instead. * * @example * ```ts * const orgEditor = defineRole('org-editor') * .scope('org-1') * .grant('create', 'post') * .grant('update', 'post') * .build() * ``` * * @param s - The scope string to restrict all permissions to * @returns `this` for chaining */ scope(s: TScope): this; /** * Grants a single unconditional permission on an action/resource pair. * * Pass `'*'` for either argument to match all actions or all resources. * Pass an optional `scope` to restrict this permission to a specific scope * (e.g. a tenant or workspace). Without a scope the permission is global. * * @example * ```ts * defineRole('viewer') * .grant('read', 'post') * .grant('read', 'comment') * * // With permission-level scope * defineRole('hybrid') * .grant('read', 'post') // global * .grant('update', 'post', 'org-1') // org-1 only * .grant('create', 'comment', 'org-2') // org-2 only * ``` * * @param action - The action to permit, or `'*'` for all actions * @param resource - The resource to permit, or `'*'` for all resources * @param scope - Optional scope to restrict this permission to * @returns `this` for chaining */ grant(action: TAction | '*', resource: TResource | '*', scope?: TScope): this; /** * Grants a single permission restricted to a specific scope. * * Unlike {@link scope}, which scopes the entire role, `grantScoped` lets * you mix global and scoped permissions within the same role. * * @example * ```ts * defineRole('hybrid') * .grant('read', 'post') // global * .grantScoped('org-1', 'update', 'post') // org-1 only * .grantScoped('org-2', 'create', 'comment') // org-2 only * ``` * * @param scope - The scope this permission is restricted to * @param action - The action to permit, or `'*'` for all actions * @param resource - The resource to permit, or `'*'` for all resources * @returns `this` for chaining */ grantScoped(scope: TScope, action: TAction | '*', resource: TResource | '*'): this; /** * Grants a permission that only applies when a condition holds. * * The callback receives a {@link When} builder. All conditions added inside * the callback must hold simultaneously (`AND` semantics). Use * `w.isOwner()` as a shorthand for checking `resource.attributes.ownerId eq $subject.id`. * * @example * ```ts * defineRole('author') * .grant('read', 'post') * .grantWhen('update', 'post', w => w.isOwner()) * .grantWhen('delete', 'post', w => w.isOwner()) * * // Complex condition * defineRole('team-lead') * .grantWhen('approve', 'expense', w => w * .attr('department', 'eq', 'engineering') * .resourceAttr('amount', 'lte', 10000) * ) * ``` * * @param action - The action to permit conditionally * @param resource - The resource to permit conditionally * @param fn - Callback that builds the condition using a {@link When} builder * @returns `this` for chaining */ grantWhen(action: TAction | '*', resource: R, fn: (w: When) => When): this; /** * Grants all actions (`'*'`) on a resource. * * Use `grantAll('*')` to grant unrestricted access to everything (typical * for a super-admin role). For a more explicit alternative that only covers * standard CRUD, see {@link grantCRUD}. * * @example * ```ts * defineRole('super-admin').grantAll('*') // all actions, all resources * defineRole('post-admin').grantAll('post') // all actions on posts only * ``` * * @param resource - The resource to grant all actions on, or `'*'` for all resources * @returns `this` for chaining */ grantAll(resource: TResource | '*'): this; /** * Grants `read` access to one or more resources. * * Accepts multiple resource arguments. Equivalent to calling * `.grant('read', resource)` for each. * * @example * ```ts * defineRole('auditor') * .grantRead('post', 'comment', 'user', 'audit-log') * ``` * * @param resources - One or more resource strings to grant read access on * @returns `this` for chaining */ grantRead(...resources: (TResource | '*')[]): this; /** * Grants `create`, `read`, `update`, and `delete` on a resource. * * More explicit than {@link grantAll} - does not include custom actions * like `publish` or `archive`. Equivalent to four separate `.grant()` calls. * * @example * ```ts * defineRole('content-manager') * .grantCRUD('post') * .grantCRUD('comment') * ``` * * @param resource - The resource to grant CRUD access on * @returns `this` for chaining */ grantCRUD(resource: TResource | '*'): this; /** * Attaches arbitrary metadata to the role. * * Metadata is stored on the {@link AccessControl.IRole} object but is never consulted * during policy evaluation. Use it for admin dashboards, audit logs, * UI labels, or any other application-level bookkeeping. * * @example * ```ts * defineRole('beta-tester') * .meta({ createdBy: 'system', tier: 'beta', maxSeats: 10 }) * .grant('read', 'beta-feature') * ``` * * @param m - Key-value map of metadata attributes * @returns `this` for chaining */ meta(m: IamPrimitives.Attributes): this; /** * Finalises the builder and returns a plain {@link AccessControl.IRole} object. * * The returned object is a plain data record with no builder methods. * Pass it to `engine.admin.saveRole()` or `access.()`. * * @returns A fully constructed {@link AccessControl.IRole} */ build(): AccessControl.IRole; } /** * Creates a new {@link RoleBuilder} for the given role ID. * * The role ID is preserved as a literal type (`TId`) so that references to * it in `.inherits()` calls and adapter lookups remain type-safe when using * `createIam`. * * For type-safe action, resource, and scope constraints, use * `access.defineRole()` returned by `createIam()` instead. * * @example * ```ts * import { defineRole } from '@gentleduck/iam' * * const viewer = defineRole('viewer') * .name('Viewer') * .desc('Read-only access to published content') * .grant('read', 'post') * .grant('read', 'comment') * .build() * ``` * * @param id - Unique identifier for this role * @returns A new {@link RoleBuilder} instance typed to the given ID * * @template TId - Inferred literal type of the role ID * @template TAction - Union of valid action strings (defaults to `string`) * @template TResource - Union of valid resource strings (defaults to `string`) * @template TScope - Union of valid scope strings (defaults to `string`) * @template TContext - Shape of the full evaluation context for typed dot-paths */ declare const defineRole: (id: TRole) => RoleBuilder; //#endregion export { RuleBuilder as a, when as c, definePolicy as i, DotPath as l, defineRole as n, defineRule as o, PolicyBuilder as r, When as s, RoleBuilder as t }; //# sourceMappingURL=index-CU--1O9m.d.ts.map