@nmnmcc/ability 0.2.0 → 0.3.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.
Files changed (83) hide show
  1. package/README.md +322 -469
  2. package/dist/Authorizer.d.ts +216 -0
  3. package/dist/Authorizer.d.ts.map +1 -0
  4. package/dist/Authorizer.js +55 -0
  5. package/dist/Authorizer.js.map +1 -0
  6. package/dist/Condition.d.ts +536 -0
  7. package/dist/Condition.d.ts.map +1 -0
  8. package/dist/Condition.js +141 -0
  9. package/dist/Condition.js.map +1 -0
  10. package/dist/Decision.d.ts +131 -0
  11. package/dist/Decision.d.ts.map +1 -0
  12. package/dist/Decision.js +83 -0
  13. package/dist/Decision.js.map +1 -0
  14. package/dist/Domain.d.ts +148 -0
  15. package/dist/Domain.d.ts.map +1 -0
  16. package/dist/Domain.js +34 -0
  17. package/dist/Domain.js.map +1 -0
  18. package/dist/HttpApi.d.ts +129 -0
  19. package/dist/HttpApi.d.ts.map +1 -0
  20. package/dist/HttpApi.js +95 -0
  21. package/dist/HttpApi.js.map +1 -0
  22. package/dist/Policy.d.ts +429 -0
  23. package/dist/Policy.d.ts.map +1 -0
  24. package/dist/Policy.js +162 -0
  25. package/dist/Policy.js.map +1 -0
  26. package/dist/Principal.d.ts +35 -0
  27. package/dist/Principal.d.ts.map +1 -0
  28. package/dist/Principal.js +37 -0
  29. package/dist/Principal.js.map +1 -0
  30. package/dist/Resource.d.ts +213 -0
  31. package/dist/Resource.d.ts.map +1 -0
  32. package/dist/Resource.js +71 -0
  33. package/dist/Resource.js.map +1 -0
  34. package/dist/Rule.d.ts +189 -0
  35. package/dist/Rule.d.ts.map +1 -0
  36. package/dist/Rule.js +50 -0
  37. package/dist/Rule.js.map +1 -0
  38. package/dist/_virtual/_rolldown/runtime.js +1 -2
  39. package/dist/index.d.ts +9 -3
  40. package/dist/index.js +9 -3
  41. package/dist/internal/authorizer.d.ts +28 -0
  42. package/dist/internal/authorizer.d.ts.map +1 -0
  43. package/dist/internal/authorizer.js +153 -0
  44. package/dist/internal/authorizer.js.map +1 -0
  45. package/dist/internal/condition.d.ts +11 -0
  46. package/dist/internal/condition.d.ts.map +1 -0
  47. package/dist/internal/condition.js +393 -0
  48. package/dist/internal/condition.js.map +1 -0
  49. package/dist/internal/domain.js +34 -0
  50. package/dist/internal/domain.js.map +1 -0
  51. package/dist/internal/httpApi.js +41 -0
  52. package/dist/internal/httpApi.js.map +1 -0
  53. package/dist/internal/policy.js +524 -0
  54. package/dist/internal/policy.js.map +1 -0
  55. package/dist/internal/resource.js +66 -0
  56. package/dist/internal/resource.js.map +1 -0
  57. package/dist/internal/rule.js +131 -0
  58. package/dist/internal/rule.js.map +1 -0
  59. package/package.json +61 -72
  60. package/src/Authorizer.ts +330 -0
  61. package/src/Condition.ts +706 -0
  62. package/src/Decision.ts +117 -0
  63. package/src/Domain.ts +189 -0
  64. package/src/HttpApi.ts +148 -0
  65. package/src/Policy.ts +575 -0
  66. package/src/Principal.ts +41 -0
  67. package/src/Resource.ts +290 -0
  68. package/src/Rule.ts +265 -0
  69. package/src/index.ts +57 -0
  70. package/src/internal/authorizer.ts +271 -0
  71. package/src/internal/condition.ts +607 -0
  72. package/src/internal/domain.ts +56 -0
  73. package/src/internal/httpApi.ts +84 -0
  74. package/src/internal/policy.ts +815 -0
  75. package/src/internal/resource.ts +98 -0
  76. package/src/internal/rule.ts +203 -0
  77. package/dist/Ability.d.ts +0 -491
  78. package/dist/Ability.js +0 -207
  79. package/dist/AbilityExtra.d.ts +0 -79
  80. package/dist/AbilityExtra.js +0 -187
  81. package/dist/internal/ability.d.ts +0 -90
  82. package/dist/internal/ability.js +0 -550
  83. package/dist/internal/serializable.js +0 -22
