@adguard/agtree 2.1.4 → 2.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.
package/dist/agtree.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * AGTree v2.1.4 (build date: Mon, 25 Nov 2024 16:56:07 GMT)
2
+ * AGTree v2.3.0 (build date: Thu, 19 Dec 2024 15:31:22 GMT)
3
3
  * (c) 2024 Adguard Software Ltd.
4
4
  * Released under the MIT license
5
5
  * https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
@@ -3699,6 +3699,7 @@ declare const modifierDataSchema: zod.ZodEffects<zod.ZodTypeAny, {
3699
3699
  exceptionOnly: boolean;
3700
3700
  valueOptional: boolean;
3701
3701
  valueFormat: string | null;
3702
+ valueFormatFlags: string | null;
3702
3703
  }, any>;
3703
3704
  /**
3704
3705
  * Type of the modifier data schema.
package/dist/agtree.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * AGTree v2.1.4 (build date: Mon, 25 Nov 2024 16:56:07 GMT)
2
+ * AGTree v2.3.0 (build date: Thu, 19 Dec 2024 15:31:22 GMT)
3
3
  * (c) 2024 Adguard Software Ltd.
4
4
  * Released under the MIT license
5
5
  * https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
@@ -13,7 +13,6 @@ var sprintfJs = require('sprintf-js');
13
13
  var cssTokenizer = require('@adguard/css-tokenizer');
14
14
  var tldts = require('tldts');
15
15
  var isIp = require('is-ip');
16
- var XRegExp = require('xregexp');
17
16
  var cloneDeep = require('clone-deep');
18
17
  var compatibilityTableData_js = require('./compatibility-table-data.js');
19
18
  var zod = require('zod');
@@ -312,6 +311,8 @@ const COMMA_DOMAIN_LIST_SEPARATOR = ',';
312
311
  const PIPE_MODIFIER_SEPARATOR = '|';
313
312
  const CSS_MEDIA_MARKER = '@media';
314
313
  const CSS_NOT_PSEUDO = 'not';
314
+ const CSS_BLOCK_OPEN = '{';
315
+ const CSS_BLOCK_CLOSE = '}';
315
316
  const HINT_MARKER = '!+';
316
317
  const HINT_MARKER_LEN = HINT_MARKER.length;
317
318
  const NETWORK_RULE_EXCEPTION_MARKER = '@@';
@@ -6086,14 +6087,24 @@ class CssTokenStream {
6086
6087
  // - end: end index of the token
6087
6088
  // - props: additional properties of the token, if any (we don't use it here, this is why we use underscore)
6088
6089
  // - balance: balance level of the token
6089
- tokenizeBalanced(source, (type, start, end, _, balance) => {
6090
- this.tokens.push({
6091
- type,
6092
- start,
6093
- end,
6094
- balance,
6090
+ try {
6091
+ tokenizeBalanced(source, (type, start, end, _, balance) => {
6092
+ this.tokens.push({
6093
+ type,
6094
+ start,
6095
+ end,
6096
+ balance,
6097
+ });
6095
6098
  });
6096
- });
6099
+ }
6100
+ catch (error) {
6101
+ // If the error is an AdblockSyntaxError, adjust the error positions to the base offset
6102
+ if (error instanceof AdblockSyntaxError) {
6103
+ error.start += baseOffset;
6104
+ error.end += baseOffset;
6105
+ throw error;
6106
+ }
6107
+ }
6097
6108
  this.index = 0;
6098
6109
  this.baseOffset = baseOffset;
6099
6110
  }
@@ -7653,6 +7664,40 @@ class AdgScriptletInjectionBodyParser extends ParserBase {
7653
7664
  }
7654
7665
  }
7655
7666
 
7667
+ /**
7668
+ * Represents an error that occurs when an operation is aborted.
7669
+ */
7670
+ class AbortError extends Error {
7671
+ constructor() {
7672
+ super('Aborted');
7673
+ }
7674
+ }
7675
+ // TODO: AG-38480 add a stop function to the tokenizers callback and move `hasToken` to CSS Tokenizer as well
7676
+ /**
7677
+ * Checks if the given raw string contains any of the specified tokens.
7678
+ * This function uses error throwing inside the abort tokenization process.
7679
+ *
7680
+ * @param raw - The raw string to be tokenized and checked.
7681
+ * @param tokens - A set of token types to check for in the raw string.
7682
+ * @returns `true` if any of the specified tokens are found in the raw string, otherwise `false`.
7683
+ */
7684
+ const hasToken = (raw, tokens) => {
7685
+ try {
7686
+ cssTokenizer.tokenizeExtended(raw, (type) => {
7687
+ if (tokens.has(type)) {
7688
+ throw new AbortError();
7689
+ }
7690
+ });
7691
+ }
7692
+ catch (e) {
7693
+ if (e instanceof AbortError) {
7694
+ return true;
7695
+ }
7696
+ throw e;
7697
+ }
7698
+ return false;
7699
+ };
7700
+
7656
7701
  /* eslint-disable no-param-reassign */
