@nmnmcc/ability 0.1.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/LICENSE +201 -0
- package/README.md +251 -0
- package/dist/Ability.d.ts +516 -0
- package/dist/Ability.js +211 -0
- package/dist/AbilityExtra.d.ts +79 -0
- package/dist/AbilityExtra.js +195 -0
- package/dist/AbilityRef.d.ts +110 -0
- package/dist/AbilityRef.js +160 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/internal/ability.d.ts +114 -0
- package/dist/internal/ability.js +624 -0
- package/dist/internal/serializable.js +22 -0
- package/package.json +75 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { fromRawRules, isAbility } from "./Ability.js";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Equal from "effect/Equal";
|
|
5
|
+
import * as Formatter from "effect/Formatter";
|
|
6
|
+
import { dual } from "effect/Function";
|
|
7
|
+
import * as Hash from "effect/Hash";
|
|
8
|
+
import * as Inspectable from "effect/Inspectable";
|
|
9
|
+
import * as Pipeable from "effect/Pipeable";
|
|
10
|
+
import * as Predicate from "effect/Predicate";
|
|
11
|
+
//#region src/AbilityRef.ts
|
|
12
|
+
/**
|
|
13
|
+
* Mutable ability references for applications that need to swap immutable
|
|
14
|
+
* abilities at runtime.
|
|
15
|
+
*
|
|
16
|
+
* Mental model:
|
|
17
|
+
* - `AbilityRef` is the explicit mutable boundary for this package.
|
|
18
|
+
* - The current ability can be replaced while subscribers observe update events.
|
|
19
|
+
* - All policy evaluation still happens against immutable `Ability` values.
|
|
20
|
+
*
|
|
21
|
+
* Common tasks:
|
|
22
|
+
* - Create a reference with {@link make}.
|
|
23
|
+
* - Read the current ability with {@link get}.
|
|
24
|
+
* - Replace the current ability with {@link set} or {@link update}.
|
|
25
|
+
* - Subscribe to update events with {@link on}.
|
|
26
|
+
*
|
|
27
|
+
* Gotchas:
|
|
28
|
+
* - Event handlers run in registration order.
|
|
29
|
+
* - `update` accepts raw rules and reuses the current ability options.
|
|
30
|
+
* - `AbilityRef` equality is referential because the current value is mutable.
|
|
31
|
+
*
|
|
32
|
+
* @since 0.1.0
|
|
33
|
+
* @module
|
|
34
|
+
*/
|
|
35
|
+
var AbilityRef_exports = /* @__PURE__ */ __exportAll({
|
|
36
|
+
TypeId: () => TypeId,
|
|
37
|
+
get: () => get,
|
|
38
|
+
isAbilityRef: () => isAbilityRef,
|
|
39
|
+
make: () => make,
|
|
40
|
+
on: () => on,
|
|
41
|
+
set: () => set,
|
|
42
|
+
update: () => update
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* @since 0.1.0
|
|
46
|
+
* @category Type Ids
|
|
47
|
+
*/
|
|
48
|
+
const TypeId = Symbol.for("@nmnmcc/ability/AbilityRef");
|
|
49
|
+
const RefIdentityTypeId = Symbol.for("@nmnmcc/ability/AbilityRef/identity");
|
|
50
|
+
let refIdentity = 0;
|
|
51
|
+
const inspectableToString = function() {
|
|
52
|
+
return Formatter.format(this.toJSON(), {
|
|
53
|
+
ignoreToString: true,
|
|
54
|
+
space: 2
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const inspectableNode = function() {
|
|
58
|
+
return this.toJSON();
|
|
59
|
+
};
|
|
60
|
+
const AbilityRefProto = {
|
|
61
|
+
...Pipeable.Prototype,
|
|
62
|
+
[TypeId]: TypeId,
|
|
63
|
+
[Equal.symbol](that) {
|
|
64
|
+
return isAbilityRef(that) && this[RefIdentityTypeId] === that[RefIdentityTypeId];
|
|
65
|
+
},
|
|
66
|
+
[Hash.symbol]() {
|
|
67
|
+
return Hash.number(this[RefIdentityTypeId]);
|
|
68
|
+
},
|
|
69
|
+
toString: inspectableToString,
|
|
70
|
+
toJSON() {
|
|
71
|
+
return {
|
|
72
|
+
_id: "AbilityRef",
|
|
73
|
+
current: this.current
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
[Inspectable.NodeInspectSymbol]: inspectableNode
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* @since 0.1.0
|
|
80
|
+
* @category Constructors
|
|
81
|
+
*/
|
|
82
|
+
const make = (ability) => {
|
|
83
|
+
const ref = Object.create(AbilityRefProto);
|
|
84
|
+
ref.current = ability;
|
|
85
|
+
ref[RefIdentityTypeId] = refIdentity;
|
|
86
|
+
refIdentity = refIdentity + 1;
|
|
87
|
+
ref.handlers = {
|
|
88
|
+
update: [],
|
|
89
|
+
updated: []
|
|
90
|
+
};
|
|
91
|
+
return ref;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* @since 0.1.0
|
|
95
|
+
* @category Guards
|
|
96
|
+
*/
|
|
97
|
+
const isAbilityRef = (value) => Predicate.hasProperty(value, TypeId);
|
|
98
|
+
/**
|
|
99
|
+
* @since 0.1.0
|
|
100
|
+
* @category Accessors
|
|
101
|
+
*/
|
|
102
|
+
const get = (self) => self.current;
|
|
103
|
+
const toEffect = (evaluate) => Effect.flatMap(Effect.sync(evaluate), (result) => Effect.isEffect(result) ? result : Effect.void);
|
|
104
|
+
const emit = Effect.fnUntraced(function* (self, name, event) {
|
|
105
|
+
const handlers = Array.from(self.handlers[name]);
|
|
106
|
+
for (const handler of handlers) yield* toEffect(() => handler(event));
|
|
107
|
+
});
|
|
108
|
+
/**
|
|
109
|
+
* @since 0.1.0
|
|
110
|
+
* @category Combinators
|
|
111
|
+
*/
|
|
112
|
+
const setEffect = Effect.fnUntraced(function* (self, ability) {
|
|
113
|
+
const previous = self.current;
|
|
114
|
+
const next = ability;
|
|
115
|
+
const mutable = self;
|
|
116
|
+
yield* emit(self, "update", {
|
|
117
|
+
previous,
|
|
118
|
+
next
|
|
119
|
+
});
|
|
120
|
+
mutable.current = next;
|
|
121
|
+
yield* emit(self, "updated", {
|
|
122
|
+
previous,
|
|
123
|
+
current: next
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
/**
|
|
127
|
+
* @since 0.1.0
|
|
128
|
+
* @category Combinators
|
|
129
|
+
*/
|
|
130
|
+
const set = dual(2, setEffect);
|
|
131
|
+
/**
|
|
132
|
+
* @since 0.1.0
|
|
133
|
+
* @category Combinators
|
|
134
|
+
*/
|
|
135
|
+
const update = dual(2, Effect.fnUntraced(function* (self, next) {
|
|
136
|
+
if (Symbol.iterator in Object(next) && !isAbility(next)) return yield* setEffect(self, yield* fromRawRules(next, self.current.options));
|
|
137
|
+
return yield* setEffect(self, next);
|
|
138
|
+
}));
|
|
139
|
+
/**
|
|
140
|
+
* @since 0.1.0
|
|
141
|
+
* @category Combinators
|
|
142
|
+
*/
|
|
143
|
+
const onEffect = (self, name, handler) => Effect.sync(() => {
|
|
144
|
+
const handlers = self.handlers[name];
|
|
145
|
+
handlers.push(handler);
|
|
146
|
+
let active = true;
|
|
147
|
+
return () => {
|
|
148
|
+
if (!active) return;
|
|
149
|
+
active = false;
|
|
150
|
+
const index = handlers.indexOf(handler);
|
|
151
|
+
if (index >= 0) handlers.splice(index, 1);
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
/**
|
|
155
|
+
* @since 0.1.0
|
|
156
|
+
* @category Combinators
|
|
157
|
+
*/
|
|
158
|
+
const on = dual(3, onEffect);
|
|
159
|
+
//#endregion
|
|
160
|
+
export { AbilityRef_exports, TypeId, get, isAbilityRef, make, on, set, update };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Ability_d_exports } from "./Ability.js";
|
|
2
|
+
import { AbilityExtra_d_exports } from "./AbilityExtra.js";
|
|
3
|
+
import { AbilityRef_d_exports } from "./AbilityRef.js";
|
|
4
|
+
export { Ability_d_exports as Ability, AbilityExtra_d_exports as AbilityExtra, AbilityRef_d_exports as AbilityRef };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Ability_exports } from "./Ability.js";
|
|
2
|
+
import { AbilityExtra_exports } from "./AbilityExtra.js";
|
|
3
|
+
import { AbilityRef_exports } from "./AbilityRef.js";
|
|
4
|
+
export { Ability_exports as Ability, AbilityExtra_exports as AbilityExtra, AbilityRef_exports as AbilityRef };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as _$effect_Types0 from "effect/Types";
|
|
2
|
+
import * as _$effect_Cause0 from "effect/Cause";
|
|
3
|
+
|
|
4
|
+
//#region src/internal/ability.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
declare const TypeId: unique symbol;
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
declare const SubjectTypeId: unique symbol;
|
|
13
|
+
/**
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
declare const RuleTypeId: unique symbol;
|
|
17
|
+
declare const AuthorizationError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
18
|
+
readonly _tag: "AuthorizationError";
|
|
19
|
+
} & Readonly<A>;
|
|
20
|
+
/**
|
|
21
|
+
* @since 0.1.0
|
|
22
|
+
* @category Errors
|
|
23
|
+
*/
|
|
24
|
+
declare class AuthorizationError extends AuthorizationError_base<{
|
|
25
|
+
readonly action: string;
|
|
26
|
+
readonly subject: string;
|
|
27
|
+
readonly field?: string;
|
|
28
|
+
readonly reason?: string;
|
|
29
|
+
}> {}
|
|
30
|
+
declare const ConditionError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
31
|
+
readonly _tag: "ConditionError";
|
|
32
|
+
} & Readonly<A>;
|
|
33
|
+
/**
|
|
34
|
+
* @since 0.1.0
|
|
35
|
+
* @category Errors
|
|
36
|
+
*/
|
|
37
|
+
declare class ConditionError extends ConditionError_base<{
|
|
38
|
+
readonly action: string;
|
|
39
|
+
readonly subject: string;
|
|
40
|
+
readonly field?: string;
|
|
41
|
+
readonly cause: unknown;
|
|
42
|
+
}> {}
|
|
43
|
+
declare const PredicateEvaluationError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
44
|
+
readonly _tag: "PredicateEvaluationError";
|
|
45
|
+
} & Readonly<A>;
|
|
46
|
+
/**
|
|
47
|
+
* @since 0.1.0
|
|
48
|
+
* @category Errors
|
|
49
|
+
*/
|
|
50
|
+
declare class PredicateEvaluationError extends PredicateEvaluationError_base<{
|
|
51
|
+
readonly action: string;
|
|
52
|
+
readonly subject: string;
|
|
53
|
+
readonly field?: string;
|
|
54
|
+
readonly cause: unknown;
|
|
55
|
+
}> {}
|
|
56
|
+
declare const SerializationError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
57
|
+
readonly _tag: "SerializationError";
|
|
58
|
+
} & Readonly<A>;
|
|
59
|
+
/**
|
|
60
|
+
* @since 0.1.0
|
|
61
|
+
* @category Errors
|
|
62
|
+
*/
|
|
63
|
+
declare class SerializationError extends SerializationError_base<{
|
|
64
|
+
readonly reason: string;
|
|
65
|
+
readonly ruleIndex?: number;
|
|
66
|
+
}> {}
|
|
67
|
+
declare const RawRuleError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
68
|
+
readonly _tag: "RawRuleError";
|
|
69
|
+
} & Readonly<A>;
|
|
70
|
+
/**
|
|
71
|
+
* @since 0.1.0
|
|
72
|
+
* @category Errors
|
|
73
|
+
*/
|
|
74
|
+
declare class RawRuleError extends RawRuleError_base<{
|
|
75
|
+
readonly reason: string;
|
|
76
|
+
readonly ruleIndex?: number;
|
|
77
|
+
readonly cause?: unknown;
|
|
78
|
+
}> {}
|
|
79
|
+
declare const SubjectDetectionError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
80
|
+
readonly _tag: "SubjectDetectionError";
|
|
81
|
+
} & Readonly<A>;
|
|
82
|
+
/**
|
|
83
|
+
* @since 0.1.0
|
|
84
|
+
* @category Errors
|
|
85
|
+
*/
|
|
86
|
+
declare class SubjectDetectionError extends SubjectDetectionError_base<{
|
|
87
|
+
readonly reason: string;
|
|
88
|
+
readonly cause?: unknown;
|
|
89
|
+
}> {}
|
|
90
|
+
declare const AliasError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
91
|
+
readonly _tag: "AliasError";
|
|
92
|
+
} & Readonly<A>;
|
|
93
|
+
/**
|
|
94
|
+
* @since 0.1.0
|
|
95
|
+
* @category Errors
|
|
96
|
+
*/
|
|
97
|
+
declare class AliasError extends AliasError_base<{
|
|
98
|
+
readonly reason: string;
|
|
99
|
+
readonly action?: string;
|
|
100
|
+
}> {}
|
|
101
|
+
declare const QueryGenerationError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
102
|
+
readonly _tag: "QueryGenerationError";
|
|
103
|
+
} & Readonly<A>;
|
|
104
|
+
/**
|
|
105
|
+
* @since 0.1.0
|
|
106
|
+
* @category Errors
|
|
107
|
+
*/
|
|
108
|
+
declare class QueryGenerationError extends QueryGenerationError_base<{
|
|
109
|
+
readonly reason: string;
|
|
110
|
+
readonly ruleIndex?: number;
|
|
111
|
+
readonly cause?: unknown;
|
|
112
|
+
}> {}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { AliasError, AuthorizationError, ConditionError, PredicateEvaluationError, QueryGenerationError, RawRuleError, RuleTypeId, SerializationError, SubjectDetectionError, SubjectTypeId, TypeId };
|