@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.
- package/README.md +425 -95
- package/dist/Ability.d.ts +46 -71
- package/dist/Ability.js +12 -16
- package/dist/AbilityExtra.d.ts +3 -3
- package/dist/AbilityExtra.js +0 -8
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/internal/ability.d.ts +1 -25
- package/dist/internal/ability.js +77 -151
- package/package.json +10 -12
- package/dist/AbilityRef.d.ts +0 -110
- package/dist/AbilityRef.js +0 -160
package/dist/internal/ability.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cloneSerializable } from "./serializable.js";
|
|
2
|
-
import {
|
|
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
|
|
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
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (
|
|
245
|
-
|
|
246
|
-
|
|
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
|
|
236
|
+
const buildCaslState = (rules, aliases) => {
|
|
260
237
|
const resolver = createAliasResolver(aliases ?? {});
|
|
261
|
-
const
|
|
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
|
|
265
|
-
|
|
266
|
-
|
|
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
|
|
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
|
|
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[
|
|
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
|
|
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
|
|
422
|
-
const
|
|
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: () =>
|
|
382
|
+
try: () => candidate.caslRule.matchesConditions(request.value),
|
|
435
383
|
catch: (cause) => makeConditionError(request, cause)
|
|
436
384
|
});
|
|
437
385
|
});
|
|
438
|
-
const
|
|
439
|
-
if (
|
|
440
|
-
|
|
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
|
|
448
|
-
|
|
449
|
-
if (
|
|
450
|
-
return
|
|
451
|
-
}
|
|
452
|
-
const
|
|
453
|
-
const
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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) =>
|
|
466
|
-
const
|
|
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
|
|
407
|
+
const candidates = collectRuleCandidates(self, request, "descending");
|
|
469
408
|
let index = 0;
|
|
470
|
-
while (index <
|
|
471
|
-
const
|
|
472
|
-
if (
|
|
473
|
-
if (yield* matchesCandidateRule(
|
|
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(
|
|
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
|
-
|
|
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: () =>
|
|
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
|
|
586
|
-
const strict = options?.strict !== false;
|
|
522
|
+
const toRawRules = Effect.fnUntraced(function* (self) {
|
|
587
523
|
const rawRules = [];
|
|
588
|
-
|
|
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
|
|
533
|
+
const candidates = possibleRuleCandidatesForDefinitionOrder(self, runtimeRequest);
|
|
606
534
|
const fields = /* @__PURE__ */ new Set();
|
|
607
535
|
let index = 0;
|
|
608
|
-
while (index <
|
|
609
|
-
const
|
|
610
|
-
if (
|
|
611
|
-
if (yield* matchesConditions(
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
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,
|
|
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.
|
|
4
|
-
"description": "
|
|
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
|
}
|
package/dist/AbilityRef.d.ts
DELETED
|
@@ -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 };
|