poe-code 14.0.72 → 14.0.73

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 (33) hide show
  1. package/dist/index.js +850 -638
  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-VYUHL33E.js → chunk-I2Z37MKZ.js} +2 -2
  6. package/packages/safe-js/dist/chunks/{chunk-6UY4Z7WY.js → chunk-XKCTB5FR.js} +351 -158
  7. package/packages/safe-js/dist/chunks/chunk-XKCTB5FR.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/graph-depth.js +7 -0
  11. package/packages/safe-js/dist/index.js +2 -2
  12. package/packages/safe-js/dist/interp/globals/object-array.js +9 -7
  13. package/packages/safe-js/dist/interp/globals/object.js +2 -2
  14. package/packages/safe-js/dist/interp/interpreter.js +7 -7
  15. package/packages/safe-js/dist/interp/methods/regex.d.ts +1 -1
  16. package/packages/safe-js/dist/interp/methods/regex.js +6 -3
  17. package/packages/safe-js/dist/interp/methods/string.js +57 -18
  18. package/packages/safe-js/dist/interp/object-model.js +6 -3
  19. package/packages/safe-js/dist/interp/regexp-properties.d.ts +3 -0
  20. package/packages/safe-js/dist/interp/regexp-properties.js +7 -0
  21. package/packages/safe-js/dist/interp/values.d.ts +1 -0
  22. package/packages/safe-js/dist/interp/values.js +76 -2
  23. package/packages/safe-js/dist/snapshot/dump-format.js +27 -9
  24. package/packages/safe-js/dist/snapshot/regexp-properties.d.ts +18 -0
  25. package/packages/safe-js/dist/snapshot/regexp-properties.js +58 -0
  26. package/packages/safe-js/dist/snapshot/replay-data.d.ts +3 -2
  27. package/packages/safe-js/dist/snapshot/replay-data.js +4 -1
  28. package/packages/safe-js/dist/snapshot/restore.js +2 -0
  29. package/packages/safe-js/dist/snapshot/serialize.d.ts +3 -2
  30. package/packages/safe-js/dist/snapshot/serialize.js +23 -16
  31. package/packages/safe-js/dist/snapshot/validation.js +12 -1
  32. package/packages/safe-js/dist/chunks/chunk-6UY4Z7WY.js.map +0 -7
  33. /package/packages/safe-js/dist/chunks/{chunk-VYUHL33E.js.map → chunk-I2Z37MKZ.js.map} +0 -0
@@ -22,7 +22,7 @@ import {
22
22
  resolveFsConfig,
23
23
  splitFrontmatter,
24
24
  withBrokenPipeGuard
25
- } from "./chunks/chunk-VYUHL33E.js";
25
+ } from "./chunks/chunk-I2Z37MKZ.js";
26
26
  import {
27
27
  Budget,
28
28
  SandboxError,
@@ -34,7 +34,7 @@ import {
34
34
  replaceErrorStack,
35
35
  restore,
36
36
  run
37
- } from "./chunks/chunk-6UY4Z7WY.js";
37
+ } from "./chunks/chunk-XKCTB5FR.js";
38
38
  import "./chunks/chunk-ZWJ5BJUI.js";
39
39
  import "./chunks/chunk-3YTQEPZI.js";
40
40
  import "./chunks/chunk-4YIDX44G.js";
@@ -5,7 +5,7 @@ import {
5
5
  defineExtension,
6
6
  lint,
7
7
  run
8
- } from "./chunks/chunk-6UY4Z7WY.js";
8
+ } from "./chunks/chunk-XKCTB5FR.js";
9
9
  import "./chunks/chunk-4YIDX44G.js";
