@ptolemy2002/rgx 4.0.0 → 4.2.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,14 @@ 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' | 'OUT_OF_BOUNDS';
38
+
39
+ type RangeObject = {
40
+ min?: number | null;
41
+ max?: number | null;
42
+ inclusiveLeft?: boolean;
43
+ inclusiveRight?: boolean;
44
+ };
34
45
  type ExpectedTokenType = {
35
46
  type: "tokenType";
36
47
  values: RGXTokenTypeFlat[];
@@ -42,6 +53,11 @@ type RGXTokenCollectionMode = 'union' | 'concat';
42
53
  type RGXTokenCollectionInput = RGXToken | RGXTokenCollection;
43
54
 
44
55
  type RGXUnionInsertionPosition = 'prefix' | 'suffix';
56
+
57
+ type RGXGroupTokenArgs = {
58
+ name?: string | null;
59
+ capturing?: boolean;
60
+ };
45
61
  ```
46
62
 
47
63
  ## Classes
@@ -117,6 +133,49 @@ constructor(functionality: string, message?: string | null)
117
133
  #### Methods
118
134
  - `toString() => string`: Returns a formatted string indicating the unimplemented functionality and any additional message.
119
135
 
136
+ ### RGXInvalidIdentifierError extends RGXError
137
+ 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.
138
+
139
+ #### Constructor
140
+ ```typescript
141
+ constructor(message: string, got: string)
142
+ ```
143
+ - `message` (`string`): The error message.
144
+ - `got` (`string`): The actual string that was received, which failed validation.
145
+
146
+ #### Properties
147
+ - `got` (`string`): The actual string that was received, which failed validation.
148
+
149
+ #### Methods
150
+ - `toString() => string`: Returns a formatted string indicating the invalid identifier and the reason for failure.
151
+
152
+ ### RGXOutOfBoundsError extends RGXError
153
+ A specific error class for out-of-bounds values. This error is thrown when a numeric value falls outside an expected range. The error code is set to `OUT_OF_BOUNDS` on instantiation.
154
+
155
+ #### Constructor
156
+ ```typescript
157
+ constructor(message: string, got: number, { min, max, inclusiveLeft, inclusiveRight }?: RangeObject)
158
+ ```
159
+ - `message` (`string`): The error message.
160
+ - `got` (`number`): The actual numeric value that was received, which fell outside the expected range.
161
+ - `min` (`number | null`, optional): The minimum bound of the range. Defaults to `null` (no minimum).
162
+ - `max` (`number | null`, optional): The maximum bound of the range. Defaults to `null` (no maximum). Setting `min` to a value greater than `max` will adjust `max` to equal `min`, and vice versa.
163
+ - `inclusiveLeft` (`boolean`, optional): Whether the minimum bound is inclusive. Defaults to `true`.
164
+ - `inclusiveRight` (`boolean`, optional): Whether the maximum bound is inclusive. Defaults to `true`.
165
+
166
+ #### Properties
167
+ - `got` (`number`): The actual numeric value that was received.
168
+ - `min` (`number | null`): The minimum bound of the range. Setting this to a value greater than `max` will adjust `max` to equal `min`.
169
+ - `max` (`number | null`): The maximum bound of the range. Setting this to a value less than `min` will adjust `min` to equal `max`.
170
+ - `inclusiveLeft` (`boolean`): Whether the minimum bound is inclusive.
171
+ - `inclusiveRight` (`boolean`): Whether the maximum bound is inclusive.
172
+
173
+ #### Methods
174
+ - `failedAtMin() => boolean`: Returns `true` if the `got` value is below the minimum bound (respecting `inclusiveLeft`), otherwise `false`. Returns `false` if `min` is `null`.
175
+ - `failedAtMax() => boolean`: Returns `true` if the `got` value is above the maximum bound (respecting `inclusiveRight`), otherwise `false`. Returns `false` if `max` is `null`.
176
+ - `failedAtAny() => boolean`: Returns `true` if the value failed at either the minimum or maximum bound.
177
+ - `toString() => string`: Returns a formatted string indicating the out-of-bounds value, the expected range, and which bound was violated.
178
+
120
179
  ### RGXTokenCollection
121
180
  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
181
 
@@ -132,6 +191,7 @@ constructor(tokens: RGXTokenCollectionInput = [], mode: RGXTokenCollectionMode =
132
191
  #### Properties
133
192
  - `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
193
  - `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.
194
+ - `resolve() => ValidRegexString`: A convenience method that resolves this collection by calling `resolveRGXToken(this)`, returning the resolved regex string representation.
135
195
  - `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
196
  - `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
197
  - `toArray() => RGXToken[]`: An alias for `getTokens()`, provided for convenience.
@@ -157,10 +217,12 @@ An abstract base class for creating custom RGX token classes. Subclasses must im
157
217
 
158
218
  #### Properties
159
219
  - `isGroup` (`boolean`): Returns `false` by default. Subclasses can override this to indicate whether the token represents a group.
220
+ - `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
221
 
161
222
  #### Methods
162
223
  - `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`.
224
+ - `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.
225
+ - `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
226
 
165
227
  ### RGXClassUnionToken extends RGXClassToken
166
228
  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 +249,34 @@ constructor(tokens: RGXTokenCollectionInput = [])
187
249
  - `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
250
  - `toRgx() => RegExp`: Resolves the union by calling `toRgx()` on the internal `RGXTokenCollection`, returning a `RegExp`.
189
251
 
252
+ ### RGXGroupToken extends RGXClassToken
253
+ 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.
254
+
255
+ A function `rgxGroup` is provided with the same parameters as this class' constructor, for easier instantiation without needing to use the `new` keyword.
256
+
257
+ #### Static Properties
258
+ - `check(value: unknown): value is RGXGroupToken`: A type guard that checks if the given value is an instance of `RGXGroupToken`.
259
+ - `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.
260
+
261
+ #### Constructor
262
+ ```typescript
263
+ constructor(args?: RGXGroupTokenArgs, tokens?: RGXTokenCollectionInput)
264
+ ```
265
+ - `args` (`RGXGroupTokenArgs`, optional): An object specifying the group configuration. Defaults to `{}`.
266
+ - `name` (`string | null`, optional): The name of the group for named capture groups. Must be a valid identifier (validated via `assertValidIdentifier`). Defaults to `null`.
267
+ - `capturing` (`boolean`, optional): Whether the group is capturing. Defaults to `true`. Setting this to `false` also clears any `name`.
268
+ - `tokens` (`RGXTokenCollectionInput`, optional): The tokens to be wrapped by the group. Internally stored as an `RGXTokenCollection` in 'concat' mode. Defaults to an empty array.
269
+
270
+ #### Properties
271
+ - `tokens` (`RGXTokenCollection`): The internal collection of tokens managed in 'concat' mode.
272
+ - `name` (`string | null`): The name of the group. Setting this to a non-null value validates it as a valid identifier via `assertValidIdentifier`.
273
+ - `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`.
274
+ - `isGroup` (`boolean`): Returns `true`, indicating this token represents a group.
275
+ - `rgxGroupWrap` (`boolean`): Returns `false`, since the group already wraps itself, preventing the resolver from double-wrapping.
276
+
277
+ #### Methods
278
+ - `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.
279
+
190
280
  ## Functions
191
281
  The following functions are exported by the library:
192
282
 
@@ -273,7 +363,7 @@ Asserts that the given value is a native token (string, number, boolean, or no-o
273
363
  function isRGXConvertibleToken(value: unknown, returnCheck?: boolean): value is RGXConvertibleToken
274
364
  ```
275
365
 
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).
366
+ 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
367
 