7657
7702
  /**
7658
7703
  * Value map for binary serialization. This helps to reduce the size of the serialized data,
@@ -8025,6 +8070,33 @@ class CosmeticRuleParser extends ParserBase {
8025
8070
  body: AdgCssInjectionParser.parse(rawBody, options, baseOffset + bodyStart),
8026
8071
  };
8027
8072
  };
8073
+ /**
8074
+ * Parses Adb CSS injection rules
8075
+ * eg: example.com##.foo { display: none; }
8076
+ *
8077
+ * @returns parsed rule
8078
+ */
8079
+ const parseAbpCssInjection = () => {
8080
+ if (!options.parseAbpSpecificRules) {
8081
+ return null;
8082
+ }
8083
+ // check if the rule contains both CSS block open and close characters
8084
+ // if none of them is present we can stop parsing
8085
+ if (rawBody.indexOf(CSS_BLOCK_OPEN) === -1 && rawBody.indexOf(CSS_BLOCK_CLOSE) === -1) {
8086
+ return null;
8087
+ }
8088
+ if (!hasToken(rawBody, new Set([cssTokenizer.TokenType.OpenCurlyBracket, cssTokenizer.TokenType.CloseCurlyBracket]))) {
8089
+ return null;
8090
+ }
8091
+ // try to parse the raw body as an AdGuard CSS injection rule
8092
+ const body = AdgCssInjectionParser.parse(rawBody, options, baseOffset + bodyStart);
8093
+ // if the parsed rule type is a 'CssInjectionRuleBody', return the parsed rule
8094
+ return {
8095
+ syntax: exports.AdblockSyntax.Abp,
8096
+ type: exports.CosmeticRuleType.CssInjectionRule,
8097
+ body,
8098
+ };
8099
+ };
8028
8100
  const parseAbpSnippetInjection = () => {
8029
8101
  if (!options.parseAbpSpecificRules) {
8030
8102
  throw new AdblockSyntaxError(sprintfJs.sprintf(ERROR_MESSAGES$3.SYNTAX_DISABLED, exports.AdblockSyntax.Abp), baseOffset + bodyStart, baseOffset + bodyEnd);
@@ -8136,10 +8208,22 @@ class CosmeticRuleParser extends ParserBase {
8136
8208
  // the next function is called, and so on.
8137
8209
  // If all functions return null, an error should be thrown.
8138
8210
  const separatorMap = {
8139
- '##': [parseUboHtmlFiltering, parseUboScriptletInjection, parseUboCssInjection, parseElementHiding],
8140
- '#@#': [parseUboHtmlFiltering, parseUboScriptletInjection, parseUboCssInjection, parseElementHiding],
8141
- '#?#': [parseUboCssInjection, parseElementHiding],
8142
- '#@?#': [parseUboCssInjection, parseElementHiding],
8211
+ '##': [
8212
+ parseUboHtmlFiltering,
8213
+ parseUboScriptletInjection,
8214
+ parseUboCssInjection,
8215
+ parseAbpCssInjection,
8216
+ parseElementHiding,
8217
+ ],
8218
+ '#@#': [
8219
+ parseUboHtmlFiltering,
8220
+ parseUboScriptletInjection,
8221
+ parseUboCssInjection,
8222
+ parseAbpCssInjection,
8223
+ parseElementHiding,
8224
+ ],
8225
+ '#?#': [parseUboCssInjection, parseAbpCssInjection, parseElementHiding],
8226
+ '#@?#': [parseUboCssInjection, parseAbpCssInjection, parseElementHiding],
8143
8227
  '#$#': [parseAdgCssInjection, parseAbpSnippetInjection],
8144
8228
  '#@$#': [parseAdgCssInjection, parseAbpSnippetInjection],
8145
8229
  '#$?#': [parseAdgCssInjection],
@@ -8208,7 +8292,7 @@ class CosmeticRuleParser extends ParserBase {
8208
8292
  result = node.body.selectorList.value;
8209
8293
  break;
8210
8294
  case exports.CosmeticRuleType.CssInjectionRule:
8211
- if (node.syntax === exports.AdblockSyntax.Adg) {
8295
+ if (node.syntax === exports.AdblockSyntax.Adg || node.syntax === exports.AdblockSyntax.Abp) {
8212
8296
  result = AdgCssInjectionParser.generate(node.body);
8213
8297
  }
8214
8298
  else if (node.syntax === exports.AdblockSyntax.Ubo) {
@@ -10764,10 +10848,11 @@ const isCustomValueFormatValidator = (valueFormat) => {
10764
10848
  *
10765
10849
  * @param modifier Modifier AST node.
10766
10850
  * @param valueFormat Value format for the modifier.
10851
+ * @param valueFormatFlags Optional; RegExp flags for the value format.
10767
10852
  *
10768
10853
  * @returns Validation result.
10769
10854
  */
10770
- const validateValue = (modifier, valueFormat) => {
10855
+ const validateValue = (modifier, valueFormat, valueFormatFlags) => {
10771
10856
  if (isCustomValueFormatValidator(valueFormat)) {
10772
10857
  const validator = CUSTOM_VALUE_FORMAT_MAP[valueFormat];
10773
10858
  return validator(modifier);
@@ -10776,14 +10861,19 @@ const validateValue = (modifier, valueFormat) => {
10776
10861
  if (!modifier.value?.value) {
10777
10862
  return getValueRequiredValidationResult(modifierName);
10778
10863
  }
10779
- let xRegExp;
10864
+ let regExp;
10780
10865
  try {
10781
- xRegExp = XRegExp(valueFormat);
10866
+ if (isString(valueFormatFlags)) {
10867
+ regExp = new RegExp(valueFormat, valueFormatFlags);
10868
+ }
10869
+ else {
10870
+ regExp = new RegExp(valueFormat);
10871
+ }
10782
10872
  }
10783
10873
  catch (e) {
10784
10874
  throw new Error(`${SOURCE_DATA_ERROR_PREFIX.INVALID_VALUE_FORMAT_REGEXP}: '${modifierName}'`);
10785
10875
  }
10786
- const isValid = xRegExp.test(modifier.value?.value);
10876
+ const isValid = regExp.test(modifier.value?.value);
10787
10877
  if (!isValid) {
10788
10878
  return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
10789
10879
  }
@@ -11367,7 +11457,7 @@ const validateForSpecificSyntax = (syntax, modifier, isException) => {
11367
11457
  if (!specificBlockerData.valueFormat) {
11368
11458
  throw new Error(`${SOURCE_DATA_ERROR_PREFIX.NO_VALUE_FORMAT_FOR_ASSIGNABLE}: '${modifierName}'`);
11369
11459
  }
11370
- return validateValue(modifier, specificBlockerData.valueFormat);
11460
+ return validateValue(modifier, specificBlockerData.valueFormat, specificBlockerData.valueFormatFlags);
11371
11461
  }
11372
11462
  if (modifier?.value) {
11373
11463
  // e.g. 'third-party=true'
@@ -13064,8 +13154,14 @@ function getErrorMessage(error) {
13064
13154
  * Known validators that don't need to be validated as regex.
13065
13155
  */
13066
13156
  const KNOWN_VALIDATORS = new Set([
13157
+ 'csp_value',
13067
13158
  'domain',
13159
+ 'permissions_value',
13160
+ 'pipe_separated_apps',
13161
+ 'pipe_separated_denyallow_domains',
13068
13162
  'pipe_separated_domains',
13163
+ 'pipe_separated_methods',
13164
+ 'pipe_separated_stealth_options',
13069
13165
  'regexp',
13070
13166
  'url',
13071
13167
  ]);
@@ -13116,6 +13212,10 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
13116
13212
  * Its value can be a regex pattern or a known validator name (e.g. `domain`, `pipe_separated_domains`, etc.).
13117
13213
  */
13118
13214
  value_format: nonEmptyStringSchema.nullable().default(null),
13215
+ /**
13216
+ * Describes the flags for the `value_format` regex pattern.
13217
+ */
13218
+ value_format_flags: nonEmptyStringSchema.nullable().default(null),
13119
13219
  }).superRefine((data, ctx) => {
13120
13220
  // TODO: find something better, for now we can't add refine logic to the base schema:
13121
13221
  // https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
@@ -13136,11 +13236,17 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
13136
13236
  const valueFormat = data.value_format.trim();
13137
13237
  // if it is a known validator, we don't need to validate it further
13138
13238
  if (KNOWN_VALIDATORS.has(valueFormat)) {
13239
+ if (data.value_format_flags) {
13240
+ ctx.addIssue({
13241
+ code: zod.ZodIssueCode.custom,
13242
+ message: 'value_format_flags are not allowed for known validators',
13243
+ });
13244
+ }
13139
13245
  return;
13140
13246
  }
13141
13247
  // otherwise, we need to validate it as a regex
13142
13248
  try {
13143
- XRegExp(valueFormat);
13249
+ new RegExp(valueFormat, data.value_format_flags ?? EMPTY);
13144
13250
  }
13145
13251
  catch (error) {
13146
13252
  ctx.addIssue({
@@ -13149,6 +13255,12 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
13149
13255
  });
13150
13256
  }
13151
13257
  }
13258
+ else if (data.value_format_flags) {
13259
+ ctx.addIssue({
13260
+ code: zod.ZodIssueCode.custom,
13261
+ message: 'value_format is required for value_format_flags',
13262
+ });
13263
+ }
13152
13264
  }));
13153
13265
 
13154
13266
  /**
@@ -15699,7 +15811,7 @@ class RuleCategorizer {
15699
15811
  }
15700
15812
  }
15701
15813
 
15702
- const version = "2.1.4";
15814
+ const version = "2.3.0";
15703
15815
 
15704
15816
  /**
15705
15817
  * @file AGTree version
package/dist/agtree.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * AGTree v2.1.4 (build date: Mon, 25 Nov 2024 16:56:07 GMT)
2
+ * AGTree v2.3.0 (build date: Thu, 19 Dec 2024 15:31:22 GMT)
3
3
  * (c) 2024 Adguard Software Ltd.
4
4
  * Released under the MIT license
5
5
  * https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
@@ -12,7 +12,6 @@ import { TokenType as TokenType$1, tokenizeExtended, getFormattedTokenName } fro
12
12
  import * as tldts from 'tldts';
13
13
  import { parse } from 'tldts';
14
14
  import isIp from 'is-ip';
15
- import XRegExp from 'xregexp';
16
15
  import cloneDeep from 'clone-deep';
17
16
  import { modifiersCompatibilityTableData, redirectsCompatibilityTableData, scriptletsCompatibilityTableData } from './compatibility-table-data.js';
18
17
  import zod from 'zod';
@@ -292,6 +291,8 @@ const COMMA_DOMAIN_LIST_SEPARATOR = ',';
292
291
  const PIPE_MODIFIER_SEPARATOR = '|';
293
292
  const CSS_MEDIA_MARKER = '@media';
294
293
  const CSS_NOT_PSEUDO = 'not';
294
+ const CSS_BLOCK_OPEN = '{';
295
+ const CSS_BLOCK_CLOSE = '}';
295
296
  const HINT_MARKER = '!+';
296
297
  const HINT_MARKER_LEN = HINT_MARKER.length;
297
298
  const NETWORK_RULE_EXCEPTION_MARKER = '@@';
@@ -6066,14 +6067,24 @@ class CssTokenStream {
6066
6067
  // - end: end index of the token
6067
6068
  // - props: additional properties of the token, if any (we don't use it here, this is why we use underscore)
6068
6069
  // - balance: balance level of the token
6069
- tokenizeBalanced(source, (type, start, end, _, balance) => {
6070
- this.tokens.push({
6071
- type,
6072
- start,
6073
- end,
6074
- balance,
6070
+ try {
6071
+ tokenizeBalanced(source, (type, start, end, _, balance) => {
6072
+ this.tokens.push({
6073
+ type,
6074
+ start,
6075
+ end,
6076
+ balance,
6077
+ });
6075
6078
  });
6076
- });
6079
+ }
6080
+ catch (error) {
6081
+ // If the error is an AdblockSyntaxError, adjust the error positions to the base offset
6082
+ if (error instanceof AdblockSyntaxError) {
6083
+ error.start += baseOffset;
6084
+ error.end += baseOffset;
6085
+ throw error;
6086
+ }
6087
+ }
6077
6088
  this.index = 0;
6078
6089
  this.baseOffset = baseOffset;
6079
6090
  }
@@ -7633,6 +7644,40 @@ class AdgScriptletInjectionBodyParser extends ParserBase {
7633
7644
  }
7634
7645
  }
7635
7646
 
7647
+ /**
7648
+ * Represents an error that occurs when an operation is aborted.
7649
+ */
7650
+ class AbortError extends Error {
7651
+ constructor() {
7652
+ super('Aborted');
7653
+ }
7654
+ }
7655
+ // TODO: AG-38480 add a stop function to the tokenizers callback and move `hasToken` to CSS Tokenizer as well
7656
+ /**
7657
+ * Checks if the given raw string contains any of the specified tokens.
7658
+ * This function uses error throwing inside the abort tokenization process.
7659
+ *
7660
+ * @param raw - The raw string to be tokenized and checked.
7661
+ * @param tokens - A set of token types to check for in the raw string.
7662
+ * @returns `true` if any of the specified tokens are found in the raw string, otherwise `false`.
7663
+ */
7664
+ const hasToken = (raw, tokens) => {
7665
+ try {
7666
+ tokenizeExtended(raw, (type) => {
7667
+ if (tokens.has(type)) {
7668
+ throw new AbortError();
7669
+ }
7670
+ });
7671
+ }
7672
+ catch (e) {
7673
+ if (e instanceof AbortError) {
7674
+ return true;
7675
+ }
7676
+ throw e;
7677
+ }
7678
+ return false;
7679
+ };
7680
+
7636
7681
  /* eslint-disable no-param-reassign */
7637
7682
  /**
7638
7683
  * Value map for binary serialization. This helps to reduce the size of the serialized data,
@@ -8005,6 +8050,33 @@ class CosmeticRuleParser extends ParserBase {
8005
8050
  body: AdgCssInjectionParser.parse(rawBody, options, baseOffset + bodyStart),
8006
8051
  };
8007
8052
  };
8053
+ /**
8054
+ * Parses Adb CSS injection rules
8055
+ * eg: example.com##.foo { display: none; }
8056
+ *
8057
+ * @returns parsed rule
8058
+ */
8059
+ const parseAbpCssInjection = () => {
8060
+ if (!options.parseAbpSpecificRules) {
8061
+ return null;
8062
+ }
8063
+ // check if the rule contains both CSS block open and close characters
8064
+ // if none of them is present we can stop parsing
8065
+ if (rawBody.indexOf(CSS_BLOCK_OPEN) === -1 && rawBody.indexOf(CSS_BLOCK_CLOSE) === -1) {
8066
+ return null;
8067
+ }
8068
+ if (!hasToken(rawBody, new Set([TokenType$1.OpenCurlyBracket, TokenType$1.CloseCurlyBracket]))) {
8069
+ return null;
8070
+ }
8071
+ // try to parse the raw body as an AdGuard CSS injection rule
8072
+ const body = AdgCssInjectionParser.parse(rawBody, options, baseOffset + bodyStart);
8073
+ // if the parsed rule type is a 'CssInjectionRuleBody', return the parsed rule
8074
+ return {
8075
+ syntax: AdblockSyntax.Abp,
8076
+ type: CosmeticRuleType.CssInjectionRule,
8077
+ body,
8078
+ };
8079
+ };
8008
8080
  const parseAbpSnippetInjection = () => {
8009
8081
  if (!options.parseAbpSpecificRules) {
8010
8082
  throw new AdblockSyntaxError(sprintf(ERROR_MESSAGES$3.SYNTAX_DISABLED, AdblockSyntax.Abp), baseOffset + bodyStart, baseOffset + bodyEnd);
@@ -8116,10 +8188,22 @@ class CosmeticRuleParser extends ParserBase {
8116
8188
  // the next function is called, and so on.
8117
8189
  // If all functions return null, an error should be thrown.
8118
8190
  const separatorMap = {
8119
- '##': [parseUboHtmlFiltering, parseUboScriptletInjection, parseUboCssInjection, parseElementHiding],
8120
- '#@#': [parseUboHtmlFiltering, parseUboScriptletInjection, parseUboCssInjection, parseElementHiding],
8121
- '#?#': [parseUboCssInjection, parseElementHiding],
8122
- '#@?#': [parseUboCssInjection, parseElementHiding],
8191
+ '##': [
8192
+ parseUboHtmlFiltering,
8193
+ parseUboScriptletInjection,
8194
+ parseUboCssInjection,
8195
+ parseAbpCssInjection,
8196
+ parseElementHiding,
8197
+ ],
8198
+ '#@#': [
8199
+ parseUboHtmlFiltering,
8200
+ parseUboScriptletInjection,
8201
+ parseUboCssInjection,
8202
+ parseAbpCssInjection,
8203
+ parseElementHiding,
8204
+ ],
8205
+ '#?#': [parseUboCssInjection, parseAbpCssInjection, parseElementHiding],
8206
+ '#@?#': [parseUboCssInjection, parseAbpCssInjection, parseElementHiding],
8123
8207
  '#$#': [parseAdgCssInjection, parseAbpSnippetInjection],
8124
8208
  '#@$#': [parseAdgCssInjection, parseAbpSnippetInjection],
8125
8209
  '#$?#': [parseAdgCssInjection],
@@ -8188,7 +8272,7 @@ class CosmeticRuleParser extends ParserBase {
8188
8272
  result = node.body.selectorList.value;
8189
8273
  break;
8190
8274
  case CosmeticRuleType.CssInjectionRule:
8191
- if (node.syntax === AdblockSyntax.Adg) {
8275
+ if (node.syntax === AdblockSyntax.Adg || node.syntax === AdblockSyntax.Abp) {
8192
8276
  result = AdgCssInjectionParser.generate(node.body);
8193
8277
  }
8194
8278
  else if (node.syntax === AdblockSyntax.Ubo) {
@@ -10744,10 +10828,11 @@ const isCustomValueFormatValidator = (valueFormat) => {
10744
10828
  *
10745
10829
  * @param modifier Modifier AST node.
10746
10830
  * @param valueFormat Value format for the modifier.
10831
+ * @param valueFormatFlags Optional; RegExp flags for the value format.
10747
10832
  *
10748
10833
  * @returns Validation result.
10749
10834
  */
10750
- const validateValue = (modifier, valueFormat) => {
10835
+ const validateValue = (modifier, valueFormat, valueFormatFlags) => {
10751
10836
  if (isCustomValueFormatValidator(valueFormat)) {
10752
10837
  const validator = CUSTOM_VALUE_FORMAT_MAP[valueFormat];
10753
10838
  return validator(modifier);
@@ -10756,14 +10841,19 @@ const validateValue = (modifier, valueFormat) => {
10756
10841
  if (!modifier.value?.value) {
10757
10842
  return getValueRequiredValidationResult(modifierName);
10758
10843
  }
10759
- let xRegExp;
10844
+ let regExp;
10760
10845
  try {
10761
- xRegExp = XRegExp(valueFormat);
10846
+ if (isString(valueFormatFlags)) {
10847
+ regExp = new RegExp(valueFormat, valueFormatFlags);
10848
+ }
10849
+ else {
10850
+ regExp = new RegExp(valueFormat);
10851
+ }
10762
10852
  }
10763
10853
  catch (e) {
10764
10854
  throw new Error(`${SOURCE_DATA_ERROR_PREFIX.INVALID_VALUE_FORMAT_REGEXP}: '${modifierName}'`);
10765
10855
  }
10766
- const isValid = xRegExp.test(modifier.value?.value);
10856
+ const isValid = regExp.test(modifier.value?.value);
10767
10857
  if (!isValid) {
10768
10858
  return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.VALUE_INVALID}: '${modifierName}'`);
10769
10859
  }
@@ -11347,7 +11437,7 @@ const validateForSpecificSyntax = (syntax, modifier, isException) => {
11347
11437
  if (!specificBlockerData.valueFormat) {
11348
11438
  throw new Error(`${SOURCE_DATA_ERROR_PREFIX.NO_VALUE_FORMAT_FOR_ASSIGNABLE}: '${modifierName}'`);
11349
11439
  }
11350
- return validateValue(modifier, specificBlockerData.valueFormat);
11440
+ return validateValue(modifier, specificBlockerData.valueFormat, specificBlockerData.valueFormatFlags);
11351
11441
  }
11352
11442
  if (modifier?.value) {
11353
11443
  // e.g. 'third-party=true'
@@ -13044,8 +13134,14 @@ function getErrorMessage(error) {
13044
13134
  * Known validators that don't need to be validated as regex.
13045
13135
  */
13046
13136
  const KNOWN_VALIDATORS = new Set([
13137
+ 'csp_value',
13047
13138
  'domain',
13139
+ 'permissions_value',
13140
+ 'pipe_separated_apps',
13141
+ 'pipe_separated_denyallow_domains',
13048
13142
  'pipe_separated_domains',
13143
+ 'pipe_separated_methods',
13144
+ 'pipe_separated_stealth_options',
13049
13145
  'regexp',
13050
13146
  'url',
13051
13147
  ]);
@@ -13096,6 +13192,10 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
13096
13192
  * Its value can be a regex pattern or a known validator name (e.g. `domain`, `pipe_separated_domains`, etc.).
13097
13193
  */
13098
13194
  value_format: nonEmptyStringSchema.nullable().default(null),
13195
+ /**
13196
+ * Describes the flags for the `value_format` regex pattern.
13197
+ */
13198
+ value_format_flags: nonEmptyStringSchema.nullable().default(null),
13099
13199
  }).superRefine((data, ctx) => {
13100
13200
  // TODO: find something better, for now we can't add refine logic to the base schema:
13101
13201
  // https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
@@ -13116,11 +13216,17 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
13116
13216
  const valueFormat = data.value_format.trim();
13117
13217
  // if it is a known validator, we don't need to validate it further
13118
13218
  if (KNOWN_VALIDATORS.has(valueFormat)) {
13219
+ if (data.value_format_flags) {
13220
+ ctx.addIssue({
13221
+ code: zod.ZodIssueCode.custom,
13222
+ message: 'value_format_flags are not allowed for known validators',
13223
+ });
13224
+ }
13119
13225
  return;
13120
13226
  }
13121
13227
  // otherwise, we need to validate it as a regex
13122
13228
  try {
13123
- XRegExp(valueFormat);
13229
+ new RegExp(valueFormat, data.value_format_flags ?? EMPTY);
13124
13230
  }
13125
13231
  catch (error) {
13126
13232
  ctx.addIssue({
@@ -13129,6 +13235,12 @@ zodToCamelCase(baseCompatibilityDataSchema.extend({
13129
13235
  });
13130
13236
  }
13131
13237
  }
13238
+ else if (data.value_format_flags) {
13239
+ ctx.addIssue({
13240
+ code: zod.ZodIssueCode.custom,
13241
+ message: 'value_format is required for value_format_flags',
13242
+ });
13243
+ }
13132
13244
  }));
13133
13245
 
13134
13246
  /**
@@ -15679,7 +15791,7 @@ class RuleCategorizer {
15679
15791
  }
15680
15792
  }
15681
15793
 
15682
- const version = "2.1.4";
15794
+ const version = "2.3.0";
15683
15795
 
15684
15796
  /**
15685
15797
  * @file AGTree version