@@ -1,550 +0,0 @@
1
- import { cloneSerializable } from "./serializable.js";
2
- import { createMongoAbility, mongoQueryMatcher } from "@casl/ability";
3
- import * as Data from "effect/Data";
4
- import * as Effect from "effect/Effect";
5
- import * as Equal from "effect/Equal";
6
- import * as Formatter from "effect/Formatter";
7
- import { dual } from "effect/Function";
8
- import * as Hash from "effect/Hash";
9
- import * as Inspectable from "effect/Inspectable";
10
- import * as Pipeable from "effect/Pipeable";
11
- import * as Predicate from "effect/Predicate";
12
- //#region src/internal/ability.ts
13
- /**
14
- * @since 0.1.0
15
- */
16
- /**
17
- * @internal
18
- */
19
- const TypeId = Symbol.for("@nmnmcc/ability/Ability");
20
- /**
21
- * @internal
22
- */
23
- const SubjectTypeId = Symbol.for("@nmnmcc/ability/Subject");
24
- /**
25
- * @internal
26
- */
27
- const RuleTypeId = Symbol.for("@nmnmcc/ability/Rule");
28
- const anyAction = "manage";
29
- const anySubject = "all";
30
- const CaslStateTypeId = Symbol.for("@nmnmcc/ability/Ability/caslState");
31
- /**
32
- * @since 0.1.0
33
- * @category Errors
34
- */
35
- var AuthorizationError = class extends Data.TaggedError("AuthorizationError") {};
36
- /**
37
- * @since 0.1.0
38
- * @category Errors
39
- */
40
- var ConditionError = class extends Data.TaggedError("ConditionError") {};
41
- /**
42
- * @since 0.1.0
43
- * @category Errors
44
- */
45
- var RawRuleError = class extends Data.TaggedError("RawRuleError") {};
46
- /**
47
- * @since 0.1.0
48
- * @category Errors
49
- */
50
- var SubjectDetectionError = class extends Data.TaggedError("SubjectDetectionError") {};
51
- /**
52
- * @since 0.1.0
53
- * @category Errors
54
- */
55
- var AliasError = class extends Data.TaggedError("AliasError") {};
56
- /**
57
- * @since 0.1.0
58
- * @category Errors
59
- */
60
- var QueryGenerationError = class extends Data.TaggedError("QueryGenerationError") {};
61
- const copyArray = (value) => Object.freeze(Array.from(value));
62
- const wrapArray = (value) => Array.isArray(value) ? value : [value];
63
- const normalizeArray = (value) => Array.isArray(value) ? copyArray(value) : value;
64
- const unique = (values) => Object.freeze(Array.from(new Set(values)));
65
- const aliasError = (reason, action) => {
66
- const error = { reason };
67
- if (action !== void 0) error.action = action;
68
- return new AliasError(error);
69
- };
70
- const normalizeActionAliases = (aliases) => {
71
- if (aliases === void 0) return Object.freeze({});
72
- const normalized = {};
73
- for (const action of Object.keys(aliases)) {
74
- if (action === anyAction) throw aliasError(`Cannot use "${anyAction}" as an action alias`, action);
75
- const targets = wrapArray(aliases[action]);
76
- if (targets.length === 0) throw aliasError("Action alias cannot target an empty action list", action);
77
- for (const target of targets) if (target === anyAction) throw aliasError(`Cannot create an alias to reserved action "${anyAction}"`, action);
78
- normalized[action] = copyArray(targets);
79
- }
80
- const visiting = /* @__PURE__ */ new Set();
81
- const visited = /* @__PURE__ */ new Set();
82
- const visit = (action, path) => {
83
- if (visited.has(action)) return;
84
- if (visiting.has(action)) throw aliasError(`Detected cyclic action alias: ${[...path, action].join(" -> ")}`, action);
85
- const targets = normalized[action];
86
- if (targets === void 0) return;
87
- visiting.add(action);
88
- for (const target of targets) {
89
- if (target === action) throw aliasError(`Action alias "${action}" cannot reference itself`, action);
90
- visit(target, [...path, action]);
91
- }
92
- visiting.delete(action);
93
- visited.add(action);
94
- };
95
- for (const action of Object.keys(normalized)) visit(action, []);
96
- return Object.freeze(normalized);
97
- };
98
- const expandAction = (aliases, action, output) => {
99
- if (output.has(action)) return;
100
- output.add(action);
101
- const targets = aliases[action];
102
- if (targets === void 0) return;
103
- for (const target of targets) expandAction(aliases, target, output);
104
- };
105
- /**
106
- * @internal
107
- */
108
- const createAliasResolver = (aliases) => {
109
- const normalized = normalizeActionAliases(aliases);
110
- return (action) => {
111
- const actions = /* @__PURE__ */ new Set();
112
- for (const value of wrapArray(action)) expandAction(normalized, value, actions);
113
- return unique(actions);
114
- };
115
- };
116
- function* ruleIterator(rule) {
117
- return yield rule;
118
- }
119
- const inspectableToString = function() {
120
- return Formatter.format(this.toJSON(), {
121
- ignoreToString: true,
122
- space: 2
123
- });
124
- };
125
- const inspectableNode = function() {
126
- return this.toJSON();
127
- };
128
- const ruleHashInput = (rule) => ({
129
- _tag: rule._tag,
130
- action: rule.action,
131
- subject: rule.subject,
132
- fields: rule.fields,
133
- conditions: rule.conditions,
134
- reason: rule.reason
135
- });
136
- const RuleProto = {
137
- ...Pipeable.Prototype,
138
- [RuleTypeId]: RuleTypeId,
139
- [Symbol.iterator]() {
140
- return ruleIterator(this);
141
- },
142
- asEffect() {
143
- return Effect.succeed(this);
144
- },
145
- [Equal.symbol](that) {
146
- return isRule(that) && Equal.equals(ruleHashInput(this), ruleHashInput(that));
147
- },
148
- [Hash.symbol]() {
149
- return Hash.structure(ruleHashInput(this));
150
- },
151
- toString: inspectableToString,
152
- toJSON() {
153
- const json = {
154
- _tag: this._tag,
155
- action: this.action,
156
- subject: this.subject
157
- };
158
- if (this.fields !== void 0) json.fields = this.fields;
159
- if (this.conditions !== void 0) json.conditions = this.conditions;
160
- if (this.reason !== void 0) json.reason = this.reason;
161
- return json;
162
- },
163
- [Inspectable.NodeInspectSymbol]: inspectableNode
164
- };
165
- const AbilityProto = {
166
- ...Pipeable.Prototype,
167
- [TypeId]: TypeId,
168
- [Equal.symbol](that) {
169
- return isAbility(that) && Equal.equals(this.rules, that.rules) && Equal.equals(this.options, that.options);
170
- },
171
- [Hash.symbol]() {
172
- return Hash.structure({
173
- rules: this.rules,
174
- options: this.options
175
- });
176
- },
177
- toString: inspectableToString,
178
- toJSON() {
179
- return {
180
- _id: "Ability",
181
- rules: this.rules,
182
- options: this.options
183
- };
184
- },
185
- [Inspectable.NodeInspectSymbol]: inspectableNode
186
- };
187
- const SubjectProto = {
188
- ...Pipeable.Prototype,
189
- [SubjectTypeId]: SubjectTypeId,
190
- [Equal.symbol](that) {
191
- return isSubject(that) && this.subject === that.subject && Equal.equals(this.value, that.value);
192
- },
193
- [Hash.symbol]() {
194
- return Hash.structure({
195
- subject: this.subject,
196
- value: this.value
197
- });
198
- },
199
- toString: inspectableToString,
200
- toJSON() {
201
- return {
202
- _id: "Subject",
203
- subject: this.subject,
204
- value: this.value
205
- };
206
- },
207
- [Inspectable.NodeInspectSymbol]: inspectableNode
208
- };
209
- const freezeOptions = (options) => {
210
- const copy = {};
211
- if (options?.detectSubjectType !== void 0) copy.detectSubjectType = options.detectSubjectType;
212
- if (options?.actionAliases !== void 0) copy.actionAliases = normalizeActionAliases(options.actionAliases);
213
- return Object.freeze(copy);
214
- };
215
- const freezeRule = (rule) => {
216
- const copy = Object.create(RuleProto);
217
- copy._tag = rule._tag;
218
- copy.action = normalizeArray(rule.action);
219
- copy.subject = normalizeArray(rule.subject);
220
- if (rule.fields !== void 0) copy.fields = copyArray(rule.fields);
221
- if (rule.conditions !== void 0) copy.conditions = cloneSerializable(rule.conditions);
222
- if (rule.reason !== void 0) copy.reason = rule.reason;
223
- return Object.freeze(copy);
224
- };
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;
235
- };
236
- const buildCaslState = (rules, aliases) => {
237
- const resolver = createAliasResolver(aliases ?? {});
238
- const rulesByRawRule = /* @__PURE__ */ new WeakMap();
239
- const indexByRule = /* @__PURE__ */ new Map();
240
- const rawRules = [];
241
- let ruleIndex = 0;
242
- for (const rule of rules) {
243
- const rawRule = toCaslRawRule(rule);
244
- rawRules.push(rawRule);
245
- rulesByRawRule.set(rawRule, rule);
246
- indexByRule.set(rule, ruleIndex);
247
- ruleIndex = ruleIndex + 1;
248
- }
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
- });
258
- };
259
- const getCaslState = (self) => self[CaslStateTypeId];
260
- /**
261
- * @internal
262
- */
263
- const make = (rules, options) => {
264
- const self = Object.create(AbilityProto);
265
- self.rules = Object.freeze(Array.from(rules, freezeRule));
266
- self.options = freezeOptions(options);
267
- self[CaslStateTypeId] = buildCaslState(self.rules, self.options.actionAliases);
268
- return Object.freeze(self);
269
- };
270
- const makeRule = (kind, action, subjectName, options) => {
271
- const rule = Object.create(RuleProto);
272
- rule._tag = kind;
273
- rule.action = normalizeArray(action);
274
- rule.subject = normalizeArray(subjectName);
275
- if (options?.fields !== void 0) rule.fields = copyArray(wrapArray(options.fields));
276
- if (options?.conditions !== void 0) rule.conditions = cloneSerializable(options.conditions);
277
- if (options?.reason !== void 0) rule.reason = options.reason;
278
- return Object.freeze(rule);
279
- };
280
- const builder = () => ({
281
- allow: (action, subjectName, options) => makeRule("Allow", action, subjectName, options),
282
- deny: (action, subjectName, options) => makeRule("Deny", action, subjectName, options)
283
- });
284
- /**
285
- * @internal
286
- */
287
- const define = (() => (body, options) => {
288
- const rules = [];
289
- const iterator = body(builder());
290
- let state = iterator.next();
291
- while (!state.done) {
292
- rules.push(state.value);
293
- state = iterator.next(state.value);
294
- }
295
- return make(rules, options);
296
- });
297
- /**
298
- * @internal
299
- */
300
- const subject = (subjectName, value) => {
301
- const wrapped = Object.create(SubjectProto);
302
- wrapped._tag = "Subject";
303
- wrapped.subject = subjectName;
304
- wrapped.value = value;
305
- return Object.freeze(wrapped);
306
- };
307
- /**
308
- * @internal
309
- */
310
- const isSubject = (value) => Predicate.hasProperty(value, SubjectTypeId);
311
- /**
312
- * @internal
313
- */
314
- const isRule = (value) => Predicate.hasProperty(value, RuleTypeId);
315
- /**
316
- * @internal
317
- */
318
- const isAbility = (value) => Predicate.hasProperty(value, TypeId);
319
- /**
320
- * @internal
321
- */
322
- const unwrapSubject = (value) => isSubject(value) ? value.value : value;
323
- const detectFromConstructor = (value) => {
324
- const constructor = value.constructor;
325
- if (typeof constructor?.modelName === "string" && constructor.modelName.length > 0) return constructor.modelName;
326
- if (typeof constructor?.name === "string" && constructor.name.length > 0) return constructor.name;
327
- };
328
- /**
329
- * @internal
330
- */
331
- const detectSubjectType = Effect.fnUntraced(function* (self, value) {
332
- if (isSubject(value)) return value.subject;
333
- if (self.options.detectSubjectType !== void 0) return yield* Effect.try({
334
- try: () => self.options.detectSubjectType?.(value),
335
- catch: (cause) => new SubjectDetectionError({
336
- reason: "Failed to detect subject type with the configured detector",
337
- cause
338
- })
339
- });
340
- const detected = detectFromConstructor(value);
341
- if (detected !== void 0) return detected;
342
- return yield* Effect.fail(new SubjectDetectionError({ reason: "Unable to detect subject type" }));
343
- });
344
- const hasOwnSubject = (request) => "subject" in request && typeof request.subject === "string";
345
- const hasOwnValue = (request) => "value" in request && typeof request.value === "object" && request.value !== null;
346
- const toRuntimeRequest = Effect.fnUntraced(function* (self, request) {
347
- const runtime = {
348
- action: request.action,
349
- subject: ""
350
- };
351
- if (hasOwnSubject(request)) runtime.subject = request.subject;
352
- else if (hasOwnValue(request)) runtime.subject = yield* detectSubjectType(self, request.value);
353
- else return yield* Effect.fail(new SubjectDetectionError({ reason: "A check request must include either a subject or a value" }));
354
- if (hasOwnValue(request)) runtime.value = unwrapSubject(request.value);
355
- if (request.field !== void 0) runtime.field = request.field;
356
- return runtime;
357
- });
358
- const toRuntimeSubjectRequest = Effect.fnUntraced(function* (self, request) {
359
- const runtime = { subject: "" };
360
- if (hasOwnSubject(request)) runtime.subject = request.subject;
361
- else if (hasOwnValue(request)) runtime.subject = yield* detectSubjectType(self, request.value);
362
- else return yield* Effect.fail(new SubjectDetectionError({ reason: "A subject query request must include either a subject or a value" }));
363
- if (hasOwnValue(request)) runtime.value = unwrapSubject(request.value);
364
- return runtime;
365
- });
366
- const matchesField = (candidate, request) => candidate.caslRule.matchesField(request.field);
367
- const isEmptyCondition = (conditions) => Object.keys(conditions).length === 0;
368
- const makeConditionError = (request, cause) => {
369
- const error = {
370
- action: request.action,
371
- subject: request.subject,
372
- cause
373
- };
374
- if (request.field !== void 0) error.field = request.field;
375
- return new ConditionError(error);
376
- };
377
- const matchesConditions = Effect.fnUntraced(function* (candidate, request) {
378
- const { rule } = candidate;
379
- if (rule.conditions === void 0) return true;
380
- if (request.value === void 0) return rule._tag === "Allow" || isEmptyCondition(rule.conditions);
381
- return yield* Effect.try({
382
- try: () => candidate.caslRule.matchesConditions(request.value),
383
- catch: (cause) => makeConditionError(request, cause)
384
- });
385
- });
386
- const matchesCandidateRule = Effect.fnUntraced(function* (candidate, request) {
387
- if (!matchesField(candidate, request)) return false;
388
- return yield* matchesConditions(candidate, request);
389
- });
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);
403
- };
404
- const possibleRulesForRequest = (self, request) => Object.freeze(collectRuleCandidates(self, request, "descending").map((candidate) => candidate.rule));
405
- const possibleRuleCandidatesForDefinitionOrder = (self, request) => collectRuleCandidates(self, request, "ascending");
406
- const relevantRuleForEffect = Effect.fnUntraced(function* (self, request) {
407
- const candidates = collectRuleCandidates(self, request, "descending");
408
- let index = 0;
409
- while (index < candidates.length) {
410
- const candidate = candidates[index];
411
- if (candidate !== void 0) {
412
- if (yield* matchesCandidateRule(candidate, request)) return candidate.rule;
413
- }
414
- index = index + 1;
415
- }
416
- });
417
- const makeAuthorizationError = (request, rule) => {
418
- const error = {
419
- action: request.action,
420
- subject: request.subject
421
- };
422
- if (request.field !== void 0) error.field = request.field;
423
- if (rule?.reason !== void 0) error.reason = rule.reason;
424
- return new AuthorizationError(error);
425
- };
426
- /**
427
- * @internal
428
- */
429
- const check = dual(2, Effect.fnUntraced(function* (self, request) {
430
- const runtimeRequest = yield* toRuntimeRequest(self, request);
431
- const rule = yield* relevantRuleForEffect(self, runtimeRequest);
432
- if (rule?._tag === "Allow") return;
433
- return yield* Effect.fail(makeAuthorizationError(runtimeRequest, rule));
434
- }));
435
- /**
436
- * @internal
437
- */
438
- const possibleRulesFor = dual(2, Effect.fnUntraced(function* (self, request) {
439
- return possibleRulesForRequest(self, yield* toRuntimeRequest(self, request));
440
- }));
441
- /**
442
- * @internal
443
- */
444
- const rulesFor = dual(2, Effect.fnUntraced(function* (self, request) {
445
- const runtimeRequest = yield* toRuntimeRequest(self, request);
446
- return Object.freeze(collectRuleCandidates(self, runtimeRequest, "descending").filter((candidate) => matchesField(candidate, runtimeRequest)).map((candidate) => candidate.rule));
447
- }));
448
- /**
449
- * @internal
450
- */
451
- const relevantRuleFor = dual(2, Effect.fnUntraced(function* (self, request) {
452
- return yield* relevantRuleForEffect(self, yield* toRuntimeRequest(self, request));
453
- }));
454
- /**
455
- * @internal
456
- */
457
- const actionsFor = dual(2, Effect.fnUntraced(function* (self, request) {
458
- const runtimeRequest = yield* toRuntimeSubjectRequest(self, request);
459
- return unique(getCaslState(self).ability.actionsFor(runtimeRequest.subject));
460
- }));
461
- const rawRuleError = (reason, ruleIndex, cause) => {
462
- const error = {
463
- reason,
464
- ruleIndex
465
- };
466
- if (cause !== void 0) error.cause = cause;
467
- return new RawRuleError(error);
468
- };
469
- const validateArrayValue = (value, name, index) => {
470
- if (Array.isArray(value) && value.length === 0) return Effect.fail(rawRuleError(`rawRule.${name} cannot be an empty array`, index));
471
- return Effect.void;
472
- };
473
- const validateRawConditions = (conditions, index) => {
474
- if (conditions === void 0) return Effect.void;
475
- return Effect.asVoid(Effect.try({
476
- try: () => mongoQueryMatcher(conditions),
477
- catch: (cause) => rawRuleError("rawRule.conditions cannot be compiled", index, cause)
478
- }));
479
- };
480
- /**
481
- * @internal
482
- */
483
- const fromRawRules = Effect.fnUntraced(function* (rules, options) {
484
- const built = [];
485
- let index = 0;
486
- for (const rawRule of rules) {
487
- yield* validateArrayValue(rawRule.action, "action", index);
488
- yield* validateArrayValue(rawRule.subject, "subject", index);
489
- if (rawRule.fields !== void 0) yield* validateArrayValue(rawRule.fields, "fields", index);
490
- yield* validateRawConditions(rawRule.conditions, index);
491
- const ruleOptions = {};
492
- if (rawRule.fields !== void 0) ruleOptions.fields = rawRule.fields;
493
- if (rawRule.conditions !== void 0) ruleOptions.conditions = rawRule.conditions;
494
- if (rawRule.reason !== void 0) ruleOptions.reason = rawRule.reason;
495
- built.push(makeRule(rawRule.inverted === true ? "Deny" : "Allow", rawRule.action, rawRule.subject, ruleOptions));
496
- index = index + 1;
497
- }
498
- return yield* Effect.try({
499
- try: () => make(built, options),
500
- catch: (cause) => cause instanceof AliasError ? cause : rawRuleError("Unable to construct Ability from raw rules", index, cause)
501
- });
502
- });
503
- const updateEffect = (self, rules) => fromRawRules(rules, self.options);
504
- /**
505
- * @internal
506
- */
507
- const update = dual(2, updateEffect);
508
- const ruleToRaw = (rule) => {
509
- const raw = {
510
- action: cloneSerializable(rule.action),
511
- subject: cloneSerializable(rule.subject)
512
- };
513
- if (rule.fields !== void 0) raw.fields = cloneSerializable(rule.fields);
514
- if (rule.conditions !== void 0) raw.conditions = cloneSerializable(rule.conditions);
515
- if (rule._tag === "Deny") raw.inverted = true;
516
- if (rule.reason !== void 0) raw.reason = rule.reason;
517
- return Object.freeze(raw);
518
- };
519
- /**
520
- * @internal
521
- */
522
- const toRawRules = Effect.fnUntraced(function* (self) {
523
- const rawRules = [];
524
- for (const rule of self.rules) rawRules.push(ruleToRaw(rule));
525
- return Object.freeze(rawRules);
526
- });
527
- const fieldsForRule = (rule, options) => rule.fields ?? options.fieldsFrom(rule);
528
- /**
529
- * @internal
530
- */
531
- const permittedFields = dual(3, Effect.fnUntraced(function* (self, request, options) {
532
- const runtimeRequest = yield* toRuntimeRequest(self, request);
533
- const candidates = possibleRuleCandidatesForDefinitionOrder(self, runtimeRequest);
534
- const fields = /* @__PURE__ */ new Set();
535
- let index = 0;
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);
543
- }
544
- }
545
- index = index + 1;
546
- }
547
- return Object.freeze(Array.from(fields));
548
- }));
549
- //#endregion
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 };
@@ -1,22 +0,0 @@
1
- //#region src/internal/serializable.ts
2
- /**
3
- * @since 0.1.0
4
- */
5
- /**
6
- * @internal
7
- */
8
- const isPlainObject = (value) => value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype;
9
- /**
10
- * @internal
11
- */
12
- const cloneSerializable = (value) => {
13
- if (Array.isArray(value)) return Object.freeze(value.map(cloneSerializable));
14
- if (isPlainObject(value)) {
15
- const output = {};
16
- for (const key of Object.keys(value)) output[key] = cloneSerializable(value[key]);
17
- return Object.freeze(output);
18
- }
19
- return value;
20
- };
21
- //#endregion
22
- export { cloneSerializable, isPlainObject };