@poe-platform/safe-js 0.1.197 → 0.1.199

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.
@@ -711,7 +711,7 @@ var RegexCompileGuard = class {
711
711
  this.work(flags.length);
712
712
  }
713
713
  retain(pattern, valueUnits = 0) {
714
- const units = 10 + pattern.source.length + measureNode(pattern.body);
714
+ const units = measurePattern(pattern);
715
715
  if (this.ticket !== void 0) {
716
716
  this.ticket.owner.budget.resizeCompileTicket(this.ticket, units + valueUnits);
717
717
  }
@@ -728,7 +728,15 @@ var RegexCompileGuard = class {
728
728
  }
729
729
  };
730
730
  function regexCompiledData(pattern) {
731
- return compiledData.get(pattern) ?? { units: 10 + pattern.source.length + measureNode(pattern.body) };
731
+ return compiledData.get(pattern) ?? { units: measurePattern(pattern) };
732
+ }
733
+ function measurePattern(pattern) {
734
+ let units = 10 + pattern.source.length + measureNode(pattern.body);
735
+ if (pattern.groups !== void 0) {
736
+ units += 1;
737
+ for (const [name, indices] of Object.entries(pattern.groups)) units += name.length + 2 + indices.length;
738
+ }
739
+ return units;
732
740
  }
