/** * Public types and constructors for the Node-Level Security (NLS) policy * layer introduced in v1.7.0. Policies are vocabulary-neutral building * blocks (`override`, `permissive`, `restrictive`); user codebases compose * role-aware sugar on top. */ /** * Generic policy context. Users extend this with their own typed shape * (e.g. `{ userId: string; capabilities: string[] }`). The OGM never * inspects ctx beyond passing it to policy callbacks. */ export type PolicyContext = Record; /** * Operations a policy can target. A query maps to exactly one operation. * The wildcard `'*'` matches every operation. */ export type Operation = 'read' | 'create' | 'update' | 'delete' | 'aggregate' | 'count'; export type OperationOrWildcard = Operation | '*'; /** * Override — compile-time short-circuit. If `when(ctx)` returns true and * the operation matches, ALL other policies for this (type, operation) * are dropped. The compiled Cypher is byte-identical to a no-policy * query. Cannot reference node properties — only ctx. */ export interface OverridePolicy { readonly kind: 'override'; readonly operations: ReadonlyArray; readonly when: (ctx: C) => boolean; /** Optional debug name surfaced in audit metadata + logging. */ readonly name?: string; } /** * Permissive — two-stage OR-grant. `appliesWhen(ctx)` is compile-time; * if false, the policy is dropped from this query (NOT compiled as * `false`). `when(ctx)` returns a `Where` partial that compiles * to a row predicate. Multiple permissives OR together — any match * grants access. The `cypher` escape hatch is for power users who * need raw fragments (with parameterized values). */ export interface PermissivePolicy = Record> { readonly kind: 'permissive'; readonly operations: ReadonlyArray; readonly appliesWhen?: (ctx: C) => boolean; readonly when?: (ctx: C) => W; readonly cypher?: { fragment: (ctx: C, alias: { node: string; }) => string; params: (ctx: C) => Record; }; readonly name?: string; } /** * Read-side operations a `ReadRestrictivePolicy` may target. These are * row-filter operations that compile to a `WHERE` clause; they have no * mutation input to validate. */ export type ReadOperation = 'read' | 'delete' | 'aggregate' | 'count'; /** * Write-side operations a `WriteRestrictivePolicy` may target. These run * at the application layer ("WITH CHECK" semantics) and inspect the * input bag the user is creating/updating. * * Note: `update` is a write-side op for restrictives because the * restrictive's purpose is to validate the new values. The WHERE-side * row filter for `update` queries is enforced via `ReadRestrictive` * policies registered on `'read'` (or via permissive policies). */ export type WriteOperation = 'create' | 'update'; /** * Read-side restrictive — AND-row predicate compiled into the `WHERE` * clause. `when(ctx)` returns a `Where` partial OR `false` for * a hard deny. Cannot reference mutation input (there is none on a * read). */ export interface ReadRestrictivePolicy = Record> { readonly kind: 'restrictive'; readonly operations: ReadonlyArray; /** Optional compile-time gate. Like `appliesWhen` on permissive. */ readonly appliesWhen?: (ctx: C) => boolean; readonly when?: (ctx: C) => W | boolean; readonly cypher?: { fragment: (ctx: C, alias: { node: string; }) => string; params: (ctx: C) => Record; }; readonly name?: string; } /** * Write-side restrictive — application-layer "WITH CHECK" predicate. * `when(ctx, input)` runs once per write op (create/update) with the * exact input bag the user submitted. Returning `false` rejects the * operation with `PolicyDeniedError`. The `cypher` escape hatch is NOT * supported for write restrictives because there is no compiled WHERE * clause to AND-stitch into. * * `when` MUST return a boolean; returning a where-partial would * conflate WITH CHECK semantics with row-filter semantics. Use a * `ReadRestrictive` if you need a row filter on update/delete query * targets. */ export interface WriteRestrictivePolicy = Record> { readonly kind: 'restrictive'; readonly operations: ReadonlyArray; /** Optional compile-time gate. Skips evaluation entirely when false. */ readonly appliesWhen?: (ctx: C) => boolean; /** * "WITH CHECK" predicate over the write input. The operation proceeds * ONLY on an explicit `true` return — any other value (including * `undefined`/`null` from expressions like `ctx.canWrite && * input.tenantId === ctx.tenantId` with an anonymous ctx) rejects with * `PolicyDeniedError`. (v1.8.7 — previously only `false` rejected.) */ readonly when: (ctx: C, input: I) => boolean; readonly name?: string; } /** * Discriminated union over the two restrictive flavors. The `operations` * array is the discriminant: read-side ops (`read|delete|aggregate|count`) * select `ReadRestrictivePolicy`; write-side ops (`create|update`) select * `WriteRestrictivePolicy`. Mixed-operation arrays are rejected at * construction time — split them into two separate restrictives. * * The `restrictive()` constructor enforces the discriminant with * function overloads, so authoring code gets the correct `when` * signature inferred from the literal `operations` tuple. */ export type RestrictivePolicy = Record, I extends Record = Record> = ReadRestrictivePolicy | WriteRestrictivePolicy; export type Policy = OverridePolicy | PermissivePolicy | RestrictivePolicy; /** * Map of typeName → policies. Validated against the schema at OGM init. * * The optional `M` parameter pulls per-model `Where`/`CreateInput`/ * `UpdateInput` shapes from a generated `ModelMap` so the * `permissive`/`restrictive` callbacks are typed against the user's * schema rather than `Record`. Falls back to the * generic shape when no model map is provided (purely additive). */ export type PoliciesByModel = Record, C extends PolicyContext = PolicyContext> = { [K in keyof M & string]?: ReadonlyArray>; } & { [typeName: string]: ReadonlyArray> | undefined; }; export interface PolicyDefaults { /** What to do when no permissive matches. Default `'empty'`. */ onDeny?: 'empty' | 'throw'; /** Inject audit metadata into tx? Default `true` when policies are set. */ auditMetadata?: boolean; } export declare function override(spec: Omit, 'kind'>): OverridePolicy; export declare function permissive = Record>(spec: Omit, 'kind'>): PermissivePolicy; export declare function restrictive = Record>(spec: Omit, 'kind'>): WriteRestrictivePolicy; export declare function restrictive = Record>(spec: Omit, 'kind'>): ReadRestrictivePolicy; export declare function isReadRestrictive(p: RestrictivePolicy): p is ReadRestrictivePolicy; export declare function isWriteRestrictive(p: RestrictivePolicy): p is WriteRestrictivePolicy; /** * Resolved policy set for a single (typeName, operation) pair after * override short-circuit and `appliesWhen` filtering. Consumed by the * compilers to AND-stitch into the WHERE clause. */ export interface ResolvedPolicies { /** True → emit nothing; query is byte-identical to a no-policy query. */ overridden: boolean; permissives: ReadonlyArray>; restrictives: ReadonlyArray>; /** Names of policies that fired (for audit logging). */ evaluated: ReadonlyArray; } /** * Resolution used for a ROOT type that has no policy for the operation, * when a policy context is bound anyway (v2.3.0). Binding it — instead of * dropping the whole bundle — keeps `resolveForType` reachable, so the * policies of every TARGET type reached through relationships (nested * selection, traversal filters, nested writes) stay enforced; a type * without policies must not be a gateway around its neighbours'. * * `overridden: true` carries exactly its documented operational meaning * here ("emit no root clause"): every root-level consumer treats it like * the absence of a bundle, and no target-policy consumer reads it. * * Compare by identity (`resolved === NO_ROOT_POLICY`) to ask "did the * root resolve anything for this operation?" — e.g. `aggregate`'s * fallback to `read` policies. A real override resolution is a different * object and must NOT be mistaken for it. */ export declare const NO_ROOT_POLICY: ResolvedPolicies; /** * One operation-matching policy as seen by * `PolicyResolver.resolveDetailed()`. Unlike `ResolvedPolicies`, nothing * is dropped: policies gated off by `appliesWhen` and policies never * evaluated because an override fired are reported too. Consumed by the * explain path (`Model.explainPolicies`). */ export interface DetailedPolicy { readonly policy: Policy; readonly kind: Policy['kind']; /** Type or interface whose registry entry declared this policy. */ readonly source: string; /** Position in `source`'s registration list (all operations counted). */ readonly index: number; /** `policy.name`, or `.[]` when unnamed. */ readonly name: string; /** False when `name` is the synthesized fallback identifier. */ readonly named: boolean; /** * `appliesWhen(ctx)` held (absent counts as held). For an override: * its `when(ctx)` returned true. Always false when `skipped`. */ readonly applied: boolean; /** Not evaluated at all because an earlier override fired. */ readonly skipped: boolean; } /** * Outcome of one policy for one candidate node, as reported by * `Model.explainPolicies()`: * * - `pass` — applied; its predicate evaluated to `true`. * - `fail` — applied; evaluated to `false` (includes a restrictive hard * deny, `when: () => false`). * - `null` — applied; evaluated to NULL (three-valued logic, e.g. a * missing property). Enforcement treats it as not-true. * - `abstain` — applied, but emitted no predicate. A permissive abstain * grants nothing; a restrictive abstain restricts nothing. * - `not-applied` — `appliesWhen(ctx)` was false (override: `when(ctx)` * was false). Not compiled or evaluated. * - `skipped` — not evaluated because an earlier override fired. */ export type PolicyClauseOutcome = 'pass' | 'fail' | 'null' | 'abstain' | 'not-applied' | 'skipped'; /** One policy's report within a `PolicyExplanation`. */ export interface PolicyClauseExplanation { /** `policy.name`, or `.[]` when unnamed. */ readonly name: string; /** False when `name` is the synthesized fallback identifier. */ readonly named: boolean; readonly kind: 'override' | 'permissive' | 'restrictive'; /** Type or interface whose registry entry declared the policy. */ readonly source: string; /** `appliesWhen(ctx)` held (override: `when(ctx)` was true). */ readonly applied: boolean; readonly outcome: PolicyClauseOutcome; } /** * Per-candidate result of `Model.explainPolicies()`. `visible` is the * exact verdict `find` enforces for the same bound context; the other * fields explain it. */ export interface PolicyExplanation> { /** The candidate, projected with the requested selection. */ readonly node: T; /** Would `find` return this node for the bound context? */ readonly visible: boolean; /** Name of the override that fired, else `null`. */ readonly overriddenBy: string | null; /** * Some permissive granted access (`true` when an override fired or the * type has no policies). `false` → default deny. */ readonly permissiveGranted: boolean; /** Names of applied restrictives whose outcome is `fail` or `null`. */ readonly failedRestrictives: ReadonlyArray; /** Every `read` policy for the type, in registration order. */ readonly policies: ReadonlyArray; } /** * Full resolution for one (typeName, operation, ctx) triple. Entries are * in registration order — the type's own policies first, then each * implemented interface's. */ export interface DetailedResolution { /** Fallback-aware name of the override that fired, else `null`. */ readonly overriddenBy: string | null; readonly entries: ReadonlyArray>; } /** * Carries policy state through one compile pass. Created per query in * `Model` / `InterfaceModel` and threaded into `WhereCompiler` / * `SelectionCompiler`. */ export interface PolicyContextBundle { ctx: C; resolved: ResolvedPolicies; operation: Operation; /** * Resolver callback: given a target type's name and an operation, * return its `ResolvedPolicies` for use during nested-selection * enforcement. Returns `null` when no policies apply (no policy * registered for that type). */ resolveForType: (typeName: string, op: Operation) => ResolvedPolicies | null; /** Defaults snapshot — read by compilers for `onDeny`. */ defaults: PolicyDefaults; } //# sourceMappingURL=types.d.ts.map