@nmnmcc/ability 0.1.0 → 0.2.0

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.
@@ -1,5 +1,5 @@
1
1
  import { cloneSerializable } from "./serializable.js";
2
- import { guard } from "@ucast/mongo2js";
2
+ import { createMongoAbility, mongoQueryMatcher } from "@casl/ability";
3
3
  import * as Data from "effect/Data";
4
4
  import * as Effect from "effect/Effect";
5
5
  import * as Equal from "effect/Equal";
@@ -27,7 +27,7 @@ const SubjectTypeId = Symbol.for("@nmnmcc/ability/Subject");
27
27
  const RuleTypeId = Symbol.for("@nmnmcc/ability/Rule");
28
28
  const anyAction = "manage";
29
29
  const anySubject = "all";
30
- const RuleIndexTypeId = Symbol.for("@nmnmcc/ability/Ability/ruleIndex");
30
+ const CaslStateTypeId = Symbol.for("@nmnmcc/ability/Ability/caslState");
31
31
  /**
32
32
  * @since 0.1.0
33
33
  * @category Errors
@@ -42,16 +42,6 @@ var ConditionError = class extends Data.TaggedError("ConditionError") {};
42
42
  * @since 0.1.0
43
43
  * @category Errors
44
44
  */
45
- var PredicateEvaluationError = class extends Data.TaggedError("PredicateEvaluationError") {};
46
- /**
47
- * @since 0.1.0
48
- * @category Errors
49
- */
50
- var SerializationError = class extends Data.TaggedError("SerializationError") {};
51
- /**
52
- * @since 0.1.0
53
- * @category Errors
54
- */
55
45
  var RawRuleError = class extends Data.TaggedError("RawRuleError") {};