733
741
  function measureNode(node) {
734
742
  switch (node.type) {
@@ -737,6 +745,8 @@ function measureNode(node) {
737
745
  return 2;
738
746
  case "literal":
739
747
  return 3 + node.value.length;
748
+ case "namedBackreference":
749
+ return 3 + node.name.length;
740
750
  case "anchor":
741
751
  case "wordBoundary":
742
752
  case "backreference":
@@ -744,7 +754,7 @@ function measureNode(node) {
744
754
  case "characterClass": {
745
755
  let usage = 5 + node.items.length;
746
756
  for (const item of node.items) {
747
- usage += item.type === "character" ? 3 + item.value.length : item.type === "range" ? 4 + item.from.length + item.to.length : 4;
757
+ usage += item.type === "character" ? 3 + item.value.length : item.type === "range" ? 4 + item.from.length + item.to.length : item.type === "property" ? 4 + item.value.length : 4;
748
758
  }
749
759
  return usage;
750
760
  }
@@ -756,7 +766,7 @@ function measureNode(node) {
756
766
  return usage;
757
767
  }
758
768
  case "group":
759
- return 5 + measureNode(node.body);
769
+ return 5 + measureNode(node.body) + (node.name === void 0 ? 0 : 1 + node.name.length);
760
770
  case "lookahead":
761
771
  case "lookbehind":
762
772
  return 4 + measureNode(node.body);
@@ -2049,6 +2059,8 @@ function createIdentifier(token, name) {
2049
2059
  }
2050
2060
 
2051
2061
  // packages/safe-js/src/interp/regex/parse.ts
2062
+ var groupNameStart = /^[$_\p{ID_Start}]$/u;
2063
+ var groupNamePart = /^[$\u200c\u200d\p{ID_Continue}]$/u;
2052
2064
  function parseRegex(source, flags = "", compilation, valueUnits = 0) {
2053
2065
  const guard = new RegexCompileGuard(compilation);
2054
2066
  try {
@@ -2056,10 +2068,11 @@ function parseRegex(source, flags = "", compilation, valueUnits = 0) {
2056
2068
  guard.checkLength(flags.length, true);
2057
2069
  guard.allocate(5 + valueUnits);
2058
2070
  const parsedFlags = parseFlags(flags, guard);
2059
- const parser = new RegexParser(source, guard);
2071
+ const parser = new RegexParser(source, guard, parsedFlags.unicode);
2060
2072
  const body = parser.parse();
2061
2073
  guard.allocate(5 + source.length);
2062
2074
  const pattern = { source, flags: parsedFlags, captureCount: parser.captureCount, body };
2075
+ if (Object.keys(parser.namedGroups).length > 0) pattern.groups = parser.namedGroups;
2063
2076
  guard.retain(pattern, valueUnits);
2064
2077
  return pattern;
2065
2078
  } finally {
@@ -2067,15 +2080,19 @@ function parseRegex(source, flags = "", compilation, valueUnits = 0) {
2067
2080
  }
2068
2081
  }
2069
2082
  var RegexParser = class {
2070
- constructor(source, guard) {
2083
+ constructor(source, guard, unicode) {
2071
2084
  this.source = source;
2072
2085
  this.guard = guard;
2086
+ this.unicode = unicode;
2073
2087
  }
2074
2088
  source;
2075
2089
  guard;
2090
+ unicode;
2076
2091
  captureCount = 0;
2077
2092
  cursor = 0;
2078
2093
  totalCaptureCount;
2094
+ hasNamedCaptures = false;
2095
+ namedGroups = /* @__PURE__ */ Object.create(null);
2079
2096
  get position() {
2080
2097
  return this.cursor;
2081
2098
  }
@@ -2091,6 +2108,7 @@ var RegexParser = class {
2091
2108
  }
2092
2109
  this.fail(`Unexpected character '${this.peek()}'`);
2093
2110
  }
2111
+ if (this.unicode || Object.keys(this.namedGroups).length > 0) this.validateNamedGroups(body);
2094
2112
  return body;
2095
2113
  }
2096
2114
  parseAlternation() {
@@ -2132,7 +2150,7 @@ var RegexParser = class {
2132
2150
  if (quantifier === void 0) {
2133
2151
  return body;
2134
2152
  }
2135
- if (body.type === "anchor" || body.type === "wordBoundary" || body.type === "lookbehind") {
2153
+ if (body.type === "anchor" || body.type === "wordBoundary" || body.type === "lookbehind" || this.unicode && body.type === "lookahead") {
2136
2154
  this.fail("Invalid quantifier target", quantifierStart);
2137
2155
  }
2138
2156
  const greedy = this.peek() !== "?";
@@ -2143,7 +2161,7 @@ var RegexParser = class {
2143
2161
  return { type: "quantifier", body, ...quantifier, greedy };
2144
2162
  }
2145
2163
  parseAtom() {
2146
- const character = this.take();
2164
+ const character = this.takeCharacter();
2147
2165
  switch (character) {
2148
2166
  case ".":
2149
2167
  this.guard.allocate(2);
@@ -2161,12 +2179,14 @@ var RegexParser = class {
2161
2179
  case "\\":
2162
2180
  return this.parseEscape(false, this.position - 1);
2163
2181
  default:
2182
+ if (this.unicode && (character === "]" || character === "}")) this.fail("Invalid Unicode pattern character");
2164
2183
  this.guard.allocate(3 + character.length);
2165
2184
  return { type: "literal", value: character };
2166
2185
  }
2167
2186
  }
2168
2187
  parseGroup(start) {
2169
2188
  let capturing = true;
2189
+ let name;
2170
2190
  let assertionNegated;
2171
2191
  let lookbehind = false;
2172
2192
  if (this.peek() === "?") {
@@ -2186,13 +2206,20 @@ var RegexParser = class {
2186
2206
  lookbehind = true;
2187
2207
  this.position += 3;
2188
2208
  } else if (extension.startsWith("?<")) {
2189
- this.fail("Named groups are not supported", start);
2209
+ this.position += 2;
2210
+ name = this.parseGroupName(start);
2190
2211
  } else {
2191
2212
  this.fail("Unsupported group construct", start);
2192
2213
  }
2193
2214
  }
2194
2215
  if (capturing) this.guard.allocate(1);
2195
2216
  const index = capturing ? ++this.captureCount : void 0;
2217
+ if (name !== void 0) {
2218
+ this.guard.allocate(name.length + 2);
2219
+ const indices = this.namedGroups[name] ??= [];
2220
+ this.guard.array(indices.length + 1);
2221
+ indices.push(index);
2222
+ }
2196
2223
  this.guard.enterGroup();
2197
2224
  let body;
2198
2225
  try {
@@ -2209,7 +2236,7 @@ var RegexParser = class {
2209
2236
  return { type: lookbehind ? "lookbehind" : "lookahead", negated: assertionNegated, body };
2210
2237
  }
2211
2238
  this.guard.allocate(5);
2212
- return { type: "group", capturing, index, body };
2239
+ return { type: "group", capturing, index, body, ...name === void 0 ? {} : { name } };
2213
2240
  }
2214
2241
  parseCharacterClass(start) {
2215
2242
  const negated = this.peek() === "^";
@@ -2233,7 +2260,7 @@ var RegexParser = class {
2233
2260
  if (left.type !== "character" || right.type !== "character") {
2234
2261
  this.fail("Character class ranges require literal endpoints", rangePosition);
2235
2262
  }
2236
- if (left.value.charCodeAt(0) > right.value.charCodeAt(0)) {
2263
+ if (left.value.codePointAt(0) > right.value.codePointAt(0)) {
2237
2264
  this.fail("Character class range is out of order", rangePosition);
2238
2265
  }
2239
2266
  this.guard.allocate(4 + left.value.length + right.value.length);
@@ -2262,13 +2289,22 @@ var RegexParser = class {
2262
2289
  this.fail("Unsupported character class escape", escapeStart);
2263
2290
  }
2264
2291
  this.guard.allocate(4);
2265
- return { type: "character", value: this.take() };
2292
+ return { type: "character", value: this.takeCharacter() };
2266
2293
  }
2267
2294
  parseEscape(inCharacterClass, start) {
2268
2295
  if (this.atEnd()) {
2269
2296
  this.fail("Trailing escape", start);
2270
2297
  }
2271
2298
  const escaped = this.take();
2299
+ if (escaped === "k") {
2300
+ this.totalCaptureCount ??= this.countAllCaptures();
2301
+ if (this.unicode || this.hasNamedCaptures) {
2302
+ if (inCharacterClass || this.take() !== "<") this.fail("Invalid named backreference", start);
2303
+ const name = this.parseGroupName(start);
2304
+ this.guard.allocate(3 + name.length);
2305
+ return { type: "namedBackreference", name };
2306
+ }
2307
+ }
2272
2308
  if (isDecimalDigit2(escaped)) {
2273
2309
  if (!inCharacterClass && escaped !== "0") {
2274
2310
  let end = this.position;
@@ -2284,6 +2320,8 @@ var RegexParser = class {
2284
2320
  return { type: "backreference", index };
2285
2321
  }
2286
2322
  }
2323
+ if (this.unicode && (escaped !== "0" || isDecimalDigit2(this.peek())))
2324
+ this.fail("Invalid decimal escape", start);
2287
2325
  this.guard.allocate(4);
2288
2326
  if (escaped === "8" || escaped === "9") return { type: "literal", value: escaped };
2289
2327
  let code = Number(escaped);
@@ -2294,7 +2332,24 @@ var RegexParser = class {
2294
2332
  return { type: "literal", value: String.fromCharCode(code) };
2295
2333
  }
2296
2334
  if (escaped === "p" || escaped === "P") {
2297
- this.fail("Unicode property escapes are not supported", start);
2335
+ if (!this.unicode) this.fail("Unicode property escapes are not supported", start);
2336
+ if (this.take() !== "{") this.fail("Invalid Unicode property escape", start);
2337
+ const begin = this.position;
2338
+ while (!this.atEnd() && this.peek() !== "}") {
2339
+ const character = this.take();
2340
+ if (!isDecimalDigit2(character) && !(character >= "a" && character <= "z") && !(character >= "A" && character <= "Z") && character !== "_" && character !== "=")
2341
+ this.fail("Invalid Unicode property escape", start);
2342
+ }
2343
+ this.guard.allocate(this.position - begin + 9);
2344
+ const value = this.source.slice(begin, this.position);
2345
+ if (this.take() !== "}") this.fail("Unterminated Unicode property escape", start);
2346
+ try {
2347
+ new RegExp(`\\p{${value}}`, "u");
2348
+ } catch {
2349
+ this.fail("Unknown Unicode property", start);
2350
+ }
2351
+ this.guard.array(1);
2352
+ return { type: "characterClass", negated: false, items: [{ type: "property", value, negated: escaped === "P" }] };
2298
2353
  }
2299
2354
  if (escaped === "x") {
2300
2355
  this.guard.allocate(4);
@@ -2302,7 +2357,29 @@ var RegexParser = class {
2302
2357
  }
2303
2358
  if (escaped === "u") {
2304
2359
  this.guard.allocate(4);
2305
- return { type: "literal", value: this.parseHexEscape(4, "Unicode", start) };
2360
+ if (this.unicode && this.peek() === "{") {
2361
+ this.position++;
2362
+ const begin = this.position;
2363
+ while (!this.atEnd() && this.peek() !== "}") this.position++;
2364
+ this.guard.allocate(this.position - begin);
2365
+ const digits = this.source.slice(begin, this.position);
2366
+ const point = Number.parseInt(digits, 16);
2367
+ if (this.take() !== "}" || digits.length === 0 || !allHexDigits(digits) || point > 1114111)
2368
+ this.fail("Invalid Unicode escape", start);
2369
+ return { type: "literal", value: String.fromCodePoint(point) };
2370
+ }
2371
+ let value = this.parseHexEscape(4, "Unicode", start);
2372
+ if (this.unicode && value.charCodeAt(0) >= 55296 && value.charCodeAt(0) <= 56319 && this.source.startsWith("\\u", this.position)) {
2373
+ const digits = this.source.slice(this.position + 2, this.position + 6);
2374
+ const point = Number.parseInt(digits, 16);
2375
+ this.guard.work(digits.length);
2376
+ this.guard.allocate(digits.length);
2377
+ if (digits.length === 4 && allHexDigits(digits) && point >= 56320 && point <= 57343) {
2378
+ value += String.fromCharCode(point);
2379
+ this.position += 6;
2380
+ }
2381
+ }
2382
+ return { type: "literal", value };
2306
2383
  }
2307
2384
  this.guard.allocate(25);
2308
2385
  const kinds = {
@@ -2333,6 +2410,15 @@ var RegexParser = class {
2333
2410
  v: "\v",
2334
2411
  "0": "\0"
2335
2412
  };
2413
+ if (escaped === "c" && this.unicode) {
2414
+ const letter = this.take();
2415
+ if (!(letter >= "a" && letter <= "z") && !(letter >= "A" && letter <= "Z"))
2416
+ this.fail("Invalid control escape", start);
2417
+ this.guard.allocate(4);
2418
+ return { type: "literal", value: String.fromCharCode(letter.charCodeAt(0) % 32) };
2419
+ }
2420
+ if (this.unicode && controls[escaped] === void 0 && !"^$\\.*+?()[]{}|/".includes(escaped) && !(inCharacterClass && escaped === "-"))
2421
+ this.fail("Invalid identity escape", start);
2336
2422
  this.guard.allocate(4);
2337
2423
  return { type: "literal", value: controls[escaped] ?? escaped };
2338
2424
  }
@@ -2358,10 +2444,62 @@ var RegexParser = class {
2358
2444
  else if (character === "\\") escaped = true;
2359
2445
  else if (character === "[") characterClass = true;
2360
2446
  else if (character === "]") characterClass = false;
2361
- else if (!characterClass && character === "(" && this.source[index + 1] !== "?") count++;
2447
+ else if (!characterClass && character === "(") {
2448
+ const named = this.source[index + 1] === "?" && this.source[index + 2] === "<" && this.source[index + 3] !== "=" && this.source[index + 3] !== "!";
2449
+ if (named) this.hasNamedCaptures = true;
2450
+ if (named || this.source[index + 1] !== "?") count++;
2451
+ }
2362
2452
  }
2363
2453
  return count;
2364
2454
  }
2455
+ parseGroupName(start) {
2456
+ let name = "";
2457
+ while (!this.atEnd() && this.peek() !== ">") {
2458
+ let character = this.take();
2459
+ if (character === "\\") {
2460
+ if (this.take() !== "u") this.fail("Invalid group name escape", start);
2461
+ if (this.peek() === "{") {
2462
+ this.position++;
2463
+ const begin = this.position;
2464
+ while (!this.atEnd() && this.peek() !== "}") this.position++;
2465
+ this.guard.allocate(this.position - begin);
2466
+ const digits = this.source.slice(begin, this.position);
2467
+ const point = Number.parseInt(digits, 16);
2468
+ if (this.take() !== "}" || digits.length === 0 || !allHexDigits(digits) || point > 1114111)
2469
+ this.fail("Invalid group name escape", start);
2470
+ character = String.fromCodePoint(point);
2471
+ } else character = this.parseHexEscape(4, "Unicode", start);
2472
+ }
2473
+ this.guard.allocate(character.length);
2474
+ name += character;
2475
+ }
2476
+ if (this.take() !== ">" || name.length === 0) this.fail("Invalid group name", start);
2477
+ let first = true;
2478
+ for (const character of name) {
2479
+ this.guard.work(1);
2480
+ if (!(first ? groupNameStart : groupNamePart).test(character)) this.fail("Invalid group name", start);
2481
+ first = false;
2482
+ }
2483
+ return name;
2484
+ }
2485
+ validateNamedGroups(node) {
2486
+ this.guard.work(1);
2487
+ this.guard.allocate(1);
2488
+ const names = /* @__PURE__ */ new Set();
2489
+ if (node.type === "namedBackreference" && !Object.hasOwn(this.namedGroups, node.name))
2490
+ this.fail("Unknown named backreference");
2491
+ if (node.type === "group" && node.name !== void 0) names.add(node.name);
2492
+ const children = node.type === "sequence" ? node.elements : node.type === "alternation" ? node.alternatives : node.type === "group" || node.type === "quantifier" || node.type === "lookahead" || node.type === "lookbehind" ? [node.body] : [];
2493
+ for (const child of children) {
2494
+ for (const name of this.validateNamedGroups(child)) {
2495
+ this.guard.work(1);
2496
+ if (names.has(name) && node.type !== "alternation") this.fail("Duplicate capture group name");
2497
+ this.guard.allocate(1);
2498
+ names.add(name);
2499
+ }
2500
+ }
2501
+ return names;
2502
+ }
2365
2503
  parseQuantifier() {
2366
2504
  const character = this.peek();
2367
2505
  if (character === "*") {
@@ -2433,6 +2571,12 @@ var RegexParser = class {
2433
2571
  this.position += 1;
2434
2572
  return character;
2435
2573
  }
2574
+ takeCharacter() {
2575
+ if (!this.unicode || this.atEnd()) return this.take();
2576
+ const character = String.fromCodePoint(this.source.codePointAt(this.position));
2577
+ this.position += character.length;
2578
+ return character;
2579
+ }
2436
2580
  atEnd() {
2437
2581
  return this.position >= this.source.length;
2438
2582
  }
@@ -2446,6 +2590,7 @@ function parseFlags(flags, guard) {
2446
2590
  hasIndices: false,
2447
2591
  global: false,
2448
2592
  sticky: false,
2593
+ unicode: false,
2449
2594
  ignoreCase: false,
2450
2595
  multiline: false,
2451
2596
  dotAll: false
@@ -2456,7 +2601,8 @@ function parseFlags(flags, guard) {
2456
2601
  i: "ignoreCase",
2457
2602
  m: "multiline",
2458
2603
  s: "dotAll",
2459
- y: "sticky"
2604
+ y: "sticky",
2605
+ u: "unicode"
2460
2606
  };
2461
2607
  for (let position = 0; position < flags.length; position += 1) {
2462
2608
  guard.work(1);
@@ -9372,8 +9518,9 @@ function matchRegexFrom(pattern, input, startIndex) {
9372
9518
  if (startIndex > input.length) {
9373
9519
  return null;
9374
9520
  }
9375
- for (let attempt = startIndex; attempt <= input.length; attempt += 1) {
9376
- const context = { input, flags: pattern.flags, direction: 1, work: { steps: 0 } };
9521
+ if (pattern.flags.unicode && startIndex > 0 && startIndex < input.length && input.charCodeAt(startIndex) >= 56320 && input.charCodeAt(startIndex) <= 57343 && input.charCodeAt(startIndex - 1) >= 55296 && input.charCodeAt(startIndex - 1) <= 56319) startIndex--;
9522
+ for (let attempt = startIndex; attempt <= input.length; attempt = advanceStringIndex(input, attempt, pattern.flags.unicode)) {
9523
+ const context = { input, flags: pattern.flags, groups: pattern.groups, direction: 1, work: { steps: 0 } };
9377
9524
  charge(context);
9378
9525
  const initialState = {
9379
9526
  position: attempt,
@@ -9381,7 +9528,7 @@ function matchRegexFrom(pattern, input, startIndex) {
9381
9528
  };
9382
9529
  const result = matchNode(pattern.body, initialState, context).next();
9383
9530
  if (!result.done) {
9384
- return toRegexMatch(input, attempt, result.value, pattern.flags.hasIndices);
9531
+ return toRegexMatch(input, attempt, result.value, pattern.flags.hasIndices, pattern.groups);
9385
9532
  }
9386
9533
  if (pattern.flags.sticky) break;
9387
9534
  }
@@ -9389,35 +9536,50 @@ function matchRegexFrom(pattern, input, startIndex) {
9389
9536
  }
9390
9537
  function* matchNode(node, state, context) {
9391
9538
  charge(context);
9392
- const characterIndex = context.direction === 1 ? state.position : state.position - 1;
9539
+ const character = readCharacter(context.input, state.position, context.direction, context.flags.unicode);
9393
9540
  switch (node.type) {
9394
9541
  case "empty":
9395
9542
  yield state;
9396
9543
  return;
9397
9544
  case "literal":
9398
- if (charactersEqual(context.input[characterIndex], node.value, context.flags.ignoreCase)) {
9399
- yield { ...state, position: state.position + context.direction };
9545
+ if (charactersEqual(character, node.value, context.flags.ignoreCase, context.flags.unicode)) {
9546
+ yield { ...state, position: state.position + context.direction * character.length };
9400
9547
  }
9401
9548
  return;
9402
- case "backreference": {
9403
- const capture = state.captures[node.index - 1];
9549
+ case "backreference":
9550
+ case "namedBackreference": {
9551
+ let capture;
9552
+ if (node.type === "backreference") capture = state.captures[node.index - 1];
9553
+ else {
9554
+ for (const index of context.groups?.[node.name] ?? []) {
9555
+ charge(context);
9556
+ if (state.captures[index - 1] !== void 0) {
9557
+ capture = state.captures[index - 1];
9558
+ break;
9559
+ }
9560
+ }
9561
+ }
9404
9562
  if (capture === void 0) {
9405
9563
  yield state;
9406
9564
  return;
9407
9565
  }
9408
- const length = capture.end - capture.start;
9409
- const start = context.direction === 1 ? state.position : state.position - length;
9410
- if (start < 0 || start + length > context.input.length) return;
9411
- for (let offset = 0; offset < length; offset++) {
9566
+ let position = state.position;
9567
+ let reference = context.direction === 1 ? capture.start : capture.end;
9568
+ const end = context.direction === 1 ? capture.end : capture.start;
9569
+ while (reference !== end) {
9412
9570
  charge(context);
9413
- if (!charactersEqual(context.input[start + offset], context.input[capture.start + offset], context.flags.ignoreCase)) return;
9571
+ const expected = readCharacter(context.input, reference, context.direction, context.flags.unicode);
9572
+ const actual = readCharacter(context.input, position, context.direction, context.flags.unicode);
9573
+ if (!charactersEqual(actual, expected, context.flags.ignoreCase, context.flags.unicode)) return;
9574
+ reference += context.direction * expected.length;
9575
+ position += context.direction * actual.length;
9414
9576
  }
9415
- yield { ...state, position: state.position + context.direction * length };
9577
+ yield { ...state, position };
9416
9578
  return;
9417
9579
  }
9418
9580
  case "dot":
9419
- if (characterIndex >= 0 && characterIndex < context.input.length && (context.flags.dotAll || !isLineTerminator(context.input[characterIndex]))) {
9420
- yield { ...state, position: state.position + context.direction };
9581
+ if (character !== void 0 && (context.flags.dotAll || !isLineTerminator(character))) {
9582
+ yield { ...state, position: state.position + context.direction * character.length };
9421
9583
  }
9422
9584
  return;
9423
9585
  case "anchor":
@@ -9426,17 +9588,16 @@ function* matchNode(node, state, context) {
9426
9588
  }
9427
9589
  return;
9428
9590
  case "wordBoundary": {
9429
- const previousWord = state.position > 0 && isWordCharacter(context.input[state.position - 1]);
9430
- const nextWord = state.position < context.input.length && isWordCharacter(context.input[state.position]);
9591
+ const previousWord = state.position > 0 && matchesCharacterClassItem(context.input[state.position - 1], { type: "kind", kind: "word", negated: false }, context.flags.ignoreCase, context.flags.unicode);
9592
+ const nextWord = state.position < context.input.length && matchesCharacterClassItem(context.input[state.position], { type: "kind", kind: "word", negated: false }, context.flags.ignoreCase, context.flags.unicode);
9431
9593
  if (previousWord !== nextWord !== node.negated) {
9432
9594
  yield state;
9433
9595
  }
9434
9596
  return;
9435
9597
  }
9436
9598
  case "characterClass": {
9437
- const character = context.input[characterIndex];
9438
- if (character !== void 0 && matchesCharacterClass(character, node.items, node.negated, context.flags.ignoreCase)) {
9439
- yield { ...state, position: state.position + context.direction };
9599
+ if (character !== void 0 && matchesCharacterClass(character, node.items, node.negated, context)) {
9600
+ yield { ...state, position: state.position + context.direction * character.length };
9440
9601
  }
9441
9602
  return;
9442
9603
  }
@@ -9519,11 +9680,27 @@ function matchesAnchor(kind, position, context) {
9519
9680
  }
9520
9681
  return position === context.input.length || context.flags.multiline && position < context.input.length && isLineTerminator(context.input[position]);
9521
9682
  }
9522
- function matchesCharacterClass(character, items, negated, ignoreCase) {
9523
- const matched = items.some((item) => matchesCharacterClassItem(character, item, ignoreCase));
9683
+ function matchesCharacterClass(character, items, negated, context) {
9684
+ const matched = items.some((item) => {
9685
+ if (context.flags.unicode) charge(context);
9686
+ return matchesCharacterClassItem(character, item, context.flags.ignoreCase, context.flags.unicode);
9687
+ });
9524
9688
  return negated ? !matched : matched;
9525
9689
  }
9526
- function matchesCharacterClassItem(character, item, ignoreCase) {
9690
+ function matchesCharacterClassItem(character, item, ignoreCase, unicode) {
9691
+ if (unicode) {
9692
+ const hex = (value) => `\\u{${value.codePointAt(0).toString(16)}}`;
9693
+ let atom;
9694
+ if (item.type === "character") atom = hex(item.value);
9695
+ else if (item.type === "range") atom = `[${hex(item.from)}-${hex(item.to)}]`;
9696
+ else if (item.type === "property") atom = `\\${item.negated ? "P" : "p"}{${item.value}}`;
9697
+ else {
9698
+ const kind = item.kind === "digit" ? "d" : item.kind === "word" ? "w" : "s";
9699
+ atom = `\\${item.negated ? kind.toUpperCase() : kind}`;
9700
+ }
9701
+ return new RegExp(`^(?:${atom})$`, ignoreCase ? "iu" : "u").test(character);
9702
+ }
9703
+ if (item.type === "property") return false;
9527
9704
  if (item.type === "character") {
9528
9705
  return charactersEqual(character, item.value, ignoreCase);
9529
9706
  }
@@ -9545,7 +9722,7 @@ function matchesCharacterClassItem(character, item, ignoreCase) {
9545
9722
  const matched = item.kind === "digit" ? isDigit(character) : item.kind === "word" ? isWordCharacter(character) : isSpaceCharacter(character);
9546
9723
  return item.negated ? !matched : matched;
9547
9724
  }
9548
- function toRegexMatch(input, start, state, hasIndices) {
9725
+ function toRegexMatch(input, start, state, hasIndices, groups) {
9549
9726
  const match = {
9550
9727
  index: start,
9551
9728
  text: input.slice(start, state.position),
@@ -9556,6 +9733,15 @@ function toRegexMatch(input, start, state, hasIndices) {
9556
9733
  if (hasIndices) {
9557
9734
  match.indices = [[start, state.position], ...state.captures.map((capture) => capture === void 0 ? void 0 : [capture.start, capture.end])];
9558
9735
  }
9736
+ if (groups !== void 0) {
9737
+ match.groups = /* @__PURE__ */ Object.create(null);
9738
+ if (hasIndices) match.indicesGroups = /* @__PURE__ */ Object.create(null);
9739
+ for (const [name, indices] of Object.entries(groups)) {
9740
+ const index = indices.find((index2) => state.captures[index2 - 1] !== void 0);
9741
+ match.groups[name] = index === void 0 ? void 0 : match.captures[index - 1];
9742
+ if (match.indicesGroups !== void 0) match.indicesGroups[name] = index === void 0 ? void 0 : match.indices[index];
9743
+ }
9744
+ }
9559
9745
  return match;
9560
9746
  }
9561
9747
  function cloneState(state) {
@@ -9600,9 +9786,20 @@ function normalizeLastIndex(lastIndex) {
9600
9786
  }
9601
9787
  return Math.min(Math.floor(lastIndex), Number.MAX_SAFE_INTEGER);
9602
9788
  }
9603
- function charactersEqual(left, right, ignoreCase) {
9789
+ function charactersEqual(left, right, ignoreCase, unicode = false) {
9790
+ if (unicode && ignoreCase && left !== void 0)
9791
+ return new RegExp(`^\\u{${right.codePointAt(0).toString(16)}}$`, "iu").test(left);
9604
9792
  return left !== void 0 && foldCharacter(left, ignoreCase) === foldCharacter(right, ignoreCase);
9605
9793
  }
9794
+ function advanceStringIndex(input, index, unicode) {
9795
+ return index + (unicode && (input.codePointAt(index) ?? 0) > 65535 ? 2 : 1);
9796
+ }
9797
+ function readCharacter(input, position, direction, unicode) {
9798
+ let index = direction === 1 ? position : position - 1;
9799
+ if (index < 0 || index >= input.length) return void 0;
9800
+ if (unicode && direction === -1 && index > 0 && input.charCodeAt(index) >= 56320 && input.charCodeAt(index) <= 57343 && input.charCodeAt(index - 1) >= 55296 && input.charCodeAt(index - 1) <= 56319) index--;
9801
+ return unicode ? String.fromCodePoint(input.codePointAt(index)) : input[index];
9802
+ }
9606
9803
  function foldCharacter(character, ignoreCase) {
9607
9804
  if (!ignoreCase) {
9608
9805
  return character;
@@ -10178,7 +10375,7 @@ function callReplaceLikeMethod(value, methodName, args, budget, callClosure, use
10178
10375
  );
10179
10376
  }
10180
10377
  async function replaceRegex(value, regex, replacement, budget, callClosure, context) {
10181
- if (hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget))
10378
+ if (getSandboxRegexPattern(regex).groups !== void 0 || hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget))
10182
10379
  return regexReplace(value, regex, replacement, budget, callClosure, context);
10183
10380
  if (regex.flags.includes("g")) regex.lastIndex = 0;
10184
10381
  const cursor = regex.lastIndex;
@@ -10228,7 +10425,7 @@ async function regexReplace(input, regex, replacementInput, budget, callClosure,
10228
10425
  input = void 0;
10229
10426
  replacement = isSandboxClosure(replacementInput) ? replacementInput : await sandboxString(replacementInput, budget, context);
10230
10427
  replacementInput = void 0;
10231
- if (isSandboxRegex(regex) && !hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget)) {
10428
+ if (isSandboxRegex(regex) && getSandboxRegexPattern(regex).groups === void 0 && !hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget)) {
10232
10429
  release();
10233
10430
  return await replaceRegex(value, regex, replacement, budget, callClosure, context);
10234
10431
  }
@@ -10443,7 +10640,7 @@ function splitNormalized(value, separator, limit, budget, parent) {
10443
10640
  while (result2.length < limit) {
10444
10641
  const match = executeRegex(splitter, value, Number(splitter.lastIndex));
10445
10642
  if (match === null) break;
10446
- if (match.text.length === 0) splitter.lastIndex = match.index + 1;
10643
+ if (match.text.length === 0) splitter.lastIndex = advanceStringIndex(value, match.index, splitter.flags.includes("u"));
10447
10644
  endedWithZeroWidthMatch = match.text.length === 0 && match.index === value.length;
10448
10645
  if (match.text.length === 0 && (match.index === copiedThrough || match.index === value.length))
10449
10646
  continue;
@@ -10618,7 +10815,7 @@ function collectRegexMatches(regex, value, all, budget, lastIndex = 0) {
10618
10815
  budget?.allocateArrayLength(matches.length + 1);
10619
10816
  matches.push(match);
10620
10817
  lastIndex = match.index + match.text.length;
10621
- if (all && match.text.length === 0) regex.lastIndex = ++lastIndex;
10818
+ if (all && match.text.length === 0) regex.lastIndex = lastIndex = advanceStringIndex(value, lastIndex, regex.flags.includes("u"));
10622
10819
  } while (all);
10623
10820
  return matches;
10624
10821
  }
@@ -16829,11 +17026,13 @@ function toMatchArray(match, input, budget) {
16829
17026
  }
16830
17027
  budget?.allocateArrayLength(match.captures.length + 1);
16831
17028
  const result = [match.text, ...match.captures];
16832
- Object.assign(result, { index: match.index, input, groups: void 0 });
17029
+ if (match.groups !== void 0) setSandboxPrototype(match.groups, null, budget);
17030
+ if (match.indicesGroups !== void 0) setSandboxPrototype(match.indicesGroups, null, budget);
17031
+ Object.assign(result, { index: match.index, input, groups: match.groups });
16833
17032
  if (match.indices !== void 0) {
16834
17033
  budget?.allocateArrayLength(match.indices.length);
16835
17034
  budget?.allocateArrayLength(2);
16836
- Object.assign(match.indices, { groups: void 0 });
17035
+ Object.assign(match.indices, { groups: match.indicesGroups });
16837
17036
  Object.assign(result, { indices: match.indices });
16838
17037
  }
16839
17038
  return result;
@@ -37182,4 +37381,4 @@ export {
37182
37381
  FileSnapshotBackend,
37183
37382
  run
37184
37383
  };
37185
- //# sourceMappingURL=chunk-7KLRGTR4.js.map
37384
+ //# sourceMappingURL=chunk-X5VIB5JV.js.map