@aooth/arbac 0.1.70 → 0.1.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +719 -196
- package/dist/index.d.cts +346 -13
- package/dist/index.d.mts +346 -13
- package/dist/index.mjs +702 -197
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -86,18 +86,6 @@ declare function defineRole<TUserAttrs extends object = object, TScope extends o
|
|
|
86
86
|
*/
|
|
87
87
|
declare function definePrivilege<TUserAttrs extends object = object, TScope extends object = object>(): <TArgs extends unknown[]>(factory: (...args: TArgs) => TArbacRule<TUserAttrs, TScope>[]) => ((...args: TArgs) => TPrivilegeFunction<TUserAttrs, TScope>);
|
|
88
88
|
//#endregion
|
|
89
|
-
//#region src/db-privileges.d.ts
|
|
90
|
-
type ScopeFn<TUserAttrs, TScope> = (attrs: TUserAttrs, userId: string) => TScope;
|
|
91
|
-
interface ScopeOpts<TUserAttrs, TScope> {
|
|
92
|
-
scope?: ScopeFn<TUserAttrs, TScope>;
|
|
93
|
-
}
|
|
94
|
-
/** Read-side actions on an `AsDbController` table: `query`, `pages`, `getOne`, `getOneComposite`, `meta`, `metaForm`. */
|
|
95
|
-
declare function allowTableRead<TUserAttrs extends object = object, TScope extends object = object>(resource: string, opts?: ScopeOpts<TUserAttrs, TScope>): TPrivilegeFunction<TUserAttrs, TScope>;
|
|
96
|
-
/** All actions on an `AsDbController` table: read + `insert`, `update`, `replace`, `remove`, `removeComposite`. */
|
|
97
|
-
declare function allowTableWrite<TUserAttrs extends object = object, TScope extends object = object>(resource: string, opts?: ScopeOpts<TUserAttrs, TScope>): TPrivilegeFunction<TUserAttrs, TScope>;
|
|
98
|
-
/** One or more declarative actions on the same table, sharing a scope. */
|
|
99
|
-
declare function allowTableAction<TUserAttrs extends object = object, TScope extends object = object>(resource: string, name: string | string[], opts?: ScopeOpts<TUserAttrs, TScope>): TPrivilegeFunction<TUserAttrs, TScope>;
|
|
100
|
-
//#endregion
|
|
101
89
|
//#region src/scope/types.d.ts
|
|
102
90
|
/**
|
|
103
91
|
* Mongo-style field projection: `{ field: 0 | 1 }`.
|
|
@@ -135,6 +123,98 @@ type TScopeFilter = Record<string, unknown>;
|
|
|
135
123
|
*/
|
|
136
124
|
type ControlGate = boolean | readonly string[];
|
|
137
125
|
//#endregion
|
|
126
|
+
//#region src/db-privileges.d.ts
|
|
127
|
+
/**
|
|
128
|
+
* Read-side handler actions of an `AsDbController` table: `query`, `pages`,
|
|
129
|
+
* `getOne`, `getOneComposite`, `geo`, `meta`, `metaForm`.
|
|
130
|
+
*/
|
|
131
|
+
declare const TABLE_READ_ACTIONS: readonly ["query", "pages", "getOne", "getOneComposite", "geo", "meta", "metaForm"];
|
|
132
|
+
/** Write-side handler actions of an `AsDbController` table. */
|
|
133
|
+
declare const TABLE_WRITE_ACTIONS: readonly ["insert", "update", "replace", "remove", "removeComposite"];
|
|
134
|
+
/** The `/meta` + `/meta/form/:name` handler actions (a subset of {@link TABLE_READ_ACTIONS}). */
|
|
135
|
+
declare const TABLE_META_ACTIONS: readonly ["meta", "metaForm"];
|
|
136
|
+
/** Table operation names accepted by {@link allowTableOps} / {@link defineTableAccess}. */
|
|
137
|
+
type TTableOp = "read" | "meta" | "insert" | "update" | "replace" | "remove";
|
|
138
|
+
/**
|
|
139
|
+
* Operation → handler actions. `meta` is the form-metadata subset of `read`,
|
|
140
|
+
* for write-only principals that still render forms from `/meta`.
|
|
141
|
+
*/
|
|
142
|
+
declare const TABLE_OP_ACTIONS: Readonly<Record<TTableOp, readonly string[]>>;
|
|
143
|
+
type ScopeFn<TUserAttrs, TScope> = (attrs: TUserAttrs, userId: string) => TScope;
|
|
144
|
+
interface ScopeOpts<TUserAttrs, TScope> {
|
|
145
|
+
scope?: ScopeFn<TUserAttrs, TScope>;
|
|
146
|
+
}
|
|
147
|
+
/** Read-side actions on an `AsDbController` table — see {@link TABLE_READ_ACTIONS}. */
|
|
148
|
+
declare function allowTableRead<TUserAttrs extends object = object, TScope extends object = object>(resource: string, opts?: ScopeOpts<TUserAttrs, TScope>): TPrivilegeFunction<TUserAttrs, TScope>;
|
|
149
|
+
/** All actions on an `AsDbController` table: read + `insert`, `update`, `replace`, `remove`, `removeComposite`. */
|
|
150
|
+
declare function allowTableWrite<TUserAttrs extends object = object, TScope extends object = object>(resource: string, opts?: ScopeOpts<TUserAttrs, TScope>): TPrivilegeFunction<TUserAttrs, TScope>;
|
|
151
|
+
/**
|
|
152
|
+
* Selected operations on an `AsDbController` table, sharing a scope — e.g. an
|
|
153
|
+
* insert-only form principal: `allowTableOps("leads", ["insert", "meta"])`.
|
|
154
|
+
*
|
|
155
|
+
* - `read` → `query`, `pages`, `getOne`, `getOneComposite`, `geo`, `meta`, `metaForm`
|
|
156
|
+
* - `meta` → `meta`, `metaForm` (forms for principals without `read`)
|
|
157
|
+
* - `remove` → `remove`, `removeComposite`
|
|
158
|
+
* - `insert` / `update` / `replace` → the same-named action
|
|
159
|
+
*
|
|
160
|
+
* Throws on an unknown op name.
|
|
161
|
+
*/
|
|
162
|
+
declare function allowTableOps<TUserAttrs extends object = object, TScope extends object = object>(resource: string, ops: readonly TTableOp[], opts?: ScopeOpts<TUserAttrs, TScope>): TPrivilegeFunction<TUserAttrs, TScope>;
|
|
163
|
+
/** One or more declarative actions on the same table, sharing a scope. */
|
|
164
|
+
declare function allowTableAction<TUserAttrs extends object = object, TScope extends object = object>(resource: string, name: string | string[], opts?: ScopeOpts<TUserAttrs, TScope>): TPrivilegeFunction<TUserAttrs, TScope>;
|
|
165
|
+
/** The scope keys {@link defineTableAccess} conjoins instead of overriding. */
|
|
166
|
+
interface TTableAccessScope {
|
|
167
|
+
filter?: TScopeFilter;
|
|
168
|
+
check?: TScopeFilter;
|
|
169
|
+
}
|
|
170
|
+
/** Write ops for {@link TTableAccessDef.write}: any {@link TTableOp} but `read`. */
|
|
171
|
+
type TTableWriteOp = Exclude<TTableOp, "read">;
|
|
172
|
+
type PartScopeFn<TUserAttrs, TScope> = ScopeFn<NoInfer<TUserAttrs>, NoInfer<TScope>>;
|
|
173
|
+
/**
|
|
174
|
+
* Table policy for {@link defineTableAccess}. A part's own `scope` merges over
|
|
175
|
+
* the shared one (see there).
|
|
176
|
+
*/
|
|
177
|
+
interface TTableAccessDef<TUserAttrs, TScope> {
|
|
178
|
+
/** Shared scope — row filter, projection, controls, `with`, `check`, … — for every part. */
|
|
179
|
+
scope?: PartScopeFn<TUserAttrs, TScope>;
|
|
180
|
+
/** Read side — {@link TABLE_READ_ACTIONS}. */
|
|
181
|
+
read?: boolean | {
|
|
182
|
+
scope?: PartScopeFn<TUserAttrs, TScope>;
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* Write side: `true` = insert + update + replace + remove. An op list may
|
|
186
|
+
* add `meta` (forms for a principal without `read`).
|
|
187
|
+
*/
|
|
188
|
+
write?: boolean | readonly TTableWriteOp[] | {
|
|
189
|
+
ops?: readonly TTableWriteOp[];
|
|
190
|
+
scope?: PartScopeFn<TUserAttrs, TScope>;
|
|
191
|
+
};
|
|
192
|
+
/** Declarative `@DbAction` names. */
|
|
193
|
+
actions?: readonly string[] | {
|
|
194
|
+
names: readonly string[];
|
|
195
|
+
scope?: PartScopeFn<TUserAttrs, TScope>;
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* One table policy → consistent rules for every endpoint of an
|
|
200
|
+
* `AsDbController` table. The shared `scope` (row filter + projection +
|
|
201
|
+
* controls + `with` + `check` …) applies to every part; a part's own `scope`
|
|
202
|
+
* merges over it — `filter` / `check` conjoined (`$and`), other keys from the
|
|
203
|
+
* part. An action granted by an earlier part (`read` → `write` → `actions`)
|
|
204
|
+
* is not repeated by a later one. Throws on an unknown write op.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```ts
|
|
208
|
+
* defineTableAccess<Attrs, ArbacDbScope<typeof Task>>("tasks", {
|
|
209
|
+
* scope: (a) => ({ filter: { tenantId: a.tenantId }, projection: { secret: 0 } }),
|
|
210
|
+
* read: true,
|
|
211
|
+
* write: { ops: ["insert", "update"], scope: () => ({ allowedFields: ["title", "status"] }) },
|
|
212
|
+
* actions: ["markDone"],
|
|
213
|
+
* });
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
declare function defineTableAccess<TUserAttrs extends object = object, TScope extends TTableAccessScope = TTableAccessScope>(resource: string, def: TTableAccessDef<TUserAttrs, TScope>): TPrivilegeFunction<TUserAttrs, TScope>;
|
|
217
|
+
//#endregion
|
|
138
218
|
//#region src/scope/projection.d.ts
|
|
139
219
|
type TProjectionMode = "include" | "exclude" | "empty";
|
|
140
220
|
/**
|
|
@@ -326,6 +406,259 @@ declare function unionControlsPolicy(scopes: ReadonlyArray<{
|
|
|
326
406
|
*/
|
|
327
407
|
declare function intersectControlsPolicy(a: Record<string, ControlGate>, b: Record<string, ControlGate>): Record<string, ControlGate>;
|
|
328
408
|
//#endregion
|
|
409
|
+
//#region src/scope/db-scope.d.ts
|
|
410
|
+
/**
|
|
411
|
+
* The DB scope shape the scope algebra reads — structurally what
|
|
412
|
+
* `ArbacDbScope` (`@aooth/arbac-moost`) carries. Every facet is optional.
|
|
413
|
+
*
|
|
414
|
+
* @since 0.1.72
|
|
415
|
+
*/
|
|
416
|
+
interface TDbScope {
|
|
417
|
+
filter?: TScopeFilter;
|
|
418
|
+
check?: TScopeFilter;
|
|
419
|
+
projection?: object;
|
|
420
|
+
controls?: object;
|
|
421
|
+
allowedFields?: readonly string[];
|
|
422
|
+
set?: object;
|
|
423
|
+
nestedWrites?: readonly string[];
|
|
424
|
+
checkRefs?: true | readonly string[];
|
|
425
|
+
with?: object;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* The built-in {@link TDbScope} keys — the facets the scope algebra itself
|
|
429
|
+
* combines. Any other key is a custom (declaration-merged) scope field, which
|
|
430
|
+
* {@link conjoinScopes} combines only through a registered
|
|
431
|
+
* {@link TScopeFieldRule}.
|
|
432
|
+
*
|
|
433
|
+
* @since 0.1.72
|
|
434
|
+
*/
|
|
435
|
+
declare const DB_SCOPE_KEYS: readonly string[];
|
|
436
|
+
/**
|
|
437
|
+
* The conjunction rule of ONE custom scope field (a key an app adds to its
|
|
438
|
+
* scope type by declaration merging). {@link conjoinScopes} calls `conjoin`
|
|
439
|
+
* only when some scope on either side carries the field.
|
|
440
|
+
*
|
|
441
|
+
* @since 0.1.72
|
|
442
|
+
*/
|
|
443
|
+
interface TScopeFieldRule<S extends object = TDbScope> {
|
|
444
|
+
/**
|
|
445
|
+
* Optional ROW restriction the field implies: the filter a scope carrying
|
|
446
|
+
* `value` admits (`undefined` / `{}` = no row restriction). Folded into
|
|
447
|
+
* that scope's `filter` by {@link applyScopeFieldFilters} — ANDed within
|
|
448
|
+
* the scope, so the union across scopes stays an `$or`. An explicit
|
|
449
|
+
* `check` is left as written; an absent one follows the folded filter.
|
|
450
|
+
*
|
|
451
|
+
* Must depend ONLY on `(value, scope)` — never on request context: the
|
|
452
|
+
* result is cached process-wide per scope object. Put user-dependent data
|
|
453
|
+
* into the scope value from the role's scope predicate.
|
|
454
|
+
*/
|
|
455
|
+
rowFilter?(value: unknown, scope: S): TScopeFilter | undefined;
|
|
456
|
+
/**
|
|
457
|
+
* Combine the field of two restrict-only sides into the ONE value the
|
|
458
|
+
* composite scope carries — never wider than either side. Each side is a
|
|
459
|
+
* scope LIST the app unions with its own rule (a scope without the field is
|
|
460
|
+
* typically unrestricted for it). In a credential attenuation `a` is the
|
|
461
|
+
* user's full authority and `b` the credential's view. Return `undefined`
|
|
462
|
+
* for "unrestricted" (the key is omitted).
|
|
463
|
+
*/
|
|
464
|
+
conjoin(a: readonly S[], b: readonly S[]): unknown;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Thrown by {@link conjoinScopes} for a custom scope field without a
|
|
468
|
+
* conjunction rule — a server CONFIGURATION error (never a client error):
|
|
469
|
+
* the conjunction fails closed instead of dropping the field.
|
|
470
|
+
*
|
|
471
|
+
* @since 0.1.72
|
|
472
|
+
*/
|
|
473
|
+
declare class ScopeFieldConfigError extends Error {
|
|
474
|
+
/** The custom scope field without a rule. */
|
|
475
|
+
readonly field: string;
|
|
476
|
+
readonly name = "ScopeFieldConfigError";
|
|
477
|
+
constructor(
|
|
478
|
+
/** The custom scope field without a rule. */
|
|
479
|
+
field: string, message: string);
|
|
480
|
+
}
|
|
481
|
+
/** Custom scope field name → its {@link TScopeFieldRule}. @since 0.1.72 */
|
|
482
|
+
type TScopeFieldRules<S extends object = TDbScope> = Readonly<Record<string, TScopeFieldRule<S>>>;
|
|
483
|
+
/**
|
|
484
|
+
* The effective (unioned) policy of a list of DB scopes — one scope per
|
|
485
|
+
* allowing rule, so each facet unions ADDITIVELY (more roles = more access).
|
|
486
|
+
* Every facet's union rule is defined here once; facets compute lazily (a
|
|
487
|
+
* misconfigured facet throws only where it is used). An empty list is the
|
|
488
|
+
* identity (unrestricted) of every union — denial is decided before, by
|
|
489
|
+
* {@link normalizeScopes}.
|
|
490
|
+
*
|
|
491
|
+
* @since 0.1.72
|
|
492
|
+
*/
|
|
493
|
+
interface TEffectiveDbScope<S extends TDbScope = TDbScope> {
|
|
494
|
+
/** Row filter union (`$or`, {@link mergeScopeFilters}); `undefined` = unrestricted. */
|
|
495
|
+
readonly filter: TScopeFilter | undefined;
|
|
496
|
+
/**
|
|
497
|
+
* WITH CHECK union — each scope's `check`, defaulting to its `filter`
|
|
498
|
+
* (`{}` = no check); `undefined` = nothing to check.
|
|
499
|
+
*/
|
|
500
|
+
readonly check: TScopeFilter | undefined;
|
|
501
|
+
/** Whether any scope sets an explicit `check`. */
|
|
502
|
+
readonly hasExplicitCheck: boolean;
|
|
503
|
+
/** Projection union ({@link unionProjections}); `{}` = every field. */
|
|
504
|
+
readonly projection: TProjection;
|
|
505
|
+
/** Controls union ({@link unionControlsPolicy}); an absent key = allowed. */
|
|
506
|
+
readonly controls: Record<string, ControlGate>;
|
|
507
|
+
/**
|
|
508
|
+
* Union of the `allowedFields` whitelists; `undefined` when no scope
|
|
509
|
+
* carries one (field-unrestricted writes).
|
|
510
|
+
*/
|
|
511
|
+
readonly allowedFields: ReadonlySet<string> | undefined;
|
|
512
|
+
/** The `set` overlays, later scopes winning; `undefined` when none sets one. */
|
|
513
|
+
readonly set: Readonly<Record<string, unknown>> | undefined;
|
|
514
|
+
/** Union of the `nestedWrites` opt-ins. */
|
|
515
|
+
readonly nestedWrites: ReadonlySet<string>;
|
|
516
|
+
/**
|
|
517
|
+
* `checkRefs` entries EVERY scope enables (intersection of enforcement —
|
|
518
|
+
* a scope without the flag grants unconstrained writes of that FK);
|
|
519
|
+
* `true` = all. Names as written (a schema-aware caller resolves them).
|
|
520
|
+
*/
|
|
521
|
+
readonly checkRefs: true | ReadonlySet<string>;
|
|
522
|
+
/** Relation names some scope declares a `with.<name>` sub-scope for. */
|
|
523
|
+
readonly withNames: ReadonlySet<string>;
|
|
524
|
+
/** The declared `with.<name>` sub-scopes (identity-stable per name). */
|
|
525
|
+
withScopes(name: string): readonly S[];
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* The {@link TEffectiveDbScope} of `scopes`, memoized per scopes ARRAY (an
|
|
529
|
+
* identity-stable per-request list reuses every computed facet).
|
|
530
|
+
*
|
|
531
|
+
* @since 0.1.72
|
|
532
|
+
*/
|
|
533
|
+
declare function effectiveScope<S extends TDbScope>(scopes: readonly S[]): TEffectiveDbScope<S>;
|
|
534
|
+
/**
|
|
535
|
+
* Intersection of enforcement: an entry is enforced only when EVERY input
|
|
536
|
+
* enables it (`true` enables all). No input → nothing enforced.
|
|
537
|
+
*
|
|
538
|
+
* @since 0.1.72
|
|
539
|
+
*/
|
|
540
|
+
declare function intersectEnabled<K>(inputs: Iterable<true | ReadonlySet<K>>): true | Set<K>;
|
|
541
|
+
/**
|
|
542
|
+
* The unified empty-scope rule for an evaluation outcome: denied → `undefined`;
|
|
543
|
+
* allowed with `scopes` absent (a scope-agnostic evaluator) → `[{}]`
|
|
544
|
+
* (unrestricted); a scope predicate that returned nothing contributes nothing
|
|
545
|
+
* (fail closed for that rule); allowed with no scope left → `undefined` (deny).
|
|
546
|
+
*
|
|
547
|
+
* @since 0.1.72
|
|
548
|
+
*/
|
|
549
|
+
declare function normalizeScopes<S extends object>(outcome: {
|
|
550
|
+
allowed: boolean;
|
|
551
|
+
scopes?: ReadonlyArray<S | null | undefined>;
|
|
552
|
+
}): S[] | undefined;
|
|
553
|
+
/**
|
|
554
|
+
* Several evaluations served as ONE surface (e.g. the handlers of one crud
|
|
555
|
+
* op): each normalized with {@link normalizeScopes}, the allowed ones'
|
|
556
|
+
* scopes concatenated — `undefined` when none allows.
|
|
557
|
+
*
|
|
558
|
+
* @since 0.1.72
|
|
559
|
+
*/
|
|
560
|
+
declare function unionOutcomes<S extends object>(outcomes: ReadonlyArray<{
|
|
561
|
+
allowed: boolean;
|
|
562
|
+
scopes?: ReadonlyArray<S | null | undefined>;
|
|
563
|
+
}>): S[] | undefined;
|
|
564
|
+
/** A row policy: the read / USING `filter` and the WITH CHECK `check` (defaults to `filter`). */
|
|
565
|
+
interface TRowPolicy {
|
|
566
|
+
filter?: TScopeFilter;
|
|
567
|
+
check?: TScopeFilter;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Conjoin two row policies (`$and`, restrict-only): `filter = a.filter ∧
|
|
571
|
+
* b.filter`; the effective check `(a.check ?? a.filter) ∧ (b.check ??
|
|
572
|
+
* b.filter)`. `check` is set only when it differs from the conjoined
|
|
573
|
+
* `filter` (which an absent `check` defaults to) — `{}` when it is
|
|
574
|
+
* unrestricted while the filter is not.
|
|
575
|
+
*
|
|
576
|
+
* @since 0.1.72
|
|
577
|
+
*/
|
|
578
|
+
declare function conjoinRowPolicies(a: TRowPolicy, b: TRowPolicy): TRowPolicy;
|
|
579
|
+
/** Options of {@link conjoinScopes}. */
|
|
580
|
+
interface TConjoinScopesOptions<S extends TDbScope> {
|
|
581
|
+
/** Schema lookup to subtract a nested exclusion exactly (without it: fail closed). */
|
|
582
|
+
childrenOf?: TProjectionChildren;
|
|
583
|
+
/**
|
|
584
|
+
* Schema-aware `checkRefs` conjunction; default: the union of each side's
|
|
585
|
+
* {@link TEffectiveDbScope.checkRefs} names.
|
|
586
|
+
*/
|
|
587
|
+
checkRefs?: (a: readonly S[], b: readonly S[]) => true | string[] | undefined;
|
|
588
|
+
/**
|
|
589
|
+
* Rules for custom (non-built-in) scope fields, applied at every level
|
|
590
|
+
* (`with` sub-scopes too). A custom field present on either side without a
|
|
591
|
+
* rule throws — it is never dropped (a dropped field reads as absent, i.e.
|
|
592
|
+
* typically unrestricted).
|
|
593
|
+
*/
|
|
594
|
+
fields?: TScopeFieldRules<S>;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Conjoin two scope lists (credential attenuation: the user's ceiling ∧ the
|
|
598
|
+
* credential's narrowed view) into ONE composite scope. Each side is first
|
|
599
|
+
* unioned ({@link effectiveScope}), then the two results are CONJOINED facet
|
|
600
|
+
* by facet — never with the additive union helpers, which would widen:
|
|
601
|
+
*
|
|
602
|
+
* - `filter` / `check` — {@link conjoinRowPolicies};
|
|
603
|
+
* - `projection` — {@link intersectProjections} (no common field → the
|
|
604
|
+
* first side's projection plus a match-nothing filter, never `{}`);
|
|
605
|
+
* - `controls` — {@link intersectControlsPolicy} (deny wins);
|
|
606
|
+
* - `allowedFields` — intersection (a side without a whitelist is unrestricted);
|
|
607
|
+
* - `nestedWrites` — intersection (a side without the key allows none);
|
|
608
|
+
* - `set` — combined, the FIRST side winning a key conflict;
|
|
609
|
+
* - `checkRefs` — enforced when EITHER side enforces it;
|
|
610
|
+
* - `with` — per relation, recursively; a relation declared by ONE side only
|
|
611
|
+
* is marked {@link INHERITED_CONJUNCTION} (the silent side's inherited
|
|
612
|
+
* grant on the related table must be conjoined at resolution time);
|
|
613
|
+
* - a custom field — its `opts.fields` rule; none registered → throws
|
|
614
|
+
* (fail closed: never silently dropped).
|
|
615
|
+
*
|
|
616
|
+
* @since 0.1.72
|
|
617
|
+
*/
|
|
618
|
+
declare function conjoinScopes<S extends TDbScope>(a: readonly S[], b: readonly S[], opts?: TConjoinScopesOptions<S>): S;
|
|
619
|
+
/** Options of {@link applyScopeFieldFilters}. @since 0.1.72 */
|
|
620
|
+
interface TApplyScopeFieldFiltersOptions {
|
|
621
|
+
/**
|
|
622
|
+
* Called for a non-built-in scope key with no rule in `fields` (once per
|
|
623
|
+
* scope object carrying it) — e.g. to warn about a field that will fail
|
|
624
|
+
* the first attenuated conjunction.
|
|
625
|
+
*/
|
|
626
|
+
onUnknownField?: (field: string) => void;
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Fold every custom field's {@link TScopeFieldRule.rowFilter} into its
|
|
630
|
+
* scope's `filter` (`filter ∧ rowFilter(value, scope)`), recursively into
|
|
631
|
+
* `with` sub-scopes. Apply it per evaluated scope list — before any union or
|
|
632
|
+
* conjunction — so every row path sees the restriction. The custom value
|
|
633
|
+
* stays on the scope. Scopes are never mutated: a changed scope is a copy.
|
|
634
|
+
* Memoized per rule registry and scope object (process-wide), and
|
|
635
|
+
* idempotent (a folded scope folds to itself); without row rules (and no
|
|
636
|
+
* `onUnknownField`) the input list is returned as is.
|
|
637
|
+
*
|
|
638
|
+
* @since 0.1.72
|
|
639
|
+
*/
|
|
640
|
+
declare function applyScopeFieldFilters<S extends TDbScope>(scopes: readonly S[], fields: TScopeFieldRules<S> | undefined, opts?: TApplyScopeFieldFiltersOptions): readonly S[];
|
|
641
|
+
/**
|
|
642
|
+
* Marks a `with.<rel>` sub-scope {@link conjoinScopes} built from ONE side's
|
|
643
|
+
* declaration. The other side is silent on the relation, so ITS policy for
|
|
644
|
+
* the joined rows is its inherited grant on the related table — unknown
|
|
645
|
+
* here. A resolver must conjoin the marked sub-scope with the caller's own
|
|
646
|
+
* grant on the related table (never apply it alone as parent authority);
|
|
647
|
+
* unresolved, the relation is hidden. See {@link needsInheritedConjunction}.
|
|
648
|
+
*
|
|
649
|
+
* @since 0.1.72
|
|
650
|
+
*/
|
|
651
|
+
declare const INHERITED_CONJUNCTION: unique symbol;
|
|
652
|
+
/** A `with` sub-scope marked {@link INHERITED_CONJUNCTION}. @since 0.1.72 */
|
|
653
|
+
declare function needsInheritedConjunction(scope: object): boolean;
|
|
654
|
+
/**
|
|
655
|
+
* A stable string key for a JSON-like value (bigint-safe) — for
|
|
656
|
+
* de-duplicating filters / tuples and comparing them structurally.
|
|
657
|
+
*
|
|
658
|
+
* @since 0.1.72
|
|
659
|
+
*/
|
|
660
|
+
declare function stableKey(value: unknown): string;
|
|
661
|
+
//#endregion
|
|
329
662
|
//#region src/codegen/extract.d.ts
|
|
330
663
|
interface TResourceActionMap {
|
|
331
664
|
/** Map of resource string → Set of action strings */
|
|
@@ -361,4 +694,4 @@ interface TCodegenOptions {
|
|
|
361
694
|
*/
|
|
362
695
|
declare function generateResourceTypes(map: TResourceActionMap, options?: TCodegenOptions): string;
|
|
363
696
|
//#endregion
|
|
364
|
-
export { type ControlGate, DENY_FILTER, type RoleBuilder, type TCodegenOptions, type TPrivilegeFunction, type TProjection, type TProjectionChildren, type TProjectionMode, type TResourceActionMap, type TScopeFilter, allowTableAction, allowTableRead, allowTableWrite, conjoinScopeFilters, definePrivilege, defineRole, expandExcludeToLeaves, extractResourceActions, generateResourceTypes, getProjectionMode, intersectControlsPolicy, intersectProjections, isFieldAllowed, mergeScopeFilters, restrictProjection, unionControlsPolicy, unionProjections };
|
|
697
|
+
export { type ControlGate, DB_SCOPE_KEYS, DENY_FILTER, INHERITED_CONJUNCTION, type RoleBuilder, ScopeFieldConfigError, TABLE_META_ACTIONS, TABLE_OP_ACTIONS, TABLE_READ_ACTIONS, TABLE_WRITE_ACTIONS, type TApplyScopeFieldFiltersOptions, type TCodegenOptions, type TConjoinScopesOptions, type TDbScope, type TEffectiveDbScope, type TPrivilegeFunction, type TProjection, type TProjectionChildren, type TProjectionMode, type TResourceActionMap, type TRowPolicy, type TScopeFieldRule, type TScopeFieldRules, type TScopeFilter, type TTableAccessDef, type TTableAccessScope, type TTableOp, type TTableWriteOp, allowTableAction, allowTableOps, allowTableRead, allowTableWrite, applyScopeFieldFilters, conjoinRowPolicies, conjoinScopeFilters, conjoinScopes, definePrivilege, defineRole, defineTableAccess, effectiveScope, expandExcludeToLeaves, extractResourceActions, generateResourceTypes, getProjectionMode, intersectControlsPolicy, intersectEnabled, intersectProjections, isFieldAllowed, mergeScopeFilters, needsInheritedConjunction, normalizeScopes, restrictProjection, stableKey, unionControlsPolicy, unionOutcomes, unionProjections };
|