poe-code 14.0.74 → 14.0.75

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.
Files changed (38) hide show
  1. package/dist/index.js +926 -84
  2. package/dist/index.js.map +4 -4
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safe-js/dist/chunks/{chunk-2J6W3YGB.js → chunk-BAJI3K4B.js} +2 -2
  6. package/packages/safe-js/dist/chunks/{chunk-YEZOXLY4.js → chunk-EOTY4PCT.js} +901 -84
  7. package/packages/safe-js/dist/chunks/chunk-EOTY4PCT.js.map +7 -0
  8. package/packages/safe-js/dist/cli.js +2 -2
  9. package/packages/safe-js/dist/core.js +1 -1
  10. package/packages/safe-js/dist/index.js +2 -2
  11. package/packages/safe-js/dist/interp/globals/regex.js +108 -2
  12. package/packages/safe-js/dist/interp/iteration.js +5 -2
  13. package/packages/safe-js/dist/interp/methods/regex-split.d.ts +3 -0
  14. package/packages/safe-js/dist/interp/methods/regex-split.js +102 -0
  15. package/packages/safe-js/dist/interp/methods/regex.d.ts +1 -1
  16. package/packages/safe-js/dist/interp/methods/regex.js +16 -5
  17. package/packages/safe-js/dist/interp/methods/regexp-iterator.d.ts +5 -1
  18. package/packages/safe-js/dist/interp/methods/regexp-iterator.js +59 -7
  19. package/packages/safe-js/dist/interp/methods/string.js +7 -7
  20. package/packages/safe-js/dist/interp/object-model.d.ts +1 -0
  21. package/packages/safe-js/dist/interp/object-model.js +4 -1
  22. package/packages/safe-js/dist/interp/regex/compile-guard.js +26 -4
  23. package/packages/safe-js/dist/interp/regex/engine.d.ts +4 -0
  24. package/packages/safe-js/dist/interp/regex/engine.js +230 -30
  25. package/packages/safe-js/dist/interp/regex/parse.d.ts +42 -4
  26. package/packages/safe-js/dist/interp/regex/parse.js +429 -25
  27. package/packages/safe-js/dist/interp/regexp-iterator.d.ts +4 -2
  28. package/packages/safe-js/dist/interp/regexp-iterator.js +6 -1
  29. package/packages/safe-js/dist/interp/values.js +5 -3
  30. package/packages/safe-js/dist/snapshot/dump-format.js +10 -2
  31. package/packages/safe-js/dist/snapshot/replay-data.d.ts +3 -0
  32. package/packages/safe-js/dist/snapshot/replay-data.js +14 -4
  33. package/packages/safe-js/dist/snapshot/restore.js +8 -2
  34. package/packages/safe-js/dist/snapshot/serialize.d.ts +3 -0
  35. package/packages/safe-js/dist/snapshot/serialize.js +4 -1
  36. package/packages/safe-js/dist/snapshot/validation.js +8 -0
  37. package/packages/safe-js/dist/chunks/chunk-YEZOXLY4.js.map +0 -7
  38. /package/packages/safe-js/dist/chunks/{chunk-2J6W3YGB.js.map → chunk-BAJI3K4B.js.map} +0 -0
@@ -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,13 +745,18 @@ 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":
752
+ case "backreference":
742
753
  return 3;
743
754
  case "characterClass": {
744
755
  let usage = 5 + node.items.length;
756
+ if (node.operation !== void 0) usage += 1 + node.operation.length;
757
+ if (node.strings !== void 0) usage += 1;
745
758
  for (const item of node.items) {
746
- usage += item.type === "character" ? 3 + item.value.length : item.type === "range" ? 4 + item.from.length + item.to.length : 4;
759
+ 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 + (item.strings === void 0 ? 0 : 1) : item.type === "set" ? 3 + measureNode({ type: "characterClass", ...item.value }) : item.type === "strings" ? 4 + item.values.reduce((total, value) => total + 1 + value.length, 0) : 4;
747
760
  }
748
761
  return usage;
749
762
  }
@@ -755,7 +768,10 @@ function measureNode(node) {
755
768
  return usage;
756
769
  }
757
770
  case "group":
758
- return 5 + measureNode(node.body);
771
+ return 5 + measureNode(node.body) + (node.name === void 0 ? 0 : 1 + node.name.length) + (node.modifiers === void 0 ? 0 : 2 + Object.keys(node.modifiers).length);
772
+ case "lookahead":
773
+ case "lookbehind":
774
+ return 4 + measureNode(node.body);
759
775
  case "quantifier":
760
776
  return (Object.hasOwn(node, "max") ? 6 : 5) + measureNode(node.body);
761
777
  }
@@ -2045,6 +2061,8 @@ function createIdentifier(token, name) {
2045
2061
  }
2046
2062
 
2047
2063
  // packages/safe-js/src/interp/regex/parse.ts