278
368
  #### Parameters
279
369
  - `value` (`unknown`): The value to check.
@@ -286,7 +376,7 @@ Checks if the given value is a convertible token (an object with a `toRgx` metho
286
376
  ```typescript
287
377
  function assertRGXConvertibleToken(value: unknown, returnCheck?: boolean): asserts value is RGXConvertibleToken
288
378
  ```
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.
379
+ 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
380
 
291
381
  #### Parameters
292
382
  - `value` (`unknown`): The value to assert.
@@ -499,6 +589,30 @@ Asserts that the given string is a valid combination of vanilla regex flags (g,
499
589
  #### Returns
500
590
  - `void`: This function does not return a value, but will throw an error if the assertion fails.
501
591
 
592
+ ### isValidIdentifier
593
+ ```typescript
594
+ function isValidIdentifier(value: string): value is ValidIdentifier
595
+ ```
596
+ 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.
597
+
598
+ #### Parameters
599
+ - `value` (`string`): The string to check.
600
+
601
+ #### Returns
602
+ - `boolean`: `true` if the string is a valid identifier, otherwise `false`.
603
+
604
+ ### assertValidIdentifier
605
+ ```typescript
606
+ function assertValidIdentifier(value: string): asserts value is ValidIdentifier
607
+ ```
608
+ 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.
609
+
610
+ #### Parameters
611
+ - `value` (`string`): The string to assert.
612
+
613
+ #### Returns
614
+ - `void`: This function does not return a value, but will throw an error if the assertion fails.
615
+
502
616
  ### escapeRegex
503
617
  ```typescript
504
618
  function escapeRegex(value: string): ValidRegexString
@@ -514,14 +628,17 @@ Escapes special regex characters in the given string and brands the result as a
514
628
 
515
629
  ### resolveRGXToken
516
630
  ```typescript
517
- function resolveRGXToken(token: RGXToken, groupWrap?: boolean): ValidRegexString
631
+ function resolveRGXToken(token: RGXToken, groupWrap?: boolean, topLevel?: boolean): ValidRegexString
518
632
  ```
519
633
 
520
634
  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
635
 
636
+ 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.
637
+
522
638
  #### Parameters
523
639
  - `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.
640
+ - `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.
641
+ - `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
642
 
526
643
  #### Returns
527
644
  - `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 +732,82 @@ Removes duplicate tokens from the provided list using `Set` equality and returns
615
732
  function rgxClassInit(): void
616
733
  ```
617
734
 
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.
735
+ 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.
736
+
737
+ ### isInRange
738
+ ```typescript
739
+ function isInRange(value: number, { min, max, inclusiveLeft, inclusiveRight }?: RangeObject): boolean
740
+ ```
741
+
742
+ Checks if the given numeric value falls within the specified range.
743
+
744
+ #### Parameters
745
+ - `value` (`number`): The value to check.
746
+ - `min` (`number | null`, optional): The minimum bound of the range. Defaults to `null` (no minimum).
747
+ - `max` (`number | null`, optional): The maximum bound of the range. Defaults to `null` (no maximum).
748
+ - `inclusiveLeft` (`boolean`, optional): Whether the minimum bound is inclusive. Defaults to `true`.
749
+ - `inclusiveRight` (`boolean`, optional): Whether the maximum bound is inclusive. Defaults to `true`.
750
+
751
+ #### Returns
752
+ - `boolean`: `true` if the value is within the specified range, otherwise `false`.
753
+
754
+ ### assertInRange
755
+ ```typescript
756
+ function assertInRange(value: number, range: RangeObject, message?: string): void
757
+ ```
758
+
759
+ Asserts that the given numeric value falls within the specified range. If the assertion fails, an `RGXOutOfBoundsError` will be thrown.
760
+
761
+ #### Parameters
762
+ - `value` (`number`): The value to assert.
763
+ - `range` (`RangeObject`): The range to check against.
764
+ - `message` (`string`, optional): A custom error message. Defaults to `"Value out of bounds"`.
765
+
766
+ #### Returns
767
+ - `void`: This function does not return a value, but will throw an error if the assertion fails.
768
+
769
+ ### normalizeVanillaRegexFlags
770
+ ```typescript
771
+ function normalizeVanillaRegexFlags(flags: string): string
772
+ ```
773
+
774
+ Normalizes a string of vanilla regex flags by removing duplicate flags while preserving order. If any character in the string is not a valid vanilla regex flag (g, i, m, s, u, y), an `RGXInvalidVanillaRegexFlagsError` will be thrown.
775
+
776
+ #### Parameters
777
+ - `flags` (`string`): The flags string to normalize.
778
+
779
+ #### Returns
780
+ - `string`: The normalized flags string with duplicates removed.
781
+
782
+ ### regexWithFlags
783
+ ```typescript
784
+ function regexWithFlags(exp: RegExp, flags: string, replace?: boolean): RegExp
785
+ ```
786
+
787
+ Creates a new `RegExp` from an existing one with additional or replaced flags. When `replace` is `false` (the default), the provided flags are merged with the existing flags and normalized (duplicates removed). When `replace` is `true`, the existing flags are discarded and only the provided flags are used. The provided flags are validated as valid vanilla regex flags via `assertValidVanillaRegexFlags`.
788
+
789
+ #### Parameters
790
+ - `exp` (`RegExp`): The source regular expression.
791
+ - `flags` (`string`): The flags to add or replace with. Must be valid vanilla regex flags, or an `RGXInvalidVanillaRegexFlagsError` will be thrown.
792
+ - `replace` (`boolean`, optional): Whether to replace the existing flags entirely instead of merging. Defaults to `false`.
793
+
794
+ #### Returns
795
+ - `RegExp`: A new `RegExp` with the same source pattern and the resulting flags.
796
+
797
+ ### regexMatchAtPosition
798
+ ```typescript
799
+ function regexMatchAtPosition(regex: RegExp, str: string, position: number): boolean
800
+ ```
801
+
802
+ Tests whether the given regular expression matches at a specific position in the string. This is done by creating a sticky (`y` flag) copy of the regex and setting its `lastIndex` to the desired position. The position must be within the bounds of the string (>= 0 and < string length), or an `RGXOutOfBoundsError` will be thrown.
803
+
804
+ #### Parameters
805
+ - `regex` (`RegExp`): The regular expression to test.
806
+ - `str` (`string`): The string to test against.
807
+ - `position` (`number`): The zero-based index in the string at which to test the match. Must be >= 0 and < `str.length`.
808
+
809
+ #### Returns
810
+ - `boolean`: `true` if the regex matches at the specified position, otherwise `false`.
619
811
 
620
812
  ## Peer Dependencies
621
813
  - `@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' | 'OUT_OF_BOUNDS';
2
2
  export declare class RGXError extends Error {
3
3
  code: RGXErrorCode;
4
4
  constructor(message: string, code?: RGXErrorCode);
@@ -3,3 +3,5 @@ export * from './invalidToken';
3
3
  export * from './invalidRegexString';
4
4
  export * from './invalidVanillaRegexFlags';
5
5
  export * from './notImplemented';
6
+ export * from './invalidIdentifier';
7
+ export * from './outOfBounds';
@@ -19,3 +19,5 @@ __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);
23
+ __exportStar(require("./outOfBounds"), 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;
@@ -0,0 +1,20 @@
1
+ import { RGXError } from "./base";
2
+ import { RangeObject } from "../types";
3
+ export declare class RGXOutOfBoundsError extends RGXError {
4
+ got: number;
5
+ _min: number | null;
6
+ _max: number | null;
7
+ inclusiveLeft: boolean;
8
+ inclusiveRight: boolean;
9
+ get min(): number | null;
10
+ set min(value: number | null);
11
+ get max(): number | null;
12
+ set max(value: number | null);
13
+ constructor(message: string, got: number, { min, max, inclusiveLeft, inclusiveRight }?: RangeObject);
14
+ failedAtMin(): boolean;
15
+ failedAtMax(): boolean;
16
+ failedAtAny(): boolean;
17
+ toString(): string;
18
+ }
19
+ export declare function isInRange(value: number, { min, max, inclusiveLeft, inclusiveRight }?: RangeObject): boolean;
20
+ export declare function assertInRange(value: number, range: RangeObject, message?: string): void;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RGXOutOfBoundsError = void 0;
4
+ exports.isInRange = isInRange;
5
+ exports.assertInRange = assertInRange;
6
+ const base_1 = require("./base");
7
+ class RGXOutOfBoundsError extends base_1.RGXError {
8
+ get min() {
9
+ return this._min;
10
+ }
11
+ set min(value) {
12
+ this._min = value;
13
+ if (this._max !== null && value !== null && value > this._max) {
14
+ this._max = value;
15
+ }
16
+ }
17
+ get max() {
18
+ return this._max;
19
+ }
20
+ set max(value) {
21
+ this._max = value;
22
+ if (this._min !== null && value !== null && value < this._min) {
23
+ this._min = value;
24
+ }
25
+ }
26
+ constructor(message, got, { min = null, max = null, inclusiveLeft = true, inclusiveRight = true } = {}) {
27
+ super(message, 'OUT_OF_BOUNDS');
28
+ this.name = 'RGXOutOfBoundsError';
29
+ this.got = got;
30
+ this.min = min;
31
+ this.max = max;
32
+ this.inclusiveLeft = inclusiveLeft;
33
+ this.inclusiveRight = inclusiveRight;
34
+ }
35
+ failedAtMin() {
36
+ return this.min !== null && (this.inclusiveLeft ? this.got < this.min : this.got <= this.min);
37
+ }
38
+ failedAtMax() {
39
+ return this.max !== null && (this.inclusiveRight ? this.got > this.max : this.got >= this.max);
40
+ }
41
+ failedAtAny() {
42
+ return this.failedAtMin() || this.failedAtMax();
43
+ }
44
+ toString() {
45
+ const rangeParts = [];
46
+ if (this.min !== null) {
47
+ if (this.inclusiveLeft)
48
+ rangeParts.push(`>= ${this.min}`);
49
+ else
50
+ rangeParts.push(`> ${this.min}`);
51
+ }
52
+ if (this.max !== null) {
53
+ if (this.inclusiveRight)
54
+ rangeParts.push(`<= ${this.max}`);
55
+ else
56
+ rangeParts.push(`< ${this.max}`);
57
+ }
58
+ const rangeStr = rangeParts.join(" and ");
59
+ // Determine which one was failed
60
+ if (!this.failedAtAny()) {
61
+ return `${this.name}: ${this.message}; Got: [${this.got}]; Expected: [${rangeStr}]`;
62
+ }
63
+ else if (this.failedAtMin()) {
64
+ let thirdPart;
65
+ if (!this.inclusiveLeft && this.got === this.min)
66
+ thirdPart = `${this.got} == ${this.min}`;
67
+ else
68
+ thirdPart = `${this.got} < ${this.min}`;
69
+ return `${this.name}: ${this.message}; Got: [${this.got}]; Expected: [${rangeStr}]; ${thirdPart}`;
70
+ }
71
+ else {
72
+ let thirdPart;
73
+ if (!this.inclusiveRight && this.got === this.max)
74
+ thirdPart = `${this.got} == ${this.max}`;
75
+ else
76
+ thirdPart = `${this.got} > ${this.max}`;
77
+ return `${this.name}: ${this.message}; Got: [${this.got}]; Expected: [${rangeStr}]; ${thirdPart}`;
78
+ }
79
+ }
80
+ }
81
+ exports.RGXOutOfBoundsError = RGXOutOfBoundsError;
82
+ function isInRange(value, { min = null, max = null, inclusiveLeft = true, inclusiveRight = true } = {}) {
83
+ if (min !== null) {
84
+ if (inclusiveLeft) {
85
+ if (value < min)
86
+ return false;
87
+ }
88
+ else {
89
+ if (value <= min)
90
+ return false;
91
+ }
92
+ }
93
+ if (max !== null) {
94
+ if (inclusiveRight) {
95
+ if (value > max)
96
+ return false;
97
+ }
98
+ else {
99
+ if (value >= max)
100
+ return false;
101
+ }
102
+ }
103
+ return true;
104
+ }
105
+ function assertInRange(value, range, message = "Value out of bounds") {
106
+ if (!isInRange(value, range)) {
107
+ throw new RGXOutOfBoundsError(message, value, range);
108
+ }
109
+ }
package/dist/index.d.ts CHANGED
@@ -6,5 +6,6 @@ export * from "./collection";
6
6
  export * from "./class";
7
7
  export * from "./resolve";
8
8
  export * from "./concat";
9
+ export * from "./utils";
9
10
  export declare function rgxa(tokens: t.RGXToken[], flags?: string): RegExp;
10
11
  export default function rgx(flags?: string): (strings: TemplateStringsArray, ...tokens: t.RGXToken[]) => RegExp;
package/dist/index.js CHANGED
@@ -49,6 +49,7 @@ __exportStar(require("./collection"), exports);
49
49
  __exportStar(require("./class"), exports);
50
50
  __exportStar(require("./resolve"), exports);
51
51
  __exportStar(require("./concat"), exports);
52
+ __exportStar(require("./utils"), exports);
52
53
  // Call this for certain class methods to work correctly
53
54
  (0, class_1.rgxClassInit)();
54
55
  function rgxa(tokens, flags = '') {
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;
@@ -14,9 +15,18 @@ export type RGXTokenTypeGuardInput = RGXTokenTypeFlat | null | RGXClassTokenCons
14
15
  export type RGXTokenFromType<T extends RGXTokenTypeGuardInput> = T extends null ? RGXToken : T extends 'no-op' ? RGXNoOpToken : T extends 'literal' ? RGXLiteralToken : T extends 'native' ? RGXNativeToken : T extends 'convertible' ? RGXConvertibleToken : T extends 'class' ? RGXClassToken : T extends 'array' ? RGXToken[] : T extends RGXTokenTypeGuardInput[] ? {
15
16
  [K in keyof T]: T[K] extends RGXTokenTypeGuardInput ? RGXTokenFromType<T[K]> : never;
16
17
  } : T extends RGXClassTokenConstructor ? InstanceType<T> : never;
18
+ export type RangeObject = {
19
+ min?: number | null;
20
+ max?: number | null;
21
+ inclusiveLeft?: boolean;
22
+ inclusiveRight?: boolean;
23
+ };
17
24
  export declare const validRegexSymbol: unique symbol;
18
25
  export type ValidRegexBrandSymbol = typeof validRegexSymbol;
19
26
  export type ValidRegexString = Branded<string, [ValidRegexBrandSymbol]>;
20
27
  export declare const validVanillaRegexFlagsSymbol: unique symbol;
21
28
  export type ValidVanillaRegexFlagsBrandSymbol = typeof validVanillaRegexFlagsSymbol;
22
29
  export type ValidVanillaRegexFlags = Branded<string, [ValidVanillaRegexFlagsBrandSymbol]>;
30
+ export declare const validIdentifierSymbol: unique symbol;
31
+ export type ValidIdentifierBrandSymbol = typeof validIdentifierSymbol;
32
+ 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');
@@ -0,0 +1,3 @@
1
+ export * from "./regexMatchAtPosition";
2
+ export * from "./regexWithFlags";
3
+ export * from "./normalizeRegexFlags";
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./regexMatchAtPosition"), exports);
18
+ __exportStar(require("./regexWithFlags"), exports);
19
+ __exportStar(require("./normalizeRegexFlags"), exports);
@@ -0,0 +1 @@
1
+ export declare function normalizeVanillaRegexFlags(flags: string): string;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeVanillaRegexFlags = normalizeVanillaRegexFlags;
4
+ const errors_1 = require("../errors");
5
+ function normalizeVanillaRegexFlags(flags) {
6
+ const validFlags = ['g', 'i', 'm', 's', 'u', 'y'];
7
+ const seenFlags = new Set();
8
+ let normalizedFlags = '';
9
+ for (const flag of flags) {
10
+ if (!validFlags.includes(flag)) {
11
+ throw new errors_1.RGXInvalidVanillaRegexFlagsError(`[${flag}] is not valid.`, flags);
12
+ }
13
+ if (!seenFlags.has(flag)) {
14
+ seenFlags.add(flag);
15
+ normalizedFlags += flag;
16
+ }
17
+ }
18
+ return normalizedFlags;
19
+ }
@@ -0,0 +1 @@
1
+ export declare function regexMatchAtPosition(regex: RegExp, str: string, position: number): boolean;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.regexMatchAtPosition = regexMatchAtPosition;
4
+ const outOfBounds_1 = require("../errors/outOfBounds");
5
+ const regexWithFlags_1 = require("./regexWithFlags");
6
+ function regexMatchAtPosition(regex, str, position) {
7
+ /*
8
+ The y flag means sticky mode, which means the next match must start at
9
+ lastIndex. By setting lastIndex to the position we want to check, we can test
10
+ if the regex matches at that position.
11
+ */
12
+ (0, outOfBounds_1.assertInRange)(position, { min: 0, max: str.length, inclusiveRight: false }, 'String index out of bounds');
13
+ const stickyRegex = (0, regexWithFlags_1.regexWithFlags)(regex, "y");
14
+ stickyRegex.lastIndex = position;
15
+ return stickyRegex.test(str);
16
+ }
@@ -0,0 +1 @@
1
+ export declare function regexWithFlags(exp: RegExp, flags: string, replace?: boolean): RegExp;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.regexWithFlags = regexWithFlags;
4
+ const typeGuards_1 = require("../typeGuards");
5
+ const normalizeRegexFlags_1 = require("./normalizeRegexFlags");
6
+ function regexWithFlags(exp, flags, replace = false) {
7
+ (0, typeGuards_1.assertValidVanillaRegexFlags)(flags);
8
+ if (replace)
9
+ return new RegExp(exp.source, flags);
10
+ const existingFlags = exp.flags;
11
+ const newFlags = existingFlags + flags;
12
+ return new RegExp(exp.source, (0, normalizeRegexFlags_1.normalizeVanillaRegexFlags)(newFlags));
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ptolemy2002/rgx",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",