10
10
  export {
11
11
  Budget,
@@ -4,6 +4,7 @@ import { getSandboxArgumentEntries, isSandboxArguments } from "./interp/argument
4
4
  import { collectionIteratorState, isSandboxCollectionIterator } from "./interp/collection-iterator.js";
5
5
  import { isSandboxMap, isSandboxSet } from "./interp/collection-brands.js";
6
6
  import { boxedDataProperties, isSandboxBox } from "./interp/boxed.js";
7
+ import { regexGuestProperties } from "./interp/regexp-properties.js";
7
8
  export const MAX_DATA_DEPTH = 1_024;
8
9
  export class SnapshotBudgetError extends Error {
9
10
  code = "budgetExceeded";
@@ -64,6 +65,12 @@ function walkGraphDepth(root, rootPath, assertDepth) {
64
65
  }
65
66
  }
66
67
  function graphEntries(value) {
68
+ const properties = regexGuestProperties.get(value);
69
+ if (properties !== undefined)
70
+ return Reflect.ownKeys(properties).flatMap(key => {
71
+ const descriptor = Object.getOwnPropertyDescriptor(properties, key);
72
+ return "value" in descriptor ? [[`.${String(key)}`, descriptor.value]] : [];
73
+ });
67
74
  if (isSandboxBox(value))
68
75
  return boxedDataProperties(value).map(([key, descriptor]) => [`.${key}`, descriptor.value]);
69
76
  if (isSandboxArguments(value)) {
@@ -26,7 +26,7 @@ import {
26
26
  runWithSpawnUsageAccumulator,
27
27
  splitFrontmatter,
28
28
  supportsSpawnMode
29
- } from "./chunks/chunk-VYUHL33E.js";
29
+ } from "./chunks/chunk-I2Z37MKZ.js";
30
30
  import {
31
31
  Budget,
32
32
  FileSnapshotBackend,
@@ -50,7 +50,7 @@ import {
50
50
  registerPendingHostCallPolicy,
51
51
  restore,
52
52
  run
53
- } from "./chunks/chunk-6UY4Z7WY.js";
53
+ } from "./chunks/chunk-XKCTB5FR.js";
54
54
  import "./chunks/chunk-ZWJ5BJUI.js";
55
55
  import "./chunks/chunk-3YTQEPZI.js";
56
56
  import "./chunks/chunk-4YIDX44G.js";
@@ -17,7 +17,7 @@ import { toPropertyKey } from "../property-key.js";
17
17
  import { createNumericParsers } from "./numeric-parsers.js";
18
18
  import { createPrimitiveConstructor } from "./primitives.js";
19
19
  import { getSandboxDataProperty, getSandboxPropertyDescriptor, getSandboxPrototype, isGuestClosure, markDescriptorObject, materializeFunctionProperties, setSandboxPrototype } from "../object-model.js";
20
- import { allocateProducedSandboxValue, createSandboxClosure, deepCopyToSandbox, defineOwnDataProperty, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet, measureSandboxData, ownEnumerableSandboxKeys as getOwnEnumerableKeys, ownSandboxSymbolKeys, ownEnumerableSandboxEntries as getDirectEntries } from "../values.js";
20
+ import { allocateProducedSandboxValue, createSandboxClosure, deepCopyToSandbox, defineOwnDataProperty, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, getRegexProperties, isSandboxSet, measureSandboxData, ownEnumerableSandboxKeys as getOwnEnumerableKeys, ownSandboxSymbolKeys, ownEnumerableSandboxEntries as getDirectEntries } from "../values.js";
21
21
  export function createObjectArrayGlobals(options) {
22
22
  return {
23
23
  Object: createObjectGlobal({
@@ -171,14 +171,14 @@ export function createObjectArrayGlobals(options) {
171
171
  call: ([value]) => {
172
172
  if (isGuestHostObject(value))
173
173
  throw new TypeError("Live host objects cannot be made non-extensible.");
174
- Object.preventExtensions(isGuestClosure(value) ? materializeFunctionProperties(value) : value);
174
+ Object.preventExtensions(isGuestClosure(value) ? materializeFunctionProperties(value) : isSandboxRegex(value) ? getRegexProperties(value) : value);
175
175
  return value;
176
176
  },
177
177
  name: "preventExtensions"
178
178
  }),
179
179
  isExtensible: createSandboxClosure({
180
180
  sandbox: true,
181
- call: ([value]) => Object.isExtensible(isGuestClosure(value) ? materializeFunctionProperties(value) : value),
181
+ call: ([value]) => Object.isExtensible(isGuestClosure(value) ? materializeFunctionProperties(value) : isSandboxRegex(value) ? getRegexProperties(value) : value),
182
182
  name: "isExtensible"
183
183
  }),
184
184
  seal: createSandboxClosure({
@@ -186,14 +186,14 @@ export function createObjectArrayGlobals(options) {
186
186
  call: ([value]) => {
187
187
  if (isGuestHostObject(value))
188
188
  throw new TypeError("Live host objects cannot be sealed.");
189
- Object.seal(isGuestClosure(value) ? materializeFunctionProperties(value) : value);
189
+ Object.seal(isGuestClosure(value) ? materializeFunctionProperties(value) : isSandboxRegex(value) ? getRegexProperties(value) : value);
190
190
  return value;
191
191
  },
192
192
  name: "seal"
193
193
  }),
194
194
  isSealed: createSandboxClosure({
195
195
  sandbox: true,
196
- call: ([value]) => Object.isSealed(isGuestClosure(value) ? materializeFunctionProperties(value) : value),
196
+ call: ([value]) => Object.isSealed(isGuestClosure(value) ? materializeFunctionProperties(value) : isSandboxRegex(value) ? getRegexProperties(value) : value),
197
197
  name: "isSealed"
198
198
  }),
199
199
  freeze: createSandboxClosure({
@@ -202,7 +202,7 @@ export function createObjectArrayGlobals(options) {
202
202
  if (isGuestHostObject(value))
203
203
  throw new TypeError("Live host objects cannot be frozen.");
204
204
  if (typeof value === "object" && value !== null) {
205
- Object.freeze(isGuestClosure(value) ? materializeFunctionProperties(value) : value);
205
+ Object.freeze(isGuestClosure(value) ? materializeFunctionProperties(value) : isSandboxRegex(value) ? getRegexProperties(value) : value);
206
206
  }
207
207
  return value;
208
208
  },
@@ -210,7 +210,7 @@ export function createObjectArrayGlobals(options) {
210
210
  }),
211
211
  isFrozen: createSandboxClosure({
212
212
  sandbox: true,
213
- call: ([value]) => Object.isFrozen(isGuestClosure(value) ? materializeFunctionProperties(value) : value),
213
+ call: ([value]) => Object.isFrozen(isGuestClosure(value) ? materializeFunctionProperties(value) : isSandboxRegex(value) ? getRegexProperties(value) : value),
214
214
  name: "isFrozen"
215
215
  }),
216
216
  assign: createSandboxClosure({
@@ -425,6 +425,8 @@ function assignSandboxValues(target, sources, budget, context) {
425
425
  })();
426
426
  }
427
427
  function objectProperties(value, mutable = false) {
428
+ if (isSandboxRegex(value))
429
+ return getRegexProperties(value);
428
430
  if (isSandboxDate(value)) {
429
431
  return value;
430
432
  }
@@ -9,7 +9,7 @@ import { hasHostObjectMember, isGuestHostObject } from "../host-capabilities.js"
9
9
  import { collectionIteratorState, isSandboxCollectionIterator } from "../collection-iterator.js";
10
10
  import { getSandboxPrototype, getSandboxPropertyDescriptor, hasExplicitSandboxPrototype, installObjectPrototype, isGuestClosure, markDescriptorObject, materializeFunctionProperties, setSandboxPrototype } from "../object-model.js";
11
11
  import { toPropertyKey } from "../property-key.js";
12
- import { createSandboxClosure, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet } from "../values.js";
12
+ import { createSandboxClosure, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, getRegexProperties, isSandboxSet } from "../values.js";
13
13
  import { measureSandboxData } from "../values.js";
14
14
  export function createObjectGlobal(methods, budget) {
15
15
  const construct = ([value]) => {
@@ -138,7 +138,7 @@ export function hasOwnSandboxProperty(value, key, enumerable) {
138
138
  isSandboxGenerator(value))
139
139
  return false;
140
140
  else if (isSandboxRegex(value))
141
- return key === "lastIndex" && !enumerable;
141
+ properties = getRegexProperties(value);
142
142
  else
143
143
  properties = Object(value);
144
144
  const descriptor = Object.getOwnPropertyDescriptor(properties, key);
@@ -26,7 +26,7 @@ import { getPromiseMember, isSandboxPromiseConstructor } from "./promise.js";
26
26
  import { acquireSandboxIterator, closeIterator, readIteratorResult } from "./iteration.js";
27
27
  import { assertCollectionMutable } from "./running-state.js";
28
28
  import { getGeneratorMember } from "./methods/generator.js";
29
- import { getRegexMember, isRegexMethodName, setRegexMember } from "./methods/regex.js";
29
+ import { getRegexMember, setRegexMember } from "./methods/regex.js";
30
30
  import { bindPattern } from "./patterns.js";
31
31
  import { callStringMethod, getStringMember, isStringMethodName, validateStringMethodArguments } from "./methods/string.js";
32
32
  import { callSetMethod, getSetMember, isSetMethodName } from "./methods/set.js";
@@ -39,7 +39,7 @@ import { getFloat32Member, isFloat32ArrayConstructor, setFloat32Member } from ".
39
39
  import { isFloat32Array } from "./float32.js";
40
40
  import { dateString, dateTime, isSandboxDate } from "./date.js";
41
41
  import { getDateMember, getDatePrototype, isDateConstructor } from "./globals/date.js";
42
- import { createSandboxRegex, allocateProducedSandboxValue, ownEnumerableSandboxKeys, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet, reconcileCompiledValues } from "./values.js";
42
+ import { createSandboxRegex, allocateProducedSandboxValue, ownEnumerableSandboxKeys, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, getRegexProperties, isSandboxSet, reconcileCompiledValues } from "./values.js";
43
43
  import { Scope } from "./scope.js";
44
44
  import { hoistVarDeclarations } from "./var-hoist.js";
45
45
  const taggedTemplateRawArrays = new WeakMap();
@@ -1772,7 +1772,7 @@ async function evaluateDeleteExpression(node, context) {
1772
1772
  }
1773
1773
  throw new TypeError("Cannot delete properties of null or undefined.");
1774
1774
  }
1775
- if (!isIndexableSandboxValue(member.object)) {
1775
+ if (!isIndexableSandboxValue(member.object) && !isSandboxRegex(member.object)) {
1776
1776
  throw new TypeError("Unary operator 'delete' requires a sandbox object property.");
1777
1777
  }
1778
1778
  const property = await toPropertyKey(member.property, context.budget, createCoercionContext(context));
@@ -2132,8 +2132,8 @@ async function evaluateMemberCallExpression(node, context) {
2132
2132
  if (isSandboxPromise(member.object)) {
2133
2133
  return evaluateResolvedCallExpression(node, getPromiseMember(member.property, context.budget), context, member.object);
2134
2134
  }
2135
- if (isSandboxRegex(member.object) && isRegexMethodName(member.property)) {
2136
- return evaluateResolvedCallExpression(node, getRegexMember(member.object, member.property, context.budget), context, member.object);
2135
+ if (isSandboxRegex(member.object)) {
2136
+ return evaluateResolvedCallExpression(node, await getPropertyValue(member.object, member.property, context), context, member.object);
2137
2137
  }
2138
2138
  if (!isIndexableSandboxValue(member.object)) {
2139
2139
  throw new TypeError("Attempted to read a property from a non-object value.");
@@ -2646,8 +2646,6 @@ export function setSandboxProperty(target, property, value, budget, checkInherit
2646
2646
  return;
2647
2647
  }
2648
2648
  if (isSandboxRegex(target)) {
2649
- if (typeof property === "symbol")
2650
- throw new TypeError("RegExp symbol properties are not yet supported.");
2651
2649
  setRegexMember(target, property, value);
2652
2650
  return;
2653
2651
  }
@@ -2714,6 +2712,8 @@ function deleteSandboxProperty(target, property) {
2714
2712
  return deleteHostObjectMember(target, String(property));
2715
2713
  if (isGuestClosure(target))
2716
2714
  target = materializeFunctionProperties(target);
2715
+ if (isSandboxRegex(target))
2716
+ target = getRegexProperties(target);
2717
2717
  if (Array.isArray(target)) {
2718
2718
  assertCollectionMutable(target);
2719
2719
  }
@@ -4,7 +4,7 @@ import type { Budget } from "../budget.js";
4
4
  export type RegexMethodName = "exec" | "test";
5
5
  export declare function isRegexMethodName(property: string | number): property is RegexMethodName;
6
6
  export declare function getRegexMember(target: SandboxRegex, property: string | number, budget: Budget): SandboxValue | undefined;
7
- export declare function setRegexMember(target: SandboxRegex, property: string | number, value: SandboxValue): void;
7
+ export declare function setRegexMember(target: SandboxRegex, property: PropertyKey, value: SandboxValue): void;
8
8
  export declare function callRegexMethod(target: SandboxValue, methodName: RegexMethodName, args: readonly SandboxValue[], budget: Budget, context?: SandboxCallContext): Promise<SandboxValue>;
9
9
  export declare function executeRegex(target: SandboxRegex, input: string, lastIndex: number): RegexMatch | null;
10
10
  export declare function toMatchArray(match: RegexMatch | null, input: string): SandboxValue;
@@ -1,4 +1,4 @@
1
- import { createSandboxClosure, getSandboxRegexPattern, isSandboxClosure, isSandboxRegex } from "../values.js";
1
+ import { createSandboxClosure, getSandboxRegexPattern, getRegexProperties, isSandboxClosure, isSandboxRegex } from "../values.js";
2
2
  import { matchRegex } from "../regex/engine.js";
3
3
  import { invokeBuiltinClosure } from "../builtin-call.js";
4
4
  import { getSandboxDataProperty } from "../object-model.js";
@@ -67,10 +67,13 @@ function escapeRegexSource(source, budget) {
67
67
  return budget === undefined ? text : budget.allocateString(text);
68
68
  }
69
69
  export function setRegexMember(target, property, value) {
70
- if (property !== "lastIndex") {
70
+ const properties = getRegexProperties(target);
71
+ if (!Object.hasOwn(properties, property) &&
72
+ (property === "source" || property === "flags" || Object.hasOwn(regexFlagProperties, property))) {
71
73
  throw new TypeError(`RegExp#${String(property)} is not writable.`);
72
74
  }
73
- target.lastIndex = value;
75
+ if (!Reflect.set(properties, property, value))
76
+ throw new TypeError(`RegExp#${String(property)} is not writable.`);
74
77
  }
75
78
  export async function callRegexMethod(target, methodName, args, budget, context) {
76
79
  if (target === null || typeof target !== "object" || (methodName === "exec" && !isSandboxRegex(target))) {
@@ -1,9 +1,10 @@
1
+ import { types } from "node:util";
1
2
  import { invokeBuiltinClosure } from "../builtin-call.js";
2
3
  import { CompileScope } from "../regex/compile-guard.js";
3
4
  import { normalizeLastIndex } from "../regex/engine.js";
4
5
  import { sandboxNumber, sandboxString } from "../string-coercion.js";
5
6
  import { retainValues } from "../resources.js";
6
- import { getSandboxDataProperty } from "../object-model.js";
7
+ import { getSandboxDataProperty, getSandboxPropertyDescriptor } from "../object-model.js";
7
8
  import { createSandboxClosure, createSandboxRegex, deepCopyFromSandbox, isSandboxClosure, isSandboxRegex } from "../values.js";
8
9
  import { executeRegex, toMatchArray } from "./regex.js";
9
10
  const SPLIT_STRING_MESSAGE = "String#split only supports string separator values.";
@@ -91,8 +92,12 @@ export function callStringMethod(value, methodName, args, budget, callClosure =
91
92
  : Promise.resolve(sandboxString(value, budget, context)).then(string => callStringMethodBody(string, methodName, args, budget, callClosure, parent, context));
92
93
  const symbol = methodName === "match" ? Symbol.match
93
94
  : methodName === "search" ? Symbol.search
94
- : methodName === "split" ? Symbol.split : undefined;
95
+ : methodName === "matchAll" ? Symbol.matchAll
96
+ : methodName === "replace" || methodName === "replaceAll" ? Symbol.replace
97
+ : methodName === "split" ? Symbol.split : undefined;
95
98
  const pattern = args[0];
99
+ if (symbol !== undefined && types.isRegExp(pattern))
100
+ throw new TypeError(`String#${methodName} does not accept unbranded host RegExp values.`);
96
101
  if (symbol === undefined || pattern === null || pattern === undefined)
97
102
  return fallback();
98
103
  const applyHook = (hook) => {
@@ -100,11 +105,37 @@ export function callStringMethod(value, methodName, args, budget, callClosure =
100
105
  return fallback();
101
106
  if (!isSandboxClosure(hook))
102
107
  throw new TypeError(`String#${methodName} symbol hook must be callable.`);
103
- return invokeBuiltinClosure(hook, methodName === "split" ? [value, args[1]] : [value], budget, context, pattern);
108
+ return invokeBuiltinClosure(hook, symbol === Symbol.split || symbol === Symbol.replace ? [value, args[1]] : [value], budget, context, pattern);
104
109
  };
105
- return context?.getProperty !== undefined
106
- ? Promise.resolve(context.getProperty(pattern, symbol)).then(applyHook)
107
- : applyHook(getSandboxDataProperty(pattern, symbol, budget));
110
+ const readProperty = (key) => context?.getProperty !== undefined
111
+ ? context.getProperty(pattern, key)
112
+ : key === "flags" && isSandboxRegex(pattern) && getSandboxPropertyDescriptor(pattern, key, budget) === undefined
113
+ ? pattern.flags : getSandboxDataProperty(pattern, key, budget);
114
+ const dispatch = () => {
115
+ const hook = readProperty(symbol);
116
+ return hook instanceof Promise ? hook.then(applyHook) : applyHook(hook);
117
+ };
118
+ if ((methodName === "replaceAll" || methodName === "matchAll") &&
119
+ (typeof pattern === "object" || typeof pattern === "function")) {
120
+ const checkFlags = (flags) => {
121
+ const checkText = (text) => {
122
+ if (!text.includes("g"))
123
+ throw new TypeError(`String#${methodName} requires a global regex.`);
124
+ return dispatch();
125
+ };
126
+ const text = sandboxString(flags, budget, context);
127
+ return typeof text === "string" ? checkText(text) : text.then(checkText);
128
+ };
129
+ const checkMatch = (match) => {
130
+ if (!(match === undefined ? isSandboxRegex(pattern) : Boolean(match)))
131
+ return dispatch();
132
+ const flags = readProperty("flags");
133
+ return flags instanceof Promise ? flags.then(checkFlags) : checkFlags(flags);
134
+ };
135
+ const match = readProperty(Symbol.match);
136
+ return match instanceof Promise ? match.then(checkMatch) : checkMatch(match);
137
+ }
138
+ return dispatch();
108
139
  }
109
140
  function callStringMethodBody(value, methodName, args, budget, callClosure = async (closure, closureArgs) => await closure.call(closureArgs), parent, context) {
110
141
  if (methodName === "concat" && args.some(argument => argument !== null && typeof argument === "object")) {
@@ -153,7 +184,7 @@ function callStringMethodBody(value, methodName, args, budget, callClosure = asy
153
184
  const regex = args[0];
154
185
  if (isSandboxRegex(regex) && regex.lastIndex !== null && typeof regex.lastIndex === "object" &&
155
186
  ((methodName === "match" && !regex.flags.includes("g")) ||
156
- (methodName === "matchAll" && regex.flags.includes("g")))) {
187
+ methodName === "matchAll")) {
157
188
  return callStringRegexCursor(value, methodName, regex, budget, parent, context);
158
189
  }
159
190
  if ((methodName === "match" || methodName === "matchAll" || methodName === "search") && !isSandboxRegex(args[0])) {
@@ -283,15 +314,25 @@ async function callConcat(value, args, budget, context) {
283
314
  function callReplaceLikeMethod(value, methodName, args, budget, callClosure, context) {
284
315
  const search = args[0];
285
316
  const replacement = args[1];
286
- if ((!isSandboxRegex(search) && typeof search !== "string") ||
317
+ const useRegex = isSandboxRegex(search) && getSandboxPropertyDescriptor(search, Symbol.replace, budget) === undefined;
318
+ if ((!useRegex && typeof search !== "string") ||
287
319
  (typeof replacement !== "string" && !isSandboxClosure(replacement))) {
288
- throw new TypeError(`String#${methodName} only supports string or regex search values and string or function replacements.`);
320
+ return (async () => {
321
+ let normalizedSearch;
322
+ let normalizedReplacement;
323
+ const release = retainValues(budget, () => [normalizedSearch, normalizedReplacement]);
324
+ try {
325
+ normalizedSearch = useRegex ? search : await sandboxString(search, budget, context);
326
+ normalizedReplacement = isSandboxClosure(replacement) ? replacement : await sandboxString(replacement, budget, context);
327
+ return await callReplaceLikeMethod(value, methodName, [normalizedSearch, normalizedReplacement], budget, callClosure, context);
328
+ }
329
+ finally {
330
+ release();
331
+ }
332
+ })();
289
333
  }
290
334
  if (isSandboxRegex(search)) {
291
- if (methodName === "replaceAll" && !search.flags.includes("g")) {
292
- throw new TypeError("String#replaceAll requires a global regex.");
293
- }
294
- return replaceRegex(value, search, replacement, methodName === "replaceAll", budget, callClosure, context);
335
+ return replaceRegex(value, search, replacement, budget, callClosure, context);
295
336
  }
296
337
  if (typeof replacement === "string") {
297
338
  return budget.allocateString(methodName === "replace"
@@ -300,7 +341,7 @@ function callReplaceLikeMethod(value, methodName, args, budget, callClosure, con
300
341
  }
301
342
  return replaceWithClosure(value, search, replacement, methodName === "replaceAll", budget, callClosure);
302
343
  }
303
- async function replaceRegex(value, regex, replacement, replaceAll, budget, callClosure, context) {
344
+ async function replaceRegex(value, regex, replacement, budget, callClosure, context) {
304
345
  if (regex.flags.includes("g"))
305
346
  regex.lastIndex = 0;
306
347
  const cursor = regex.lastIndex;
@@ -308,7 +349,7 @@ async function replaceRegex(value, regex, replacement, replaceAll, budget, callC
308
349
  budget.setRetainedValues(retained, () => [value, regex, cursor, replacement]);
309
350
  try {
310
351
  const lastIndex = await sandboxNumber(cursor, budget, context);
311
- const matches = collectRegexMatches(regex, value, replaceAll || regex.flags.includes("g"), undefined, lastIndex);
352
+ const matches = collectRegexMatches(regex, value, regex.flags.includes("g"), undefined, lastIndex);
312
353
  let result = "";
313
354
  let copiedThrough = 0;
314
355
  for (const match of matches) {
@@ -484,8 +525,6 @@ function callMatchLikeMethod(value, methodName, args, compilation, lastIndex) {
484
525
  regex.lastIndex = lastIndex;
485
526
  return match?.index ?? -1;
486
527
  }
487
- if (methodName === "matchAll" && !regex.flags.includes("g"))
488
- throw new TypeError("String#matchAll requires a global regex.");
489
528
  if (methodName === "match" && !regex.flags.includes("g"))
490
529
  return toMatchArray(executeRegex(regex, value, lastIndex ?? Number(regex.lastIndex)), value);
491
530
  if (methodName === "match")
@@ -493,7 +532,7 @@ function callMatchLikeMethod(value, methodName, args, compilation, lastIndex) {
493
532
  const matcher = methodName === "matchAll"
494
533
  ? createSandboxRegex(regex.source, regex.flags, normalizeLastIndex(lastIndex ?? Number(regex.lastIndex)), compilation)
495
534
  : regex;
496
- const matches = collectRegexMatches(matcher, value, true, compilation.owner?.budget, Number(matcher.lastIndex));
535
+ const matches = collectRegexMatches(matcher, value, matcher.flags.includes("g"), compilation.owner?.budget, Number(matcher.lastIndex));
497
536
  if (methodName === "match" && matches.length === 0)
498
537
  return null;
499
538
  return methodName === "match"
@@ -3,7 +3,7 @@ import { isSandboxDate } from "./date.js";
3
3
  import { retainedAccessorClosures } from "./accessors.js";
4
4
  import { getHostObjectMember, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
5
5
  import { boxedValue, isSandboxBox } from "./boxed.js";
6
- import { isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet } from "./values.js";
6
+ import { isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, getRegexProperties, isSandboxSet } from "./values.js";
7
7
  const guestClosures = new WeakSet();
8
8
  const functionProperties = new WeakMap();
9
9
  const prototypes = new WeakMap();
@@ -160,8 +160,9 @@ export function getSandboxPropertyDescriptor(value, key, budget) {
160
160
  let depth = 0;
161
161
  while (typeof current === "object" &&
162
162
  current !== null &&
163
- (Array.isArray(current) || isSandboxDate(current) || isPrototypeRecord(current))) {
164
- const properties = isGuestClosure(current) ? getGuestFunctionProperties(current) : current;
163
+ (Array.isArray(current) || isSandboxDate(current) || isSandboxRegex(current) || isPrototypeRecord(current))) {
164
+ const properties = isGuestClosure(current) ? getGuestFunctionProperties(current)
165
+ : isSandboxRegex(current) ? getRegexProperties(current) : current;
165
166
  const descriptor = properties === undefined ? undefined : Object.getOwnPropertyDescriptor(properties, key);
166
167
  if (descriptor !== undefined)
167
168
  return descriptor;
@@ -179,6 +180,8 @@ export function getSandboxDataProperty(value, key, budget) {
179
180
  while (typeof current === "object" && current !== null) {
180
181
  if (isGuestHostObject(current))
181
182
  return typeof key === "symbol" ? undefined : getHostObjectMember(current, String(key));
183
+ if (isSandboxRegex(current))
184
+ return Object.getOwnPropertyDescriptor(getRegexProperties(current), key)?.value;
182
185
  if (isGuestClosure(current)) {
183
186
  const entry = getGuestFunctionProperty(current, key);
184
187
  if (entry !== undefined || Object.hasOwn(current.properties ?? {}, key))
@@ -0,0 +1,3 @@
1
+ import type { SandboxObject, SandboxRegex } from "./values.js";
2
+ export declare const regexGuestProperties: WeakMap<object, SandboxObject>;
3
+ export declare function getRegexProperties(value: SandboxRegex): SandboxObject;
@@ -0,0 +1,7 @@
1
+ export const regexGuestProperties = new WeakMap();
2
+ export function getRegexProperties(value) {
3
+ const properties = regexGuestProperties.get(value);
4
+ if (properties === undefined)
5
+ throw new TypeError("Invalid sandbox RegExp storage.");
6
+ return properties;
7
+ }
@@ -1,3 +1,4 @@
1
+ export { getRegexProperties } from "./regexp-properties.js";
1
2
  import { sandboxMapBrand, sandboxSetBrand } from "./collection-brands.js";
2
3
  import { type SandboxCollectionIterator } from "./collection-iterator.js";
3
4
  import type { Budget, CompileTicket } from "./budget.js";
@@ -1,5 +1,7 @@
1
1
  import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
2
2
  import { internalSymbols } from "./internal-symbols.js";
3
+ import { getRegexProperties, regexGuestProperties } from "./regexp-properties.js";
4
+ export { getRegexProperties } from "./regexp-properties.js";
3
5
  import { retainedAccessorClosures } from "./accessors.js";
4
6
  import { isSandboxMap, isSandboxSet, sandboxMapBrand, sandboxSetBrand } from "./collection-brands.js";
5
7
  import { collectionIteratorState, isSandboxCollectionIterator, restoreSandboxCollectionIterator, snapshotCollectionIterator } from "./collection-iterator.js";
@@ -98,6 +100,8 @@ export function ownEnumerableSandboxEntries(value, excludedKeys) {
98
100
  let entries;
99
101
  if (isGuestClosure(value))
100
102
  entries = Object.entries(value.properties ?? {});
103
+ else if (isSandboxRegex(value))
104
+ entries = Object.entries(getRegexProperties(value));
101
105
  else if (isSandboxClosure(value) || isSandboxGenerator(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxPromise(value) || isSandboxRegex(value))
102
106
  return [];
103
107
  else
@@ -111,13 +115,15 @@ export function ownSandboxSymbolKeys(value) {
111
115
  return [];
112
116
  if (isSandboxClosure(value))
113
117
  value = value.properties ?? {};
118
+ else if (isSandboxRegex(value))
119
+ value = getRegexProperties(value);
114
120
  else if (isSandboxGenerator(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxPromise(value) || isSandboxRegex(value))
115
121
  return [];
116
122
  return Object.getOwnPropertySymbols(Object(value)).filter(key => !internalSymbols.has(key));
117
123
  }
118
124
  export function ownEnumerableSandboxKeys(value, includeSymbols = false) {
119
125
  if (includeSymbols) {
120
- const properties = isSandboxClosure(value) ? value.properties ?? {} : Object(value);
126
+ const properties = isSandboxClosure(value) ? value.properties ?? {} : isSandboxRegex(value) ? getRegexProperties(value) : Object(value);
121
127
  return [...ownEnumerableSandboxKeys(value), ...ownSandboxSymbolKeys(value).filter(key => Object.getOwnPropertyDescriptor(properties, key)?.enumerable === true)];
122
128
  }
123
129
  if (isGuestHostObject(value))
@@ -126,6 +132,8 @@ export function ownEnumerableSandboxKeys(value, includeSymbols = false) {
126
132
  throw new TypeError("Cannot convert undefined or null to object.");
127
133
  if (isGuestClosure(value))
128
134
  return Object.keys(value.properties ?? {});
135
+ if (isSandboxRegex(value))
136
+ return Object.keys(getRegexProperties(value));
129
137
  if (isSandboxClosure(value) || isSandboxGenerator(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxPromise(value) || isSandboxRegex(value))
130
138
  return [];
131
139
  return Object.keys(Object(value));
@@ -218,6 +226,32 @@ export function createSandboxRegex(source, flags = "", lastIndex = 0, compilatio
218
226
  }
219
227
  const pattern = parseRegex(source, flags, compilation, 7 + source.length + flags.length);
220
228
  const regex = { kind: "regex", source, flags, lastIndex };
229
+ const storage = Object.create(null);
230
+ Object.defineProperty(storage, "lastIndex", { value: lastIndex, writable: true });
231
+ const syncCursor = () => {
232
+ const descriptor = Object.getOwnPropertyDescriptor(regex, "lastIndex");
233
+ Object.defineProperty(storage, "lastIndex", { value: descriptor.value, writable: descriptor.writable });
234
+ };
235
+ const properties = new Proxy(storage, {
236
+ get: (target, key, receiver) => key === "lastIndex" ? regex.lastIndex : Reflect.get(target, key, receiver),
237
+ set: (target, key, value, receiver) => key === "lastIndex"
238
+ ? Reflect.set(regex, key, value) : Reflect.set(target, key, value, receiver),
239
+ getOwnPropertyDescriptor(target, key) {
240
+ if (key === "lastIndex")
241
+ syncCursor();
242
+ return Object.getOwnPropertyDescriptor(target, key);
243
+ },
244
+ defineProperty(target, key, descriptor) {
245
+ if (key !== "lastIndex")
246
+ return Reflect.defineProperty(target, key, descriptor);
247
+ syncCursor();
248
+ if (!Reflect.defineProperty(target, key, descriptor))
249
+ return false;
250
+ const cursor = Object.getOwnPropertyDescriptor(target, key);
251
+ return Reflect.defineProperty(regex, key, { value: cursor.value, writable: cursor.writable });
252
+ }
253
+ });
254
+ regexGuestProperties.set(regex, properties);
221
255
  Object.defineProperties(regex, {
222
256
  [sandboxRegexBrand]: { value: true },
223
257
  [sandboxRegexPattern]: { value: pattern }
@@ -424,6 +458,19 @@ export function measureSandboxData(values, options = {}) {
424
458
  if (compiled.ticket !== undefined)
425
459
  options.compileTickets?.add(compiled.ticket);
426
460
  visit(lastIndex, depth + 1);
461
+ for (const key of Reflect.ownKeys(getRegexProperties(value))) {
462
+ if (key === "lastIndex")
463
+ continue;
464
+ const descriptor = Object.getOwnPropertyDescriptor(getRegexProperties(value), key);
465
+ usage += 1 + (typeof key === "string" ? key.length : 0);
466
+ if (typeof key === "symbol")
467
+ visit(key, depth + 1);
468
+ if ("value" in descriptor)
469
+ visit(descriptor.value, depth + 1);
470
+ else
471
+ for (const closure of retainedAccessorClosures(descriptor))
472
+ visit(closure, depth + 1);
473
+ }
427
474
  return;
428
475
  }
429
476
  if (isSandboxArguments(value)) {
@@ -491,7 +538,7 @@ function captureRegexData(value) {
491
538
  typeof flags.value !== "string") {
492
539
  throw new TypeError("Invalid sandbox RegExp source or flags: expected own string data properties.");
493
540
  }
494
- const cursor = Object.getOwnPropertyDescriptor(value, "lastIndex");
541
+ const cursor = Object.getOwnPropertyDescriptor(regexGuestProperties.get(value) ?? value, "lastIndex");
495
542
  if (cursor === undefined || !("value" in cursor)) {
496
543
  throw new TypeError("Invalid sandbox RegExp lastIndex: expected an own data property.");
497
544
  }
@@ -519,6 +566,21 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
519
566
  state.seen.set(value, copy);
520
567
  if (!state.resetRegexLastIndex)
521
568
  copy.lastIndex = copyToSandbox(value.lastIndex, state, `${path}.lastIndex`, true, depth + 1);
569
+ if (!state.structuredClone) {
570
+ const properties = getRegexProperties(value);
571
+ for (const key of Reflect.ownKeys(properties)) {
572
+ const descriptor = Object.getOwnPropertyDescriptor(properties, key);
573
+ if (!("value" in descriptor))
574
+ throw new TypeError("RegExp accessor properties cannot be copied as data.");
575
+ Object.defineProperty(getRegexProperties(copy), key, {
576
+ ...descriptor,
577
+ value: key === "lastIndex" && state.resetRegexLastIndex ? 0
578
+ : copyToSandbox(descriptor.value, state, `${path}.${String(key)}`, true, depth + 1)
579
+ });
580
+ }
581
+ if (!Object.isExtensible(properties))
582
+ Object.preventExtensions(getRegexProperties(copy));
583
+ }
522
584
  return copy;
523
585
  }
524
586
  if (isSandboxClosure(value) ||
@@ -745,6 +807,18 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
745
807
  const regex = new RegExp(source, flags);
746
808
  state.seen.set(value, regex);
747
809
  Reflect.set(regex, "lastIndex", copyFromSandbox(lastIndex, state, `${path}.lastIndex`, options, depth + 1));
810
+ const properties = regexGuestProperties.get(value);
811
+ if (properties !== undefined) {
812
+ for (const key of Reflect.ownKeys(properties)) {
813
+ const descriptor = Object.getOwnPropertyDescriptor(properties, key);
814
+ if (!("value" in descriptor))
815
+ throw new TypeError("RegExp accessor properties cannot be copied as data.");
816
+ Object.defineProperty(regex, key, { ...descriptor,
817
+ value: copyFromSandbox(descriptor.value, state, `${path}.${String(key)}`, options, depth + 1) });
818
+ }
819
+ if (!Object.isExtensible(properties))
820
+ Object.preventExtensions(regex);
821
+ }
748
822
  guard.retainScratch();
749
823
  return regex;
750
824
  }