2064
+ var groupNameStart = /^[$_\p{ID_Start}]$/u;
2065
+ var groupNamePart = /^[$\u200c\u200d\p{ID_Continue}]$/u;
2048
2066
  function parseRegex(source, flags = "", compilation, valueUnits = 0) {
2049
2067
  const guard = new RegexCompileGuard(compilation);
2050
2068
  try {
@@ -2052,10 +2070,11 @@ function parseRegex(source, flags = "", compilation, valueUnits = 0) {
2052
2070
  guard.checkLength(flags.length, true);
2053
2071
  guard.allocate(5 + valueUnits);
2054
2072
  const parsedFlags = parseFlags(flags, guard);
2055
- const parser = new RegexParser(source, guard);
2073
+ const parser = new RegexParser(source, guard, parsedFlags.unicode || parsedFlags.unicodeSets, parsedFlags.unicodeSets);
2056
2074
  const body = parser.parse();
2057
2075
  guard.allocate(5 + source.length);
2058
2076
  const pattern = { source, flags: parsedFlags, captureCount: parser.captureCount, body };
2077
+ if (Object.keys(parser.namedGroups).length > 0) pattern.groups = parser.namedGroups;
2059
2078
  guard.retain(pattern, valueUnits);
2060
2079
  return pattern;
2061
2080
  } finally {
@@ -2063,14 +2082,21 @@ function parseRegex(source, flags = "", compilation, valueUnits = 0) {
2063
2082
  }
2064
2083
  }
2065
2084
  var RegexParser = class {
2066
- constructor(source, guard) {
2085
+ constructor(source, guard, unicode, unicodeSets) {
2067
2086
  this.source = source;
2068
2087
  this.guard = guard;
2088
+ this.unicode = unicode;
2089
+ this.unicodeSets = unicodeSets;
2069
2090
  }
2070
2091
  source;
2071
2092
  guard;
2093
+ unicode;
2094
+ unicodeSets;
2072
2095
  captureCount = 0;
2073
2096
  cursor = 0;
2097
+ totalCaptureCount;
2098
+ hasNamedCaptures = false;
2099
+ namedGroups = /* @__PURE__ */ Object.create(null);
2074
2100
  get position() {
2075
2101
  return this.cursor;
2076
2102
  }
@@ -2086,6 +2112,7 @@ var RegexParser = class {
2086
2112
  }
2087
2113
  this.fail(`Unexpected character '${this.peek()}'`);
2088
2114
  }
2115
+ if (this.unicode || Object.keys(this.namedGroups).length > 0) this.validateNamedGroups(body);
2089
2116
  return body;
2090
2117
  }
2091
2118
  parseAlternation() {
@@ -2119,15 +2146,20 @@ var RegexParser = class {
2119
2146
  parseQuantifiedAtom() {
2120
2147
  const quantifierStart = this.position;
2121
2148
  const current = this.peek();
2122
- if (current === "*" || current === "+" || current === "?" || current === "{") {
2149
+ if (current === "*" || current === "+" || current === "?" || current === "{" && this.unicode) {
2123
2150
  this.fail("Nothing to repeat", quantifierStart);
2124
2151
  }
2152
+ if (current === "{") {
2153
+ const quantifier2 = this.parseQuantifier();
2154
+ this.position = quantifierStart;
2155
+ if (quantifier2 !== void 0) this.fail("Nothing to repeat", quantifierStart);
2156
+ }
2125
2157
  const body = this.parseAtom();
2126
2158
  const quantifier = this.parseQuantifier();
2127
2159
  if (quantifier === void 0) {
2128
2160
  return body;
2129
2161
  }
2130
- if (body.type === "anchor" || body.type === "wordBoundary") {
2162
+ if (body.type === "anchor" || body.type === "wordBoundary" || body.type === "lookbehind" || this.unicode && body.type === "lookahead") {
2131
2163
  this.fail("Invalid quantifier target", quantifierStart);
2132
2164
  }
2133
2165
  const greedy = this.peek() !== "?";
@@ -2138,7 +2170,7 @@ var RegexParser = class {
2138
2170
  return { type: "quantifier", body, ...quantifier, greedy };
2139
2171
  }
2140
2172
  parseAtom() {
2141
- const character = this.take();
2173
+ const character = this.takeCharacter();
2142
2174
  switch (character) {
2143
2175
  case ".":
2144
2176
  this.guard.allocate(2);
@@ -2156,12 +2188,17 @@ var RegexParser = class {
2156
2188
  case "\\":
2157
2189
  return this.parseEscape(false, this.position - 1);
2158
2190
  default:
2191
+ if (this.unicode && (character === "]" || character === "}")) this.fail("Invalid Unicode pattern character");
2159
2192
  this.guard.allocate(3 + character.length);
2160
2193
  return { type: "literal", value: character };
2161
2194
  }
2162
2195
  }
2163
2196
  parseGroup(start) {
2164
2197
  let capturing = true;
2198
+ let name;
2199
+ let modifiers;
2200
+ let assertionNegated;
2201
+ let lookbehind = false;
2165
2202
  if (this.peek() === "?") {
2166
2203
  this.guard.allocate(Math.min(3, this.source.length - this.position));
2167
2204
  this.guard.work(Math.min(3, this.source.length - this.position));
@@ -2170,17 +2207,33 @@ var RegexParser = class {
2170
2207
  capturing = false;
2171
2208
  this.position += 2;
2172
2209
  } else if (extension.startsWith("?=") || extension.startsWith("?!")) {
2173
- this.fail("Lookahead is not supported", start);
2210
+ capturing = false;
2211
+ assertionNegated = extension.startsWith("?!");
2212
+ this.position += 2;
2174
2213
  } else if (extension.startsWith("?<=") || extension.startsWith("?<!")) {
2175
- this.fail("Lookbehind is not supported", start);
2214
+ capturing = false;
2215
+ assertionNegated = extension.startsWith("?<!");
2216
+ lookbehind = true;
2217
+ this.position += 3;
2176
2218
  } else if (extension.startsWith("?<")) {
2177
- this.fail("Named groups are not supported", start);
2219
+ this.position += 2;
2220
+ name = this.parseGroupName(start);
2221
+ } else if ("ims-".includes(extension[1] ?? "") && extension.length > 1) {
2222
+ capturing = false;
2223
+ this.position++;
2224
+ modifiers = this.parseModifiers(start);
2178
2225
  } else {
2179
2226
  this.fail("Unsupported group construct", start);
2180
2227
  }
2181
2228
  }
2182
2229
  if (capturing) this.guard.allocate(1);
2183
2230
  const index = capturing ? ++this.captureCount : void 0;
2231
+ if (name !== void 0) {
2232
+ this.guard.allocate(name.length + 2);
2233
+ const indices = this.namedGroups[name] ??= [];
2234
+ this.guard.array(indices.length + 1);
2235
+ indices.push(index);
2236
+ }
2184
2237
  this.guard.enterGroup();
2185
2238
  let body;
2186
2239
  try {
@@ -2192,10 +2245,40 @@ var RegexParser = class {
2192
2245
  this.fail("Unterminated group", start);
2193
2246
  }
2194
2247
  this.position += 1;
2248
+ if (assertionNegated !== void 0) {
2249
+ this.guard.allocate(4);
2250
+ return { type: lookbehind ? "lookbehind" : "lookahead", negated: assertionNegated, body };
2251
+ }
2195
2252
  this.guard.allocate(5);
2196
- return { type: "group", capturing, index, body };
2253
+ return {
2254
+ type: "group",
2255
+ capturing,
2256
+ index,
2257
+ body,
2258
+ ...name === void 0 ? {} : { name },
2259
+ ...modifiers === void 0 ? {} : { modifiers }
2260
+ };
2261
+ }
2262
+ parseModifiers(start) {
2263
+ this.guard.allocate(2);
2264
+ const modifiers = {};
2265
+ let enabled = true;
2266
+ while (!this.atEnd() && this.peek() !== ":") {
2267
+ const flag = this.take();
2268
+ if (flag === "-" && enabled) {
2269
+ enabled = false;
2270
+ continue;
2271
+ }
2272
+ const name = flag === "i" ? "ignoreCase" : flag === "m" ? "multiline" : flag === "s" ? "dotAll" : void 0;
2273
+ if (name === void 0 || Object.hasOwn(modifiers, name)) this.fail("Invalid regex modifiers", start);
2274
+ this.guard.allocate(1);
2275
+ modifiers[name] = enabled;
2276
+ }
2277
+ if (this.take() !== ":" || Object.keys(modifiers).length === 0) this.fail("Invalid regex modifiers", start);
2278
+ return modifiers;
2197
2279
  }
2198
2280
  parseCharacterClass(start) {
2281
+ if (this.unicodeSets) return { type: "characterClass", ...this.parseUnicodeSet(start) };
2199
2282
  const negated = this.peek() === "^";
2200
2283
  if (negated) {
2201
2284
  this.position += 1;
@@ -2215,9 +2298,13 @@ var RegexParser = class {
2215
2298
  this.position += 1;
2216
2299
  const right = this.parseClassItem(start);
2217
2300
  if (left.type !== "character" || right.type !== "character") {
2218
- this.fail("Character class ranges require literal endpoints", rangePosition);
2301
+ if (this.unicode) this.fail("Character class ranges require literal endpoints", rangePosition);
2302
+ this.guard.array(items.length + 3);
2303
+ this.guard.allocate(4);
2304
+ items.push(left, { type: "character", value: "-" }, right);
2305
+ continue;
2219
2306
  }
2220
- if (left.value.charCodeAt(0) > right.value.charCodeAt(0)) {
2307
+ if (left.value.codePointAt(0) > right.value.codePointAt(0)) {
2221
2308
  this.fail("Character class range is out of order", rangePosition);
2222
2309
  }
2223
2310
  this.guard.allocate(4 + left.value.length + right.value.length);
@@ -2246,26 +2333,193 @@ var RegexParser = class {
2246
2333
  this.fail("Unsupported character class escape", escapeStart);
2247
2334
  }
2248
2335
  this.guard.allocate(4);
2249
- return { type: "character", value: this.take() };
2336
+ return { type: "character", value: this.takeCharacter() };
2337
+ }
2338
+ parseUnicodeSet(start) {
2339
+ this.guard.enterGroup();
2340
+ try {
2341
+ const negated = this.peek() === "^";
2342
+ if (negated) this.position++;
2343
+ this.guard.allocate(5);
2344
+ const items = [];
2345
+ let operation;
2346
+ while (!this.atEnd() && this.peek() !== "]") {
2347
+ this.guard.array(items.length + 1);
2348
+ let item = this.parseUnicodeSetItem(start);
2349
+ if (this.peek() === "-" && this.source[this.position + 1] !== "-") {
2350
+ this.position++;
2351
+ const right = this.parseUnicodeSetItem(start);
2352
+ if (item.type !== "character" || right.type !== "character") this.fail("Invalid set range", start);
2353
+ if (item.value.codePointAt(0) > right.value.codePointAt(0)) this.fail("Set range is out of order", start);
2354
+ this.guard.allocate(4 + item.value.length + right.value.length);
2355
+ item = { type: "range", from: item.value, to: right.value };
2356
+ }
2357
+ if (operation !== void 0 && item.type === "range") this.fail("Set operations require nested ranges", start);
2358
+ items.push(item);
2359
+ const operator = this.source.slice(this.position, this.position + 2);
2360
+ if (operator === "&&" || operator === "--") {
2361
+ const next = operator === "&&" ? "intersection" : "subtraction";
2362
+ if (operation === void 0 && (items.length !== 1 || item.type === "range") || operation !== void 0 && operation !== next) this.fail("Mixed set operations", start);
2363
+ if (operation === void 0) this.guard.allocate(1 + next.length);
2364
+ operation = next;
2365
+ this.position += 2;
2366
+ if (this.peek() === "]" || this.atEnd()) this.fail("Missing set operand", start);
2367
+ } else if (operation !== void 0 && this.peek() !== "]") this.fail("Missing set operator", start);
2368
+ }
2369
+ if (this.take() !== "]") this.fail("Unterminated character set", start);
2370
+ const mayContainStrings = (item) => {
2371
+ if (item.type === "set") return item.value.strings === true;
2372
+ if (item.type === "property") return item.strings === true;
2373
+ if (item.type === "strings") return item.values.some((value) => value.length !== ((value.codePointAt(0) ?? 0) > 65535 ? 2 : 1));
2374
+ return false;
2375
+ };
2376
+ const strings = operation === "intersection" ? items.every(mayContainStrings) : operation === "subtraction" ? mayContainStrings(items[0]) : items.some(mayContainStrings);
2377
+ if (negated && strings) this.fail("Cannot complement a set containing strings", start);
2378
+ if (strings) this.guard.allocate(1);
2379
+ return { negated, items, ...operation === void 0 ? {} : { operation }, ...strings ? { strings: true } : {} };
2380
+ } finally {
2381
+ this.guard.leaveGroup();
2382
+ }
2383
+ }
2384
+ parseUnicodeSetItem(start) {
2385
+ if (this.peek() === "[") {
2386
+ this.position++;
2387
+ this.guard.allocate(3);
2388
+ return { type: "set", value: this.parseUnicodeSet(start) };
2389
+ }
2390
+ if (this.source.startsWith("\\q{", this.position)) {
2391
+ this.position += 3;
2392
+ this.guard.allocate(4);
2393
+ const values = [];
2394
+ let value2 = "";
2395
+ while (!this.atEnd()) {
2396
+ if (this.peek() === "|" || this.peek() === "}") {
2397
+ this.guard.array(values.length + 1);
2398
+ this.guard.allocate(1);
2399
+ values.push(value2);
2400
+ value2 = "";
2401
+ if (this.take() === "}") return { type: "strings", values };
2402
+ } else {
2403
+ const item = this.parseSetCharacter(start);
2404
+ this.guard.allocate(item.length);
2405
+ value2 += item;
2406
+ }
2407
+ }
2408
+ this.fail("Unterminated class string", start);
2409
+ }
2410
+ if (this.peek() === "\\") return this.parseClassItem(start);
2411
+ const value = this.parseSetCharacter(start);
2412
+ this.guard.allocate(3 + value.length);
2413
+ return { type: "character", value };
2414
+ }
2415
+ parseSetCharacter(start) {
2416
+ if (this.peek() === "\\") {
2417
+ this.position++;
2418
+ const node = this.parseEscape(true, start);
2419
+ if (node.type !== "literal") this.fail("Class strings require literal characters", start);
2420
+ return node.value;
2421
+ }
2422
+ const character = this.takeCharacter();
2423
+ if (!character || "()[]{}/-|".includes(character) || "!#$%&*+,.:;<=>?@^`~".includes(character) && this.peek() === character)
2424
+ this.fail("Invalid character in Unicode set", start);
2425
+ return character;
2250
2426
  }
2251
2427
  parseEscape(inCharacterClass, start) {
2252
2428
  if (this.atEnd()) {
2253
2429
  this.fail("Trailing escape", start);
2254
2430
  }
2255
2431
  const escaped = this.take();
2256
- if (escaped >= "1" && escaped <= "9") {
2257
- this.fail("Backreferences are not supported", start);
2258
- }
2259
- if (escaped === "p" || escaped === "P") {
2260
- this.fail("Unicode property escapes are not supported", start);
2432
+ if (escaped === "k") {
2433
+ this.totalCaptureCount ??= this.countAllCaptures();
2434
+ if (this.unicode || this.hasNamedCaptures) {
2435
+ if (inCharacterClass || this.take() !== "<") this.fail("Invalid named backreference", start);
2436
+ const name = this.parseGroupName(start);
2437
+ this.guard.allocate(3 + name.length);
2438
+ return { type: "namedBackreference", name };
2439
+ }
2440
+ }
2441
+ if (isDecimalDigit2(escaped)) {
2442
+ if (!inCharacterClass && escaped !== "0") {
2443
+ let end = this.position;
2444
+ let index = Number(escaped);
2445
+ while (isDecimalDigit2(this.source[end] ?? "")) {
2446
+ this.guard.work(1);
2447
+ index = index * 10 + Number(this.source[end++]);
2448
+ }
2449
+ this.totalCaptureCount ??= this.countAllCaptures();
2450
+ if (index <= this.totalCaptureCount) {
2451
+ this.position = end;
2452
+ this.guard.allocate(3);
2453
+ return { type: "backreference", index };
2454
+ }
2455
+ }
2456
+ if (this.unicode && (escaped !== "0" || isDecimalDigit2(this.peek())))
2457
+ this.fail("Invalid decimal escape", start);
2458
+ this.guard.allocate(4);
2459
+ if (escaped === "8" || escaped === "9") return { type: "literal", value: escaped };
2460
+ let code = Number(escaped);
2461
+ const maximum = escaped <= "3" ? 3 : 2;
2462
+ for (let digits = 1; digits < maximum && this.peek() >= "0" && this.peek() <= "7"; digits++) {
2463
+ code = code * 8 + Number(this.take());
2464
+ }
2465
+ return { type: "literal", value: String.fromCharCode(code) };
2466
+ }
2467
+ if (this.unicode && (escaped === "p" || escaped === "P")) {
2468
+ if (this.take() !== "{") this.fail("Invalid Unicode property escape", start);
2469
+ const begin = this.position;
2470
+ while (!this.atEnd() && this.peek() !== "}") {
2471
+ const character = this.take();
2472
+ if (!isDecimalDigit2(character) && !(character >= "a" && character <= "z") && !(character >= "A" && character <= "Z") && character !== "_" && character !== "=")
2473
+ this.fail("Invalid Unicode property escape", start);
2474
+ }
2475
+ this.guard.allocate(this.position - begin + 9);
2476
+ const value = this.source.slice(begin, this.position);
2477
+ if (this.take() !== "}") this.fail("Unterminated Unicode property escape", start);
2478
+ try {
2479
+ new RegExp(`\\${escaped}{${value}}`, this.unicodeSets ? "v" : "u");
2480
+ } catch {
2481
+ this.fail("Unknown Unicode property", start);
2482
+ }
2483
+ let strings = false;
2484
+ if (this.unicodeSets) {
2485
+ try {
2486
+ new RegExp(`\\p{${value}}`, "u");
2487
+ } catch {
2488
+ strings = true;
2489
+ }
2490
+ }
2491
+ this.guard.array(1);
2492
+ return { type: "characterClass", negated: false, items: [{ type: "property", value, negated: escaped === "P", ...strings ? { strings: true } : {} }] };
2261
2493
  }
2262
2494
  if (escaped === "x") {
2263
2495
  this.guard.allocate(4);
2264
- return { type: "literal", value: this.parseHexEscape(2, "hexadecimal", start) };
2496
+ return { type: "literal", value: this.parseHexEscape(2, "hexadecimal", start, this.unicode ? void 0 : "x") };
2265
2497
  }
2266
2498
  if (escaped === "u") {
2267
2499
  this.guard.allocate(4);
2268
- return { type: "literal", value: this.parseHexEscape(4, "Unicode", start) };
2500
+ if (this.unicode && this.peek() === "{") {
2501
+ this.position++;
2502
+ const begin = this.position;
2503
+ while (!this.atEnd() && this.peek() !== "}") this.position++;
2504
+ this.guard.allocate(this.position - begin);
2505
+ const digits = this.source.slice(begin, this.position);
2506
+ const point = Number.parseInt(digits, 16);
2507
+ if (this.take() !== "}" || digits.length === 0 || !allHexDigits(digits) || point > 1114111)
2508
+ this.fail("Invalid Unicode escape", start);
2509
+ return { type: "literal", value: String.fromCodePoint(point) };
2510
+ }
2511
+ let value = this.parseHexEscape(4, "Unicode", start, this.unicode ? void 0 : "u");
2512
+ if (this.unicode && value.charCodeAt(0) >= 55296 && value.charCodeAt(0) <= 56319 && this.source.startsWith("\\u", this.position)) {
2513
+ const digits = this.source.slice(this.position + 2, this.position + 6);
2514
+ const point = Number.parseInt(digits, 16);
2515
+ this.guard.work(digits.length);
2516
+ this.guard.allocate(digits.length);
2517
+ if (digits.length === 4 && allHexDigits(digits) && point >= 56320 && point <= 57343) {
2518
+ value += String.fromCharCode(point);
2519
+ this.position += 6;
2520
+ }
2521
+ }
2522
+ return { type: "literal", value };
2269
2523
  }
2270
2524
  this.guard.allocate(25);
2271
2525
  const kinds = {
@@ -2296,20 +2550,101 @@ var RegexParser = class {
2296
2550
  v: "\v",
2297
2551
  "0": "\0"
2298
2552
  };
2553
+ if (escaped === "c") {
2554
+ const letter = this.peek();
2555
+ this.guard.allocate(4);
2556
+ if (letter >= "a" && letter <= "z" || letter >= "A" && letter <= "Z" || !this.unicode && inCharacterClass && (isDecimalDigit2(letter) || letter === "_")) {
2557
+ this.position++;
2558
+ return { type: "literal", value: String.fromCharCode(letter.charCodeAt(0) % 32) };
2559
+ }
2560
+ if (this.unicode) this.fail("Invalid control escape", start);
2561
+ this.position--;
2562
+ return { type: "literal", value: "\\" };
2563
+ }
2564
+ if (this.unicode && controls[escaped] === void 0 && !"^$\\.*+?()[]{}|/".includes(escaped) && !(inCharacterClass && (escaped === "-" || this.unicodeSets && "!#$%&+,.:;<=>?@`~".includes(escaped))))
2565
+ this.fail("Invalid identity escape", start);
2299
2566
  this.guard.allocate(4);
2300
2567
  return { type: "literal", value: controls[escaped] ?? escaped };
2301
2568
  }
2302
- parseHexEscape(length, name, start) {
2569
+ parseHexEscape(length, name, start, identity) {
2303
2570
  const end = this.position + length;
2304
2571
  this.guard.allocate(Math.min(length, this.source.length - this.position));
2305
2572
  this.guard.work(Math.min(length, this.source.length - this.position));
2306
2573
  const digits = this.source.slice(this.position, end);
2307
2574
  if (digits.length !== length || !allHexDigits(digits)) {
2575
+ if (identity !== void 0) return identity;
2308
2576
  this.fail(`Invalid ${name} escape`, start);
2309
2577
  }
2310
2578
  this.position = end;
2311
2579
  return String.fromCharCode(Number.parseInt(digits, 16));
2312
2580
  }
2581
+ countAllCaptures() {
2582
+ let count = 0;
2583
+ let escaped = false;
2584
+ let characterClass = false;
2585
+ for (let index = 0; index < this.source.length; index++) {
2586
+ this.guard.work(1);
2587
+ const character = this.source[index];
2588
+ if (escaped) escaped = false;
2589
+ else if (character === "\\") escaped = true;
2590
+ else if (character === "[") characterClass = true;
2591
+ else if (character === "]") characterClass = false;
2592
+ else if (!characterClass && character === "(") {
2593
+ const named = this.source[index + 1] === "?" && this.source[index + 2] === "<" && this.source[index + 3] !== "=" && this.source[index + 3] !== "!";
2594
+ if (named) this.hasNamedCaptures = true;
2595
+ if (named || this.source[index + 1] !== "?") count++;
2596
+ }
2597
+ }
2598
+ return count;
2599
+ }
2600
+ parseGroupName(start) {
2601
+ let name = "";
2602
+ while (!this.atEnd() && this.peek() !== ">") {
2603
+ let character = this.take();
2604
+ if (character === "\\") {
2605
+ if (this.take() !== "u") this.fail("Invalid group name escape", start);
2606
+ if (this.peek() === "{") {
2607
+ this.position++;
2608
+ const begin = this.position;
2609
+ while (!this.atEnd() && this.peek() !== "}") this.position++;
2610
+ this.guard.allocate(this.position - begin);
2611
+ const digits = this.source.slice(begin, this.position);
2612
+ const point = Number.parseInt(digits, 16);
2613
+ if (this.take() !== "}" || digits.length === 0 || !allHexDigits(digits) || point > 1114111)
2614
+ this.fail("Invalid group name escape", start);
2615
+ character = String.fromCodePoint(point);
2616
+ } else character = this.parseHexEscape(4, "Unicode", start);
2617
+ }
2618
+ this.guard.allocate(character.length);
2619
+ name += character;
2620
+ }
2621
+ if (this.take() !== ">" || name.length === 0) this.fail("Invalid group name", start);
2622
+ let first = true;
2623
+ for (const character of name) {
2624
+ this.guard.work(1);
2625
+ if (!(first ? groupNameStart : groupNamePart).test(character)) this.fail("Invalid group name", start);
2626
+ first = false;
2627
+ }
2628
+ return name;
2629
+ }
2630
+ validateNamedGroups(node) {
2631
+ this.guard.work(1);
2632
+ this.guard.allocate(1);
2633
+ const names = /* @__PURE__ */ new Set();
2634
+ if (node.type === "namedBackreference" && !Object.hasOwn(this.namedGroups, node.name))
2635
+ this.fail("Unknown named backreference");
2636
+ if (node.type === "group" && node.name !== void 0) names.add(node.name);
2637
+ 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] : [];
2638
+ for (const child of children) {
2639
+ for (const name of this.validateNamedGroups(child)) {
2640
+ this.guard.work(1);
2641
+ if (names.has(name) && node.type !== "alternation") this.fail("Duplicate capture group name");
2642
+ this.guard.allocate(1);
2643
+ names.add(name);
2644
+ }
2645
+ }
2646
+ return names;
2647
+ }
2313
2648
  parseQuantifier() {
2314
2649
  const character = this.peek();
2315
2650
  if (character === "*") {
@@ -2339,18 +2674,29 @@ var RegexParser = class {
2339
2674
  }
2340
2675
  if (this.peek() === "}") {
2341
2676
  this.position += 1;
2677
+ if (!Number.isSafeInteger(min)) this.fail("Quantifier is too large", start);
2342
2678
  this.guard.allocate(3);
2343
2679
  return { min, max: min };
2344
2680
  }
2345
2681
  if (this.peek() !== ",") {
2682
+ if (!this.unicode) {
2683
+ this.position = start;
2684
+ return void 0;
2685
+ }
2346
2686
  this.fail("Invalid quantifier", start);
2347
2687
  }
2348
2688
  this.position += 1;
2349
2689
  const max = this.parseDecimal();
2350
2690
  if (this.peek() !== "}") {
2691
+ if (!this.unicode) {
2692
+ this.position = start;
2693
+ return void 0;
2694
+ }
2351
2695
  this.fail("Unterminated quantifier", start);
2352
2696
  }
2353
2697
  this.position += 1;
2698
+ if (!Number.isSafeInteger(min) || max !== void 0 && !Number.isSafeInteger(max))
2699
+ this.fail("Quantifier is too large", start);
2354
2700
  if (max !== void 0 && min > max) {
2355
2701
  this.fail("Quantifier range is out of order", start);
2356
2702
  }
@@ -2367,11 +2713,7 @@ var RegexParser = class {
2367
2713
  }
2368
2714
  this.guard.allocate(this.position - start);
2369
2715
  this.guard.work(this.position - start);
2370
- const value = Number(this.source.slice(start, this.position));
2371
- if (!Number.isSafeInteger(value)) {
2372
- this.fail("Quantifier is too large", start);
2373
- }
2374
- return value;
2716
+ return Number(this.source.slice(start, this.position));
2375
2717
  }
2376
2718
  peek() {
2377
2719
  return this.source[this.position] ?? "";
@@ -2381,6 +2723,12 @@ var RegexParser = class {
2381
2723
  this.position += 1;
2382
2724
  return character;
2383
2725
  }
2726
+ takeCharacter() {
2727
+ if (!this.unicode || this.atEnd()) return this.take();
2728
+ const character = String.fromCodePoint(this.source.codePointAt(this.position));
2729
+ this.position += character.length;
2730
+ return character;
2731
+ }
2384
2732
  atEnd() {
2385
2733
  return this.position >= this.source.length;
2386
2734
  }
@@ -2391,16 +2739,24 @@ var RegexParser = class {
2391
2739
  function parseFlags(flags, guard) {
2392
2740
  guard.allocate(10);
2393
2741
  const parsed = {
2742
+ hasIndices: false,
2394
2743
  global: false,
2744
+ sticky: false,
2745
+ unicode: false,
2746
+ unicodeSets: false,
2395
2747
  ignoreCase: false,
2396
2748
  multiline: false,
2397
2749
  dotAll: false
2398
2750
  };
2399
2751
  const names = {
2752
+ d: "hasIndices",
2400
2753
  g: "global",
2401
2754
  i: "ignoreCase",
2402
2755
  m: "multiline",
2403
- s: "dotAll"
2756
+ s: "dotAll",
2757
+ y: "sticky",
2758
+ u: "unicode",
2759
+ v: "unicodeSets"
2404
2760
  };
2405
2761
  for (let position = 0; position < flags.length; position += 1) {
2406
2762
  guard.work(1);
@@ -2414,6 +2770,7 @@ function parseFlags(flags, guard) {
2414
2770
  }
2415
2771
  parsed[name] = true;
2416
2772
  }
2773
+ if (parsed.unicode && parsed.unicodeSets) throw new SyntaxError("Unicode flags u and v cannot be combined");
2417
2774
  return parsed;
2418
2775
  }
2419
2776
  function isDecimalDigit2(character) {
@@ -6723,9 +7080,13 @@ function isSandboxRegExpIterator(value) {
6723
7080
  return typeof value === "object" && value !== null && states2.has(value);
6724
7081
  }
6725
7082
  function restoreSandboxRegExpIterator(state, target = /* @__PURE__ */ Object.create(null)) {
7083
+ if ((state.global !== void 0 || state.unicode !== void 0) && (typeof state.global !== "boolean" || typeof state.unicode !== "boolean"))
7084
+ throw new TypeError("Invalid RegExp iterator modes.");
7085
+ if (state.matcher !== void 0 && (state.matcher === null || typeof state.matcher !== "object"))
7086
+ throw new TypeError("Invalid RegExp iterator matcher.");
6726
7087
  if (!state.exhausted && (state.matcher === void 0 || state.input === void 0))
6727
7088
  throw new TypeError("A live RegExp iterator requires its matcher and input.");
6728
- states2.set(target, state.exhausted ? { matcher: void 0, input: void 0, exhausted: true } : { ...state });
7089
+ states2.set(target, state.exhausted ? { ...state, matcher: void 0, input: void 0, exhausted: true } : { ...state });
6729
7090
  return target;
6730
7091
  }
6731
7092
  function regexpIteratorState(value) {
@@ -7634,6 +7995,9 @@ function getSandboxPrototype(value, budget) {
7634
7995
  function hasExplicitSandboxPrototype(value) {
7635
7996
  return prototypes.has(value);
7636
7997
  }
7998
+ function hasNullObjectPrototype(value) {
7999
+ return prototypes.get(value) === null && !Array.isArray(value) && !isSandboxClosure(value) && !isSandboxRegex(value);
8000
+ }
7637
8001
  function getSandboxPropertyDescriptor(value, key, budget) {
7638
8002
  const hostProperties = isSandboxClosure(value) ? value.properties : void 0;
7639
8003
  if (isSandboxClosure(value) && !isGuestClosure(value))
@@ -7715,7 +8079,7 @@ function hasGuestObjectState(value) {
7715
8079
  const intrinsicUnchanged = intrinsicConstructors.get(value);
7716
8080
  if (intrinsicUnchanged !== void 0) return !intrinsicUnchanged();
7717
8081
  if (isLiveCapability(value)) return true;
7718
- if (functionProperties.has(value) || prototypes.has(value)) return true;
8082
+ if (functionProperties.has(value) || prototypes.has(value) && !hasNullObjectPrototype(value)) return true;
7719
8083
  if (isSandboxBox(value) || isSandboxDate(value)) return false;
7720
8084
  return descriptorObjects.has(value) && Object.values(Object.getOwnPropertyDescriptors(value)).some(
7721
8085
  (descriptor) => !descriptor.enumerable || !descriptor.configurable || !descriptor.writable
@@ -7956,11 +8320,14 @@ function serializeHeapReference(value, path, state) {
7956
8320
  if (isSandboxRegExpIterator(value)) {
7957
8321
  const snapshot = regexpIteratorState(value);
7958
8322
  const matcher = snapshot.matcher;
8323
+ const serializedMatcher = snapshot.global === void 0 && isSandboxRegex(matcher) ? { kind: "regex", source: matcher.source, flags: matcher.flags, lastIndex: Number(matcher.lastIndex) } : serializeDumpValue(matcher, `${path}.<matcher>`, state);
8324
+ if (serializedMatcher === SKIP_VALUE) throw new TypeError("Unsupported RegExp iterator matcher in public dump.");
7959
8325
  const entries = /* @__PURE__ */ Object.create(null);
7960
8326
  state.heap[String(id)] = {
7961
8327
  kind: "regexp-iterator",
7962
8328
  exhausted: snapshot.exhausted,
7963
- matcher: matcher === void 0 ? { kind: "undefined" } : { kind: "regex", source: matcher.source, flags: matcher.flags, lastIndex: Number(matcher.lastIndex) },
8329
+ matcher: serializedMatcher,
8330
+ ...snapshot.global === void 0 ? {} : { global: snapshot.global, unicode: snapshot.unicode },
7964
8331
  input: snapshot.input ?? { kind: "undefined" },
7965
8332
  entries,
7966
8333
  symbolEntries: serializeSymbolProperties(value, (entry) => {
@@ -8033,6 +8400,7 @@ function serializeHeapReference(value, path, state) {
8033
8400
  });
8034
8401
  state.heap[String(id)] = {
8035
8402
  kind: "object",
8403
+ ...hasNullObjectPrototype(value) ? { sandboxNullPrototype: true } : {},
8036
8404
  entries: serializeObjectEntries(value, path, state),
8037
8405
  ...symbolEntries.length === 0 ? {} : { symbolEntries },
8038
8406
  ...errorType === void 0 ? {} : { errorType }
@@ -8066,7 +8434,7 @@ function indexHeapContainers(snapshot) {
8066
8434
  const heapIds = /* @__PURE__ */ new Map();
8067
8435
  let nextId = 1;
8068
8436
  for (const [value, stat2] of stats.entries()) {
8069
- if (stat2.count > 1 || stat2.cyclic || ownSerializableSymbolKeys(value).length > 0 || isSandboxBox(value) || isSandboxRegExpIterator(value) || isSandboxRegex(value) && hasCustomRegexProperties(value) || isSandboxDate(value) || isFloat32Array(value) || Array.isArray(value) && requiresArrayEntries(value) || isSandboxArguments(value) || sandboxErrorTypes.has(value)) {
8437
+ if (stat2.count > 1 || stat2.cyclic || hasNullObjectPrototype(value) || ownSerializableSymbolKeys(value).length > 0 || isSandboxBox(value) || isSandboxRegExpIterator(value) || isSandboxRegex(value) && hasCustomRegexProperties(value) || isSandboxDate(value) || isFloat32Array(value) || Array.isArray(value) && requiresArrayEntries(value) || isSandboxArguments(value) || sandboxErrorTypes.has(value)) {
8070
8438
  heapIds.set(value, nextId);
8071
8439
  nextId += 1;
8072
8440
  }
@@ -8252,6 +8620,8 @@ function validateDumpHeap(root, state) {
8252
8620
  continue;
8253
8621
  }
8254
8622
  if (entry.kind === "object") {
8623
+ if (Object.hasOwn(entry, "sandboxNullPrototype") && entry.sandboxNullPrototype !== true)
8624
+ fail("invalidValue", `${path}.sandboxNullPrototype`, "invalid object prototype");
8255
8625
  requireRecord(entry.entries, `${path}.entries`);
8256
8626
  continue;
8257
8627
  }
@@ -8372,7 +8742,11 @@ function validateTaggedValue(record2, path, state) {
8372
8742
  return;
8373
8743
  case "array":
8374
8744
  case "arguments":
8745
+ return;
8375
8746
  case "object":
8747
+ if (Object.hasOwn(record2, "sandboxNullPrototype") && record2.sandboxNullPrototype !== true)
8748
+ fail("invalidValue", `${path}.sandboxNullPrototype`, "invalid object prototype");
8749
+ return;
8376
8750
  case "map":
8377
8751
  case "set":
8378
8752
  case "collection-iterator":
@@ -8442,6 +8816,8 @@ function validateHeapValue(value, path, state, heap) {
8442
8816
  }
8443
8817
  if (record2.kind === "set") requireArray(record2.values, `${path}.values`, state);
8444
8818
  if (record2.kind === "regexp-iterator") {
8819
+ if ((record2.global !== void 0 || record2.unicode !== void 0) && (typeof record2.global !== "boolean" || typeof record2.unicode !== "boolean"))
8820
+ fail("invalidValue", path, "invalid RegExp iterator modes");
8445
8821
  if (typeof record2.exhausted !== "boolean") fail("invalidValue", `${path}.exhausted`, "invalid iterator exhaustion");
8446
8822
  if (!Object.hasOwn(record2, "matcher") || !Object.hasOwn(record2, "input")) fail("invalidValue", path, "missing RegExp iterator state");
8447
8823
  requireRecord(record2.entries, `${path}.entries`);
@@ -9290,15 +9666,17 @@ async function withRunResources(signal, execute) {
9290
9666
 
9291
9667
  // packages/safe-js/src/interp/regex/engine.ts
9292
9668
  function matchRegex(pattern, input, lastIndex = 0) {
9293
- const startIndex = pattern.flags.global ? normalizeLastIndex(lastIndex) : 0;
9669
+ const startIndex = pattern.flags.global || pattern.flags.sticky ? normalizeLastIndex(lastIndex) : 0;
9294
9670
  return matchRegexFrom(pattern, input, startIndex);
9295
9671
  }
9296
9672
  function matchRegexFrom(pattern, input, startIndex) {
9297
9673
  if (startIndex > input.length) {
9298
9674
  return null;
9299
9675
  }
9300
- for (let attempt = startIndex; attempt <= input.length; attempt += 1) {
9301
- const context = { input, flags: pattern.flags, steps: 0 };
9676
+ const unicode = pattern.flags.unicode || pattern.flags.unicodeSets;
9677
+ if (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--;
9678
+ for (let attempt = startIndex; attempt <= input.length; attempt = advanceStringIndex(input, attempt, unicode)) {
9679
+ const context = { input, flags: pattern.flags, groups: pattern.groups, direction: 1, work: { steps: 0 } };
9302
9680
  charge(context);
9303
9681
  const initialState = {
9304
9682
  position: attempt,
@@ -9306,25 +9684,59 @@ function matchRegexFrom(pattern, input, startIndex) {
9306
9684
  };
9307
9685
  const result = matchNode(pattern.body, initialState, context).next();
9308
9686
  if (!result.done) {
9309
- return toRegexMatch(input, attempt, result.value);
9687
+ return toRegexMatch(input, attempt, result.value, pattern.flags.hasIndices, pattern.groups);
9310
9688
  }
9689
+ if (pattern.flags.sticky) break;
9311
9690
  }
9312
9691
  return null;
9313
9692
  }
9314
9693
  function* matchNode(node, state, context) {
9315
9694
  charge(context);
9695
+ const unicode = context.flags.unicode || context.flags.unicodeSets;
9696
+ const character = readCharacter(context.input, state.position, context.direction, unicode);
9316
9697
  switch (node.type) {
9317
9698
  case "empty":
9318
9699
  yield state;
9319
9700
  return;
9320
9701
  case "literal":
9321
- if (charactersEqual(context.input[state.position], node.value, context.flags.ignoreCase)) {
9322
- yield { ...state, position: state.position + 1 };
9702
+ if (charactersEqual(character, node.value, context.flags.ignoreCase, unicode)) {
9703
+ yield { ...state, position: state.position + context.direction * character.length };
9704
+ }
9705
+ return;
9706
+ case "backreference":
9707
+ case "namedBackreference": {
9708
+ let capture;
9709
+ if (node.type === "backreference") capture = state.captures[node.index - 1];
9710
+ else {
9711
+ for (const index of context.groups?.[node.name] ?? []) {
9712
+ charge(context);
9713
+ if (state.captures[index - 1] !== void 0) {
9714
+ capture = state.captures[index - 1];
9715
+ break;
9716
+ }
9717
+ }
9718
+ }
9719
+ if (capture === void 0) {
9720
+ yield state;
9721
+ return;
9722
+ }
9723
+ let position = state.position;
9724
+ let reference = context.direction === 1 ? capture.start : capture.end;
9725
+ const end = context.direction === 1 ? capture.end : capture.start;
9726
+ while (reference !== end) {
9727
+ charge(context);
9728
+ const expected = readCharacter(context.input, reference, context.direction, unicode);
9729
+ const actual = readCharacter(context.input, position, context.direction, unicode);
9730
+ if (!charactersEqual(actual, expected, context.flags.ignoreCase, unicode)) return;
9731
+ reference += context.direction * expected.length;
9732
+ position += context.direction * actual.length;
9323
9733
  }
9734
+ yield { ...state, position };
9324
9735
  return;
9736
+ }
9325
9737
  case "dot":
9326
- if (state.position < context.input.length && (context.flags.dotAll || !isLineTerminator(context.input[state.position]))) {
9327
- yield { ...state, position: state.position + 1 };
9738
+ if (character !== void 0 && (context.flags.dotAll || !isLineTerminator(character))) {
9739
+ yield { ...state, position: state.position + context.direction * character.length };
9328
9740
  }
9329
9741
  return;
9330
9742
  case "anchor":
@@ -9333,17 +9745,24 @@ function* matchNode(node, state, context) {
9333
9745
  }
9334
9746
  return;
9335
9747
  case "wordBoundary": {
9336
- const previousWord = state.position > 0 && isWordCharacter(context.input[state.position - 1]);
9337
- const nextWord = state.position < context.input.length && isWordCharacter(context.input[state.position]);
9748
+ const previousWord = state.position > 0 && matchesCharacterClassItem(context.input[state.position - 1], { type: "kind", kind: "word", negated: false }, context.flags.ignoreCase, unicode);
9749
+ const nextWord = state.position < context.input.length && matchesCharacterClassItem(context.input[state.position], { type: "kind", kind: "word", negated: false }, context.flags.ignoreCase, unicode);
9338
9750
  if (previousWord !== nextWord !== node.negated) {
9339
9751
  yield state;
9340
9752
  }
9341
9753
  return;
9342
9754
  }
9343
9755
  case "characterClass": {
9344
- const character = context.input[state.position];
9345
- if (character !== void 0 && matchesCharacterClass(character, node.items, node.negated, context.flags.ignoreCase)) {
9346
- yield { ...state, position: state.position + 1 };
9756
+ if (context.flags.unicodeSets) {
9757
+ const lengths = [...matchUnicodeSet(node, state.position, context)].sort((a, b) => b - a);
9758
+ for (const length of lengths) {
9759
+ charge(context);
9760
+ yield { ...state, position: state.position + context.direction * length };
9761
+ }
9762
+ return;
9763
+ }
9764
+ if (character !== void 0 && matchesCharacterClass(character, node.items, node.negated, context)) {
9765
+ yield { ...state, position: state.position + context.direction * character.length };
9347
9766
  }
9348
9767
  return;
9349
9768
  }
@@ -9355,14 +9774,30 @@ function* matchNode(node, state, context) {
9355
9774
  yield* matchNode(alternative, cloneState(state), context);
9356
9775
  }
9357
9776
  return;
9777
+ case "lookahead":
9778
+ case "lookbehind": {
9779
+ const result = matchNode(node.body, cloneState(state), {
9780
+ ...context,
9781
+ direction: node.type === "lookbehind" ? -1 : 1
9782
+ }).next();
9783
+ if (node.negated) {
9784
+ if (result.done) yield state;
9785
+ } else if (!result.done) {
9786
+ yield { position: state.position, captures: result.value.captures };
9787
+ }
9788
+ return;
9789
+ }
9358
9790
  case "group":
9359
- for (const result of matchNode(node.body, cloneState(state), context)) {
9791
+ for (const result of matchNode(node.body, cloneState(state), node.modifiers === void 0 ? context : {
9792
+ ...context,
9793
+ flags: { ...context.flags, ...node.modifiers }
9794
+ })) {
9360
9795
  if (!node.capturing || node.index === void 0) {
9361
9796
  yield result;
9362
9797
  continue;
9363
9798
  }
9364
9799
  const captures = result.captures.slice();
9365
- captures[node.index - 1] = { start: state.position, end: result.position };
9800
+ captures[node.index - 1] = { start: Math.min(state.position, result.position), end: Math.max(state.position, result.position) };
9366
9801
  yield { position: result.position, captures };
9367
9802
  }
9368
9803
  return;
@@ -9376,7 +9811,8 @@ function* matchSequence(elements, index, state, context) {
9376
9811
  yield state;
9377
9812
  return;
9378
9813
  }
9379
- for (const result of matchNode(elements[index], state, context)) {
9814
+ const element = elements[context.direction === 1 ? index : elements.length - 1 - index];
9815
+ for (const result of matchNode(element, state, context)) {
9380
9816
  yield* matchSequence(elements, index + 1, result, context);
9381
9817
  }
9382
9818
  }
@@ -9412,11 +9848,28 @@ function matchesAnchor(kind, position, context) {
9412
9848
  }
9413
9849
  return position === context.input.length || context.flags.multiline && position < context.input.length && isLineTerminator(context.input[position]);
9414
9850
  }
9415
- function matchesCharacterClass(character, items, negated, ignoreCase) {
9416
- const matched = items.some((item) => matchesCharacterClassItem(character, item, ignoreCase));
9851
+ function matchesCharacterClass(character, items, negated, context) {
9852
+ const matched = items.some((item) => {
9853
+ if (context.flags.unicode) charge(context);
9854
+ return matchesCharacterClassItem(character, item, context.flags.ignoreCase, context.flags.unicode);
9855
+ });
9417
9856
  return negated ? !matched : matched;
9418
9857
  }
9419
- function matchesCharacterClassItem(character, item, ignoreCase) {
9858
+ function matchesCharacterClassItem(character, item, ignoreCase, unicode, unicodeSets = false) {
9859
+ if (item.type === "strings" || item.type === "set") return false;
9860
+ if (unicode) {
9861
+ const hex = (value) => `\\u{${value.codePointAt(0).toString(16)}}`;
9862
+ let atom;
9863
+ if (item.type === "character") atom = hex(item.value);
9864
+ else if (item.type === "range") atom = `[${hex(item.from)}-${hex(item.to)}]`;
9865
+ else if (item.type === "property") atom = `\\${item.negated ? "P" : "p"}{${item.value}}`;
9866
+ else {
9867
+ const kind = item.kind === "digit" ? "d" : item.kind === "word" ? "w" : "s";
9868
+ atom = `\\${item.negated ? kind.toUpperCase() : kind}`;
9869
+ }
9870
+ return new RegExp(`^(?:${atom})$`, (ignoreCase ? "i" : "") + (unicodeSets ? "v" : "u")).test(character);
9871
+ }
9872
+ if (item.type === "property") return false;
9420
9873
  if (item.type === "character") {
9421
9874
  return charactersEqual(character, item.value, ignoreCase);
9422
9875
  }
@@ -9430,22 +9883,102 @@ function matchesCharacterClassItem(character, item, ignoreCase) {
9430
9883
  if (!ignoreCase) {
9431
9884
  return false;
9432
9885
  }
9433
- const foldedCandidate = foldCharacter(character, true).charCodeAt(0);
9434
- const foldedFrom = foldCharacter(item.from, true).charCodeAt(0);
9435
- const foldedTo = foldCharacter(item.to, true).charCodeAt(0);
9436
- return foldedCandidate >= foldedFrom && foldedCandidate <= foldedTo;
9886
+ return new RegExp(`^[\\u${from.toString(16).padStart(4, "0")}-\\u${to.toString(16).padStart(4, "0")}]$`, "i").test(character);
9437
9887
  }
9438
9888
  const matched = item.kind === "digit" ? isDigit(character) : item.kind === "word" ? isWordCharacter(character) : isSpaceCharacter(character);
9439
9889
  return item.negated ? !matched : matched;
9440
9890
  }
9441
- function toRegexMatch(input, start, state) {
9442
- return {
9891
+ function matchUnicodeSet(set, position, context) {
9892
+ charge(context);
9893
+ let lengths = /* @__PURE__ */ new Set();
9894
+ for (let index = 0; index < set.items.length; index++) {
9895
+ const next = matchUnicodeSetItem(set.items[index], position, context);
9896
+ if (index === 0) lengths = next;
9897
+ else if (set.operation === void 0) {
9898
+ for (const length of next) {
9899
+ charge(context);
9900
+ lengths.add(length);
9901
+ }
9902
+ } else {
9903
+ for (const length of lengths) {
9904
+ charge(context);
9905
+ if (set.operation === "intersection" ? !next.has(length) : next.has(length)) lengths.delete(length);
9906
+ }
9907
+ }
9908
+ }
9909
+ if (set.negated) {
9910
+ const character = readCharacter(context.input, position, context.direction, true);
9911
+ return new Set(character !== void 0 && !lengths.has(character.length) ? [character.length] : []);
9912
+ }
9913
+ return lengths;
9914
+ }
9915
+ function matchUnicodeSetItem(item, position, context) {
9916
+ charge(context);
9917
+ if (item.type === "set") return matchUnicodeSet(item.value, position, context);
9918
+ const lengths = /* @__PURE__ */ new Set();
9919
+ if (item.type === "strings") {
9920
+ for (const value of item.values) {
9921
+ charge(context);
9922
+ let reference = context.direction === 1 ? 0 : value.length;
9923
+ let cursor = position;
9924
+ const end = context.direction === 1 ? value.length : 0;
9925
+ while (reference !== end) {
9926
+ charge(context);
9927
+ const expected = readCharacter(value, reference, context.direction, true);
9928
+ const actual = readCharacter(context.input, cursor, context.direction, true);
9929
+ if (!charactersEqual(actual, expected, context.flags.ignoreCase, true)) break;
9930
+ reference += context.direction * expected.length;
9931
+ cursor += context.direction * actual.length;
9932
+ }
9933
+ if (reference === end) lengths.add(Math.abs(cursor - position));
9934
+ }
9935
+ return lengths;
9936
+ }
9937
+ if (item.type === "property" && item.strings) {
9938
+ const atom = `\\p{${item.value}}`;
9939
+ const flags = (context.flags.ignoreCase ? "i" : "") + "v";
9940
+ const matcher = new RegExp(context.direction === 1 ? atom : `(?<=(${atom}))`, flags + "y");
9941
+ matcher.lastIndex = position;
9942
+ const match = matcher.exec(context.input);
9943
+ if (match === null) return lengths;
9944
+ const longest = context.direction === 1 ? match[0] : match[1];
9945
+ const member = new RegExp(`^(?:${atom})$`, flags);
9946
+ let consumed = 0;
9947
+ while (consumed < longest.length) {
9948
+ charge(context);
9949
+ const cursor = context.direction === 1 ? consumed : longest.length - consumed;
9950
+ consumed += readCharacter(longest, cursor, context.direction, true).length;
9951
+ const candidate = context.direction === 1 ? longest.slice(0, consumed) : longest.slice(longest.length - consumed);
9952
+ if (member.test(candidate)) lengths.add(consumed);
9953
+ }
9954
+ return lengths;
9955
+ }
9956
+ const character = readCharacter(context.input, position, context.direction, true);
9957
+ if (character !== void 0 && matchesCharacterClassItem(character, item, context.flags.ignoreCase, true, true))
9958
+ lengths.add(character.length);
9959
+ return lengths;
9960
+ }
9961
+ function toRegexMatch(input, start, state, hasIndices, groups) {
9962
+ const match = {
9443
9963
  index: start,
9444
9964
  text: input.slice(start, state.position),
9445
9965
  captures: state.captures.map(
9446
9966
  (capture) => capture === void 0 ? void 0 : input.slice(capture.start, capture.end)
9447
9967
  )
9448
9968
  };
9969
+ if (hasIndices) {
9970
+ match.indices = [[start, state.position], ...state.captures.map((capture) => capture === void 0 ? void 0 : [capture.start, capture.end])];
9971
+ }
9972
+ if (groups !== void 0) {
9973
+ match.groups = /* @__PURE__ */ Object.create(null);
9974
+ if (hasIndices) match.indicesGroups = /* @__PURE__ */ Object.create(null);
9975
+ for (const [name, indices] of Object.entries(groups)) {
9976
+ const index = indices.find((index2) => state.captures[index2 - 1] !== void 0);
9977
+ match.groups[name] = index === void 0 ? void 0 : match.captures[index - 1];
9978
+ if (match.indicesGroups !== void 0) match.indicesGroups[name] = index === void 0 ? void 0 : match.indices[index];
9979
+ }
9980
+ }
9981
+ return match;
9449
9982
  }
9450
9983
  function cloneState(state) {
9451
9984
  return { position: state.position, captures: state.captures.slice() };
@@ -9475,13 +10008,13 @@ function clearNodeCaptures(node, captures) {
9475
10008
  }
9476
10009
  return;
9477
10010
  }
9478
- if (node.type === "quantifier") {
10011
+ if (node.type === "quantifier" || node.type === "lookahead" || node.type === "lookbehind") {
9479
10012
  clearNodeCaptures(node.body, captures);
9480
10013
  }
9481
10014
  }
9482
10015
  function charge(context) {
9483
- context.steps += 1;
9484
- allocateRegexSteps(context.steps);
10016
+ context.work.steps += 1;
10017
+ allocateRegexSteps(context.work.steps);
9485
10018
  }
9486
10019
  function normalizeLastIndex(lastIndex) {
9487
10020
  if (Number.isNaN(lastIndex) || lastIndex <= 0) {
@@ -9489,9 +10022,20 @@ function normalizeLastIndex(lastIndex) {
9489
10022
  }
9490
10023
  return Math.min(Math.floor(lastIndex), Number.MAX_SAFE_INTEGER);
9491
10024
  }
9492
- function charactersEqual(left, right, ignoreCase) {
10025
+ function charactersEqual(left, right, ignoreCase, unicode = false) {
10026
+ if (unicode && ignoreCase && left !== void 0)
10027
+ return new RegExp(`^\\u{${right.codePointAt(0).toString(16)}}$`, "iu").test(left);
9493
10028
  return left !== void 0 && foldCharacter(left, ignoreCase) === foldCharacter(right, ignoreCase);
9494
10029
  }
10030
+ function advanceStringIndex(input, index, unicode) {
10031
+ return index + (unicode && (input.codePointAt(index) ?? 0) > 65535 ? 2 : 1);
10032
+ }
10033
+ function readCharacter(input, position, direction, unicode) {
10034
+ let index = direction === 1 ? position : position - 1;
10035
+ if (index < 0 || index >= input.length) return void 0;
10036
+ 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--;
10037
+ return unicode ? String.fromCodePoint(input.codePointAt(index)) : input[index];
10038
+ }
9495
10039
  function foldCharacter(character, ignoreCase) {
9496
10040
  if (!ignoreCase) {
9497
10041
  return character;
@@ -10067,7 +10611,7 @@ function callReplaceLikeMethod(value, methodName, args, budget, callClosure, use
10067
10611
  );
10068
10612
  }
10069
10613
  async function replaceRegex(value, regex, replacement, budget, callClosure, context) {
10070
- if (hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget))
10614
+ if (getSandboxRegexPattern(regex).groups !== void 0 || hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget))
10071
10615
  return regexReplace(value, regex, replacement, budget, callClosure, context);
10072
10616
  if (regex.flags.includes("g")) regex.lastIndex = 0;
10073
10617
  const cursor = regex.lastIndex;
@@ -10117,7 +10661,7 @@ async function regexReplace(input, regex, replacementInput, budget, callClosure,
10117
10661
  input = void 0;
10118
10662
  replacement = isSandboxClosure(replacementInput) ? replacementInput : await sandboxString(replacementInput, budget, context);
10119
10663
  replacementInput = void 0;
10120
- if (isSandboxRegex(regex) && !hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget)) {
10664
+ if (isSandboxRegex(regex) && getSandboxRegexPattern(regex).groups === void 0 && !hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget)) {
10121
10665
  release();
10122
10666
  return await replaceRegex(value, regex, replacement, budget, callClosure, context);
10123
10667
  }
@@ -10332,7 +10876,7 @@ function splitNormalized(value, separator, limit, budget, parent) {
10332
10876
  while (result2.length < limit) {
10333
10877
  const match = executeRegex(splitter, value, Number(splitter.lastIndex));
10334
10878
  if (match === null) break;
10335
- if (match.text.length === 0) splitter.lastIndex = match.index + 1;
10879
+ if (match.text.length === 0) splitter.lastIndex = advanceStringIndex(value, match.index, splitter.flags.includes("u") || splitter.flags.includes("v"));
10336
10880
  endedWithZeroWidthMatch = match.text.length === 0 && match.index === value.length;
10337
10881
  if (match.text.length === 0 && (match.index === copiedThrough || match.index === value.length))
10338
10882
  continue;
@@ -10491,7 +11035,7 @@ function callMatchLikeMethod(value, methodName, args, compilation, lastIndex) {
10491
11035
  return match?.index ?? -1;
10492
11036
  }
10493
11037
  if (methodName === "match" && !regex.flags.includes("g"))
10494
- return toMatchArray(executeRegex(regex, value, lastIndex ?? Number(regex.lastIndex)), value);
11038
+ return toMatchArray(executeRegex(regex, value, lastIndex ?? Number(regex.lastIndex)), value, compilation.owner?.budget);
10495
11039
  if (methodName === "match") regex.lastIndex = 0;
10496
11040
  const matcher = methodName === "matchAll" ? createSandboxRegex(regex.source, regex.flags, normalizeLastIndex(lastIndex ?? Number(regex.lastIndex)), compilation) : regex;
10497
11041
  if (methodName === "matchAll")
@@ -10507,7 +11051,7 @@ function collectRegexMatches(regex, value, all, budget, lastIndex = 0) {
10507
11051
  budget?.allocateArrayLength(matches.length + 1);
10508
11052
  matches.push(match);
10509
11053
  lastIndex = match.index + match.text.length;
10510
- if (all && match.text.length === 0) regex.lastIndex = ++lastIndex;
11054
+ if (all && match.text.length === 0) regex.lastIndex = lastIndex = advanceStringIndex(value, lastIndex, regex.flags.includes("u") || regex.flags.includes("v"));
10511
11055
  } while (all);
10512
11056
  return matches;
10513
11057
  }
@@ -16656,7 +17200,7 @@ async function callRegexMethod(target, methodName, args, budget, context) {
16656
17200
  cursor = target.lastIndex;
16657
17201
  const lastIndex = await sandboxNumber(cursor, budget, context);
16658
17202
  const match = executeRegex(target, input, lastIndex);
16659
- return toMatchArray(match, input);
17203
+ return toMatchArray(match, input, budget);
16660
17204
  } finally {
16661
17205
  budget.setRetainedValues(retained, void 0);
16662
17206
  }
@@ -16707,17 +17251,26 @@ async function regexToString(target, budget, context) {
16707
17251
  function executeRegex(target, input, lastIndex) {
16708
17252
  const pattern = getSandboxRegexPattern(target);
16709
17253
  const match = matchRegex(pattern, input, lastIndex);
16710
- if (pattern.flags.global) {
17254
+ if (pattern.flags.global || pattern.flags.sticky) {
16711
17255
  target.lastIndex = match === null ? 0 : match.index + match.text.length;
16712
17256
  }
16713
17257
  return match;
16714
17258
  }
16715
- function toMatchArray(match, input) {
17259
+ function toMatchArray(match, input, budget) {
16716
17260
  if (match === null) {
16717
17261
  return null;
16718
17262
  }
17263
+ budget?.allocateArrayLength(match.captures.length + 1);
16719
17264
  const result = [match.text, ...match.captures];
16720
- Object.assign(result, { index: match.index, input, groups: void 0 });
17265
+ if (match.groups !== void 0) setSandboxPrototype(match.groups, null, budget);
17266
+ if (match.indicesGroups !== void 0) setSandboxPrototype(match.indicesGroups, null, budget);
17267
+ Object.assign(result, { index: match.index, input, groups: match.groups });
17268
+ if (match.indices !== void 0) {
17269
+ budget?.allocateArrayLength(match.indices.length);
17270
+ budget?.allocateArrayLength(2);
17271
+ Object.assign(match.indices, { groups: match.indicesGroups });
17272
+ Object.assign(result, { indices: match.indices });
17273
+ }
16721
17274
  return result;
16722
17275
  }
16723
17276
 
@@ -16727,9 +17280,10 @@ function nextRegExpIterator(iterator, budget) {
16727
17280
  budget?.visitNode();
16728
17281
  if (state.exhausted) return { value: void 0, done: true };
16729
17282
  const matcher = state.matcher;
17283
+ if (!isSandboxRegex(matcher)) throw new TypeError("Custom RegExp iterator execution requires a sandbox context.");
16730
17284
  const input = state.input;
16731
17285
  const match = executeRegex(matcher, input, Number(matcher.lastIndex));
16732
- if (match === null || !matcher.flags.includes("g")) {
17286
+ if (match === null || !(state.global ?? matcher.flags.includes("g"))) {
16733
17287
  state.exhausted = true;
16734
17288
  state.matcher = void 0;
16735
17289
  state.input = void 0;
@@ -16737,12 +17291,52 @@ function nextRegExpIterator(iterator, budget) {
16737
17291
  if (match === null) return { value: void 0, done: true };
16738
17292
  if (!state.exhausted && match.text.length === 0) {
16739
17293
  const index = Number(matcher.lastIndex);
16740
- const unicode = matcher.flags.includes("u") || matcher.flags.includes("v");
17294
+ const unicode = state.unicode ?? (matcher.flags.includes("u") || matcher.flags.includes("v"));
16741
17295
  const codePoint = input.codePointAt(index);
16742
17296
  matcher.lastIndex = index + (unicode && codePoint !== void 0 && codePoint > 65535 ? 2 : 1);
16743
17297
  }
16744
- budget?.allocateArrayLength(match.captures.length + 1);
16745
- return { value: toMatchArray(match, input), done: false };
17298
+ return { value: toMatchArray(match, input, budget), done: false };
17299
+ }
17300
+ async function nextObservableRegExpIterator(iterator, budget, context) {
17301
+ const state = regexpIteratorState(iterator);
17302
+ if (state.exhausted) return { value: void 0, done: true };
17303
+ const matcher = state.matcher;
17304
+ if (isSandboxRegex(matcher) && !hasRegexPropertyOverride(matcher, ["exec"], budget) && (matcher.lastIndex === null || typeof matcher.lastIndex !== "object"))
17305
+ return nextRegExpIterator(iterator, budget);
17306
+ const input = state.input;
17307
+ let result;
17308
+ let field;
17309
+ const release = retainValues(budget, () => [iterator, matcher, input, result, field]);
17310
+ const read = (value, key) => {
17311
+ if (context.getProperty !== void 0) return context.getProperty(value, key);
17312
+ const descriptor = getSandboxPropertyDescriptor(value, key, budget);
17313
+ return descriptor === void 0 ? void 0 : readPropertyDescriptor(descriptor, value, context);
17314
+ };
17315
+ try {
17316
+ budget.visitNode();
17317
+ result = await regexExec(matcher, input, budget, context);
17318
+ const global = state.global ?? (isSandboxRegex(matcher) && matcher.flags.includes("g"));
17319
+ if (result === null || !global) {
17320
+ state.exhausted = true;
17321
+ state.matcher = void 0;
17322
+ state.input = void 0;
17323
+ return result === null ? { value: void 0, done: true } : { value: result, done: false };
17324
+ }
17325
+ field = await read(result, "0");
17326
+ const text = await sandboxString(field, budget, context);
17327
+ field = void 0;
17328
+ if (text.length === 0) {
17329
+ field = await read(matcher, "lastIndex");
17330
+ const index = normalizeLastIndex(await sandboxNumber(field, budget, context));
17331
+ field = void 0;
17332
+ const unicode = state.unicode ?? (isSandboxRegex(matcher) && (matcher.flags.includes("u") || matcher.flags.includes("v")));
17333
+ const point = input.codePointAt(index);
17334
+ await setSandboxProperty(matcher, "lastIndex", index + (unicode && point !== void 0 && point > 65535 ? 2 : 1), budget, true, context);
17335
+ }
17336
+ return { value: result, done: false };
17337
+ } finally {
17338
+ release();
17339
+ }
16746
17340
  }
16747
17341
  function getRegExpIteratorMember(property, budget) {
16748
17342
  if (property === Symbol.toStringTag) return "RegExp String Iterator";
@@ -16759,7 +17353,7 @@ function getRegExpIteratorMember(property, budget) {
16759
17353
  const receiver = context?.thisValue;
16760
17354
  if (!isSandboxRegExpIterator(receiver))
16761
17355
  throw new TypeError("RegExp string iterator next requires a matching receiver.");
16762
- return nextRegExpIterator(receiver, budget);
17356
+ return context === void 0 ? nextRegExpIterator(receiver, budget) : nextObservableRegExpIterator(receiver, budget, context);
16763
17357
  }
16764
17358
  });
16765
17359
  }
@@ -16947,8 +17541,11 @@ function getSandboxIterator(value, budget, context) {
16947
17541
  }
16948
17542
  if (isSandboxCollectionIterator(value))
16949
17543
  return { next: () => nextCollectionIterator(value, budget), snapshotIndex: () => 0 };
16950
- if (isSandboxRegExpIterator(value))
17544
+ if (isSandboxRegExpIterator(value)) {
17545
+ if (context !== void 0 && budget !== void 0)
17546
+ return { asynchronous: true, next: () => nextObservableRegExpIterator(value, budget, context), snapshotIndex: () => 0 };
16951
17547
  return { next: () => nextRegExpIterator(value, budget), snapshotIndex: () => 0 };
17548
+ }
16952
17549
  if (isGuestHostObject(value)) return getHostObjectIterator(value);
16953
17550
  if (isFloat32Array(value)) {
16954
17551
  return syncIterator(Float32Array.prototype.values.call(value));
@@ -17755,7 +18352,16 @@ function encodeReplayData(value, options = {}) {
17755
18352
  }
17756
18353
  let symbolIndex = 0;
17757
18354
  const symbolEntries = serializeSymbolProperties(entry, (value2) => encode(value2, depth + 1, [...path, { symbol: Math.floor(symbolIndex++ / 2) }]));
17758
- nodes[id] = { kind: "regexp-iterator", matcher: child(snapshot.matcher, "<matcher>"), input: child(snapshot.input, "<input>"), exhausted: snapshot.exhausted, properties, extensible: Object.isExtensible(entry), symbolEntries };
18355
+ nodes[id] = {
18356
+ kind: "regexp-iterator",
18357
+ matcher: child(snapshot.matcher, "<matcher>"),
18358
+ input: child(snapshot.input, "<input>"),
18359
+ exhausted: snapshot.exhausted,
18360
+ properties,
18361
+ extensible: Object.isExtensible(entry),
18362
+ symbolEntries,
18363
+ ...snapshot.global === void 0 ? {} : { global: snapshot.global, unicode: snapshot.unicode }
18364
+ };
17759
18365
  } else if (isSandboxCollectionIterator(entry)) {
17760
18366
  const snapshot = snapshotCollectionIterator(entry);
17761
18367
  const properties = /* @__PURE__ */ Object.create(null);
@@ -17809,6 +18415,7 @@ function encodeReplayData(value, options = {}) {
17809
18415
  nodes[id] = {
17810
18416
  kind: Array.isArray(entry) ? "array" : "object",
17811
18417
  nullPrototype: prototype === null,
18418
+ ...hasNullObjectPrototype(entry) ? { sandboxNullPrototype: true } : {},
17812
18419
  ...errorType === void 0 ? {} : { errorType },
17813
18420
  extensible: Object.isExtensible(entry),
17814
18421
  properties,
@@ -17953,9 +18560,15 @@ function decodeReplayData(input, options = {}, parent) {
17953
18560
  restored.set(id, result3);
17954
18561
  const matcher = child(own(node, "matcher"));
17955
18562
  const input2 = child(own(node, "input"));
17956
- if (matcher !== void 0 && !isSandboxRegex(matcher)) throw new TypeError("Invalid replay RegExp iterator matcher.");
18563
+ if ((node.global !== void 0 || node.unicode !== void 0) && (typeof node.global !== "boolean" || typeof node.unicode !== "boolean")) throw new TypeError("Invalid replay RegExp iterator modes.");
18564
+ if (matcher !== void 0 && (node.global === void 0 ? !isSandboxRegex(matcher) : matcher === null || typeof matcher !== "object")) throw new TypeError("Invalid replay RegExp iterator matcher.");
17957
18565
  if (input2 !== void 0 && typeof input2 !== "string") throw new TypeError("Invalid replay RegExp iterator input.");
17958
- restoreSandboxRegExpIterator({ matcher, input: input2, exhausted }, result3);
18566
+ restoreSandboxRegExpIterator({
18567
+ matcher,
18568
+ input: input2,
18569
+ exhausted,
18570
+ ...node.global === void 0 ? {} : { global: node.global, unicode: node.unicode }
18571
+ }, result3);
17959
18572
  defineProperties(result3, record(own(node, "properties")), child, node.symbolEntries);
17960
18573
  if (!node.extensible) Object.preventExtensions(result3);
17961
18574
  return result3;
@@ -18025,6 +18638,10 @@ function decodeReplayData(input, options = {}, parent) {
18025
18638
  throw new TypeError("Invalid replay object metadata.");
18026
18639
  }
18027
18640
  const result2 = kind === "array" ? [] : Object.create(node.nullPrototype ? null : Object.prototype);
18641
+ if (Object.hasOwn(node, "sandboxNullPrototype")) {
18642
+ if (kind !== "object" || node.sandboxNullPrototype !== true) throw new TypeError("Invalid replay object prototype.");
18643
+ setSandboxPrototype(result2, null);
18644
+ }
18028
18645
  if (Object.hasOwn(node, "errorType")) {
18029
18646
  sandboxErrorTypes.set(result2, node.errorType);
18030
18647
  }
@@ -21472,7 +22089,7 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
21472
22089
  const copy = restoreSandboxRegExpIterator({ matcher: void 0, input: void 0, exhausted: true });
21473
22090
  state.seen.set(value, copy);
21474
22091
  const matcher = copyToSandbox(snapshot.matcher, state, `${path}.<matcher>`, true, depth + 1);
21475
- if (matcher !== void 0 && !isSandboxRegex(matcher)) throw new TypeError("Invalid RegExp iterator matcher.");
22092
+ if (matcher !== void 0 && (snapshot.global === void 0 ? !isSandboxRegex(matcher) : matcher === null || typeof matcher !== "object")) throw new TypeError("Invalid RegExp iterator matcher.");
21476
22093
  restoreSandboxRegExpIterator({ ...snapshot, matcher }, copy);
21477
22094
  for (const entry of getEnumerableObjectEntries(value, path)) {
21478
22095
  defineOwnDataProperty(copy, entry.key, copyToSandbox(entry.value, state, joinPath2(path, entry.key), true, depth + 1));
@@ -21660,6 +22277,7 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
21660
22277
  const copy = createPlainObject(
21661
22278
  !cloneSandboxCollections || Object.getPrototypeOf(value) === null
21662
22279
  );
22280
+ if (!state.structuredClone && hasNullObjectPrototype(value)) setSandboxPrototype(copy, null);
21663
22281
  state.seen.set(value, copy);
21664
22282
  const errorType = sandboxErrorTypes.get(value);
21665
22283
  if (errorType !== void 0) sandboxErrorTypes.set(copy, errorType);
@@ -21871,7 +22489,7 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
21871
22489
  if (existing !== void 0) {
21872
22490
  return existing;
21873
22491
  }
21874
- const copy = createPlainObject(Object.getPrototypeOf(value) === null);
22492
+ const copy = createPlainObject(hasNullObjectPrototype(value) || Object.getPrototypeOf(value) === null);
21875
22493
  state.seen.set(value, copy);
21876
22494
  for (const entry of getEnumerableObjectEntries(value, path)) {
21877
22495
  defineOwnDataProperty(
@@ -34388,6 +35006,90 @@ function defineDataProperty2(target, key, value) {
34388
35006
  });
34389
35007
  }
34390
35008
 
35009
+ // packages/safe-js/src/interp/methods/regex-split.ts
35010
+ async function regexSplit(target, input, limit, constructor, budget, context) {
35011
+ if (target === null || typeof target !== "object") throw new TypeError("RegExp split requires an object receiver.");
35012
+ let string;
35013
+ let species;
35014
+ let field;
35015
+ let flags;
35016
+ let matcher;
35017
+ let match;
35018
+ const result = [];
35019
+ const release = retainValues(budget, () => [target, input, limit, string, species, field, flags, matcher, match, result]);
35020
+ const read = (value, key) => {
35021
+ if (context?.getProperty !== void 0) return context.getProperty(value, key);
35022
+ const descriptor = getSandboxPropertyDescriptor(value, key, budget);
35023
+ return descriptor === void 0 ? void 0 : readPropertyDescriptor(descriptor, value, context);
35024
+ };
35025
+ const append = (value) => {
35026
+ budget.allocateArrayLength(result.length + 1);
35027
+ result.push(value);
35028
+ };
35029
+ try {
35030
+ string = await sandboxString(input, budget, context);
35031
+ input = void 0;
35032
+ field = await read(target, "constructor");
35033
+ species = constructor;
35034
+ if (field !== void 0) {
35035
+ if (field === null || typeof field !== "object") throw new TypeError("Invalid RegExp species constructor.");
35036
+ species = await read(field, Symbol.species);
35037
+ if (species === null || species === void 0) species = constructor;
35038
+ }
35039
+ field = void 0;
35040
+ if (!isSandboxClosure(species) || species.construct === void 0) throw new TypeError("RegExp species must be a constructor.");
35041
+ field = await read(target, "flags");
35042
+ flags = await sandboxString(field, budget, context);
35043
+ field = void 0;
35044
+ const unicode = flags.includes("u") || flags.includes("v");
35045
+ if (!flags.includes("y")) flags = budget.allocateString(flags + "y");
35046
+ matcher = await invokeBuiltinClosure(species, [target, flags], budget, context, void 0, true);
35047
+ const maximum = limit === void 0 ? 2 ** 32 - 1 : await sandboxNumber(limit, budget, context) >>> 0;
35048
+ limit = void 0;
35049
+ if (maximum === 0) return result;
35050
+ if (string.length === 0) {
35051
+ match = await regexExec(matcher, string, budget, context);
35052
+ if (match === null) append(string);
35053
+ return result;
35054
+ }
35055
+ let end = 0;
35056
+ let index = 0;
35057
+ while (index < string.length) {
35058
+ budget.visitNode();
35059
+ match = void 0;
35060
+ await setSandboxProperty(matcher, "lastIndex", index, budget, true, context);
35061
+ match = await regexExec(matcher, string, budget, context);
35062
+ if (match !== null) {
35063
+ field = await read(matcher, "lastIndex");
35064
+ const next = Math.min(normalizeLastIndex(await sandboxNumber(field, budget, context)), string.length);
35065
+ field = void 0;
35066
+ if (next !== end) {
35067
+ append(budget.allocateString(string.slice(end, index)));
35068
+ if (result.length === maximum) return result;
35069
+ end = next;
35070
+ field = await read(match, "length");
35071
+ const length = normalizeLastIndex(await sandboxNumber(field, budget, context));
35072
+ field = void 0;
35073
+ for (let capture = 1; capture < length; capture++) {
35074
+ budget.visitNode();
35075
+ field = await read(match, String(capture));
35076
+ append(field);
35077
+ field = void 0;
35078
+ if (result.length === maximum) return result;
35079
+ }
35080
+ index = end;
35081
+ continue;
35082
+ }
35083
+ }
35084
+ index += unicode && (string.codePointAt(index) ?? 0) > 65535 ? 2 : 1;
35085
+ }
35086
+ append(budget.allocateString(string.slice(end)));
35087
+ return result;
35088
+ } finally {
35089
+ release();
35090
+ }
35091
+ }
35092
+
34391
35093
  // packages/safe-js/src/interp/globals/regex.ts
34392
35094
  function createRegexGlobals(options) {
34393
35095
  const invoke = (construct) => async (args, context) => {
@@ -34452,7 +35154,55 @@ function createRegexGlobals(options) {
34452
35154
  }
34453
35155
  };
34454
35156
  const constructor = createSandboxClosure({ guest: true, sandbox: true, name: "RegExp", length: 2, call: invoke(false), construct: invoke(true) });
35157
+ Object.defineProperty(materializeFunctionProperties(constructor), "escape", {
35158
+ value: createSandboxClosure({
35159
+ guest: true,
35160
+ sandbox: true,
35161
+ name: "escape",
35162
+ length: 1,
35163
+ call: (args) => {
35164
+ const input = args[0];
35165
+ if (typeof input !== "string") throw new TypeError("RegExp.escape requires a string.");
35166
+ const allocation = {};
35167
+ let escaped = "";
35168
+ try {
35169
+ for (const character of input) {
35170
+ options.budget.visitNode();
35171
+ const point = character.codePointAt(0);
35172
+ let part = character;
35173
+ if (escaped.length === 0 && (point >= 48 && point <= 57 || point >= 65 && point <= 90 || point >= 97 && point <= 122)) {
35174
+ part = `\\x${point.toString(16)}`;
35175
+ } else if ("^$\\.*+?()[]{}|/".includes(character)) {
35176
+ part = "\\" + character;
35177
+ } else {
35178
+ const control = "\f\n\r \v".indexOf(character);
35179
+ if (control !== -1) part = "\\" + "fnrtv"[control];
35180
+ else if (",-=<>#&!%:;@~'`\"".includes(character) || character.trim() === "" || point >= 55296 && point <= 57343) {
35181
+ part = point <= 255 ? `\\x${point.toString(16).padStart(2, "0")}` : `\\u${point.toString(16).padStart(4, "0")}`;
35182
+ }
35183
+ }
35184
+ options.budget.setRetainedDataUsage(allocation, escaped.length + part.length);
35185
+ escaped = options.budget.allocateString(escaped + part);
35186
+ }
35187
+ return escaped;
35188
+ } finally {
35189
+ options.budget.setRetainedDataUsage(allocation, 0);
35190
+ }
35191
+ }
35192
+ }),
35193
+ writable: true,
35194
+ configurable: true
35195
+ });
34455
35196
  const prototype = /* @__PURE__ */ Object.create(null);
35197
+ Object.defineProperty(materializeFunctionProperties(constructor), Symbol.species, {
35198
+ get: accessorAdapter(createSandboxClosure({
35199
+ sandbox: true,
35200
+ name: "get [Symbol.species]",
35201
+ length: 0,
35202
+ call: (_args, context) => context?.thisValue
35203
+ }), "get"),
35204
+ configurable: true
35205
+ });
34456
35206
  Object.defineProperty(materializeFunctionProperties(constructor), "prototype", { value: prototype, writable: false });
34457
35207
  Object.defineProperty(prototype, "constructor", { value: constructor, writable: true, configurable: true });
34458
35208
  Object.defineProperty(prototype, Symbol.search, {
@@ -34495,6 +35245,73 @@ function createRegexGlobals(options) {
34495
35245
  writable: true,
34496
35246
  configurable: true
34497
35247
  });
35248
+ Object.defineProperty(prototype, Symbol.matchAll, {
35249
+ value: createSandboxClosure({
35250
+ guest: true,
35251
+ sandbox: true,
35252
+ name: "[Symbol.matchAll]",
35253
+ length: 1,
35254
+ call: async (args, context) => {
35255
+ const target = context?.thisValue;
35256
+ if (target === null || typeof target !== "object") throw new TypeError("RegExp matchAll requires an object receiver.");
35257
+ let input = args[0];
35258
+ let string;
35259
+ let species;
35260
+ let field;
35261
+ let flags;
35262
+ let matcher;
35263
+ const release = retainValues(options.budget, () => [target, input, string, species, field, flags, matcher]);
35264
+ const read = (value, key) => {
35265
+ if (context?.getProperty !== void 0) return context.getProperty(value, key);
35266
+ const descriptor = getSandboxPropertyDescriptor(value, key, options.budget);
35267
+ return descriptor === void 0 ? void 0 : readPropertyDescriptor(descriptor, value, context);
35268
+ };
35269
+ try {
35270
+ string = await sandboxString(input, options.budget, context);
35271
+ input = void 0;
35272
+ field = await read(target, "constructor");
35273
+ species = constructor;
35274
+ if (field !== void 0) {
35275
+ if (field === null || typeof field !== "object") throw new TypeError("Invalid RegExp species constructor.");
35276
+ species = await read(field, Symbol.species);
35277
+ if (species === null || species === void 0) species = constructor;
35278
+ }
35279
+ field = void 0;
35280
+ if (!isSandboxClosure(species) || species.construct === void 0) throw new TypeError("RegExp species must be a constructor.");
35281
+ field = await read(target, "flags");
35282
+ flags = await sandboxString(field, options.budget, context);
35283
+ field = void 0;
35284
+ matcher = await invokeBuiltinClosure(species, [target, flags], options.budget, context, void 0, true);
35285
+ field = await read(target, "lastIndex");
35286
+ const cursor = normalizeLastIndex(await sandboxNumber(field, options.budget, context));
35287
+ field = void 0;
35288
+ await setSandboxProperty(matcher, "lastIndex", cursor, options.budget, true, context);
35289
+ return restoreSandboxRegExpIterator({
35290
+ matcher,
35291
+ input: string,
35292
+ exhausted: false,
35293
+ global: flags.includes("g"),
35294
+ unicode: flags.includes("u") || flags.includes("v")
35295
+ });
35296
+ } finally {
35297
+ release();
35298
+ }
35299
+ }
35300
+ }),
35301
+ writable: true,
35302
+ configurable: true
35303
+ });
35304
+ Object.defineProperty(prototype, Symbol.split, {
35305
+ value: createSandboxClosure({
35306
+ guest: true,
35307
+ sandbox: true,
35308
+ name: "[Symbol.split]",
35309
+ length: 2,
35310
+ call: (args, context) => regexSplit(context?.thisValue, args[0], args[1], constructor, options.budget, context)
35311
+ }),
35312
+ writable: true,
35313
+ configurable: true
35314
+ });
34498
35315
  for (const name of ["exec", "test", "toString"]) {
34499
35316
  Object.defineProperty(prototype, name, {
34500
35317
  value: createSandboxClosure({
@@ -36839,4 +37656,4 @@ export {
36839
37656
  FileSnapshotBackend,
36840
37657
  run
36841
37658
  };
36842
- //# sourceMappingURL=chunk-YEZOXLY4.js.map
37659
+ //# sourceMappingURL=chunk-EOTY4PCT.js.map