56
46
  /**
57
47
  * @since 0.1.0
@@ -141,7 +131,6 @@ const ruleHashInput = (rule) => ({
141
131
  subject: rule.subject,
142
132
  fields: rule.fields,
143
133
  conditions: rule.conditions,
144
- when: rule.when,
145
134
  reason: rule.reason
146
135
  });
147
136
  const RuleProto = {
@@ -230,49 +219,44 @@ const freezeRule = (rule) => {
230
219
  copy.subject = normalizeArray(rule.subject);
231
220
  if (rule.fields !== void 0) copy.fields = copyArray(rule.fields);
232
221
  if (rule.conditions !== void 0) copy.conditions = cloneSerializable(rule.conditions);
233
- if (rule.when !== void 0) copy.when = rule.when;
234
222
  if (rule.reason !== void 0) copy.reason = rule.reason;
235
223
  return Object.freeze(copy);
236
224
  };
237
- const addIndexedRule = (index, subjectName, action, indexedRule) => {
238
- let subjectIndex = index.get(subjectName);
239
- if (subjectIndex === void 0) {
240
- subjectIndex = /* @__PURE__ */ new Map();
241
- index.set(subjectName, subjectIndex);
242
- }
243
- let rules = subjectIndex.get(action);
244
- if (rules === void 0) {
245
- rules = [];
246
- subjectIndex.set(action, rules);
247
- }
248
- rules.push(indexedRule);
249
- };
250
- const freezeRuleIndex = (index) => {
251
- const subjects = /* @__PURE__ */ new Map();
252
- for (const [subjectName, actions] of index) {
253
- const actionIndex = /* @__PURE__ */ new Map();
254
- for (const [action, rules] of actions) actionIndex.set(action, Object.freeze(rules.slice().sort((a, b) => b.index - a.index)));
255
- subjects.set(subjectName, actionIndex);
256
- }
257
- return Object.freeze({ rulesBySubject: subjects });
225
+ const toCaslRawRule = (rule) => {
226
+ const raw = {
227
+ action: cloneSerializable(rule.action),
228
+ subject: cloneSerializable(rule.subject)
229
+ };
230
+ if (rule.fields !== void 0) raw.fields = cloneSerializable(rule.fields);
231
+ if (rule.conditions !== void 0) raw.conditions = cloneSerializable(rule.conditions);
232
+ if (rule._tag === "Deny") raw.inverted = true;
233
+ if (rule.reason !== void 0) raw.reason = rule.reason;
234
+ return raw;
258
235
  };
259
- const buildRuleIndex = (rules, aliases) => {
236
+ const buildCaslState = (rules, aliases) => {
260
237
  const resolver = createAliasResolver(aliases ?? {});
261
- const index = /* @__PURE__ */ new Map();
238
+ const rulesByRawRule = /* @__PURE__ */ new WeakMap();
239
+ const indexByRule = /* @__PURE__ */ new Map();
240
+ const rawRules = [];
262
241
  let ruleIndex = 0;
263
242
  for (const rule of rules) {
264
- const indexedRule = {
265
- rule,
266
- index: ruleIndex
267
- };
268
- const actions = resolver(rule.action);
269
- const subjects = wrapArray(rule.subject);
270
- for (const subjectName of subjects) for (const action of actions) addIndexedRule(index, subjectName, action, indexedRule);
243
+ const rawRule = toCaslRawRule(rule);
244
+ rawRules.push(rawRule);
245
+ rulesByRawRule.set(rawRule, rule);
246
+ indexByRule.set(rule, ruleIndex);
271
247
  ruleIndex = ruleIndex + 1;
272
248
  }
273
- return freezeRuleIndex(index);
249
+ return Object.freeze({
250
+ ability: createMongoAbility(rawRules, {
251
+ anyAction,
252
+ anySubjectType: anySubject,
253
+ resolveAction: (action) => resolver(action)
254
+ }),
255
+ rulesByRawRule,
256
+ indexByRule: Object.freeze(indexByRule)
257
+ });
274
258
  };
275
- const getRuleIndex = (self) => self[RuleIndexTypeId];
259
+ const getCaslState = (self) => self[CaslStateTypeId];
276
260
  /**
277
261
  * @internal
278
262
  */
@@ -280,7 +264,7 @@ const make = (rules, options) => {
280
264
  const self = Object.create(AbilityProto);
281
265
  self.rules = Object.freeze(Array.from(rules, freezeRule));
282
266
  self.options = freezeOptions(options);
283
- self[RuleIndexTypeId] = buildRuleIndex(self.rules, self.options.actionAliases);
267
+ self[CaslStateTypeId] = buildCaslState(self.rules, self.options.actionAliases);
284
268
  return Object.freeze(self);
285
269
  };
286
270
  const makeRule = (kind, action, subjectName, options) => {
@@ -290,7 +274,6 @@ const makeRule = (kind, action, subjectName, options) => {
290
274
  rule.subject = normalizeArray(subjectName);
291
275
  if (options?.fields !== void 0) rule.fields = copyArray(wrapArray(options.fields));
292
276
  if (options?.conditions !== void 0) rule.conditions = cloneSerializable(options.conditions);
293
- if (options?.when !== void 0) rule.when = options.when;
294
277
  if (options?.reason !== void 0) rule.reason = options.reason;
295
278
  return Object.freeze(rule);
296
279
  };
@@ -380,34 +363,7 @@ const toRuntimeSubjectRequest = Effect.fnUntraced(function* (self, request) {
380
363
  if (hasOwnValue(request)) runtime.value = unwrapSubject(request.value);
381
364
  return runtime;
382
365
  });
383
- const regexpSpecialChars = /[-/\\^$+?.()|[\]{}]/g;
384
- const regexpAny = /\.?\*+\.?/g;
385
- const regexpStars = /\*+/;
386
- const regexpDot = /\./g;
387
- const detectRegexpPattern = (match, index, source) => {
388
- const quantifier = source[0] === "*" || match[0] === "." && match[match.length - 1] === "." ? "+" : "*";
389
- const matcher = match.includes("**") ? "." : "[^.]";
390
- const pattern = match.replace(regexpDot, "\\$&").replace(regexpStars, matcher + quantifier);
391
- return index + match.length === source.length ? `(?:${pattern})?` : pattern;
392
- };
393
- const escapeRegexp = (match, index, source) => {
394
- if (match === "." && (source[index - 1] === "*" || source[index + 1] === "*")) return match;
395
- return `\\${match}`;
396
- };
397
- const createFieldPattern = (fields) => {
398
- const patterns = fields.map((field) => field.replace(regexpSpecialChars, escapeRegexp).replace(regexpAny, detectRegexpPattern));
399
- const pattern = patterns.length > 1 ? `(?:${patterns.join("|")})` : patterns[0] ?? "";
400
- return new RegExp(`^${pattern}$`);
401
- };
402
- const createFieldMatcher = (fields) => {
403
- const pattern = fields.some((field) => field.includes("*")) ? createFieldPattern(fields) : void 0;
404
- return pattern === void 0 ? (field) => fields.includes(field) : (field) => pattern.test(field);
405
- };
406
- const matchesField = (rule, request) => {
407
- if (rule.fields === void 0) return true;
408
- if (request.field === void 0) return rule._tag === "Allow";
409
- return createFieldMatcher(rule.fields)(request.field);
410
- };
366
+ const matchesField = (candidate, request) => candidate.caslRule.matchesField(request.field);
411
367
  const isEmptyCondition = (conditions) => Object.keys(conditions).length === 0;
412
368
  const makeConditionError = (request, cause) => {
413
369
  const error = {
@@ -418,59 +374,42 @@ const makeConditionError = (request, cause) => {
418
374
  if (request.field !== void 0) error.field = request.field;
419
375
  return new ConditionError(error);
420
376
  };
421
- const makePredicateEvaluationError = (request, cause) => {
422
- const error = {
423
- action: request.action,
424
- subject: request.subject,
425
- cause
426
- };
427
- if (request.field !== void 0) error.field = request.field;
428
- return new PredicateEvaluationError(error);
429
- };
430
- const matchesConditions = Effect.fnUntraced(function* (rule, request) {
377
+ const matchesConditions = Effect.fnUntraced(function* (candidate, request) {
378
+ const { rule } = candidate;
431
379
  if (rule.conditions === void 0) return true;
432
380
  if (request.value === void 0) return rule._tag === "Allow" || isEmptyCondition(rule.conditions);
433
381
  return yield* Effect.try({
434
- try: () => guard(rule.conditions)(request.value),
382
+ try: () => candidate.caslRule.matchesConditions(request.value),
435
383
  catch: (cause) => makeConditionError(request, cause)
436
384
  });
437
385
  });
438
- const matchesPredicate = Effect.fnUntraced(function* (rule, request) {
439
- if (rule.when === void 0) return true;
440
- if (request.value === void 0) return false;
441
- const result = yield* Effect.try({
442
- try: () => rule.when?.(request.value) ?? true,
443
- catch: (cause) => makePredicateEvaluationError(request, cause)
444
- });
445
- return Effect.isEffect(result) ? yield* result : result;
386
+ const matchesCandidateRule = Effect.fnUntraced(function* (candidate, request) {
387
+ if (!matchesField(candidate, request)) return false;
388
+ return yield* matchesConditions(candidate, request);
446
389
  });
447
- const matchesCandidateRule = Effect.fnUntraced(function* (rule, request) {
448
- if (!matchesField(rule, request)) return false;
449
- if (!(yield* matchesConditions(rule, request))) return false;
450
- return yield* matchesPredicate(rule, request);
451
- });
452
- const collectIndexedRules = (self, request, order) => {
453
- const collected = /* @__PURE__ */ new Map();
454
- const pairs = [[request.subject, request.action]];
455
- if (request.action !== anyAction) pairs.push([request.subject, anyAction]);
456
- if (request.subject !== anySubject) pairs.push([anySubject, request.action]);
457
- if (request.subject !== anySubject && request.action !== anyAction) pairs.push([anySubject, anyAction]);
458
- for (const [subjectName, action] of pairs) {
459
- const rules = getRuleIndex(self).rulesBySubject.get(subjectName)?.get(action);
460
- if (rules !== void 0) for (const indexedRule of rules) collected.set(indexedRule.rule, indexedRule);
461
- }
462
- const sorted = Array.from(collected.values()).sort((a, b) => order === "descending" ? b.index - a.index : a.index - b.index);
463
- return Object.freeze(sorted.map((indexedRule) => indexedRule.rule));
390
+ const getRuntimeRule = (state, caslRule) => {
391
+ const rule = state.rulesByRawRule.get(caslRule.origin);
392
+ if (rule === void 0) throw new Error("CASL rule is not owned by this Ability");
393
+ return rule;
394
+ };
395
+ const collectRuleCandidates = (self, request, order) => {
396
+ const state = getCaslState(self);
397
+ const candidates = state.ability.possibleRulesFor(request.action, request.subject).map((caslRule) => ({
398
+ rule: getRuntimeRule(state, caslRule),
399
+ caslRule
400
+ }));
401
+ const sorted = order === "descending" ? candidates : candidates.slice().sort((a, b) => (state.indexByRule.get(a.rule) ?? 0) - (state.indexByRule.get(b.rule) ?? 0));
402
+ return Object.freeze(sorted);
464
403
  };
465
- const possibleRulesForRequest = (self, request) => collectIndexedRules(self, request, "descending");
466
- const possibleRulesForDefinitionOrder = (self, request) => collectIndexedRules(self, request, "ascending");
404
+ const possibleRulesForRequest = (self, request) => Object.freeze(collectRuleCandidates(self, request, "descending").map((candidate) => candidate.rule));
405
+ const possibleRuleCandidatesForDefinitionOrder = (self, request) => collectRuleCandidates(self, request, "ascending");
467
406
  const relevantRuleForEffect = Effect.fnUntraced(function* (self, request) {
468
- const rules = possibleRulesForRequest(self, request);
407
+ const candidates = collectRuleCandidates(self, request, "descending");
469
408
  let index = 0;
470
- while (index < rules.length) {
471
- const rule = rules[index];
472
- if (rule !== void 0) {
473
- if (yield* matchesCandidateRule(rule, request)) return rule;
409
+ while (index < candidates.length) {
410
+ const candidate = candidates[index];
411
+ if (candidate !== void 0) {
412
+ if (yield* matchesCandidateRule(candidate, request)) return candidate.rule;
474
413
  }
475
414
  index = index + 1;
476
415
  }
@@ -504,7 +443,7 @@ const possibleRulesFor = dual(2, Effect.fnUntraced(function* (self, request) {
504
443
  */
505
444
  const rulesFor = dual(2, Effect.fnUntraced(function* (self, request) {
506
445
  const runtimeRequest = yield* toRuntimeRequest(self, request);
507
- return Object.freeze(possibleRulesForRequest(self, runtimeRequest).filter((rule) => matchesField(rule, runtimeRequest)));
446
+ return Object.freeze(collectRuleCandidates(self, runtimeRequest, "descending").filter((candidate) => matchesField(candidate, runtimeRequest)).map((candidate) => candidate.rule));
508
447
  }));
509
448
  /**
510
449
  * @internal
@@ -517,14 +456,7 @@ const relevantRuleFor = dual(2, Effect.fnUntraced(function* (self, request) {
517
456
  */
518
457
  const actionsFor = dual(2, Effect.fnUntraced(function* (self, request) {
519
458
  const runtimeRequest = yield* toRuntimeSubjectRequest(self, request);
520
- const actions = /* @__PURE__ */ new Set();
521
- const collect = (subjectName) => {
522
- const subjectIndex = getRuleIndex(self).rulesBySubject.get(subjectName);
523
- if (subjectIndex !== void 0) for (const action of subjectIndex.keys()) actions.add(action);
524
- };
525
- collect(runtimeRequest.subject);
526
- if (runtimeRequest.subject !== anySubject) collect(anySubject);
527
- return unique(actions);
459
+ return unique(getCaslState(self).ability.actionsFor(runtimeRequest.subject));
528
460
  }));
529
461
  const rawRuleError = (reason, ruleIndex, cause) => {
530
462
  const error = {
@@ -541,7 +473,7 @@ const validateArrayValue = (value, name, index) => {
541
473
  const validateRawConditions = (conditions, index) => {
542
474
  if (conditions === void 0) return Effect.void;
543
475
  return Effect.asVoid(Effect.try({
544
- try: () => guard(conditions),
476
+ try: () => mongoQueryMatcher(conditions),
545
477
  catch: (cause) => rawRuleError("rawRule.conditions cannot be compiled", index, cause)
546
478
  }));
547
479
  };
@@ -568,6 +500,11 @@ const fromRawRules = Effect.fnUntraced(function* (rules, options) {
568
500
  catch: (cause) => cause instanceof AliasError ? cause : rawRuleError("Unable to construct Ability from raw rules", index, cause)
569
501
  });
570
502
  });
503
+ const updateEffect = (self, rules) => fromRawRules(rules, self.options);
504
+ /**
505
+ * @internal
506
+ */
507
+ const update = dual(2, updateEffect);
571
508
  const ruleToRaw = (rule) => {
572
509
  const raw = {
573
510
  action: cloneSerializable(rule.action),
@@ -582,18 +519,9 @@ const ruleToRaw = (rule) => {
582
519
  /**
583
520
  * @internal
584
521
  */
585
- const toRawRules = Effect.fnUntraced(function* (self, options) {
586
- const strict = options?.strict !== false;
522
+ const toRawRules = Effect.fnUntraced(function* (self) {
587
523
  const rawRules = [];
588
- let index = 0;
589
- for (const rule of self.rules) {
590
- if (strict && rule.when !== void 0) return yield* Effect.fail(new SerializationError({
591
- reason: "Cannot serialize rules with function predicates in strict mode",
592
- ruleIndex: index
593
- }));
594
- rawRules.push(ruleToRaw(rule));
595
- index = index + 1;
596
- }
524
+ for (const rule of self.rules) rawRules.push(ruleToRaw(rule));
597
525
  return Object.freeze(rawRules);
598
526
  });
599
527
  const fieldsForRule = (rule, options) => rule.fields ?? options.fieldsFrom(rule);
@@ -602,18 +530,16 @@ const fieldsForRule = (rule, options) => rule.fields ?? options.fieldsFrom(rule)
602
530
  */
603
531
  const permittedFields = dual(3, Effect.fnUntraced(function* (self, request, options) {
604
532
  const runtimeRequest = yield* toRuntimeRequest(self, request);
605
- const rules = possibleRulesForDefinitionOrder(self, runtimeRequest);
533
+ const candidates = possibleRuleCandidatesForDefinitionOrder(self, runtimeRequest);
606
534
  const fields = /* @__PURE__ */ new Set();
607
535
  let index = 0;
608
- while (index < rules.length) {
609
- const rule = rules[index];
610
- if (rule !== void 0) {
611
- if (yield* matchesConditions(rule, runtimeRequest)) {
612
- if (yield* matchesPredicate(rule, runtimeRequest)) {
613
- const ruleFields = fieldsForRule(rule, options);
614
- for (const field of ruleFields) if (rule._tag === "Allow") fields.add(field);
615
- else fields.delete(field);
616
- }
536
+ while (index < candidates.length) {
537
+ const candidate = candidates[index];
538
+ if (candidate !== void 0) {
539
+ if (yield* matchesConditions(candidate, runtimeRequest)) {
540
+ const ruleFields = fieldsForRule(candidate.rule, options);
541
+ for (const field of ruleFields) if (candidate.rule._tag === "Allow") fields.add(field);
542
+ else fields.delete(field);
617
543
  }
618
544
  }
619
545
  index = index + 1;
@@ -621,4 +547,4 @@ const permittedFields = dual(3, Effect.fnUntraced(function* (self, request, opti
621
547
  return Object.freeze(Array.from(fields));
622
548
  }));
623
549
  //#endregion
624
- export { AliasError, AuthorizationError, ConditionError, PredicateEvaluationError, QueryGenerationError, RawRuleError, RuleTypeId, SerializationError, SubjectDetectionError, SubjectTypeId, TypeId, actionsFor, check, createAliasResolver, define, detectSubjectType, fromRawRules, isAbility, isRule, isSubject, make, permittedFields, possibleRulesFor, relevantRuleFor, rulesFor, subject, toRawRules, unwrapSubject };
550
+ export { AliasError, AuthorizationError, ConditionError, QueryGenerationError, RawRuleError, RuleTypeId, SubjectDetectionError, SubjectTypeId, TypeId, actionsFor, check, createAliasResolver, define, detectSubjectType, fromRawRules, isAbility, isRule, isSubject, make, permittedFields, possibleRulesFor, relevantRuleFor, rulesFor, subject, toRawRules, unwrapSubject, update };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nmnmcc/ability",
3
- "version": "0.1.0",
4
- "description": "An Effect-style authorization library inspired by CASL.",
3
+ "version": "0.2.0",
4
+ "description": "Authorization primitives for Effect programs, backed by CASL.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "packageManager": "yarn@4.14.1",
@@ -36,20 +36,17 @@
36
36
  "import": "./dist/AbilityExtra.js",
37
37
  "default": "./dist/AbilityExtra.js"
38
38
  },
39
- "./AbilityRef": {
40
- "types": "./dist/AbilityRef.d.ts",
41
- "import": "./dist/AbilityRef.js",
42
- "default": "./dist/AbilityRef.js"
43
- },
44
39
  "./package.json": "./package.json"
45
40
  },
46
41
  "scripts": {
47
42
  "build": "tsdown",
48
43
  "prepack": "yarn build",
49
- "prepublishOnly": "yarn typecheck && yarn test",
50
- "release:check": "yarn typecheck && yarn test && yarn pack --dry-run",
44
+ "prepublishOnly": "yarn typecheck && yarn test-types && yarn test",
45
+ "release:check": "yarn typecheck && yarn test-types && yarn test && yarn pack --dry-run",
46
+ "format": "prettier . -w",
51
47
  "test": "vitest run",
52
48
  "typecheck": "tsc --noEmit",
49
+ "test-types": "tstyche",
53
50
  "npm": "YARN_NPM_REGISTRY_SERVER=https://registry.npmjs.org yarn npm"
54
51
  },
55
52
  "publishConfig": {
@@ -57,19 +54,20 @@
57
54
  "registry": "https://registry.npmjs.org"
58
55
  },
59
56
  "devDependencies": {
57
+ "@casl/ability": "^6.8.1",
60
58
  "@effect/language-service": "^0.85.1",
61
59
  "@effect/vitest": "4.0.0-beta.59",
62
60
  "@types/node": "^25.6.0",
63
61
  "effect": "^4.0.0-beta.59",
62
+ "prettier": "^3.8.3",
64
63
  "tsdown": "^0.21.10",
64
+ "tstyche": "^7.1.0",
65
65
  "tsx": "^4.21.0",
66
66
  "typescript": "^6.0.3",
67
67
  "vitest": "4.1.4"
68
68
  },
69
69
  "peerDependencies": {
70
+ "@casl/ability": "^6.8.1",
70
71
  "effect": "^4.0.0-beta.59"
71
- },
72
- "dependencies": {
73
- "@ucast/mongo2js": "^2.0.0"
74
72
  }
75
73
  }
@@ -1,110 +0,0 @@
1
- import { Ability, AliasError, AnyRule, MongoCondition, RawRule, RawRuleError, RuleSubject, SubjectMap, SubjectUnion } from "./Ability.js";
2
- import * as Effect from "effect/Effect";
3
- import * as Equal from "effect/Equal";
4
- import * as Inspectable from "effect/Inspectable";
5
- import * as Pipeable from "effect/Pipeable";
6
-
7
- //#region src/AbilityRef.d.ts
8
- declare namespace AbilityRef_d_exports {
9
- export { AbilityRef, Event, EventHandler, EventName, TypeId, Unsubscribe, UpdateEvent, UpdatedEvent, get, isAbilityRef, make, on, set, update };
10
- }
11
- /**
12
- * @since 0.1.0
13
- * @category Type Ids
14
- */
15
- declare const TypeId: unique symbol;
16
- /**
17
- * @since 0.1.0
18
- * @category Type Ids
19
- */
20
- type TypeId = typeof TypeId;
21
- /**
22
- * @since 0.1.0
23
- * @category Models
24
- */
25
- type EventName = "update" | "updated";
26
- /**
27
- * @since 0.1.0
28
- * @category Models
29
- */
30
- interface UpdateEvent<Subjects extends SubjectMap> {
31
- readonly previous: Ability<Subjects, AnyRule>;
32
- readonly next: Ability<Subjects, AnyRule>;
33
- }
34
- /**
35
- * @since 0.1.0
36
- * @category Models
37
- */
38
- interface UpdatedEvent<Subjects extends SubjectMap> {
39
- readonly previous: Ability<Subjects, AnyRule>;
40
- readonly current: Ability<Subjects, AnyRule>;
41
- }
42
- /**
43
- * @since 0.1.0
44
- * @category Models
45
- */
46
- type Event<Subjects extends SubjectMap, Name extends EventName> = Name extends "update" ? UpdateEvent<Subjects> : UpdatedEvent<Subjects>;
47
- /**
48
- * @since 0.1.0
49
- * @category Models
50
- */
51
- type EventHandler<Subjects extends SubjectMap, Name extends EventName> = (event: Event<Subjects, Name>) => void | Effect.Effect<void>;
52
- /**
53
- * @since 0.1.0
54
- * @category Models
55
- */
56
- type Unsubscribe = () => void;
57
- /**
58
- * @since 0.1.0
59
- * @category Models
60
- */
61
- interface AbilityRef<Subjects extends SubjectMap> extends Equal.Equal, Inspectable.Inspectable, Pipeable.Pipeable {
62
- readonly [TypeId]: TypeId;
63
- readonly current: Ability<Subjects, AnyRule>;
64
- /** @internal */
65
- readonly handlers: {
66
- readonly update: Array<EventHandler<Subjects, "update">>;
67
- readonly updated: Array<EventHandler<Subjects, "updated">>;
68
- };
69
- }
70
- /**
71
- * @since 0.1.0
72
- * @category Constructors
73
- */
74
- declare const make: <Subjects extends SubjectMap, Rules extends AnyRule>(ability: Ability<Subjects, Rules>) => AbilityRef<Subjects>;
75
- /**
76
- * @since 0.1.0
77
- * @category Guards
78
- */
79
- declare const isAbilityRef: (value: unknown) => value is AbilityRef<SubjectMap>;
80
- /**
81
- * @since 0.1.0
82
- * @category Accessors
83
- */
84
- declare const get: <Subjects extends SubjectMap>(self: AbilityRef<Subjects>) => Ability<Subjects, AnyRule>;
85
- /**
86
- * @since 0.1.0
87
- * @category Combinators
88
- */
89
- declare const set: {
90
- <Subjects extends SubjectMap, Rules extends AnyRule>(ability: Ability<Subjects, Rules>): (self: AbilityRef<Subjects>) => Effect.Effect<void>;
91
- <Subjects extends SubjectMap, Rules extends AnyRule>(self: AbilityRef<Subjects>, ability: Ability<Subjects, Rules>): Effect.Effect<void>;
92
- };
93
- /**
94
- * @since 0.1.0
95
- * @category Combinators
96
- */
97
- declare const update: {
98
- <Subjects extends SubjectMap, Rules extends AnyRule>(next: Ability<Subjects, Rules> | Iterable<RawRule<string, RuleSubject<Subjects>, string, MongoCondition<SubjectUnion<Subjects>>>>): (self: AbilityRef<Subjects>) => Effect.Effect<void, AliasError | RawRuleError>;
99
- <Subjects extends SubjectMap, Rules extends AnyRule>(self: AbilityRef<Subjects>, next: Ability<Subjects, Rules> | Iterable<RawRule<string, RuleSubject<Subjects>, string, MongoCondition<SubjectUnion<Subjects>>>>): Effect.Effect<void, AliasError | RawRuleError>;
100
- };
101
- /**
102
- * @since 0.1.0
103
- * @category Combinators
104
- */
105
- declare const on: {
106
- <Subjects extends SubjectMap, Name extends EventName>(name: Name, handler: EventHandler<Subjects, Name>): (self: AbilityRef<Subjects>) => Effect.Effect<Unsubscribe>;
107
- <Subjects extends SubjectMap, Name extends EventName>(self: AbilityRef<Subjects>, name: Name, handler: EventHandler<Subjects, Name>): Effect.Effect<Unsubscribe>;
108
- };
109
- //#endregion
110
- export { AbilityRef, AbilityRef_d_exports, Event, EventHandler, EventName, TypeId, Unsubscribe, UpdateEvent, UpdatedEvent, get, isAbilityRef, make, on, set, update };