@via-profit/ability 3.6.4 → 3.7.1
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 +245 -1165
- package/dist/index.cjs +3435 -0
- package/dist/index.d.ts +19 -6
- package/dist/index.js +101 -82
- package/package.json +11 -3
package/dist/index.d.ts
CHANGED
|
@@ -7,13 +7,15 @@ declare const AbilityCompare: {
|
|
|
7
7
|
readonly and: AbilityCompareType;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
type AbilityConditionCode = '=' | '<>' | '>' | '<' | '>=' | '<=' | 'in' | 'not in' | 'contains' | 'not contains' | 'length greater than' | 'length less than' | 'length equals' | 'always' | 'never';
|
|
11
|
-
type AbilityConditionLiteral = 'equals' | 'not_equals' | 'contains' | 'not_contains' | 'in' | 'not_in' | 'greater_than' | 'less_than' | 'less_or_equal' | 'greater_or_equal' | 'length_greater_than' | 'length_less_than' | 'length_equals' | 'always' | 'never';
|
|
10
|
+
type AbilityConditionCode = '=' | '<>' | '>' | '<' | '>=' | '<=' | 'in' | 'not in' | 'contains' | 'not contains' | 'length greater than' | 'length less than' | 'length equals' | 'always' | 'never' | 'defined' | 'not_defined';
|
|
11
|
+
type AbilityConditionLiteral = 'equals' | 'not_equals' | 'contains' | 'not_contains' | 'in' | 'not_in' | 'greater_than' | 'less_than' | 'less_or_equal' | 'greater_or_equal' | 'length_greater_than' | 'length_less_than' | 'length_equals' | 'always' | 'never' | 'defined' | 'not_defined';
|
|
12
12
|
type AbilityConditionType = AbilityConditionCode & {
|
|
13
13
|
__brand: 'AbilityCondition';
|
|
14
14
|
};
|
|
15
15
|
declare const AbilityCondition: {
|
|
16
16
|
readonly equals: AbilityConditionType;
|
|
17
|
+
readonly defined: AbilityConditionType;
|
|
18
|
+
readonly not_defined: AbilityConditionType;
|
|
17
19
|
readonly not_equals: AbilityConditionType;
|
|
18
20
|
readonly greater_than: AbilityConditionType;
|
|
19
21
|
readonly less_than: AbilityConditionType;
|
|
@@ -438,7 +440,7 @@ declare abstract class AbilityStrategy<Resource extends ResourceObject = Record<
|
|
|
438
440
|
|
|
439
441
|
declare class AbilityResult<R extends ResourceObject = Record<string, unknown>, E extends EnvironmentObject = Record<string, unknown>> {
|
|
440
442
|
protected readonly effect: AbilityPolicyEffectType;
|
|
441
|
-
|
|
443
|
+
readonly strategy: AbilityStrategy<R, E>;
|
|
442
444
|
constructor(effect: AbilityPolicyEffectType, strategy: AbilityStrategy<R, E>);
|
|
443
445
|
/**
|
|
444
446
|
* Returns a list of explanations for each policy involved in the ability evaluation.
|
|
@@ -455,13 +457,23 @@ declare class AbilityResult<R extends ResourceObject = Record<string, unknown>,
|
|
|
455
457
|
|
|
456
458
|
interface AbilityResolverOptions<TTags extends string> {
|
|
457
459
|
tags?: readonly TTags[];
|
|
460
|
+
readonly onDeny?: EnforceOnDeny;
|
|
461
|
+
readonly onAllow?: EnforceOnAllow;
|
|
458
462
|
}
|
|
459
463
|
type ExtractResources<P> = P extends AbilityPolicy<infer R, any, any> ? R : never;
|
|
460
464
|
type ExtractEnvironment<P> = P extends AbilityPolicy<any, infer E, any> ? E : never;
|
|
461
465
|
type ExtractPermission<R> = R extends AbilityResolver<infer P, any, any> ? keyof ExtractResources<P> & string : never;
|
|
462
466
|
type ExtractResourceByPermission<P, Perm extends string> = P extends AbilityPolicy<infer R, any, any> ? (Perm extends keyof R ? R[Perm] : never) : never;
|
|
463
467
|
type ExtractEnvironmentByPermission<P, Perm extends string> = P extends AbilityPolicy<any, infer E, any> ? (Perm extends keyof E ? E[Perm] : never) : never;
|
|
468
|
+
type EnforceOptions<R extends ResourceObject = Record<string, unknown>, E extends EnvironmentObject = Record<string, unknown>> = {
|
|
469
|
+
readonly onDeny?: EnforceOnDeny<R, E>;
|
|
470
|
+
readonly onAllow?: EnforceOnDeny<R, E>;
|
|
471
|
+
};
|
|
472
|
+
type EnforceOnDeny<R extends ResourceObject = Record<string, unknown>, E extends EnvironmentObject = Record<string, unknown>> = (result: AbilityResult<R, E>) => void;
|
|
473
|
+
type EnforceOnAllow<R extends ResourceObject = Record<string, unknown>, E extends EnvironmentObject = Record<string, unknown>> = (result: AbilityResult<R, E>) => void;
|
|
464
474
|
declare class AbilityResolver<P extends AbilityPolicy<any, any, any>, S extends AbilityStrategy<P extends AbilityPolicy<infer R, infer E, any> ? R : never, P extends AbilityPolicy<any, infer E, any> ? E : never>, TTags extends string = P extends AbilityPolicy<any, any, infer T> ? T : never> {
|
|
475
|
+
private readonly onDeny?;
|
|
476
|
+
private readonly onAllow?;
|
|
465
477
|
private readonly StrategyClass;
|
|
466
478
|
private readonly policyEntries;
|
|
467
479
|
constructor(
|
|
@@ -477,7 +489,7 @@ declare class AbilityResolver<P extends AbilityPolicy<any, any, any>, S extends
|
|
|
477
489
|
* @param environment
|
|
478
490
|
*/
|
|
479
491
|
resolve<Permission extends keyof ExtractResources<P> & string>(permission: Permission, resource: ExtractResourceByPermission<P, Permission>, environment?: ExtractEnvironmentByPermission<P, Permission>): AbilityResult<ExtractResourceByPermission<P, Permission>, ExtractEnvironment<P>>;
|
|
480
|
-
enforce<Permission extends keyof ExtractResources<P> & string>(permission: Permission, resource: ExtractResourceByPermission<P, Permission>, environment?: ExtractEnvironmentByPermission<P, Permission
|
|
492
|
+
enforce<Permission extends keyof ExtractResources<P> & string>(permission: Permission, resource: ExtractResourceByPermission<P, Permission>, environment?: ExtractEnvironmentByPermission<P, Permission>, options?: EnforceOptions): void | never;
|
|
481
493
|
/**
|
|
482
494
|
* @deprecated - will be removed
|
|
483
495
|
*
|
|
@@ -590,7 +602,7 @@ declare class AbilityDSLParser<R extends ResourceObject = Record<string, unknown
|
|
|
590
602
|
private isStartOfAlias;
|
|
591
603
|
}
|
|
592
604
|
|
|
593
|
-
type TokenTypeCode = 'EFFECT' | 'IF' | 'PERMISSION' | 'IDENTIFIER' | 'COLON' | 'COMMA' | 'DOT' | 'LBRACKET' | 'RBRACKET' | 'ALL' | 'ANY' | 'OF' | 'EOF' | 'COMMENT' | 'EQ' | 'CONTAINS' | 'IN' | 'NOT_IN' | 'NOT_CONTAINS' | 'GT' | 'GTE' | 'LT' | 'LTE' | 'NULL' | 'EQ_NULL' | 'NOT_EQ_NULL' | 'NOT_EQ' | 'LEN_GT' | 'LEN_LT' | 'LEN_EQ' | 'ALWAYS' | 'NEVER' | 'EXCEPT' | 'ANNOTATION' | 'STRING' | 'NUMBER' | 'BOOLEAN' | 'SYMBOL' | 'KEYWORD' | 'ALIAS' | 'UNKNOWN';
|
|
605
|
+
type TokenTypeCode = 'EFFECT' | 'IF' | 'PERMISSION' | 'IDENTIFIER' | 'COLON' | 'COMMA' | 'DOT' | 'LBRACKET' | 'RBRACKET' | 'ALL' | 'ANY' | 'OF' | 'EOF' | 'COMMENT' | 'EQ' | 'CONTAINS' | 'IN' | 'NOT_IN' | 'NOT_CONTAINS' | 'GT' | 'GTE' | 'LT' | 'LTE' | 'NULL' | 'EQ_NULL' | 'NOT_EQ_NULL' | 'DEFINED' | 'NOT_EQ' | 'LEN_GT' | 'LEN_LT' | 'LEN_EQ' | 'ALWAYS' | 'NEVER' | 'EXCEPT' | 'ANNOTATION' | 'STRING' | 'NUMBER' | 'BOOLEAN' | 'SYMBOL' | 'KEYWORD' | 'ALIAS' | 'UNKNOWN';
|
|
594
606
|
type TokenType = TokenTypeCode & {
|
|
595
607
|
__brand: 'TokenType';
|
|
596
608
|
};
|
|
@@ -621,6 +633,7 @@ declare const TokenTypes: {
|
|
|
621
633
|
readonly NULL: TokenType;
|
|
622
634
|
readonly EQ_NULL: TokenType;
|
|
623
635
|
readonly NOT_EQ_NULL: TokenType;
|
|
636
|
+
readonly DEFINED: TokenType;
|
|
624
637
|
readonly NOT_EQ: TokenType;
|
|
625
638
|
readonly LEN_GT: TokenType;
|
|
626
639
|
readonly LEN_LT: TokenType;
|
|
@@ -858,4 +871,4 @@ declare class PriorityStrategy<R extends ResourceObject, E extends EnvironmentOb
|
|
|
858
871
|
}
|
|
859
872
|
|
|
860
873
|
export { AbilityCompare, AbilityCondition, AbilityDSLLexer, AbilityDSLParser, AbilityDSLToken, AbilityError, AbilityExplain, AbilityExplainPolicy, AbilityExplainRule, AbilityExplainRuleSet, AbilityJSONParser, AbilityMatch, AbilityParserError, AbilityPolicy, AbilityPolicyEffect, AbilityResolver, AbilityResult, AbilityRule, AbilityRuleSet, AbilityStrategy, AbilityTypeGenerator, AllMustPermitStrategy, AnyPermitStrategy, DenyOverridesStrategy, FirstMatchStrategy, OnlyOneApplicableStrategy, PermitOverridesStrategy, PriorityStrategy, SequentialLastMatchStrategy, TokenTypes, ability, fromLiteral, isConditionEqual, isConditionNotEqual, toLiteral };
|
|
861
|
-
export type { AbilityCompareType, AbilityConditionCode, AbilityConditionLiteral, AbilityConditionType, AbilityExplainConfig, AbilityExplainType, AbilityMatchType, AbilityPolicyConfig, AbilityPolicyConstructorProps, AbilityPolicyEffectType, AbilityResolverOptions, AbilityRuleConfig, AbilityRuleConstructorProps, AbilityRuleSetConfig, AbilityRuleSetConstructorProps, EnvironmentObject, ExtractEnvironment, ExtractEnvironmentByPermission, ExtractPermission, ExtractResourceByPermission, ExtractResources, NestedDict, Primitive, ResourceObject, ResourcesMap, TokenType, TokenTypeCode };
|
|
874
|
+
export type { AbilityCompareType, AbilityConditionCode, AbilityConditionLiteral, AbilityConditionType, AbilityExplainConfig, AbilityExplainType, AbilityMatchType, AbilityPolicyConfig, AbilityPolicyConstructorProps, AbilityPolicyEffectType, AbilityResolverOptions, AbilityRuleConfig, AbilityRuleConstructorProps, AbilityRuleSetConfig, AbilityRuleSetConstructorProps, EnforceOnAllow, EnforceOnDeny, EnforceOptions, EnvironmentObject, ExtractEnvironment, ExtractEnvironmentByPermission, ExtractPermission, ExtractResourceByPermission, ExtractResources, NestedDict, Primitive, ResourceObject, ResourcesMap, TokenType, TokenTypeCode };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
function brand$4(code) {
|
|
4
2
|
return code;
|
|
5
3
|
}
|
|
@@ -32,6 +30,8 @@ function brand$3(code) {
|
|
|
32
30
|
}
|
|
33
31
|
const AbilityCondition = {
|
|
34
32
|
equals: brand$3('='),
|
|
33
|
+
defined: brand$3('defined'),
|
|
34
|
+
not_defined: brand$3('not_defined'),
|
|
35
35
|
not_equals: brand$3('<>'),
|
|
36
36
|
greater_than: brand$3('>'),
|
|
37
37
|
less_than: brand$3('<'),
|
|
@@ -64,6 +64,8 @@ function fromLiteral(literal) {
|
|
|
64
64
|
length_equals: AbilityCondition.length_equals,
|
|
65
65
|
always: AbilityCondition.always,
|
|
66
66
|
never: AbilityCondition.never,
|
|
67
|
+
defined: AbilityCondition.defined,
|
|
68
|
+
not_defined: AbilityCondition.not_defined,
|
|
67
69
|
};
|
|
68
70
|
const value = map[literal];
|
|
69
71
|
if (!value) {
|
|
@@ -104,6 +106,10 @@ function toLiteral(cond) {
|
|
|
104
106
|
return 'always';
|
|
105
107
|
case AbilityCondition.never:
|
|
106
108
|
return 'never';
|
|
109
|
+
case AbilityCondition.defined:
|
|
110
|
+
return 'defined';
|
|
111
|
+
case AbilityCondition.not_defined:
|
|
112
|
+
return 'not_defined';
|
|
107
113
|
default:
|
|
108
114
|
return 'never';
|
|
109
115
|
}
|
|
@@ -126,15 +132,6 @@ const AbilityMatch = {
|
|
|
126
132
|
disabled: brand$2('disabled'),
|
|
127
133
|
};
|
|
128
134
|
|
|
129
|
-
const colors = {
|
|
130
|
-
reset: '\x1b[0m',
|
|
131
|
-
green: '\x1b[32m',
|
|
132
|
-
red: '\x1b[31m',
|
|
133
|
-
blue: '\x1b[34m',
|
|
134
|
-
yellow: '\x1b[33m',
|
|
135
|
-
white: '\x1b[37m',
|
|
136
|
-
gray: '\x1b[90m',
|
|
137
|
-
};
|
|
138
135
|
class AbilityExplain {
|
|
139
136
|
type;
|
|
140
137
|
children;
|
|
@@ -150,27 +147,36 @@ class AbilityExplain {
|
|
|
150
147
|
}
|
|
151
148
|
toString(indentPrefix = '', isLast = true) {
|
|
152
149
|
const isMatch = this.match === AbilityMatch.match;
|
|
153
|
-
const
|
|
150
|
+
const isMismatch = this.match === AbilityMatch.mismatch;
|
|
151
|
+
const isPending = this.match === AbilityMatch.pending;
|
|
152
|
+
// const isDisabled = this.match === AbilityMatch.disabled;
|
|
153
|
+
const mark = isMatch
|
|
154
|
+
? `<match ✓>`
|
|
155
|
+
: isMismatch
|
|
156
|
+
? `<mismatch ✗>`
|
|
157
|
+
: isPending
|
|
158
|
+
? `<pending …>`
|
|
159
|
+
: `<disabled ⊘>`;
|
|
154
160
|
let label;
|
|
155
161
|
switch (this.type) {
|
|
156
162
|
case 'policy':
|
|
157
|
-
label =
|
|
163
|
+
label = `POLICY`;
|
|
158
164
|
break;
|
|
159
165
|
case 'ruleSet':
|
|
160
|
-
label =
|
|
166
|
+
label = `RULESET`;
|
|
161
167
|
break;
|
|
162
168
|
default:
|
|
163
|
-
label =
|
|
169
|
+
label = `RULE`;
|
|
164
170
|
}
|
|
165
171
|
const branch = indentPrefix.length === 0
|
|
166
172
|
? ''
|
|
167
173
|
: isLast
|
|
168
|
-
?
|
|
169
|
-
:
|
|
174
|
+
? `└─ `
|
|
175
|
+
: `├─ `;
|
|
170
176
|
let out = `${indentPrefix}${branch}${label} ${this.name} — ${mark}`;
|
|
171
177
|
if (this.debugInfo)
|
|
172
|
-
out += `
|
|
173
|
-
const nextIndent = indentPrefix + (isLast ? ' ' :
|
|
178
|
+
out += ` (${this.debugInfo})`;
|
|
179
|
+
const nextIndent = indentPrefix + (isLast ? ' ' : `│ `);
|
|
174
180
|
this.children.forEach((child, idx) => {
|
|
175
181
|
out += '\n' + child.toString(nextIndent, idx === this.children.length - 1);
|
|
176
182
|
});
|
|
@@ -183,6 +189,7 @@ class AbilityExplainRule extends AbilityExplain {
|
|
|
183
189
|
type: 'rule',
|
|
184
190
|
match: rule.state,
|
|
185
191
|
name: rule.name,
|
|
192
|
+
debugInfo: `${rule.subject} ${rule.condition} ${JSON.stringify(rule.resource)}`,
|
|
186
193
|
});
|
|
187
194
|
}
|
|
188
195
|
}
|
|
@@ -221,11 +228,13 @@ class AbilityResult {
|
|
|
221
228
|
* Useful for debugging, logging, or building UI tools that visualize permission logic.
|
|
222
229
|
*/
|
|
223
230
|
explain() {
|
|
224
|
-
|
|
231
|
+
const resMarker = this.strategy.isDenied() ? '== DENIED==' : '== ALLOWED ==';
|
|
232
|
+
const policiesExplain = this.strategy.policies
|
|
225
233
|
.map(policy => {
|
|
226
234
|
return new AbilityExplainPolicy(policy).toString();
|
|
227
235
|
})
|
|
228
236
|
.join('\n');
|
|
237
|
+
return `${resMarker}\n${policiesExplain}\n`;
|
|
229
238
|
}
|
|
230
239
|
decisive() {
|
|
231
240
|
return this.strategy.decisivePolicy();
|
|
@@ -245,7 +254,17 @@ class AbilityResult {
|
|
|
245
254
|
};
|
|
246
255
|
}
|
|
247
256
|
|
|
257
|
+
function brand$1(code) {
|
|
258
|
+
return code;
|
|
259
|
+
}
|
|
260
|
+
const AbilityPolicyEffect = {
|
|
261
|
+
deny: brand$1('deny'),
|
|
262
|
+
permit: brand$1('permit'),
|
|
263
|
+
};
|
|
264
|
+
|
|
248
265
|
class AbilityResolver {
|
|
266
|
+
onDeny;
|
|
267
|
+
onAllow;
|
|
249
268
|
StrategyClass;
|
|
250
269
|
policyEntries;
|
|
251
270
|
constructor(
|
|
@@ -254,6 +273,8 @@ class AbilityResolver {
|
|
|
254
273
|
*/
|
|
255
274
|
policyOrListOfPolicies, strategy, options = {}) {
|
|
256
275
|
const policies = this.toArray(policyOrListOfPolicies);
|
|
276
|
+
this.onDeny = options.onDeny;
|
|
277
|
+
this.onAllow = options.onAllow;
|
|
257
278
|
const filtered = options.tags
|
|
258
279
|
? policies.filter(p => p.tags.some(tag => options.tags.includes(tag)))
|
|
259
280
|
: policies;
|
|
@@ -291,11 +312,19 @@ class AbilityResolver {
|
|
|
291
312
|
// 3. Use strategy
|
|
292
313
|
const strategy = new this.StrategyClass(filteredPolicies);
|
|
293
314
|
const effect = strategy.evaluate();
|
|
294
|
-
|
|
315
|
+
const result = new AbilityResult(effect, strategy);
|
|
316
|
+
if (effect === AbilityPolicyEffect.deny && this.onDeny) {
|
|
317
|
+
this.onDeny(result);
|
|
318
|
+
}
|
|
319
|
+
if (effect === AbilityPolicyEffect.permit && this.onAllow) {
|
|
320
|
+
this.onAllow(result);
|
|
321
|
+
}
|
|
322
|
+
return result;
|
|
295
323
|
}
|
|
296
|
-
enforce(permission, resource, environment) {
|
|
324
|
+
enforce(permission, resource, environment, options) {
|
|
297
325
|
const result = this.resolve(permission, resource, environment);
|
|
298
326
|
if (result.isDenied()) {
|
|
327
|
+
options?.onDeny && options?.onDeny(result);
|
|
299
328
|
throw new AbilityError(`Permission denied`);
|
|
300
329
|
}
|
|
301
330
|
}
|
|
@@ -416,7 +445,7 @@ class AbilityTypeGenerator {
|
|
|
416
445
|
environmentStructure[action] = {};
|
|
417
446
|
}
|
|
418
447
|
const existingEnvType = environmentStructure[action][envPath];
|
|
419
|
-
const targetType = ruleType;
|
|
448
|
+
const targetType = ruleType;
|
|
420
449
|
if (existingEnvType && existingEnvType !== targetType) {
|
|
421
450
|
environmentStructure[action][envPath] = `${existingEnvType} | ${targetType}`;
|
|
422
451
|
}
|
|
@@ -1004,14 +1033,6 @@ class AbilityPolicy {
|
|
|
1004
1033
|
}
|
|
1005
1034
|
}
|
|
1006
1035
|
|
|
1007
|
-
function brand$1(code) {
|
|
1008
|
-
return code;
|
|
1009
|
-
}
|
|
1010
|
-
const AbilityPolicyEffect = {
|
|
1011
|
-
deny: brand$1('deny'),
|
|
1012
|
-
permit: brand$1('permit'),
|
|
1013
|
-
};
|
|
1014
|
-
|
|
1015
1036
|
/**
|
|
1016
1037
|
* Represents a rule that defines a condition to be checked against a subject and resource.
|
|
1017
1038
|
*/
|
|
@@ -1063,6 +1084,8 @@ class AbilityRule {
|
|
|
1063
1084
|
static valueLen = (v) => this.isString(v) || Array.isArray(v) ? v.length : null;
|
|
1064
1085
|
static operatorHandlers = {
|
|
1065
1086
|
[toLiteral(AbilityCondition.always)]: () => true,
|
|
1087
|
+
[toLiteral(AbilityCondition.defined)]: (a) => typeof a !== 'undefined',
|
|
1088
|
+
[toLiteral(AbilityCondition.not_defined)]: (a) => typeof a === 'undefined',
|
|
1066
1089
|
[toLiteral(AbilityCondition.never)]: () => false,
|
|
1067
1090
|
[toLiteral(AbilityCondition.equals)]: (a, b) => a === b,
|
|
1068
1091
|
[toLiteral(AbilityCondition.not_equals)]: (a, b) => a !== b,
|
|
@@ -1602,6 +1625,7 @@ const TokenTypes = {
|
|
|
1602
1625
|
NULL: brand('NULL'),
|
|
1603
1626
|
EQ_NULL: brand('EQ_NULL'),
|
|
1604
1627
|
NOT_EQ_NULL: brand('NOT_EQ_NULL'),
|
|
1628
|
+
DEFINED: brand('DEFINED'),
|
|
1605
1629
|
NOT_EQ: brand('NOT_EQ'),
|
|
1606
1630
|
LEN_GT: brand('LEN_GT'),
|
|
1607
1631
|
LEN_LT: brand('LEN_LT'),
|
|
@@ -1652,9 +1676,11 @@ class AbilityDSLLexer {
|
|
|
1652
1676
|
'true',
|
|
1653
1677
|
'false',
|
|
1654
1678
|
'null',
|
|
1679
|
+
'defined',
|
|
1655
1680
|
'contains',
|
|
1656
1681
|
'includes',
|
|
1657
1682
|
'length',
|
|
1683
|
+
'len',
|
|
1658
1684
|
'has',
|
|
1659
1685
|
'in',
|
|
1660
1686
|
'gt',
|
|
@@ -1868,11 +1894,11 @@ class AbilityDSLLexer {
|
|
|
1868
1894
|
const startLine = this.line;
|
|
1869
1895
|
const startColumn = this.column;
|
|
1870
1896
|
const start = this.pos;
|
|
1871
|
-
//
|
|
1897
|
+
// First segment
|
|
1872
1898
|
while (!this.isAtEnd() && /[a-zA-Z0-9_*]/.test(this.peek())) {
|
|
1873
1899
|
this.advance();
|
|
1874
1900
|
}
|
|
1875
|
-
//
|
|
1901
|
+
// dots segments
|
|
1876
1902
|
while (!this.isAtEnd() && this.peek() === '.') {
|
|
1877
1903
|
this.advance(); // dot
|
|
1878
1904
|
if (!/[a-zA-Z_*]/.test(this.peek())) {
|
|
@@ -1889,7 +1915,7 @@ class AbilityDSLLexer {
|
|
|
1889
1915
|
if (word === 'never') {
|
|
1890
1916
|
return new AbilityDSLToken(TokenTypes.NEVER, word, startLine, startColumn);
|
|
1891
1917
|
}
|
|
1892
|
-
//
|
|
1918
|
+
// (identifier or permission)
|
|
1893
1919
|
if (word.includes('.')) {
|
|
1894
1920
|
const last = this.tokens[this.tokens.length - 1];
|
|
1895
1921
|
if (last?.type === TokenTypes.EFFECT) {
|
|
@@ -1899,16 +1925,13 @@ class AbilityDSLLexer {
|
|
|
1899
1925
|
}
|
|
1900
1926
|
return new AbilityDSLToken(TokenTypes.IDENTIFIER, word, startLine, startColumn);
|
|
1901
1927
|
}
|
|
1902
|
-
// Ключевые слова
|
|
1903
1928
|
if (this.keywords.has(word)) {
|
|
1904
|
-
// Эффекты
|
|
1905
1929
|
if (word === 'permit' || word === 'allow') {
|
|
1906
1930
|
return new AbilityDSLToken(TokenTypes.EFFECT, 'permit', startLine, startColumn);
|
|
1907
1931
|
}
|
|
1908
1932
|
if (word === 'deny' || word === 'forbidden') {
|
|
1909
1933
|
return new AbilityDSLToken(TokenTypes.EFFECT, 'deny', startLine, startColumn);
|
|
1910
1934
|
}
|
|
1911
|
-
// Групповые ключевые слова
|
|
1912
1935
|
if (word === 'all') {
|
|
1913
1936
|
return new AbilityDSLToken(TokenTypes.ALL, word, startLine, startColumn);
|
|
1914
1937
|
}
|
|
@@ -1921,13 +1944,15 @@ class AbilityDSLLexer {
|
|
|
1921
1944
|
if (word === 'if') {
|
|
1922
1945
|
return new AbilityDSLToken(TokenTypes.IF, word, startLine, startColumn);
|
|
1923
1946
|
}
|
|
1924
|
-
// Булевы и null
|
|
1925
1947
|
if (word === 'true' || word === 'false') {
|
|
1926
1948
|
return new AbilityDSLToken(TokenTypes.BOOLEAN, word, startLine, startColumn);
|
|
1927
1949
|
}
|
|
1928
1950
|
if (word === 'null') {
|
|
1929
1951
|
return new AbilityDSLToken(TokenTypes.NULL, word, startLine, startColumn);
|
|
1930
1952
|
}
|
|
1953
|
+
if (word === 'defined') {
|
|
1954
|
+
return new AbilityDSLToken(TokenTypes.DEFINED, word, startLine, startColumn);
|
|
1955
|
+
}
|
|
1931
1956
|
if (word === 'except') {
|
|
1932
1957
|
return new AbilityDSLToken(TokenTypes.EXCEPT, word, startLine, startColumn);
|
|
1933
1958
|
}
|
|
@@ -2079,7 +2104,8 @@ class AbilityDSLTokenStream {
|
|
|
2079
2104
|
if (this.eof()) {
|
|
2080
2105
|
return false;
|
|
2081
2106
|
}
|
|
2082
|
-
|
|
2107
|
+
const p = this.peek().type;
|
|
2108
|
+
return p === type;
|
|
2083
2109
|
}
|
|
2084
2110
|
match(type) {
|
|
2085
2111
|
if (this.check(type)) {
|
|
@@ -2515,6 +2541,7 @@ class AbilityDSLParser {
|
|
|
2515
2541
|
const operatorConsumesValue = operator !== TokenTypes.EQ_NULL &&
|
|
2516
2542
|
operator !== TokenTypes.NOT_EQ_NULL &&
|
|
2517
2543
|
operator !== TokenTypes.NULL &&
|
|
2544
|
+
operator !== TokenTypes.DEFINED &&
|
|
2518
2545
|
operator !== TokenTypes.ALWAYS &&
|
|
2519
2546
|
operator !== TokenTypes.NEVER;
|
|
2520
2547
|
if (operatorConsumesValue) {
|
|
@@ -2567,42 +2594,46 @@ class AbilityDSLParser {
|
|
|
2567
2594
|
this.stream.reset();
|
|
2568
2595
|
// "length equals"
|
|
2569
2596
|
this.stream.mark();
|
|
2570
|
-
if (this.matchWord('length') && this.matchWord('equals')) {
|
|
2597
|
+
if ((this.matchWord('length') || this.matchWord('len')) && this.matchWord('equals')) {
|
|
2571
2598
|
this.stream.commit();
|
|
2572
2599
|
return { condition: AbilityCondition.length_equals, operator: TokenTypes.LEN_EQ };
|
|
2573
2600
|
}
|
|
2574
2601
|
this.stream.reset();
|
|
2575
2602
|
// "length ="
|
|
2576
2603
|
this.stream.mark();
|
|
2577
|
-
if (this.matchWord('length') && this.matchSymbol('=')) {
|
|
2604
|
+
if ((this.matchWord('length') || this.matchWord('len')) && this.matchSymbol('=')) {
|
|
2578
2605
|
this.stream.commit();
|
|
2579
2606
|
return { condition: AbilityCondition.length_equals, operator: TokenTypes.LEN_EQ };
|
|
2580
2607
|
}
|
|
2581
2608
|
this.stream.reset();
|
|
2582
2609
|
// "length greater than"
|
|
2583
2610
|
this.stream.mark();
|
|
2584
|
-
if (this.matchWord('length')
|
|
2611
|
+
if ((this.matchWord('length') || this.matchWord('len')) &&
|
|
2612
|
+
this.matchWord('greater') &&
|
|
2613
|
+
this.matchWord('than')) {
|
|
2585
2614
|
this.stream.commit();
|
|
2586
2615
|
return { condition: AbilityCondition.length_greater_than, operator: TokenTypes.LEN_GT };
|
|
2587
2616
|
}
|
|
2588
2617
|
this.stream.reset();
|
|
2589
2618
|
// "length >"
|
|
2590
2619
|
this.stream.mark();
|
|
2591
|
-
if (this.matchWord('length') && this.matchSymbol('>')) {
|
|
2620
|
+
if ((this.matchWord('length') || this.matchWord('len')) && this.matchSymbol('>')) {
|
|
2592
2621
|
this.stream.commit();
|
|
2593
2622
|
return { condition: AbilityCondition.length_greater_than, operator: TokenTypes.LEN_GT };
|
|
2594
2623
|
}
|
|
2595
2624
|
this.stream.reset();
|
|
2596
2625
|
// "length less than"
|
|
2597
2626
|
this.stream.mark();
|
|
2598
|
-
if (this.matchWord('length')
|
|
2627
|
+
if ((this.matchWord('length') || this.matchWord('len')) &&
|
|
2628
|
+
this.matchWord('less') &&
|
|
2629
|
+
this.matchWord('than')) {
|
|
2599
2630
|
this.stream.commit();
|
|
2600
2631
|
return { condition: AbilityCondition.length_less_than, operator: TokenTypes.LEN_LT };
|
|
2601
2632
|
}
|
|
2602
2633
|
this.stream.reset();
|
|
2603
2634
|
// "length <"
|
|
2604
2635
|
this.stream.mark();
|
|
2605
|
-
if (this.matchWord('length') && this.matchSymbol('<')) {
|
|
2636
|
+
if ((this.matchWord('length') || this.matchWord('len')) && this.matchSymbol('<')) {
|
|
2606
2637
|
this.stream.commit();
|
|
2607
2638
|
return { condition: AbilityCondition.length_less_than, operator: TokenTypes.LEN_LT };
|
|
2608
2639
|
}
|
|
@@ -2730,6 +2761,20 @@ class AbilityDSLParser {
|
|
|
2730
2761
|
}
|
|
2731
2762
|
}
|
|
2732
2763
|
this.stream.reset();
|
|
2764
|
+
// is defined
|
|
2765
|
+
this.stream.mark();
|
|
2766
|
+
if (this.matchWord('is') && this.matchWord('defined')) {
|
|
2767
|
+
this.stream.commit();
|
|
2768
|
+
return { condition: AbilityCondition.defined, operator: TokenTypes.DEFINED };
|
|
2769
|
+
}
|
|
2770
|
+
this.stream.reset();
|
|
2771
|
+
// is not defined
|
|
2772
|
+
this.stream.mark();
|
|
2773
|
+
if (this.matchWord('is') && this.matchWord('not') && this.matchWord('defined')) {
|
|
2774
|
+
this.stream.commit();
|
|
2775
|
+
return { condition: AbilityCondition.not_defined, operator: TokenTypes.DEFINED };
|
|
2776
|
+
}
|
|
2777
|
+
this.stream.reset();
|
|
2733
2778
|
// Single token (symbol or keyword)
|
|
2734
2779
|
const token = this.stream.peek();
|
|
2735
2780
|
if (token.type !== TokenTypes.SYMBOL &&
|
|
@@ -2801,7 +2846,8 @@ class AbilityDSLParser {
|
|
|
2801
2846
|
if ((token.type === TokenTypes.KEYWORD ||
|
|
2802
2847
|
token.type === TokenTypes.IDENTIFIER ||
|
|
2803
2848
|
token.type === TokenTypes.ALWAYS ||
|
|
2804
|
-
token.type === TokenTypes.NEVER
|
|
2849
|
+
token.type === TokenTypes.NEVER ||
|
|
2850
|
+
token.type === TokenTypes.DEFINED) &&
|
|
2805
2851
|
token.value === word) {
|
|
2806
2852
|
this.stream.next();
|
|
2807
2853
|
return true;
|
|
@@ -2839,6 +2885,11 @@ class AbilityDSLParser {
|
|
|
2839
2885
|
this.stream.syntaxError(`Unexpected ${token.type} in value position`, token);
|
|
2840
2886
|
}
|
|
2841
2887
|
this.stream.next();
|
|
2888
|
+
// if (token.type === TokenTypes.IDENTIFIER) {
|
|
2889
|
+
// this.stream.next();
|
|
2890
|
+
//
|
|
2891
|
+
// return this.parseValue();
|
|
2892
|
+
// }
|
|
2842
2893
|
// CHECK THIS SWITCH COMPARE
|
|
2843
2894
|
switch (token.type) {
|
|
2844
2895
|
case TokenTypes.STRING:
|
|
@@ -2849,8 +2900,10 @@ class AbilityDSLParser {
|
|
|
2849
2900
|
return token.value === 'true';
|
|
2850
2901
|
case TokenTypes.NULL:
|
|
2851
2902
|
return null;
|
|
2903
|
+
case TokenTypes.DEFINED:
|
|
2904
|
+
return typeof token.value !== 'undefined';
|
|
2852
2905
|
case TokenTypes.IDENTIFIER:
|
|
2853
|
-
return
|
|
2906
|
+
return null;
|
|
2854
2907
|
default: {
|
|
2855
2908
|
this.stream.syntaxError(`Unexpected value token "${token.value}"`, token, [
|
|
2856
2909
|
TokenTypes.KEYWORD,
|
|
@@ -3343,38 +3396,4 @@ class PriorityStrategy extends AbilityStrategy {
|
|
|
3343
3396
|
}
|
|
3344
3397
|
}
|
|
3345
3398
|
|
|
3346
|
-
|
|
3347
|
-
exports.AbilityCondition = AbilityCondition;
|
|
3348
|
-
exports.AbilityDSLLexer = AbilityDSLLexer;
|
|
3349
|
-
exports.AbilityDSLParser = AbilityDSLParser;
|
|
3350
|
-
exports.AbilityDSLToken = AbilityDSLToken;
|
|
3351
|
-
exports.AbilityError = AbilityError;
|
|
3352
|
-
exports.AbilityExplain = AbilityExplain;
|
|
3353
|
-
exports.AbilityExplainPolicy = AbilityExplainPolicy;
|
|
3354
|
-
exports.AbilityExplainRule = AbilityExplainRule;
|
|
3355
|
-
exports.AbilityExplainRuleSet = AbilityExplainRuleSet;
|
|
3356
|
-
exports.AbilityJSONParser = AbilityJSONParser;
|
|
3357
|
-
exports.AbilityMatch = AbilityMatch;
|
|
3358
|
-
exports.AbilityParserError = AbilityParserError;
|
|
3359
|
-
exports.AbilityPolicy = AbilityPolicy;
|
|
3360
|
-
exports.AbilityPolicyEffect = AbilityPolicyEffect;
|
|
3361
|
-
exports.AbilityResolver = AbilityResolver;
|
|
3362
|
-
exports.AbilityResult = AbilityResult;
|
|
3363
|
-
exports.AbilityRule = AbilityRule;
|
|
3364
|
-
exports.AbilityRuleSet = AbilityRuleSet;
|
|
3365
|
-
exports.AbilityStrategy = AbilityStrategy;
|
|
3366
|
-
exports.AbilityTypeGenerator = AbilityTypeGenerator;
|
|
3367
|
-
exports.AllMustPermitStrategy = AllMustPermitStrategy;
|
|
3368
|
-
exports.AnyPermitStrategy = AnyPermitStrategy;
|
|
3369
|
-
exports.DenyOverridesStrategy = DenyOverridesStrategy;
|
|
3370
|
-
exports.FirstMatchStrategy = FirstMatchStrategy;
|
|
3371
|
-
exports.OnlyOneApplicableStrategy = OnlyOneApplicableStrategy;
|
|
3372
|
-
exports.PermitOverridesStrategy = PermitOverridesStrategy;
|
|
3373
|
-
exports.PriorityStrategy = PriorityStrategy;
|
|
3374
|
-
exports.SequentialLastMatchStrategy = SequentialLastMatchStrategy;
|
|
3375
|
-
exports.TokenTypes = TokenTypes;
|
|
3376
|
-
exports.ability = ability;
|
|
3377
|
-
exports.fromLiteral = fromLiteral;
|
|
3378
|
-
exports.isConditionEqual = isConditionEqual;
|
|
3379
|
-
exports.isConditionNotEqual = isConditionNotEqual;
|
|
3380
|
-
exports.toLiteral = toLiteral;
|
|
3399
|
+
export { AbilityCompare, AbilityCondition, AbilityDSLLexer, AbilityDSLParser, AbilityDSLToken, AbilityError, AbilityExplain, AbilityExplainPolicy, AbilityExplainRule, AbilityExplainRuleSet, AbilityJSONParser, AbilityMatch, AbilityParserError, AbilityPolicy, AbilityPolicyEffect, AbilityResolver, AbilityResult, AbilityRule, AbilityRuleSet, AbilityStrategy, AbilityTypeGenerator, AllMustPermitStrategy, AnyPermitStrategy, DenyOverridesStrategy, FirstMatchStrategy, OnlyOneApplicableStrategy, PermitOverridesStrategy, PriorityStrategy, SequentialLastMatchStrategy, TokenTypes, ability, fromLiteral, isConditionEqual, isConditionNotEqual, toLiteral };
|
package/package.json
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@via-profit/ability",
|
|
3
3
|
"support": "https://via-profit.ru",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.7.1",
|
|
5
5
|
"description": "Via-Profit Ability service",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"ability",
|
|
8
8
|
"access",
|
|
9
9
|
"via-profit"
|
|
10
10
|
],
|
|
11
|
-
"
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/index.cjs",
|
|
13
|
+
"module": "./dist/index.js",
|
|
14
|
+
"exports": {
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
12
19
|
"engines": {
|
|
13
20
|
"node": ">= 17.0.0",
|
|
14
21
|
"npm": ">= 8.19.3"
|
|
@@ -27,7 +34,8 @@
|
|
|
27
34
|
"bench": "npm run build && node ./bench/benchmark.js",
|
|
28
35
|
"test": "jest",
|
|
29
36
|
"lint": "tsc --noEmit && eslint --fix .",
|
|
30
|
-
"pretty": "prettier --write ./src"
|
|
37
|
+
"pretty": "prettier --write ./src",
|
|
38
|
+
"postinstall": "node scripts/postinstall.js"
|
|
31
39
|
},
|
|
32
40
|
"repository": {
|
|
33
41
|
"type": "git",
|