shelving 1.124.5 → 1.126.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.
@@ -9,9 +9,6 @@ export type MarkupMatch<T extends Data | undefined> = {
9
9
  };
10
10
  /** Function that matches a string and returns a `MarkupMatch` or `null` or `void` */
11
11
  export type MarkupMatcher<T extends Data> = (input: string, options: MarkupOptions) => MarkupMatch<T> | null | void;
12
- export declare const LINE_REGEXP: RegExp;
13
- export declare const LINE_START_REGEXP: RegExp;
14
- export declare const LINE_END_REGEXP: RegExp;
15
12
  export declare const BLOCK_REGEXP: RegExp;
16
13
  export declare const BLOCK_START_REGEXP: RegExp;
17
14
  export declare const BLOCK_END_REGEXP: RegExp;
@@ -21,27 +18,21 @@ export declare function getBlockRegExp<T extends string>(content: `(?<${T}>${str
21
18
  [K in T]: string;
22
19
  }>;
23
20
  export declare function getBlockRegExp(content?: PossibleRegExp, end?: PossibleRegExp, start?: PossibleRegExp): RegExp;
21
+ export declare const LINE_REGEXP: RegExp;
22
+ export declare const LINE_START_REGEXP: RegExp;
23
+ export declare const LINE_END_REGEXP: RegExp;
24
24
  /** Create regular expression that matches a line of content. */
25
25
  export declare function getLineRegExp<T extends NamedRegExpData>(content: NamedRegExp<T>): NamedRegExp<T>;
26
26
  export declare function getLineRegExp<T extends string>(content: `(?<${T}>${string})`): NamedRegExp<{
27
27
  [K in T]: string;
28
28
  }>;
29
29
  export declare function getLineRegExp(content?: PossibleRegExp, end?: PossibleRegExp, start?: PossibleRegExp): RegExp;
30
- /**
31
- * Regular expression that only matches complete its pattern if it's a complete word.
32
- * - Won't match if there are letters or numbers directly before/after the matched content.
33
- * - Will match if there is punctuation before/after the matched content or it is at the start/end of the string.
34
- * - e.g. `this` and `"this"` and `that this that` and `that (this) that` will match because `this` is a complete word.
35
- * - e.g. `thatthis` and `thatthisthat` will not because `this` is only part of a complete word.
36
- *
37
- * @note This isn't guaranteed to work with `String.prototype.match()` and `String.prototype.replace()`
38
- *
39
- * @todo This can be much less complicated when Safari supports lookbehinds in regular expressions.
40
- * - We use a negative lookahead for the end of the word and it works great.
41
- * - If we could use a negative lookbehind for the start of the word we wouldn't need to create a function that offsets the start.
42
- */
43
- export declare class WordRegExp extends RegExp {
44
- constructor(pattern: string, flags?: string);
45
- exec(input: string): RegExpExecArray | null;
46
- test(input: string): boolean;
47
- }
30
+ export declare const WORD_REGEXP: RegExp;
31
+ export declare const WORD_START_REGEXP: RegExp;
32
+ export declare const WORD_END_REGEXP: RegExp;
33
+ /** Create regular expression that matches a line of content. */
34
+ export declare function getWordRegExp<T extends NamedRegExpData>(content: NamedRegExp<T>): NamedRegExp<T>;
35
+ export declare function getWordRegExp<T extends string>(content: `(?<${T}>${string})`): NamedRegExp<{
36
+ [K in T]: string;
37
+ }>;
38
+ export declare function getWordRegExp(content?: PossibleRegExp, end?: PossibleRegExp, start?: PossibleRegExp): RegExp;
package/markup/regexp.js CHANGED
@@ -1,47 +1,19 @@
1
1
  import { getRegExpSource } from "../util/regexp.js";
2
- // Regular expressions.
3
- export const LINE_REGEXP = /[^\n]*/; // Match line of content (anything that's not a newline).
4
- export const LINE_START_REGEXP = /^\n*|\n+/; // Starts at start of line (one or more linebreak or start of string).
5
- export const LINE_END_REGEXP = /\n+|$/; // Ends at end of line (one or more linebreak or end of string).
6
- export const BLOCK_REGEXP = /[\s\S]*?/; // Match block of content (including newlines so don't be greedy).
7
- export const BLOCK_START_REGEXP = /^\n*|\n+/; // Starts at start of a block (one or more linebreak or start of string).
8
- export const BLOCK_END_REGEXP = /\n*$|\n\n+/; // End of a block (two or more linebreaks or end of string).
2
+ export const BLOCK_REGEXP = /[\s\S]*?/; // Block of content (shortest possible run of any character including newlines).
3
+ export const BLOCK_START_REGEXP = /^\n*|\n+/; // Start of block (one or more linebreaks, or start of string).
4
+ export const BLOCK_END_REGEXP = /\n*$|\n\n+/; // End of block (two or more linebreaks, or end of string).
9
5
  export function getBlockRegExp(content = BLOCK_REGEXP, end = BLOCK_END_REGEXP, start = BLOCK_START_REGEXP) {
10
6
  return new RegExp(`(?:${getRegExpSource(start)})(?:${getRegExpSource(content)})(?:${getRegExpSource(end)})`);
11
7
  }
8
+ export const LINE_REGEXP = /[^\n]*/; // Line of content (anything that's not a newline).
9
+ export const LINE_START_REGEXP = /^\n*|\n+/; // Start of line (one or more linebreaks, or start of string).
10
+ export const LINE_END_REGEXP = /\n+|$/; // End of line (one or more linebreaks, or end of string).
12
11
  export function getLineRegExp(content = LINE_REGEXP, end = LINE_END_REGEXP, start = LINE_START_REGEXP) {
13
12
  return new RegExp(`(?:${getRegExpSource(start)})(?:${getRegExpSource(content)})(?:${getRegExpSource(end)})`);
14
13
  }
15
- /**
16
- * Regular expression that only matches complete its pattern if it's a complete word.
17
- * - Won't match if there are letters or numbers directly before/after the matched content.
18
- * - Will match if there is punctuation before/after the matched content or it is at the start/end of the string.
19
- * - e.g. `this` and `"this"` and `that this that` and `that (this) that` will match because `this` is a complete word.
20
- * - e.g. `thatthis` and `thatthisthat` will not because `this` is only part of a complete word.
21
- *
22
- * @note This isn't guaranteed to work with `String.prototype.match()` and `String.prototype.replace()`
23
- *
24
- * @todo This can be much less complicated when Safari supports lookbehinds in regular expressions.
25
- * - We use a negative lookahead for the end of the word and it works great.
26
- * - If we could use a negative lookbehind for the start of the word we wouldn't need to create a function that offsets the start.
27
- */
28
- export class WordRegExp extends RegExp {
29
- constructor(pattern, flags) {
30
- super(`(?<lookbehind>^|[^\\p{L}\\p{N}])${pattern}(?![\\p{L}\\p{N}])`, flags);
31
- }
32
- exec(input) {
33
- const match = super.exec(input);
34
- if (match) {
35
- const { 0: zero, groups } = match;
36
- const offset = groups?.lookbehind?.length || 0;
37
- if (zero && offset) {
38
- match[0] = zero.slice(offset); // Slice off the start of the match to remove the matched first character.
39
- match.index += offset; // Increment the index to remove the matched first character.
40
- }
41
- }
42
- return match;
43
- }
44
- test(input) {
45
- return !!this.exec(input);
46
- }
14
+ export const WORD_REGEXP = /[\\p{L}\\p{N}]+/; // Match line of content (anything that's not a newline).
15
+ export const WORD_START_REGEXP = /(?<![\\p{L}\\p{N}])/; // Start of word (previous character is not a letter or number).
16
+ export const WORD_END_REGEXP = /(?![\\p{L}\\p{N}])/; // End of word (next character is not a letter or number).
17
+ export function getWordRegExp(content = WORD_REGEXP, end = WORD_END_REGEXP, start = WORD_START_REGEXP) {
18
+ return new RegExp(`(?:${getRegExpSource(start)})(?:${getRegExpSource(content)})(?:${getRegExpSource(end)})`);
47
19
  }
package/markup/render.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable no-param-reassign */
2
2
  import { isArray } from "../util/array.js";
3
+ import { mapArray } from "../util/transform.js";
3
4
  import { MARKUP_OPTIONS } from "./options.js";
4
5
  /**
5
6
  * Parse a text string as Markdownish syntax and render it as a JSX node.
@@ -55,11 +56,8 @@ function _renderNode(node, options, context) {
55
56
  return node;
56
57
  if (typeof node === "string")
57
58
  return _renderString(node, options, context);
58
- if (isArray(node)) {
59
- for (let i = 0; i <= node.length - 1; i++)
60
- node[i] = _renderNode(node[i], options, context);
61
- return node;
62
- }
59
+ if (isArray(node))
60
+ return mapArray(node, _renderNode, options, context);
63
61
  return _renderElement(node, options, context);
64
62
  }
65
63
  /**
@@ -108,7 +106,7 @@ function* _parseString(input, options, outerContext, offset = 0) {
108
106
  // We use the string offset as the `.key` property in the element because it's cheap to calculate and guaranteed to be unique within the string.
109
107
  // Trying to generate an incrementing number would require tracking the number and passing it back and forth through `_parseString()`
110
108
  const { index, length, context } = element;
111
- element.key = offset + index;
109
+ element.key = (offset + index).toString();
112
110
  yield context ? _renderElement(element, options, context) : element;
113
111
  // Decrement the content.
114
112
  const suffix = input.slice(index + length);
package/markup/rules.d.ts CHANGED
@@ -111,7 +111,6 @@ declare const INLINE_CHARS: {
111
111
  };
112
112
  export declare const INLINE_RULE: NamedRegExpMarkupRule<{
113
113
  char: keyof typeof INLINE_CHARS;
114
- wrap: string;
115
114
  text: string;
116
115
  }>;
117
116
  /**
package/markup/rules.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { getRegExp } from "../util/regexp.js";
2
- import { BLOCK_REGEXP, LINE_REGEXP, WordRegExp, getBlockRegExp, getLineRegExp } from "./regexp.js";
2
+ import { BLOCK_REGEXP, LINE_REGEXP, getBlockRegExp, getLineRegExp, getWordRegExp } from "./regexp.js";
3
3
  import { LinkRegExpMarkupRule, NamedRegExpMarkupRule, RegExpMarkupRule } from "./rule.js";
4
4
  /** React security symbol — see https://github.com/facebook/react/pull/4832 */
5
5
  const $$typeof = Symbol.for("react.element");
@@ -51,7 +51,7 @@ export const UNORDERED_RULE = new NamedRegExpMarkupRule(getBlockRegExp(`(?<list>
51
51
  }), ["block", "list"]);
