@ptolemy2002/rgx 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@ import { Branded } from "@ptolemy2002/ts-brand-utils";
8
8
  type RGXNoOpToken = null | undefined;
9
9
  type RGXLiteralToken = RegExp;
10
10
  type RGXNativeToken = string | number | boolean | RGXNoOpToken;
11
- type RGXConvertibleToken = { toRgx: () => RGXToken };
11
+ type RGXConvertibleToken = { toRgx: () => RGXToken, readonly rgxGroupWrap?: boolean };
12
12
  type RGXToken = RGXNativeToken | RGXLiteralToken | RGXConvertibleToken | RGXToken[];
13
13
  type RGXClassTokenConstructor = new (...args: unknown[]) => RGXClassToken;
14
14
 
@@ -20,6 +20,10 @@ const validVanillaRegexFlagsSymbol = Symbol('rgx.ValidVanillaRegexFlags');
20
20
  type ValidVanillaRegexFlagsBrandSymbol = typeof validVanillaRegexFlagsSymbol;
21
21
  type ValidVanillaRegexFlags = Branded<string, [ValidVanillaRegexFlagsBrandSymbol]>;
22
22
 
23
+ const validIdentifierSymbol = Symbol('rgx.ValidIdentifier');
24
+ type ValidIdentifierBrandSymbol = typeof validIdentifierSymbol;
25
+ type ValidIdentifier = Branded<string, [ValidIdentifierBrandSymbol]>;
26
+
23
27
  type RGXTokenType = 'no-op' | 'literal' | 'native' | 'convertible' | 'class' | RGXTokenType[];
24
28
  type RGXTokenTypeFlat = Exclude<RGXTokenType, RGXTokenType[]> | "array";
25
29
  type RGXTokenTypeGuardInput = RGXTokenTypeFlat | null | RGXClassTokenConstructor | RGXTokenTypeGuardInput[];
@@ -30,7 +34,7 @@ type RGXTokenFromType<T extends RGXTokenTypeGuardInput> =
30
34
  // ... see source for full definition
31
35
  ;
32
36
 
