@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/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
- protected readonly strategy: AbilityStrategy<R, E>;
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>): void | never;
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 mark = isMatch ? `${colors.green}✓${colors.reset}` : `${colors.red}✗${colors.reset}`;
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 = `${colors.blue}POLICY${colors.reset}`;
163
+ label = `POLICY`;
158
164
  break;
159
165
  case 'ruleSet':
160
- label = `${colors.yellow}RULESET${colors.reset}`;
166
+ label = `RULESET`;
161
167
  break;
162
168
  default:
163
- label = `${colors.white}RULE${colors.reset}`;
169
+ label = `RULE`;
164
170
  }
165
171
  const branch = indentPrefix.length === 0
166
172
  ? ''
167
173
  : isLast
168
- ? `${colors.gray}└─${colors.reset} `
169
- : `${colors.gray}├─${colors.reset} `;
174
+ ? `└─ `
175
+ : `├─ `;
170
176
  let out = `${indentPrefix}${branch}${label} ${this.name} — ${mark}`;
171
177
  if (this.debugInfo)
172
- out += ` ${colors.gray}(${this.debugInfo})${colors.reset}`;
173
- const nextIndent = indentPrefix + (isLast ? ' ' : `${colors.gray}│ ${colors.reset}`);
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
- return this.strategy.policies
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
- return new AbilityResult(effect, strategy);
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; // или 'unknown', если хочешь жёстко
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
- // Если есть точка — это путь (identifier или permission)
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
- return this.peek().type === type;
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') && this.matchWord('greater') && this.matchWord('than')) {
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') && this.matchWord('less') && this.matchWord('than')) {
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 token.value;
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
- exports.AbilityCompare = AbilityCompare;
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.6.4",
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
- "main": "./dist/index.js",
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",