52
52
  const _mapUnordered = (item, key) => ({
53
53
  type: "li",
54
- key,
54
+ key: key.toString(),
55
55
  ref: null,
56
56
  $$typeof,
57
57
  props: { children: item.replace(UNORDERED_INDENT, "") },
@@ -74,7 +74,7 @@ export const ORDERED_RULE = new NamedRegExpMarkupRule(getBlockRegExp(`(?<list>${
74
74
  }), ["block", "list"]);
75
75
  const _mapOrdered = (item, key) => ({
76
76
  type: "li",
77
- key,
77
+ key: key.toString(),
78
78
  ref: null,
79
79
  $$typeof,
80
80
  props: {
@@ -195,7 +195,7 @@ export const CODE_RULE = new NamedRegExpMarkupRule(new RegExp(`(?<wrap>\`+)(?<co
195
195
  * - Different to Markdown: strong is always surrounded by `*asterisks*` and emphasis is always surrounded by `_underscores_` (strong isn't 'double emphasis').
196
196
  */
197
197
  const INLINE_CHARS = { "-": "del", "~": "del", "+": "ins", "*": "strong", "_": "em", "=": "mark", ":": "mark" }; // Hyphen must be first so it works when we use the keys as a character class.
198
- export const INLINE_RULE = new NamedRegExpMarkupRule(new WordRegExp(`(?<wrap>(?<char>[${Object.keys(INLINE_CHARS).join("")}])+)(?<text>(?!\\k<char>)\\S|(?!\\k<char>)\\S[\\s\\S]*?(?!\\k<char>)\\S)\\k<wrap>`), // prettier-ignore
198
+ export const INLINE_RULE = new NamedRegExpMarkupRule(getWordRegExp(`(?<wrap>(?<char>[${Object.keys(INLINE_CHARS).join("")}])+)(?<text>(?!\\k<char>)\\S(?:[\\s\\S]*?(?!\\k<char>)\\S)?)\\k<wrap>`), // prettier-ignore
199
199
  ({ char, text }) => ({
200
200
  type: INLINE_CHARS[char],
201
201
  key: null,
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.124.5",
14
+ "version": "1.126.0",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -60,13 +60,13 @@
60
60
  "build:jest": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config=jest.config.build.cjs"
61
61
  },
62
62
  "devDependencies": {
63
- "@google-cloud/firestore": "^6.4.2",
63
+ "@google-cloud/firestore": "^7.3.0",
64
64
  "@types/jest": "^29.4.0",
65
65
  "@types/react": "^18.0.27",
66
66
  "@types/react-dom": "^18.0.10",
67
- "@typescript-eslint/eslint-plugin": "^6.7.0",
68
- "@typescript-eslint/parser": "^6.7.0",
69
- "esbuild": "^0.19.2",
67
+ "@typescript-eslint/eslint-plugin": "^7.1.0",
68
+ "@typescript-eslint/parser": "^7.1.0",
69
+ "esbuild": "^0.20.1",
70
70
  "esbuild-jest": "^0.5.0",
71
71
  "eslint": "^8.33.0",
72
72
  "eslint-config-prettier": "^9.0.0",
@@ -16,7 +16,7 @@ export class PhoneSchema extends StringSchema {
16
16
  ...options,
17
17
  type: "tel",
18
18
  min: 1,
19
- max: 16,
19
+ max: 16, // Valid phone number is 16 digits or fewer (15 numerals with a leading `+` plus).
20
20
  match: PHONE_REGEXP,
21
21
  multiline: false,
22
22
  });
package/util/async.js CHANGED
@@ -71,7 +71,7 @@ export function getDeferred() {
71
71
  resolve = x;
72
72
  reject = y;
73
73
  }),
74
- resolve: resolve,
74
+ resolve: resolve, // eslint-disable-line @typescript-eslint/no-non-null-assertion
75
75
  reject: reject, // eslint-disable-line @typescript-eslint/no-non-null-assertion
76
76
  };
77
77
  }
package/util/equal.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ImmutableArray } from "./array.js";
2
+ import type { Match } from "./filter.js";
2
3
  import type { ImmutableMap } from "./map.js";
3
- import type { Match } from "./match.js";
4
4
  import type { ImmutableObject } from "./object.js";
5
5
  /** Is unknown value `left` exactly equal to `right`? */
6
6
  export declare function isEqual<T>(left: unknown, right: T): left is T;
package/util/index.d.ts CHANGED
@@ -18,6 +18,7 @@ export * from "./entry.js";
18
18
  export * from "./equal.js";
19
19
  export * from "./error.js";
20
20
  export * from "./file.js";
21
+ export * from "./filter.js";
21
22
  export * from "./focus.js";
22
23
  export * from "./function.js";
23
24
  export * from "./hash.js";
@@ -28,7 +29,6 @@ export * from "./jsx.js";
28
29
  export * from "./lazy.js";
29
30
  export * from "./link.js";
30
31
  export * from "./map.js";
31
- export * from "./match.js";
32
32
  export * from "./merge.js";
33
33
  export * from "./null.js";
34
34
  export * from "./number.js";
package/util/index.js CHANGED
@@ -18,6 +18,7 @@ export * from "./entry.js";
18
18
  export * from "./equal.js";
19
19
  export * from "./error.js";
20
20
  export * from "./file.js";
21
+ export * from "./filter.js";
21
22
  export * from "./focus.js";
22
23
  export * from "./function.js";
23
24
  export * from "./hash.js";
@@ -28,7 +29,6 @@ export * from "./jsx.js";
28
29
  export * from "./lazy.js";
29
30
  export * from "./link.js";
30
31
  export * from "./map.js";
31
- export * from "./match.js";
32
32
  export * from "./merge.js";
33
33
  export * from "./null.js";
34
34
  export * from "./number.js";
package/util/jsx.d.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  /** Set of valid props for a JSX element. */
2
- export type JSXProps = {
2
+ export interface JSXProps {
3
3
  [key: string]: unknown;
4
4
  children?: JSXNode;
5
- };
5
+ }
6
6
  /** JSX element (similar to `React.ReactElement`) */
7
- export type JSXElement<P extends JSXProps = JSXProps> = {
8
- type: string | ((props: P) => JSXElement | null);
7
+ export interface JSXElement<P extends JSXProps = JSXProps> {
8
+ type: string | ((props: P) => JSXNode | null);
9
9
  props: P;
10
- key: string | number | null;
10
+ key: string | null;
11
11
  ref?: null;
12
12
  $$typeof?: symbol;
13
- };
13
+ }
14
14
  /** JSX node (similar to `React.ReactNode`) */
15
- export type JSXNode = undefined | null | string | JSXElement | JSXNode[];
15
+ export type JSXNode = undefined | null | string | JSXElement | readonly JSXNode[];
16
16
  /** Is an unknown value a JSX element? */
17
17
  export declare function isJSXElement(value: unknown): value is JSXElement;
18
18
  /** Is an unknown value a JSX node? */
package/util/number.d.ts CHANGED
@@ -24,6 +24,7 @@ export declare function assertMin(value: unknown, min: number): asserts value is
24
24
  *
25
25
  * Conversion rules:
26
26
  * - Finite numbers return numbers.
27
+ * - `-0` is normalised to `0`
27
28
  * - Strings are parsed as numbers.
28
29
  * - Dates return their milliseconds (e.g. `date.getTime()`).
29
30
  * - Everything else returns `undefined`
package/util/number.js CHANGED
@@ -43,13 +43,14 @@ export function assertMin(value, min) {
43
43
  *
44
44
  * Conversion rules:
45
45
  * - Finite numbers return numbers.
46
+ * - `-0` is normalised to `0`
46
47
  * - Strings are parsed as numbers.
47
48
  * - Dates return their milliseconds (e.g. `date.getTime()`).
48
49
  * - Everything else returns `undefined`
49
50
  */
50
51
  export function getOptionalNumber(value) {
51
52
  if (typeof value === "number" && Number.isFinite(value))
52
- return value === 0 ? 0 : value; // Convert `-0` to `0`
53
+ return value === 0 ? 0 : value;
53
54
  else if (typeof value === "string")
54
55
  return getOptionalNumber(parseFloat(value.replace(NOT_NUMERIC_REGEXP, "")));
55
56
  else if (value instanceof Date)
package/util/regexp.d.ts CHANGED
@@ -22,15 +22,11 @@ export declare function escapeRegExp(pattern: string): string;
22
22
  export declare function getAnyRegExp(patterns: Iterable<PossibleRegExp> & NotString, flags?: string): RegExp;
23
23
  /** Create regular expression that matches all of a list of other expressions. */
24
24
  export declare function getAllRegExp(patterns: Iterable<PossibleRegExp> & NotString, flags?: string): RegExp;
25
- /** Match function for finding strings that match against regular expressions (use with `filter()` to positively filter iterable sets of items). */
26
- export declare function isRegExpMatch(item: string, target: RegExp): boolean;
27
- /** Match function for finding strings that match against regular expressions (use with `filter()` to negatively filter iterable sets of items). */
28
- export declare function notRegExpMatch(item: string, target: RegExp): boolean;
29
- /** Regular expression match array that you've asserted contains the specified named groups. */
25
+ /** Regular expression match array that matches a specific string format. */
30
26
  export interface TypedRegExpExecArray<T extends string = string> extends RegExpExecArray {
31
27
  0: T;
32
28
  }
33
- /** Regular expression that you've asserted contains the specified named capture groups. */
29
+ /** Regular expression that matches a specific string format. */
34
30
  export interface TypedRegExp<T extends string = string> extends RegExp {
35
31
  exec(input: string): TypedRegExpExecArray<T> | null;
36
32
  }
@@ -38,11 +34,29 @@ export interface TypedRegExp<T extends string = string> extends RegExp {
38
34
  export type NamedRegExpData = {
39
35
  [named: string]: string;
40
36
  };
41
- /** Regular expression match array that you've asserted contains the specified named groups. */
37
+ /** Regular expression match array that contains the specified named groups. */
42
38
  export interface NamedRegExpExecArray<T extends NamedRegExpData = NamedRegExpData> extends RegExpExecArray {
43
39
  groups: T;
44
40
  }
45
- /** Regular expression that you've asserted contains the specified named capture groups. */
41
+ /** Regular expression that contains the specified named capture groups. */
46
42
  export interface NamedRegExp<T extends NamedRegExpData = NamedRegExpData> extends RegExp {
47
43
  exec(input: string): NamedRegExpExecArray<T> | null;
48
44
  }
45
+ /** Match function for finding strings that match against regular expressions (use with `filter()` to positively filter iterable sets of items). */
46
+ export declare function isMatch(str: string, regexp: RegExp): boolean;
47
+ /** Match function for finding strings that match against regular expressions (use with `filter()` to negatively filter iterable sets of items). */
48
+ export declare function notMatch(str: string, regexp: RegExp): boolean;
49
+ /** Get an optional regular expression match, or `undefined` if no match could be made. */
50
+ export declare function getOptionalMatch<T extends NamedRegExpData>(str: string, regexp: NamedRegExp<T>): NamedRegExpExecArray<T> | undefined;
51
+ export declare function getOptionalMatch<T extends string>(str: string, regexp: TypedRegExp<T>): TypedRegExpExecArray<T> | undefined;
52
+ export declare function getOptionalMatch(str: string, regexp: RegExp): RegExpExecArray | undefined;
53
+ /** Get a required regular expression match, or throw `ValueError` if no match could be made. */
54
+ export declare function getMatch<T extends NamedRegExpData>(str: string, regexp: NamedRegExp<T>): NamedRegExpExecArray<T>;
55
+ export declare function getMatch<T extends string>(str: string, regexp: TypedRegExp<T>): TypedRegExpExecArray<T>;
56
+ export declare function getMatch(str: string, regexp: RegExp): RegExpExecArray;
57
+ /** Get an optional regular expression match, or `undefined` if no match could be made. */
58
+ export declare function getOptionalMatchGroups<T extends NamedRegExpData>(str: string, regexp: NamedRegExp<T>): T | undefined;
59
+ export declare function getOptionalMatchGroups(str: string, regexp: RegExp): NamedRegExpData | undefined;
60
+ /** Get a required regular expression match, or throw `ValueError` if no match could be made. */
61
+ export declare function getMatchGroups<T extends NamedRegExpData>(str: string, regexp: NamedRegExp<T>): T;
62
+ export declare function getMatchGroups(str: string, regexp: RegExp): NamedRegExpData;
package/util/regexp.js CHANGED
@@ -44,10 +44,28 @@ export function getAllRegExp(patterns, flags) {
44
44
  return new RegExp(`^(?=.*?(?:${getArray(patterns).map(getRegExpSource).join("))(?=.*?(?:")}))`, flags);
45
45
  }
46
46
  /** Match function for finding strings that match against regular expressions (use with `filter()` to positively filter iterable sets of items). */
47
- export function isRegExpMatch(item, target) {
48
- return target.test(item);
47
+ export function isMatch(str, regexp) {
48
+ return regexp.test(str);
49
49
  }
50
50
  /** Match function for finding strings that match against regular expressions (use with `filter()` to negatively filter iterable sets of items). */
51
- export function notRegExpMatch(item, target) {
52
- return !target.test(item);
51
+ export function notMatch(str, regexp) {
52
+ return !regexp.test(str);
53
+ }
54
+ export function getOptionalMatch(str, regexp) {
55
+ return regexp.exec(str) || undefined;
56
+ }
57
+ export function getMatch(str, regexp) {
58
+ const match = getOptionalMatch(str, regexp);
59
+ if (!match)
60
+ throw new ValueError("Must match regular expression", str);
61
+ return match;
62
+ }
63
+ export function getOptionalMatchGroups(str, regexp) {
64
+ return regexp.exec(str)?.groups || undefined;
65
+ }
66
+ export function getMatchGroups(str, regexp) {
67
+ const groups = getOptionalMatchGroups(str, regexp);
68
+ if (!groups)
69
+ throw new ValueError("Must match regular expression", str);
70
+ return groups;
53
71
  }
File without changes
File without changes