', start: 8, end: 13},
-+ * // {name: 'match', value: '
an
', start: 13, end: 27},
-+ * // {name: 'right', value: '
', start: 27, end: 33},
-+ * // {name: 'between', value: ' example', start: 33, end: 41}
-+ * // ]
-+ *
-+ * // Omitting unneeded parts with null valueNames, and using escapeChar
-+ * str = '...{1}.\\{{function(x,y){return {y:x}}}';
-+ * XRegExp.matchRecursive(str, '{', '}', 'g', {
-+ * valueNames: ['literal', null, 'value', null],
-+ * escapeChar: '\\'
-+ * });
-+ * // -> [
-+ * // {name: 'literal', value: '...', start: 0, end: 3},
-+ * // {name: 'value', value: '1', start: 4, end: 5},
-+ * // {name: 'literal', value: '.\\{', start: 6, end: 9},
-+ * // {name: 'value', value: 'function(x,y){return {y:x}}', start: 10, end: 37}
-+ * // ]
-+ *
-+ * // Sticky mode via flag y
-+ * str = '<1><<<2>>><3>4<5>';
-+ * XRegExp.matchRecursive(str, '<', '>', 'gy');
-+ * // -> ['1', '<<2>>', '3']
-+ */
-+ XRegExp.matchRecursive = (str, left, right, flags, options) => {
-+ flags = flags || '';
-+ options = options || {};
-+ const global = flags.includes('g');
-+ const sticky = flags.includes('y');
-+ // Flag `y` is controlled internally
-+ const basicFlags = flags.replace(/y/g, '');
-+ let {escapeChar} = options;
-+ const vN = options.valueNames;
-+ const output = [];
-+ let openTokens = 0;
-+ let delimStart = 0;
-+ let delimEnd = 0;
-+ let lastOuterEnd = 0;
-+ let outerStart;
-+ let innerStart;
-+ let leftMatch;
-+ let rightMatch;
-+ let esc;
-+ left = XRegExp(left, basicFlags);
-+ right = XRegExp(right, basicFlags);
-+
-+ if (escapeChar) {
-+ if (escapeChar.length > 1) {
-+ throw new Error('Cannot use more than one escape character');
-+ }
-+ escapeChar = XRegExp.escape(escapeChar);
-+ // Example of concatenated `esc` regex:
-+ // `escapeChar`: '%'
-+ // `left`: '<'
-+ // `right`: '>'
-+ // Regex is: /(?:%[\S\s]|(?:(?!<|>)[^%])+)+/
-+ esc = new RegExp(
-+ `(?:${escapeChar}[\\S\\s]|(?:(?!${
-+ // Using `XRegExp.union` safely rewrites backreferences in `left` and `right`.
-+ // Intentionally not passing `basicFlags` to `XRegExp.union` since any syntax
-+ // transformation resulting from those flags was already applied to `left` and
-+ // `right` when they were passed through the XRegExp constructor above.
-+ XRegExp.union([left, right], '', {conjunction: 'or'}).source
-+ })[^${escapeChar}])+)+`,
-+ // Flags `gy` not needed here
-+ flags.replace(XRegExp._hasNativeFlag('s') ? /[^imsu]/g : /[^imu]/g, '')
-+ );
-+ }
-+
-+ while (true) {
-+ // If using an escape character, advance to the delimiter's next starting position,
-+ // skipping any escaped characters in between
-+ if (escapeChar) {
-+ delimEnd += (XRegExp.exec(str, esc, delimEnd, 'sticky') || [''])[0].length;
-+ }
-+ leftMatch = XRegExp.exec(str, left, delimEnd);
-+ rightMatch = XRegExp.exec(str, right, delimEnd);
-+ // Keep the leftmost match only
-+ if (leftMatch && rightMatch) {
-+ if (leftMatch.index <= rightMatch.index) {
-+ rightMatch = null;
-+ } else {
-+ leftMatch = null;
-+ }
-+ }
-+ // Paths (LM: leftMatch, RM: rightMatch, OT: openTokens):
-+ // LM | RM | OT | Result
-+ // 1 | 0 | 1 | loop
-+ // 1 | 0 | 0 | loop
-+ // 0 | 1 | 1 | loop
-+ // 0 | 1 | 0 | throw
-+ // 0 | 0 | 1 | throw
-+ // 0 | 0 | 0 | break
-+ // The paths above don't include the sticky mode special case. The loop ends after the
-+ // first completed match if not `global`.
-+ if (leftMatch || rightMatch) {
-+ delimStart = (leftMatch || rightMatch).index;
-+ delimEnd = delimStart + (leftMatch || rightMatch)[0].length;
-+ } else if (!openTokens) {
-+ break;
-+ }
-+ if (sticky && !openTokens && delimStart > lastOuterEnd) {
-+ break;
-+ }
-+ if (leftMatch) {
-+ if (!openTokens) {
-+ outerStart = delimStart;
-+ innerStart = delimEnd;
-+ }
-+ ++openTokens;
-+ } else if (rightMatch && openTokens) {
-+ if (!--openTokens) {
-+ if (vN) {
-+ if (vN[0] && outerStart > lastOuterEnd) {
-+ output.push(row(vN[0], str.slice(lastOuterEnd, outerStart), lastOuterEnd, outerStart));
-+ }
-+ if (vN[1]) {
-+ output.push(row(vN[1], str.slice(outerStart, innerStart), outerStart, innerStart));
-+ }
-+ if (vN[2]) {
-+ output.push(row(vN[2], str.slice(innerStart, delimStart), innerStart, delimStart));
-+ }
-+ if (vN[3]) {
-+ output.push(row(vN[3], str.slice(delimStart, delimEnd), delimStart, delimEnd));
-+ }
-+ } else {
-+ output.push(str.slice(innerStart, delimStart));
-+ }
-+ lastOuterEnd = delimEnd;
-+ if (!global) {
-+ break;
-+ }
-+ }
-+ } else {
-+ const delimSide = rightMatch ? 'right' : 'left';
-+ const errorPos = rightMatch ? delimStart : outerStart;
-+ throw new Error(`Unbalanced ${delimSide} delimiter found in string at position ${errorPos}`);
-+ }
-+ // If the delimiter matched an empty string, avoid an infinite loop
-+ if (delimStart === delimEnd) {
-+ ++delimEnd;
-+ }
-+ }
-+
-+ if (global && !sticky && vN && vN[0] && str.length > lastOuterEnd) {
-+ output.push(row(vN[0], str.slice(lastOuterEnd), lastOuterEnd, str.length));
-+ }
-+
-+ return output;
-+ };
-+ };
-+
-+ /*!
-+ * XRegExp Unicode Base 5.0.2
-+ *