33
- type RGXErrorCode = 'UNKNOWN' | 'INVALID_RGX_TOKEN' | 'INVALID_REGEX_STRING' | 'INVALID_VANILLA_REGEX_FLAGS' | 'NOT_IMPLEMENTED';
37
+ type RGXErrorCode = 'UNKNOWN' | 'INVALID_RGX_TOKEN' | 'INVALID_REGEX_STRING' | 'INVALID_VANILLA_REGEX_FLAGS' | 'NOT_IMPLEMENTED' | 'INVALID_IDENTIFIER';
34
38
  type ExpectedTokenType = {
35
39
  type: "tokenType";
36
40
  values: RGXTokenTypeFlat[];
@@ -42,6 +46,11 @@ type RGXTokenCollectionMode = 'union' | 'concat';
42
46
  type RGXTokenCollectionInput = RGXToken | RGXTokenCollection;
43
47
 
44
48
  type RGXUnionInsertionPosition = 'prefix' | 'suffix';
49
+
50
+ type RGXGroupTokenArgs = {
51
+ name?: string | null;
52
+ capturing?: boolean;
53
+ };
45
54
  ```
46
55
 
47
56
  ## Classes
@@ -117,6 +126,22 @@ constructor(functionality: string, message?: string | null)
117
126
  #### Methods
118
127
  - `toString() => string`: Returns a formatted string indicating the unimplemented functionality and any additional message.
119
128
 
129
+ ### RGXInvalidIdentifierError extends RGXError
130
+ A specific error class for invalid identifiers. This error is thrown when a string fails validation as a valid identifier. The error code is set to `INVALID_IDENTIFIER` on instantiation.
131
+
132
+ #### Constructor
133
+ ```typescript
134
+ constructor(message: string, got: string)
135
+ ```
136
+ - `message` (`string`): The error message.
137
+ - `got` (`string`): The actual string that was received, which failed validation.
138
+
139
+ #### Properties
140
+ - `got` (`string`): The actual string that was received, which failed validation.
141
+
142
+ #### Methods
143
+ - `toString() => string`: Returns a formatted string indicating the invalid identifier and the reason for failure.
144
+
120
145
  ### RGXTokenCollection
121
146
  A class representing a collection of RGX tokens. This class manages collections of RGX tokens like an array, but with additional metadata about the collection mode (union or concat). Since `toRgx()` returns a `RegExp`, instances of this class satisfy the `RGXConvertibleToken` interface and can be used directly as tokens in `rgx`, `rgxa`, and other token-accepting functions.
122
147
 
@@ -132,6 +157,7 @@ constructor(tokens: RGXTokenCollectionInput = [], mode: RGXTokenCollectionMode =
132
157
  #### Properties
133
158
  - `tokens` (`RGXToken[]`): The array of RGX tokens managed by the collection. In almost all cases, use `getTokens()` instead of accessing this property directly, as it will be copied to prevent external mutation.
134
159
  - `mode` (`RGXTokenCollectionMode`): The mode of the collection, either 'union' or 'concat'. This determines how the tokens in the collection will be resolved when `toRgx()` is called.
160
+ - `resolve() => ValidRegexString`: A convenience method that resolves this collection by calling `resolveRGXToken(this)`, returning the resolved regex string representation.
135
161
  - `toRgx() => RegExp`: A method that resolves the collection to a `RegExp` object based on the collection mode. In 'union' mode, the tokens are resolved as alternatives (using the `|` operator), while in 'concat' mode, the tokens are resolved as concatenated together. No flags are applied to the resulting `RegExp`. Since this method returns a `RegExp` (which is `RGXLiteralToken`), `RGXTokenCollection` instances satisfy the `RGXConvertibleToken` interface and can be used directly as tokens in `rgx`, `rgxa`, and other token-accepting functions.
136
162
  - `getTokens() => RGXToken[]`: A method that returns a copy of the array of RGX tokens managed by the collection. This is used to prevent external mutation of the internal `tokens` array.
137
163
  - `toArray() => RGXToken[]`: An alias for `getTokens()`, provided for convenience.
@@ -157,10 +183,12 @@ An abstract base class for creating custom RGX token classes. Subclasses must im
157
183
 
158
184
  #### Properties
159
185
  - `isGroup` (`boolean`): Returns `false` by default. Subclasses can override this to indicate whether the token represents a group.
186
+ - `rgxGroupWrap` (`boolean`): Returns `true` by default. Controls whether the resolver wraps this token's resolved output in a non-capturing group. Subclasses can override this to prevent double-wrapping (e.g., when the token already wraps itself in a group).
160
187
 
161
188
  #### Methods
162
189
  - `or(...others: RGXTokenCollectionInput[]) => RGXClassUnionToken`: Creates an `RGXClassUnionToken` that represents a union (alternation) of this token with the provided others. If any of the `others` are `RGXClassUnionToken` instances, their tokens are flattened into the union rather than nested. If `this` is already an `RGXClassUnionToken`, its existing tokens are preserved and the others are appended.
163
- - `resolve() => ValidRegexString`: A convenience method that resolves this token by calling `resolveRGXToken(this)`, returning the resolved regex string representation. Since this method is defined on `RGXClassToken`, it is available on all subclasses including `RGXClassUnionToken`.
190
+ - `group(args?: RGXGroupTokenArgs) => RGXGroupToken`: Wraps this token in an `RGXGroupToken` with the provided arguments. The `args` parameter defaults to `{}`, which creates a capturing group with no name. This is a convenience method that creates a new `RGXGroupToken` with `this` as the sole token.
191
+ - `resolve() => ValidRegexString`: A convenience method that resolves this token by calling `resolveRGXToken(this)`, returning the resolved regex string representation. Since this method is defined on `RGXClassToken`, it is available on all subclasses including `RGXClassUnionToken` and `RGXGroupToken`.
164
192
 
165
193
  ### RGXClassUnionToken extends RGXClassToken
166
194
  A class representing a union (alternation) of RGX tokens. This is typically created via the `or()` method on `RGXClassToken`, but can also be instantiated directly.
@@ -187,6 +215,34 @@ constructor(tokens: RGXTokenCollectionInput = [])
187
215
  - `cleanTokens() => this`: Expands any nested union tokens and removes duplicates from the internal token collection. Returns `this` for chaining. Called automatically during construction and after `add` or `concat`.
188
216
  - `toRgx() => RegExp`: Resolves the union by calling `toRgx()` on the internal `RGXTokenCollection`, returning a `RegExp`.
189
217
 
218
+ ### RGXGroupToken extends RGXClassToken
219
+ A class representing a group (capturing, non-capturing, or named) wrapping one or more RGX tokens. This is typically created via the `group()` method on `RGXClassToken`, but can also be instantiated directly.
220
+
221
+ A function `rgxGroup` is provided with the same parameters as this class' constructor, for easier instantiation without needing to use the `new` keyword.
222
+
223
+ #### Static Properties
224
+ - `check(value: unknown): value is RGXGroupToken`: A type guard that checks if the given value is an instance of `RGXGroupToken`.
225
+ - `assert(value: unknown): asserts value is RGXGroupToken`: An assertion that checks if the given value is an instance of `RGXGroupToken`. If the assertion fails, an `RGXInvalidTokenError` will be thrown.
226
+
227
+ #### Constructor
228
+ ```typescript
229
+ constructor(args?: RGXGroupTokenArgs, tokens?: RGXTokenCollectionInput)
230
+ ```
231
+ - `args` (`RGXGroupTokenArgs`, optional): An object specifying the group configuration. Defaults to `{}`.
232
+ - `name` (`string | null`, optional): The name of the group for named capture groups. Must be a valid identifier (validated via `assertValidIdentifier`). Defaults to `null`.
233
+ - `capturing` (`boolean`, optional): Whether the group is capturing. Defaults to `true`. Setting this to `false` also clears any `name`.
234
+ - `tokens` (`RGXTokenCollectionInput`, optional): The tokens to be wrapped by the group. Internally stored as an `RGXTokenCollection` in 'concat' mode. Defaults to an empty array.
235
+
236
+ #### Properties
237
+ - `tokens` (`RGXTokenCollection`): The internal collection of tokens managed in 'concat' mode.
238
+ - `name` (`string | null`): The name of the group. Setting this to a non-null value validates it as a valid identifier via `assertValidIdentifier`.
239
+ - `capturing` (`boolean`): Whether the group is capturing. Any named group is automatically capturing (returns `true` when `name` is not `null`). Setting this to `false` also clears `name` to `null`.
240
+ - `isGroup` (`boolean`): Returns `true`, indicating this token represents a group.
241
+ - `rgxGroupWrap` (`boolean`): Returns `false`, since the group already wraps itself, preventing the resolver from double-wrapping.
242
+
243
+ #### Methods
244
+ - `toRgx() => RegExp`: Resolves the group by concatenating the internal tokens and wrapping the result in the appropriate group syntax: `(?<name>...)` for named groups, `(?:...)` for non-capturing groups, or `(...)` for capturing groups.
245
+
190
246
  ## Functions
191
247
  The following functions are exported by the library:
192
248
 
@@ -273,7 +329,7 @@ Asserts that the given value is a native token (string, number, boolean, or no-o
273
329
  function isRGXConvertibleToken(value: unknown, returnCheck?: boolean): value is RGXConvertibleToken
274
330
  ```
275
331
 
276
- Checks if the given value is a convertible token (an object with a `toRgx` method). When `returnCheck` is `true` (the default), also validates that `toRgx` is callable and returns a valid `RGXToken` (which can be any RGX token type, including other convertible tokens, allowing for recursive structures).
332
+ Checks if the given value is a convertible token (an object with a `toRgx` method). If the `rgxGroupWrap` property is present, it must be a boolean; otherwise, the check fails. When `returnCheck` is `true` (the default), also validates that `toRgx` is callable and returns a valid `RGXToken` (which can be any RGX token type, including other convertible tokens, allowing for recursive structures).
277
333
 
278
334
  #### Parameters
279
335
  - `value` (`unknown`): The value to check.
@@ -286,7 +342,7 @@ Checks if the given value is a convertible token (an object with a `toRgx` metho
286
342
  ```typescript
287
343
  function assertRGXConvertibleToken(value: unknown, returnCheck?: boolean): asserts value is RGXConvertibleToken
288
344
  ```
289
- Asserts that the given value is a convertible token (an object with a `toRgx` method). When `returnCheck` is `true` (the default), also validates that `toRgx` is callable and returns a valid `RGXToken` (which can be any RGX token type, including other convertible tokens, allowing for recursive structures). If the assertion fails, an `RGXInvalidTokenError` will be thrown.
345
+ Asserts that the given value is a convertible token (an object with a `toRgx` method). If the `rgxGroupWrap` property is present, it must be a boolean; otherwise, the assertion fails. When `returnCheck` is `true` (the default), also validates that `toRgx` is callable and returns a valid `RGXToken` (which can be any RGX token type, including other convertible tokens, allowing for recursive structures). If the assertion fails, an `RGXInvalidTokenError` will be thrown.
290
346
 
291
347
  #### Parameters
292
348
  - `value` (`unknown`): The value to assert.
@@ -499,6 +555,30 @@ Asserts that the given string is a valid combination of vanilla regex flags (g,
499
555
  #### Returns
500
556
  - `void`: This function does not return a value, but will throw an error if the assertion fails.
501
557
 
558
+ ### isValidIdentifier
559
+ ```typescript
560
+ function isValidIdentifier(value: string): value is ValidIdentifier
561
+ ```
562
+ Checks if the given string is a valid identifier, used for group names and backreferences. Valid identifiers contain only letters, digits, dollar signs, and underscores, and cannot start with a digit.
563
+
564
+ #### Parameters
565
+ - `value` (`string`): The string to check.
566
+
567
+ #### Returns
568
+ - `boolean`: `true` if the string is a valid identifier, otherwise `false`.
569
+
570
+ ### assertValidIdentifier
571
+ ```typescript
572
+ function assertValidIdentifier(value: string): asserts value is ValidIdentifier
573
+ ```
574
+ Asserts that the given string is a valid identifier, used for group names and backreferences. Valid identifiers contain only letters, digits, dollar signs, and underscores, and cannot start with a digit. If the assertion fails, an `RGXInvalidIdentifierError` will be thrown.
575
+
576
+ #### Parameters
577
+ - `value` (`string`): The string to assert.
578
+
579
+ #### Returns
580
+ - `void`: This function does not return a value, but will throw an error if the assertion fails.
581
+
502
582
  ### escapeRegex
503
583
  ```typescript
504
584
  function escapeRegex(value: string): ValidRegexString
@@ -514,14 +594,17 @@ Escapes special regex characters in the given string and brands the result as a
514
594
 
515
595
  ### resolveRGXToken
516
596
  ```typescript
517
- function resolveRGXToken(token: RGXToken, groupWrap?: boolean): ValidRegexString
597
+ function resolveRGXToken(token: RGXToken, groupWrap?: boolean, topLevel?: boolean): ValidRegexString
518
598
  ```
519
599
 
520
600
  Resolves an RGX token to a string. No-op tokens resolve to an empty string, literal tokens are included as-is (wrapped in a non-capturing group when `groupWrap` is `true`), native tokens are converted to strings and escaped, convertible tokens are converted using their `toRgx` method and then resolved recursively, and arrays of tokens are resolved as unions of their resolved elements (repeats removed, placed in a non-capturing group when `groupWrap` is `true`).
521
601
 
602
+ For convertible tokens, if the token has an `rgxGroupWrap` property, that value always takes precedence. If `rgxGroupWrap` is not present, the behavior depends on whether the call is top-level: at the top level, the `groupWrap` parameter is passed through; in recursive calls, it falls back to `true` regardless of the `groupWrap` parameter. This ensures that the caller's `groupWrap` preference only affects the outermost convertible token and does not leak into deeply nested resolution.
603
+
522
604
  #### Parameters
523
605
  - `token` (`RGXToken`): The RGX token to resolve.
524
- - `groupWrap` (`boolean`, optional): Whether to wrap literal tokens and array unions in non-capturing groups (`(?:...)`). Defaults to `true`. When `false`, literals use their raw source and array unions omit the wrapping group. The `groupWrap` preference is passed through to recursive calls for convertible tokens, but array union elements always use `groupWrap=true` internally.
606
+ - `groupWrap` (`boolean`, optional): Whether to wrap literal tokens and array unions in non-capturing groups (`(?:...)`). Defaults to `true`. When `false`, literals use their raw source and array unions omit the wrapping group. For convertible tokens, the token's `rgxGroupWrap` property always takes precedence; otherwise, this value is only passed through at the top level (in recursive calls it falls back to `true`). Array union elements always use `groupWrap=true` internally.
607
+ - `topLevel` (`boolean`, optional): Tracks whether the current call is the initial (top-level) invocation. Defaults to `true`. **Warning**: This parameter is intended for internal use by the resolver's own recursion. External callers should not set this parameter, as doing so may produce unexpected wrapping behavior.
525
608
 
526
609
  #### Returns
527
610
  - `ValidRegexString`: The resolved string representation of the RGX token. This is guaranteed to be a valid regex string, as convertible tokens are validated to only produce valid regex strings or arrays of valid regex strings.
@@ -615,7 +698,7 @@ Removes duplicate tokens from the provided list using `Set` equality and returns
615
698
  function rgxClassInit(): void
616
699
  ```
617
700
 
618
- Initializes internal method patches required for `RGXClassToken` subclass methods (such as `or`) to work correctly. This function is called automatically when importing from the main module entry point, so you typically do not need to call it yourself. It only needs to be called manually if you import directly from sub-modules.
701
+ Initializes internal method patches required for `RGXClassToken` subclass methods (such as `or` and `group`) to work correctly. This function is called automatically when importing from the main module entry point, so you typically do not need to call it yourself. It only needs to be called manually if you import directly from sub-modules.
619
702
 
620
703
  ## Peer Dependencies
621
704
  - `@ptolemy2002/immutability-utils` ^2.0.0
@@ -1,11 +1,14 @@
1
1
  import { RGXToken, ValidRegexString } from "../types";
2
2
  import { RGXTokenCollectionInput } from "../collection";
3
3
  import type { RGXClassUnionToken } from "./union";
4
+ import type { RGXGroupToken, RGXGroupTokenArgs } from "./group";
4
5
  export declare abstract class RGXClassToken {
5
6
  abstract toRgx(): RGXToken;
6
7
  static check: (value: unknown) => value is RGXClassToken;
7
8
  static assert: (value: unknown) => asserts value is RGXClassToken;
8
9
  get isGroup(): boolean;
10
+ get rgxGroupWrap(): boolean;
9
11
  or(...others: RGXTokenCollectionInput[]): RGXClassUnionToken;
12
+ group(args?: RGXGroupTokenArgs): RGXGroupToken;
10
13
  resolve(): ValidRegexString;
11
14
  }
@@ -7,9 +7,15 @@ class RGXClassToken {
7
7
  get isGroup() {
8
8
  return false;
9
9
  }
10
+ get rgxGroupWrap() {
11
+ return true;
12
+ }
10
13
  or(...others) {
11
14
  throw new errors_1.RGXNotImplementedError('RGXClassToken.or(...others)', 'call rgxClassInit() first.');
12
15
  }
16
+ group(args = {}) {
17
+ throw new errors_1.RGXNotImplementedError('RGXClassToken.group(args)', 'call rgxClassInit() first.');
18
+ }
13
19
  resolve() {
14
20
  return (0, resolve_1.resolveRGXToken)(this);
15
21
  }
@@ -0,0 +1,22 @@
1
+ import { RGXTokenCollection, RGXTokenCollectionInput } from "../collection";
2
+ import { RGXClassToken } from "./base";
3
+ export type RGXGroupTokenArgs = {
4
+ name?: string | null;
5
+ capturing?: boolean;
6
+ };
7
+ export declare class RGXGroupToken extends RGXClassToken {
8
+ tokens: RGXTokenCollection;
9
+ _name: string | null;
10
+ _capturing: boolean;
11
+ static check: (value: unknown) => value is RGXGroupToken;
12
+ static assert: (value: unknown) => asserts value is RGXGroupToken;
13
+ get name(): string | null;
14
+ set name(value: string | null);
15
+ get capturing(): boolean;
16
+ set capturing(value: boolean);
17
+ get isGroup(): boolean;
18
+ get rgxGroupWrap(): boolean;
19
+ constructor({ name, capturing }?: RGXGroupTokenArgs, tokens?: RGXTokenCollectionInput);
20
+ toRgx(): RegExp;
21
+ }
22
+ export declare const rgxGroup: (args_0?: RGXGroupTokenArgs | undefined, tokens?: RGXTokenCollectionInput) => RGXGroupToken;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rgxGroup = exports.RGXGroupToken = void 0;
4
+ const collection_1 = require("../collection");
5
+ const base_1 = require("./base");
6
+ const internal_1 = require("../internal");
7
+ const typeGuards_1 = require("../typeGuards");
8
+ class RGXGroupToken extends base_1.RGXClassToken {
9
+ get name() {
10
+ return this._name;
11
+ }
12
+ set name(value) {
13
+ if (value !== null)
14
+ (0, typeGuards_1.assertValidIdentifier)(value);
15
+ this._name = value;
16
+ }
17
+ get capturing() {
18
+ // Any named group is automatically capturing
19
+ return this.name !== null || this._capturing;
20
+ }
21
+ set capturing(value) {
22
+ if (!value)
23
+ this.name = null; // Non-capturing groups cannot have names
24
+ this._capturing = value;
25
+ }
26
+ get isGroup() {
27
+ return true;
28
+ }
29
+ get rgxGroupWrap() {
30
+ // When this token is resolved, it will wrap itself in a group, so we don't want the resolver to group wrap it again.
31
+ return false;
32
+ }
33
+ constructor({ name = null, capturing = true } = {}, tokens = []) {
34
+ super();
35
+ this._name = null;
36
+ this._capturing = true;
37
+ this.name = name;
38
+ this.capturing = capturing;
39
+ if (tokens instanceof collection_1.RGXTokenCollection && tokens.mode === 'union')
40
+ this.tokens = new collection_1.RGXTokenCollection(tokens, 'concat');
41
+ else
42
+ this.tokens = new collection_1.RGXTokenCollection(tokens, 'concat');
43
+ }
44
+ toRgx() {
45
+ // The collection token doesn't group itself, so this is safe.
46
+ let result = this.tokens.toRgx().source;
47
+ if (this.name !== null)
48
+ result = `(?<${this.name}>${result})`;
49
+ else if (!this.capturing)
50
+ result = `(?:${result})`;
51
+ else
52
+ result = `(${result})`;
53
+ return new RegExp(result);
54
+ }
55
+ }
56
+ exports.RGXGroupToken = RGXGroupToken;
57
+ RGXGroupToken.check = (0, internal_1.createClassGuardFunction)(RGXGroupToken);
58
+ RGXGroupToken.assert = (0, internal_1.createAssertClassGuardFunction)(RGXGroupToken);
59
+ exports.rgxGroup = (0, internal_1.createConstructFunction)(RGXGroupToken);
@@ -1,3 +1,4 @@
1
1
  export * from "./init";
2
2
  export * from "./base";
3
3
  export * from "./union";
4
+ export * from "./group";
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./init"), exports);
18
18
  __exportStar(require("./base"), exports);
19
19
  __exportStar(require("./union"), exports);
20
+ __exportStar(require("./group"), exports);
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.rgxClassInit = rgxClassInit;
4
4
  const base_1 = require("./base");
5
5
  const union_1 = require("./union");
6
+ const group_1 = require("./group");
6
7
  function rgxClassInit() {
7
8
  // Patch RGXClassToken here, Since classes like RGXClassUnionToken are instances of RGXClassToken
8
9
  // themselves. If we tried to import RGXClassUnionToken in base.ts, it would cause a circular dependency.
@@ -19,4 +20,7 @@ function rgxClassInit() {
19
20
  return new union_1.RGXClassUnionToken([this, ...filteredOthers]);
20
21
  }
21
22
  };
23
+ base_1.RGXClassToken.prototype.group = function (args = {}) {
24
+ return new group_1.RGXGroupToken(args, [this]);
25
+ };
22
26
  }
@@ -1,4 +1,4 @@
1
- import { RGXToken } from "./types";
1
+ import { RGXToken, ValidRegexString } from "./types";
2
2
  import { CloneDepth } from "@ptolemy2002/immutability-utils";
3
3
  import { Collection } from "@ptolemy2002/ts-utils";
4
4
  export type RGXTokenCollectionMode = 'union' | 'concat';
@@ -9,6 +9,7 @@ export declare class RGXTokenCollection implements Collection<RGXToken> {
9
9
  static check: (value: unknown) => value is RGXTokenCollection;
10
10
  static assert: (value: unknown) => asserts value is RGXTokenCollection;
11
11
  constructor(tokens?: RGXTokenCollectionInput, mode?: RGXTokenCollectionMode);
12
+ resolve(): ValidRegexString;
12
13
  toRgx(): RegExp;
13
14
  getTokens(): RGXToken[];
14
15
  clone(depth?: CloneDepth): RGXTokenCollection;
@@ -19,6 +19,9 @@ class RGXTokenCollection {
19
19
  }
20
20
  this.mode = mode;
21
21
  }
22
+ resolve() {
23
+ return (0, resolve_1.resolveRGXToken)(this);
24
+ }
22
25
  toRgx() {
23
26
  let pattern;
24
27
  if (this.mode === 'union') {
@@ -1,4 +1,4 @@
1
- export type RGXErrorCode = 'UNKNOWN' | 'INVALID_RGX_TOKEN' | 'INVALID_REGEX_STRING' | 'INVALID_VANILLA_REGEX_FLAGS' | 'NOT_IMPLEMENTED';
1
+ export type RGXErrorCode = 'UNKNOWN' | 'INVALID_RGX_TOKEN' | 'INVALID_REGEX_STRING' | 'INVALID_VANILLA_REGEX_FLAGS' | 'NOT_IMPLEMENTED' | 'INVALID_IDENTIFIER';
2
2
  export declare class RGXError extends Error {
3
3
  code: RGXErrorCode;
4
4
  constructor(message: string, code?: RGXErrorCode);
@@ -3,3 +3,4 @@ export * from './invalidToken';
3
3
  export * from './invalidRegexString';
4
4
  export * from './invalidVanillaRegexFlags';
5
5
  export * from './notImplemented';
6
+ export * from './invalidIdentifier';
@@ -19,3 +19,4 @@ __exportStar(require("./invalidToken"), exports);
19
19
  __exportStar(require("./invalidRegexString"), exports);
20
20
  __exportStar(require("./invalidVanillaRegexFlags"), exports);
21
21
  __exportStar(require("./notImplemented"), exports);
22
+ __exportStar(require("./invalidIdentifier"), exports);
@@ -0,0 +1,6 @@
1
+ import { RGXError } from "./";
2
+ export declare class RGXInvalidIdentifierError extends RGXError {
3
+ got: string;
4
+ constructor(message: string, got: string);
5
+ toString(): string;
6
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RGXInvalidIdentifierError = void 0;
4
+ const errors_1 = require("./");
5
+ class RGXInvalidIdentifierError extends errors_1.RGXError {
6
+ constructor(message, got) {
7
+ super(message, 'INVALID_IDENTIFIER');
8
+ this.name = 'RGXInvalidIdentifierError';
9
+ this.got = got;
10
+ }
11
+ toString() {
12
+ return `${this.name}: ${this.message}; Got: ${JSON.stringify(this.got)}`;
13
+ }
14
+ }
15
+ exports.RGXInvalidIdentifierError = RGXInvalidIdentifierError;
package/dist/resolve.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import * as t from "./types";
2
2
  export declare function escapeRegex(value: string): t.ValidRegexString;
3
- export declare function resolveRGXToken(token: t.RGXToken, groupWrap?: boolean): t.ValidRegexString;
3
+ export declare function resolveRGXToken(token: t.RGXToken, groupWrap?: boolean, topLevel?: boolean): t.ValidRegexString;
package/dist/resolve.js CHANGED
@@ -41,7 +41,7 @@ const tg = __importStar(require("./typeGuards"));
41
41
  function escapeRegex(value) {
42
42
  return value.replaceAll(/[\-\^\$.*+?^${}()|[\]\\]/g, '\\$&');
43
43
  }
44
- function resolveRGXToken(token, groupWrap = true) {
44
+ function resolveRGXToken(token, groupWrap = true, topLevel = true) {
45
45
  if (tg.isRGXNoOpToken(token))
46
46
  return '';
47
47
  if (tg.isRGXLiteralToken(token)) {
@@ -53,7 +53,9 @@ function resolveRGXToken(token, groupWrap = true) {
53
53
  if (tg.isRGXNativeToken(token))
54
54
  return escapeRegex(String(token));
55
55
  if (tg.isRGXConvertibleToken(token)) {
56
- return resolveRGXToken(token.toRgx(), groupWrap);
56
+ // The top-level group-wrapping preference propogates to a direct convertible token, but after that
57
+ // the preference falls back to true whenever a token doesn't explicitly specify a preference.
58
+ return resolveRGXToken(token.toRgx(), token.rgxGroupWrap ?? (topLevel ? groupWrap : true), false);
57
59
  }
58
60
  // Interpret arrays as unions
59
61
  if (tg.isRGXArrayToken(token, false)) {
@@ -64,9 +66,9 @@ function resolveRGXToken(token, groupWrap = true) {
64
66
  token = [...(0, class_1.removeRgxUnionDuplicates)(...token)];
65
67
  // Don't preserve group wrapping preference for the recursive calls
66
68
  if (groupWrap)
67
- return '(?:' + token.map(t => resolveRGXToken(t, true)).join('|') + ')';
69
+ return '(?:' + token.map(t => resolveRGXToken(t, true, false)).join('|') + ')';
68
70
  else
69
- return token.map(t => resolveRGXToken(t, true)).join('|');
71
+ return token.map(t => resolveRGXToken(t, true, false)).join('|');
70
72
  }
71
73
  return resolveRGXToken(token[0]);
72
74
  }
@@ -20,3 +20,5 @@ export declare function isValidRegexString(value: string): value is t.ValidRegex
20
20
  export declare function assertValidRegexString(value: string): asserts value is t.ValidRegexString;
21
21
  export declare function isValidVanillaRegexFlags(value: string): value is t.ValidVanillaRegexFlags;
22
22
  export declare function assertValidVanillaRegexFlags(value: string): asserts value is t.ValidVanillaRegexFlags;
23
+ export declare function isValidIdentifier(value: string): value is t.ValidIdentifier;
24
+ export declare function assertValidIdentifier(value: string): asserts value is t.ValidIdentifier;
@@ -57,6 +57,8 @@ exports.isValidRegexString = isValidRegexString;
57
57
  exports.assertValidRegexString = assertValidRegexString;
58
58
  exports.isValidVanillaRegexFlags = isValidVanillaRegexFlags;
59
59
  exports.assertValidVanillaRegexFlags = assertValidVanillaRegexFlags;
60
+ exports.isValidIdentifier = isValidIdentifier;
61
+ exports.assertValidIdentifier = assertValidIdentifier;
60
62
  const e = __importStar(require("./errors"));
61
63
  const is_callable_1 = __importDefault(require("is-callable"));
62
64
  const internal_1 = require("./internal");
@@ -87,6 +89,9 @@ function assertRGXNativeToken(value) {
87
89
  }
88
90
  function isRGXConvertibleToken(value, returnCheck = true) {
89
91
  if (typeof value === 'object' && value !== null && 'toRgx' in value) {
92
+ // The rgxGroupWrap property is optional, but if it exists it must be a boolean.
93
+ if ('rgxGroupWrap' in value && typeof value.rgxGroupWrap !== 'boolean')
94
+ return false;
90
95
  if ((0, is_callable_1.default)(value.toRgx)) {
91
96
  if (!returnCheck)
92
97
  return true;
@@ -215,3 +220,12 @@ function assertValidVanillaRegexFlags(value) {
215
220
  throw new e.RGXInvalidVanillaRegexFlagsError("Invalid vanilla regex flags", value);
216
221
  }
217
222
  }
223
+ function isValidIdentifier(value) {
224
+ // This regex checks for valid JavaScript identifiers, which can be used for named capture groups.
225
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
226
+ }
227
+ function assertValidIdentifier(value) {
228
+ if (!isValidIdentifier(value)) {
229
+ throw new e.RGXInvalidIdentifierError("Invalid identifier", value);
230
+ }
231
+ }
package/dist/types.d.ts CHANGED
@@ -5,6 +5,7 @@ export type RGXLiteralToken = RegExp;
5
5
  export type RGXNativeToken = string | number | boolean | RGXNoOpToken;
6
6
  export type RGXConvertibleToken = {
7
7
  toRgx: () => RGXToken;
8
+ readonly rgxGroupWrap?: boolean;
8
9
  };
9
10
  export type RGXToken = RGXNativeToken | RGXLiteralToken | RGXConvertibleToken | RGXToken[];
10
11
  export type RGXClassTokenConstructor = new (...args: unknown[]) => RGXClassToken;
@@ -20,3 +21,6 @@ export type ValidRegexString = Branded<string, [ValidRegexBrandSymbol]>;
20
21
  export declare const validVanillaRegexFlagsSymbol: unique symbol;
21
22
  export type ValidVanillaRegexFlagsBrandSymbol = typeof validVanillaRegexFlagsSymbol;
22
23
  export type ValidVanillaRegexFlags = Branded<string, [ValidVanillaRegexFlagsBrandSymbol]>;
24
+ export declare const validIdentifierSymbol: unique symbol;
25
+ export type ValidIdentifierBrandSymbol = typeof validIdentifierSymbol;
26
+ export type ValidIdentifier = Branded<string, [ValidIdentifierBrandSymbol]>;
package/dist/types.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validVanillaRegexFlagsSymbol = exports.validRegexSymbol = void 0;
3
+ exports.validIdentifierSymbol = exports.validVanillaRegexFlagsSymbol = exports.validRegexSymbol = void 0;
4
4
  exports.validRegexSymbol = Symbol('rgx.ValidRegex');
5
5
  exports.validVanillaRegexFlagsSymbol = Symbol('rgx.ValidVanillaRegexFlags');
6
+ exports.validIdentifierSymbol = Symbol('rgx.ValidIdentifier');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ptolemy2002/rgx",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",