poe-code 14.0.72 → 14.0.74

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 (45) hide show
  1. package/dist/index.js +2674 -1760
  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-2J6W3YGB.js} +2 -2
  6. package/packages/safe-js/dist/chunks/{chunk-6UY4Z7WY.js → chunk-YEZOXLY4.js} +6279 -5426
  7. package/packages/safe-js/dist/chunks/chunk-YEZOXLY4.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 +10 -0
  11. package/packages/safe-js/dist/index.js +2 -2
  12. package/packages/safe-js/dist/interp/cancel.js +3 -0
  13. package/packages/safe-js/dist/interp/globals/misc.js +2 -1
  14. package/packages/safe-js/dist/interp/globals/object-array.js +9 -7
  15. package/packages/safe-js/dist/interp/globals/object.js +5 -2
  16. package/packages/safe-js/dist/interp/globals/regex.js +88 -7
  17. package/packages/safe-js/dist/interp/interpreter.js +21 -14
  18. package/packages/safe-js/dist/interp/iteration.js +6 -1
  19. package/packages/safe-js/dist/interp/methods/regex.d.ts +7 -3
  20. package/packages/safe-js/dist/interp/methods/regex.js +132 -23
  21. package/packages/safe-js/dist/interp/methods/regexp-iterator.d.ts +8 -0
  22. package/packages/safe-js/dist/interp/methods/regexp-iterator.js +49 -0
  23. package/packages/safe-js/dist/interp/methods/string.d.ts +2 -0
  24. package/packages/safe-js/dist/interp/methods/string.js +460 -127
  25. package/packages/safe-js/dist/interp/object-model.d.ts +2 -0
  26. package/packages/safe-js/dist/interp/object-model.js +45 -12
  27. package/packages/safe-js/dist/interp/regexp-iterator.d.ts +14 -0
  28. package/packages/safe-js/dist/interp/regexp-iterator.js +18 -0
  29. package/packages/safe-js/dist/interp/regexp-properties.d.ts +3 -0
  30. package/packages/safe-js/dist/interp/regexp-properties.js +7 -0
  31. package/packages/safe-js/dist/interp/string-coercion.js +11 -7
  32. package/packages/safe-js/dist/interp/values.d.ts +3 -1
  33. package/packages/safe-js/dist/interp/values.js +111 -2
  34. package/packages/safe-js/dist/snapshot/dump-format.js +51 -9
  35. package/packages/safe-js/dist/snapshot/regexp-properties.d.ts +18 -0
  36. package/packages/safe-js/dist/snapshot/regexp-properties.js +58 -0
  37. package/packages/safe-js/dist/snapshot/replay-data.d.ts +12 -3
  38. package/packages/safe-js/dist/snapshot/replay-data.js +35 -1
  39. package/packages/safe-js/dist/snapshot/replay-inputs.js +17 -0
  40. package/packages/safe-js/dist/snapshot/restore.js +19 -1
  41. package/packages/safe-js/dist/snapshot/serialize.d.ts +12 -3
  42. package/packages/safe-js/dist/snapshot/serialize.js +40 -17
  43. package/packages/safe-js/dist/snapshot/validation.js +25 -2
  44. package/packages/safe-js/dist/chunks/chunk-6UY4Z7WY.js.map +0 -7
  45. /package/packages/safe-js/dist/chunks/{chunk-VYUHL33E.js.map → chunk-2J6W3YGB.js.map} +0 -0
@@ -9,6 +9,8 @@ export declare function getGuestFunctionProperty(closure: SandboxClosure, key: P
9
9
  export declare function installObjectPrototype(budget: Budget, prototype: SandboxObject, constructor: SandboxClosure): void;
10
10
  export declare function installBoxedPrototype(budget: Budget, prototype: SandboxObject, constructor: SandboxClosure, kind?: BoxedKind): void;
11
11
  export declare function getBoxedPrototype(value: BoxedPrimitive, budget: Budget): SandboxObject | undefined;
12
+ export declare function installRegexPrototype(budget: Budget, prototype: SandboxObject, constructor: SandboxClosure): void;
13
+ export declare function hasRegexPropertyOverride(value: SandboxValue, keys: readonly string[], budget: Budget): boolean;
12
14
  export declare function isDefaultBoxedMethod(value: BoxedPrimitive, key: string, budget: Budget): boolean;
13
15
  export declare function isIntrinsicConstructor(value: object): boolean;
14
16
  export declare function releaseObjectPrototype(budget: Budget): void;
@@ -3,12 +3,14 @@ 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();
10
10
  const intrinsicPrototypes = new WeakMap();
11
11
  const boxedPrototypes = new WeakMap();
12
+ const regexPrototypes = new WeakMap();
13
+ const initialRegexDescriptors = new WeakMap();
12
14
  const intrinsicPrototypeRoots = new WeakMap();
13
15
  const intrinsicConstructors = new WeakMap();
14
16
  const initialBoxedMethods = new WeakMap();
@@ -80,6 +82,24 @@ export function installBoxedPrototype(budget, prototype, constructor, kind = typ
80
82
  export function getBoxedPrototype(value, budget) {
81
83
  return boxedPrototypes.get(budget)?.get(typeof value);
82
84
  }
85
+ export function installRegexPrototype(budget, prototype, constructor) {
86
+ regexPrototypes.set(budget, prototype);
87
+ initialRegexDescriptors.set(budget, Object.getOwnPropertyDescriptors(prototype));
88
+ registerIntrinsicPrototype(budget, prototype, constructor);
89
+ }
90
+ export function hasRegexPropertyOverride(value, keys, budget) {
91
+ const initial = initialRegexDescriptors.get(budget);
92
+ const prototype = regexPrototypes.get(budget);
93
+ const defaultPrototype = isSandboxRegex(value) && prototype !== undefined && getSandboxPrototype(value, budget) === prototype;
94
+ return keys.some(key => {
95
+ const current = defaultPrototype
96
+ ? Object.getOwnPropertyDescriptor(getRegexProperties(value), key) ?? Object.getOwnPropertyDescriptor(prototype, key)
97
+ : getSandboxPropertyDescriptor(value, key, budget);
98
+ const expected = initial?.[key];
99
+ return expected === undefined ? current !== undefined : current === undefined ||
100
+ current.value !== expected.value || current.get !== expected.get || current.set !== expected.set;
101
+ });
102
+ }
83
103
  export function isDefaultBoxedMethod(value, key, budget) {
84
104
  const prototype = getBoxedPrototype(value, budget);
85
105
  return prototype !== undefined && initialBoxedMethods.get(prototype)?.has(key) === true &&
@@ -93,7 +113,10 @@ function registerIntrinsicPrototype(budget, prototype, constructor) {
93
113
  if (roots === undefined)
94
114
  intrinsicPrototypeRoots.set(budget, (roots = new Set()));
95
115
  roots.add(prototype);
96
- const records = [prototype, constructor]
116
+ const methods = Reflect.ownKeys(prototype)
117
+ .map(key => Object.getOwnPropertyDescriptor(prototype, key)?.value)
118
+ .filter(isGuestClosure);
119
+ const records = [...new Set([prototype, constructor, ...methods])]
97
120
  .map((target) => ({
98
121
  target,
99
122
  value: isGuestClosure(target) ? materializeFunctionProperties(target) : target,
@@ -102,7 +125,7 @@ function registerIntrinsicPrototype(budget, prototype, constructor) {
102
125
  }))
103
126
  .map((record) => ({
104
127
  ...record,
105
- descriptors: new Map(Object.entries(Object.getOwnPropertyDescriptors(record.value)))
128
+ descriptors: new Map(Reflect.ownKeys(record.value).map(key => [key, Object.getOwnPropertyDescriptor(record.value, key)]))
106
129
  }));
107
130
  const unchanged = (before, after) => before !== undefined &&
108
131
  after !== undefined &&
@@ -116,16 +139,19 @@ function registerIntrinsicPrototype(budget, prototype, constructor) {
116
139
  const current = Object.getOwnPropertyDescriptors(value);
117
140
  return (getSandboxPrototype(target) === parent &&
118
141
  hasExplicitSandboxPrototype(target) === explicit &&
119
- Object.keys(current).length === descriptors.size &&
120
- Object.keys(current).every((key) => unchanged(descriptors.get(key), current[key])));
142
+ Reflect.ownKeys(current).length === descriptors.size &&
143
+ Reflect.ownKeys(value).every((key) => unchanged(descriptors.get(key), Object.getOwnPropertyDescriptor(value, key))));
121
144
  }));
122
145
  budget.setRetainedValues(prototype, () => records.flatMap(({ target, value, descriptors, prototype: parent }) => [
123
146
  ...(getSandboxPrototype(target) === parent
124
147
  ? []
125
148
  : [getSandboxPrototype(target)]),
126
- ...Object.entries(Object.getOwnPropertyDescriptors(value)).flatMap(([key, descriptor]) => unchanged(descriptors.get(key), descriptor)
127
- ? []
128
- : [key, descriptor.value, ...retainedAccessorClosures(descriptor)])
149
+ ...Reflect.ownKeys(value).flatMap(key => {
150
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
151
+ return unchanged(descriptors.get(key), descriptor)
152
+ ? []
153
+ : [key, descriptor.value, ...retainedAccessorClosures(descriptor)];
154
+ })
129
155
  ]));
130
156
  }
131
157
  export function releaseObjectPrototype(budget) {
@@ -133,11 +159,15 @@ export function releaseObjectPrototype(budget) {
133
159
  budget.setRetainedValues(prototype, undefined);
134
160
  intrinsicPrototypeRoots.delete(budget);
135
161
  boxedPrototypes.delete(budget);
162
+ regexPrototypes.delete(budget);
163
+ initialRegexDescriptors.delete(budget);
136
164
  intrinsicPrototypes.delete(budget);
137
165
  }
138
166
  export function getSandboxPrototype(value, budget) {
139
167
  if (prototypes.has(value))
140
168
  return prototypes.get(value) ?? null;
169
+ if (budget !== undefined && isSandboxRegex(value))
170
+ return regexPrototypes.get(budget) ?? null;
141
171
  if (budget !== undefined && isSandboxBox(value)) {
142
172
  const prototype = getBoxedPrototype(boxedValue(value), budget);
143
173
  if (prototype !== undefined && prototype !== value)
@@ -160,8 +190,9 @@ export function getSandboxPropertyDescriptor(value, key, budget) {
160
190
  let depth = 0;
161
191
  while (typeof current === "object" &&
162
192
  current !== null &&
163
- (Array.isArray(current) || isSandboxDate(current) || isPrototypeRecord(current))) {
164
- const properties = isGuestClosure(current) ? getGuestFunctionProperties(current) : current;
193
+ (Array.isArray(current) || isSandboxDate(current) || isSandboxRegex(current) || isPrototypeRecord(current))) {
194
+ const properties = isGuestClosure(current) ? getGuestFunctionProperties(current)
195
+ : isSandboxRegex(current) ? getRegexProperties(current) : current;
165
196
  const descriptor = properties === undefined ? undefined : Object.getOwnPropertyDescriptor(properties, key);
166
197
  if (descriptor !== undefined)
167
198
  return descriptor;
@@ -179,6 +210,8 @@ export function getSandboxDataProperty(value, key, budget) {
179
210
  while (typeof current === "object" && current !== null) {
180
211
  if (isGuestHostObject(current))
181
212
  return typeof key === "symbol" ? undefined : getHostObjectMember(current, String(key));
213
+ if (isSandboxRegex(current))
214
+ return Object.getOwnPropertyDescriptor(getRegexProperties(current), key)?.value;
182
215
  if (isGuestClosure(current)) {
183
216
  const entry = getGuestFunctionProperty(current, key);
184
217
  if (entry !== undefined || Object.hasOwn(current.properties ?? {}, key))
@@ -210,13 +243,13 @@ export function setSandboxPrototype(value, prototype, budget) {
210
243
  if (budget !== undefined && intrinsicPrototypes.get(budget) === value && prototype !== null) {
211
244
  throw new TypeError("Object.prototype has an immutable null prototype.");
212
245
  }
213
- if ((!Array.isArray(value) && !isPrototypeRecord(value)) ||
246
+ if ((!Array.isArray(value) && !isSandboxRegex(value) && !isPrototypeRecord(value)) ||
214
247
  (prototype !== null && !Array.isArray(prototype) && !isPrototypeRecord(prototype))) {
215
248
  throw new TypeError("Prototype links require ordinary sandbox objects, arrays, or guest functions.");
216
249
  }
217
250
  if (prototypes.has(value) && getSandboxPrototype(value, budget) === prototype)
218
251
  return;
219
- if (!Object.isExtensible(isGuestClosure(value) ? materializeFunctionProperties(value) : value)) {
252
+ if (!Object.isExtensible(isGuestClosure(value) ? materializeFunctionProperties(value) : isSandboxRegex(value) ? getRegexProperties(value) : value)) {
220
253
  throw new TypeError("Cannot change the prototype of a non-extensible object.");
221
254
  }
222
255
  let depth = 0;
@@ -0,0 +1,14 @@
1
+ import type { SandboxRegex } from "./values.js";
2
+ declare const regexpIteratorBrand: unique symbol;
3
+ export type SandboxRegExpIterator = {
4
+ readonly [regexpIteratorBrand]: true;
5
+ };
6
+ export type RegExpIteratorState = {
7
+ matcher: SandboxRegex | undefined;
8
+ input: string | undefined;
9
+ exhausted: boolean;
10
+ };
11
+ export declare function isSandboxRegExpIterator(value: unknown): value is SandboxRegExpIterator;
12
+ export declare function restoreSandboxRegExpIterator(state: RegExpIteratorState, target?: SandboxRegExpIterator): SandboxRegExpIterator;
13
+ export declare function regexpIteratorState(value: SandboxRegExpIterator): RegExpIteratorState;
14
+ export {};
@@ -0,0 +1,18 @@
1
+ const states = new WeakMap();
2
+ export function isSandboxRegExpIterator(value) {
3
+ return typeof value === "object" && value !== null && states.has(value);
4
+ }
5
+ export function restoreSandboxRegExpIterator(state, target = Object.create(null)) {
6
+ if (!state.exhausted && (state.matcher === undefined || state.input === undefined))
7
+ throw new TypeError("A live RegExp iterator requires its matcher and input.");
8
+ states.set(target, state.exhausted
9
+ ? { matcher: undefined, input: undefined, exhausted: true }
10
+ : { ...state });
11
+ return target;
12
+ }
13
+ export function regexpIteratorState(value) {
14
+ const state = states.get(value);
15
+ if (state === undefined)
16
+ throw new TypeError("Expected a RegExp string iterator.");
17
+ return state;
18
+ }
@@ -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
+ }
@@ -7,12 +7,13 @@ import { float32Storage, isFloat32Array } from "./float32.js";
7
7
  import { assertSandboxDataDepth } from "../graph-depth.js";
8
8
  import { isGuestHostObject } from "./host-capabilities.js";
9
9
  import { collectionIteratorState, isSandboxCollectionIterator } from "./collection-iterator.js";
10
+ import { isSandboxRegExpIterator } from "./regexp-iterator.js";
10
11
  import { getGuestFunctionProperties, getSandboxPrototype, hasExplicitSandboxPrototype, isGuestClosure } from "./object-model.js";
11
- import { getRegexMember } from "./methods/regex.js";
12
+ import { regexToString } from "./methods/regex.js";
12
13
  import { retainValues } from "./resources.js";
13
14
  import { functionString } from "./function-string.js";
14
15
  import { boxedValue, isSandboxBox } from "./boxed.js";
15
- import { isSandboxClosure, isSandboxPromise, isSandboxMap, isSandboxSet, isSandboxRegex, isSandboxGenerator } from "./values.js";
16
+ import { isSandboxClosure, isSandboxPromise, isSandboxMap, isSandboxSet, isSandboxRegex, getRegexProperties, isSandboxGenerator } from "./values.js";
16
17
  const defaultStringHook = Symbol("defaultStringHook");
17
18
  const defaultValueHook = Symbol("defaultValueHook");
18
19
  const joiningArrays = new WeakSet();
@@ -115,14 +116,16 @@ function conversionHook(value, name, budget, context) {
115
116
  isSandboxMap(value) ||
116
117
  isSandboxSet(value) ||
117
118
  isSandboxCollectionIterator(value) ||
119
+ isSandboxRegExpIterator(value) ||
118
120
  isSandboxPromise(value) ||
119
- isSandboxRegex(value) ||
121
+ (isSandboxRegex(value) && getSandboxPrototype(value, budget) === null) ||
120
122
  isSandboxGenerator(value) ||
121
123
  isGuestHostObject(value));
122
124
  let current = value;
123
125
  let depth = 0;
124
126
  while (current !== null) {
125
- const properties = isGuestClosure(current) ? getGuestFunctionProperties(current) : current;
127
+ const properties = isGuestClosure(current) ? getGuestFunctionProperties(current)
128
+ : isSandboxRegex(current) ? getRegexProperties(current) : current;
126
129
  const descriptor = properties === undefined ? undefined : Object.getOwnPropertyDescriptor(properties, name);
127
130
  if (descriptor !== undefined) {
128
131
  return readPropertyDescriptor(descriptor, value, context);
@@ -154,15 +157,16 @@ async function defaultToString(value, budget, context, joining) {
154
157
  return "[object Map]";
155
158
  if (isSandboxSet(value))
156
159
  return "[object Set]";
160
+ if (isSandboxRegExpIterator(value))
161
+ return "[object RegExp String Iterator]";
157
162
  if (isSandboxCollectionIterator(value))
158
163
  return collectionIteratorState(value).collectionKind === "map"
159
164
  ? "[object Map Iterator]"
160
165
  : "[object Set Iterator]";
161
166
  if (isSandboxGenerator(value))
162
167
  return "[object Generator]";
163
- if (isSandboxRegex(value)) {
164
- return budget.allocateString(`/${String(getRegexMember(value, "source", budget))}/${String(getRegexMember(value, "flags", budget))}`);
165
- }
168
+ if (isSandboxRegex(value))
169
+ return regexToString(value, budget, context);
166
170
  if (isSandboxDate(value))
167
171
  return budget.allocateString(dateString(value));
168
172
  if (Array.isArray(value) || isFloat32Array(value)) {
@@ -1,5 +1,7 @@
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";
4
+ import { type SandboxRegExpIterator } from "./regexp-iterator.js";
3
5
  import type { Budget, CompileTicket } from "./budget.js";
4
6
  import { CompileScope } from "./regex/compile-guard.js";
5
7
  import type { GeneratorChannel } from "./generator.js";
@@ -14,7 +16,7 @@ declare const sandboxRegexBrand: unique symbol;
14
16
  declare const sandboxRegexPattern: unique symbol;
15
17
  declare const sandboxRetainedValues: unique symbol;
16
18
  export type SandboxPrimitive = string | number | boolean | symbol | null | undefined;
17
- export type SandboxValue = SandboxPrimitive | Date | Float32Array | SandboxObject | SandboxArray | SandboxClosure | SandboxGenerator | SandboxCollectionIterator | SandboxMap | SandboxSet | SandboxPromise | SandboxRegex;
19
+ export type SandboxValue = SandboxPrimitive | Date | Float32Array | SandboxObject | SandboxArray | SandboxClosure | SandboxGenerator | SandboxCollectionIterator | SandboxRegExpIterator | SandboxMap | SandboxSet | SandboxPromise | SandboxRegex;
18
20
  export type SandboxObject = {
19
21
  [key: string]: SandboxValue;
20
22
  [key: symbol]: SandboxValue;
@@ -1,8 +1,11 @@
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";
8
+ import { regexpIteratorState, isSandboxRegExpIterator, restoreSandboxRegExpIterator } from "./regexp-iterator.js";
6
9
  import { copyNativeDate, dateDataProperties, exportDate, isSandboxDate } from "./date.js";
7
10
  import { boxedDataProperties, boxedValue, createSandboxBox, isSandboxBox, nativeBoxedValue } from "./boxed.js";
8
11
  import { getHostObjectKeys, getHostObjectMember, hasHostObjectMember, measureHostObjectData, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
@@ -98,6 +101,8 @@ export function ownEnumerableSandboxEntries(value, excludedKeys) {
98
101
  let entries;
99
102
  if (isGuestClosure(value))
100
103
  entries = Object.entries(value.properties ?? {});
104
+ else if (isSandboxRegex(value))
105
+ entries = Object.entries(getRegexProperties(value));
101
106
  else if (isSandboxClosure(value) || isSandboxGenerator(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxPromise(value) || isSandboxRegex(value))
102
107
  return [];
103
108
  else
@@ -111,13 +116,15 @@ export function ownSandboxSymbolKeys(value) {
111
116
  return [];
112
117
  if (isSandboxClosure(value))
113
118
  value = value.properties ?? {};
119
+ else if (isSandboxRegex(value))
120
+ value = getRegexProperties(value);
114
121
  else if (isSandboxGenerator(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxPromise(value) || isSandboxRegex(value))
115
122
  return [];
116
123
  return Object.getOwnPropertySymbols(Object(value)).filter(key => !internalSymbols.has(key));
117
124
  }
118
125
  export function ownEnumerableSandboxKeys(value, includeSymbols = false) {
119
126
  if (includeSymbols) {
120
- const properties = isSandboxClosure(value) ? value.properties ?? {} : Object(value);
127
+ const properties = isSandboxClosure(value) ? value.properties ?? {} : isSandboxRegex(value) ? getRegexProperties(value) : Object(value);
121
128
  return [...ownEnumerableSandboxKeys(value), ...ownSandboxSymbolKeys(value).filter(key => Object.getOwnPropertyDescriptor(properties, key)?.enumerable === true)];
122
129
  }
123
130
  if (isGuestHostObject(value))
@@ -126,6 +133,8 @@ export function ownEnumerableSandboxKeys(value, includeSymbols = false) {
126
133
  throw new TypeError("Cannot convert undefined or null to object.");
127
134
  if (isGuestClosure(value))
128
135
  return Object.keys(value.properties ?? {});
136
+ if (isSandboxRegex(value))
137
+ return Object.keys(getRegexProperties(value));
129
138
  if (isSandboxClosure(value) || isSandboxGenerator(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxPromise(value) || isSandboxRegex(value))
130
139
  return [];
131
140
  return Object.keys(Object(value));
@@ -218,6 +227,32 @@ export function createSandboxRegex(source, flags = "", lastIndex = 0, compilatio
218
227
  }
219
228
  const pattern = parseRegex(source, flags, compilation, 7 + source.length + flags.length);
220
229
  const regex = { kind: "regex", source, flags, lastIndex };
230
+ const storage = Object.create(null);
231
+ Object.defineProperty(storage, "lastIndex", { value: lastIndex, writable: true });
232
+ const syncCursor = () => {
233
+ const descriptor = Object.getOwnPropertyDescriptor(regex, "lastIndex");
234
+ Object.defineProperty(storage, "lastIndex", { value: descriptor.value, writable: descriptor.writable });
235
+ };
236
+ const properties = new Proxy(storage, {
237
+ get: (target, key, receiver) => key === "lastIndex" ? regex.lastIndex : Reflect.get(target, key, receiver),
238
+ set: (target, key, value, receiver) => key === "lastIndex"
239
+ ? Reflect.set(regex, key, value) : Reflect.set(target, key, value, receiver),
240
+ getOwnPropertyDescriptor(target, key) {
241
+ if (key === "lastIndex")
242
+ syncCursor();
243
+ return Object.getOwnPropertyDescriptor(target, key);
244
+ },
245
+ defineProperty(target, key, descriptor) {
246
+ if (key !== "lastIndex")
247
+ return Reflect.defineProperty(target, key, descriptor);
248
+ syncCursor();
249
+ if (!Reflect.defineProperty(target, key, descriptor))
250
+ return false;
251
+ const cursor = Object.getOwnPropertyDescriptor(target, key);
252
+ return Reflect.defineProperty(regex, key, { value: cursor.value, writable: cursor.writable });
253
+ }
254
+ });
255
+ regexGuestProperties.set(regex, properties);
221
256
  Object.defineProperties(regex, {
222
257
  [sandboxRegexBrand]: { value: true },
223
258
  [sandboxRegexPattern]: { value: pattern }
@@ -366,6 +401,17 @@ export function measureSandboxData(values, options = {}) {
366
401
  }
367
402
  return;
368
403
  }
404
+ if (isSandboxRegExpIterator(value)) {
405
+ const state = regexpIteratorState(value);
406
+ visit(state.matcher, depth + 1);
407
+ visit(state.input, depth + 1);
408
+ for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
409
+ usage += key.length + 1;
410
+ if ("value" in descriptor)
411
+ visit(descriptor.value, depth + 1);
412
+ }
413
+ return;
414
+ }
369
415
  if (isSandboxCollectionIterator(value)) {
370
416
  visit(collectionIteratorState(value).collection, depth + 1);
371
417
  for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
@@ -424,6 +470,19 @@ export function measureSandboxData(values, options = {}) {
424
470
  if (compiled.ticket !== undefined)
425
471
  options.compileTickets?.add(compiled.ticket);
426
472
  visit(lastIndex, depth + 1);
473
+ for (const key of Reflect.ownKeys(getRegexProperties(value))) {
474
+ if (key === "lastIndex")
475
+ continue;
476
+ const descriptor = Object.getOwnPropertyDescriptor(getRegexProperties(value), key);
477
+ usage += 1 + (typeof key === "string" ? key.length : 0);
478
+ if (typeof key === "symbol")
479
+ visit(key, depth + 1);
480
+ if ("value" in descriptor)
481
+ visit(descriptor.value, depth + 1);
482
+ else
483
+ for (const closure of retainedAccessorClosures(descriptor))
484
+ visit(closure, depth + 1);
485
+ }
427
486
  return;
428
487
  }
429
488
  if (isSandboxArguments(value)) {
@@ -491,7 +550,7 @@ function captureRegexData(value) {
491
550
  typeof flags.value !== "string") {
492
551
  throw new TypeError("Invalid sandbox RegExp source or flags: expected own string data properties.");
493
552
  }
494
- const cursor = Object.getOwnPropertyDescriptor(value, "lastIndex");
553
+ const cursor = Object.getOwnPropertyDescriptor(regexGuestProperties.get(value) ?? value, "lastIndex");
495
554
  if (cursor === undefined || !("value" in cursor)) {
496
555
  throw new TypeError("Invalid sandbox RegExp lastIndex: expected an own data property.");
497
556
  }
@@ -519,6 +578,21 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
519
578
  state.seen.set(value, copy);
520
579
  if (!state.resetRegexLastIndex)
521
580
  copy.lastIndex = copyToSandbox(value.lastIndex, state, `${path}.lastIndex`, true, depth + 1);
581
+ if (!state.structuredClone) {
582
+ const properties = getRegexProperties(value);
583
+ for (const key of Reflect.ownKeys(properties)) {
584
+ const descriptor = Object.getOwnPropertyDescriptor(properties, key);
585
+ if (!("value" in descriptor))
586
+ throw new TypeError("RegExp accessor properties cannot be copied as data.");
587
+ Object.defineProperty(getRegexProperties(copy), key, {
588
+ ...descriptor,
589
+ value: key === "lastIndex" && state.resetRegexLastIndex ? 0
590
+ : copyToSandbox(descriptor.value, state, `${path}.${String(key)}`, true, depth + 1)
591
+ });
592
+ }
593
+ if (!Object.isExtensible(properties))
594
+ Object.preventExtensions(getRegexProperties(copy));
595
+ }
522
596
  return copy;
523
597
  }
524
598
  if (isSandboxClosure(value) ||
@@ -526,6 +600,26 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
526
600
  isSandboxPromise(value)) {
527
601
  return value;
528
602
  }
603
+ if (isSandboxRegExpIterator(value)) {
604
+ if (hasGuestObjectState(value))
605
+ throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
606
+ if (!cloneSandboxCollections)
607
+ return value;
608
+ const existing = state.seen.get(value);
609
+ if (existing !== undefined)
610
+ return existing;
611
+ const snapshot = regexpIteratorState(value);
612
+ const copy = restoreSandboxRegExpIterator({ matcher: undefined, input: undefined, exhausted: true });
613
+ state.seen.set(value, copy);
614
+ const matcher = copyToSandbox(snapshot.matcher, state, `${path}.<matcher>`, true, depth + 1);
615
+ if (matcher !== undefined && !isSandboxRegex(matcher))
616
+ throw new TypeError("Invalid RegExp iterator matcher.");
617
+ restoreSandboxRegExpIterator({ ...snapshot, matcher }, copy);
618
+ for (const entry of getEnumerableObjectEntries(value, path)) {
619
+ defineOwnDataProperty(copy, entry.key, copyToSandbox(entry.value, state, joinPath(path, entry.key), true, depth + 1));
620
+ }
621
+ return copy;
622
+ }
529
623
  if (isSandboxCollectionIterator(value)) {
530
624
  if (hasGuestObjectState(value))
531
625
  throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
@@ -745,6 +839,18 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
745
839
  const regex = new RegExp(source, flags);
746
840
  state.seen.set(value, regex);
747
841
  Reflect.set(regex, "lastIndex", copyFromSandbox(lastIndex, state, `${path}.lastIndex`, options, depth + 1));
842
+ const properties = regexGuestProperties.get(value);
843
+ if (properties !== undefined) {
844
+ for (const key of Reflect.ownKeys(properties)) {
845
+ const descriptor = Object.getOwnPropertyDescriptor(properties, key);
846
+ if (!("value" in descriptor))
847
+ throw new TypeError("RegExp accessor properties cannot be copied as data.");
848
+ Object.defineProperty(regex, key, { ...descriptor,
849
+ value: copyFromSandbox(descriptor.value, state, `${path}.${String(key)}`, options, depth + 1) });
850
+ }
851
+ if (!Object.isExtensible(properties))
852
+ Object.preventExtensions(regex);
853
+ }
748
854
  guard.retainScratch();
749
855
  return regex;
750
856
  }
@@ -816,6 +922,9 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
816
922
  if (isSandboxCollectionIterator(value)) {
817
923
  throw new TypeError("Sandbox collection iterators cannot cross into host values.");
818
924
  }
925
+ if (isSandboxRegExpIterator(value)) {
926
+ throw new TypeError("Sandbox RegExp iterators cannot cross into host values.");
927
+ }
819
928
  if (isSandboxRegex(value)) {
820
929
  throw new TypeError("Invalid sandbox RegExp brand.");
821
930
  }
@@ -1,4 +1,7 @@
1
1
  export const DUMP_FORMAT_VERSION = 1;
2
+ import { getRegexProperties, isSandboxRegex } from "../interp/values.js";
3
+ import { isSandboxRegExpIterator, regexpIteratorState } from "../interp/regexp-iterator.js";
4
+ import { hasCustomRegexProperties, serializeRegexProperties } from "./regexp-properties.js";
2
5
  export const EXECUTION_SEMANTICS = "jobs-v8";
3
6
  import { assertSnapshotGraphDepth } from "../graph-depth.js";
4
7
  import { hasGuestObjectState } from "../interp/object-model.js";
@@ -79,7 +82,7 @@ function serializeDumpValue(value, path, state) {
79
82
  if (hasGuestObjectState(value)) {
80
83
  throw new TypeError("Guest function properties and prototype links cannot be serialized.");
81
84
  }
82
- if (isSandboxBox(value) || isSandboxDate(value) || isFloat32Array(value))
85
+ if (isSandboxRegExpIterator(value) || (isSandboxRegex(value) && hasCustomRegexProperties(value)) || isSandboxBox(value) || isSandboxDate(value) || isFloat32Array(value))
83
86
  return serializeHeapReference(value, path, state);
84
87
  if (Array.isArray(value)) {
85
88
  const reference = serializeHeapReference(value, path, state);
@@ -104,7 +107,39 @@ function serializeHeapReference(value, path, state) {
104
107
  }
105
108
  if (!state.serializedHeapIds.has(id)) {
106
109
  state.serializedHeapIds.add(id);
107
- if (isSandboxBox(value)) {
110
+ if (isSandboxRegExpIterator(value)) {
111
+ const snapshot = regexpIteratorState(value);
112
+ const matcher = snapshot.matcher;
113
+ const entries = Object.create(null);
114
+ state.heap[String(id)] = {
115
+ kind: "regexp-iterator", exhausted: snapshot.exhausted,
116
+ matcher: matcher === undefined ? { kind: "undefined" } : { kind: "regex", source: matcher.source, flags: matcher.flags, lastIndex: Number(matcher.lastIndex) },
117
+ input: snapshot.input ?? { kind: "undefined" }, entries,
118
+ symbolEntries: serializeSymbolProperties(value, entry => {
119
+ const serialized = serializeDumpValue(entry, `${path}.[symbol]`, state);
120
+ if (serialized === SKIP_VALUE)
121
+ throw new TypeError("Unsupported RegExp iterator symbol property in public dump.");
122
+ return serialized;
123
+ })
124
+ };
125
+ for (const [key, entry] of getEnumerableDataEntries(value)) {
126
+ const serialized = serializeDumpValue(entry, `${path}.${key}`, state);
127
+ if (serialized === SKIP_VALUE)
128
+ throw new TypeError("Unsupported RegExp iterator property in public dump.");
129
+ entries[key] = serialized;
130
+ }
131
+ }
132
+ else if (isSandboxRegex(value) && hasCustomRegexProperties(value)) {
133
+ const encode = (entry) => {
134
+ const serialized = serializeDumpValue(entry, `${path}.<regex-property>`, state);
135
+ if (serialized === SKIP_VALUE)
136
+ throw new TypeError("Unsupported RegExp property in public dump.");
137
+ return serialized;
138
+ };
139
+ state.heap[String(id)] = { kind: "regex-object", source: value.source, flags: value.flags,
140
+ lastIndex: encode(value.lastIndex), ...serializeRegexProperties(value, encode) };
141
+ }
142
+ else if (isSandboxBox(value)) {
108
143
  state.heap[String(id)] = encodeBoxedData(value, (entry, key) => {
109
144
  if (Object.is(entry, -0))
110
145
  return { kind: "number", value: "-0" };
@@ -194,6 +229,8 @@ function indexHeapContainers(snapshot) {
194
229
  stat.cyclic ||
195
230
  ownSerializableSymbolKeys(value).length > 0 ||
196
231
  isSandboxBox(value) ||
232
+ isSandboxRegExpIterator(value) ||
233
+ (isSandboxRegex(value) && hasCustomRegexProperties(value)) ||
197
234
  isSandboxDate(value) ||
198
235
  isFloat32Array(value) ||
199
236
  (Array.isArray(value) && requiresArrayEntries(value)) ||
@@ -231,13 +268,18 @@ function collectContainerStats(value, stats, ancestors) {
231
268
  }
232
269
  stat.expanded = true;
233
270
  ancestors.add(value);
234
- const entries = isSandboxDate(value)
235
- ? dateDataProperties(value).flatMap(([key, descriptor]) => typeof key === "string" ? [descriptor.value] : [])
236
- : isSandboxBox(value)
237
- ? boxedDataProperties(value).map(([, descriptor]) => descriptor.value)
238
- : isSandboxArguments(value)
239
- ? getSandboxArgumentEntries(value).map(([, entry]) => entry)
240
- : getEnumerableDataValues(value);
271
+ const entries = isSandboxRegex(value)
272
+ ? Reflect.ownKeys(getRegexProperties(value)).flatMap(key => {
273
+ const descriptor = Object.getOwnPropertyDescriptor(getRegexProperties(value), key);
274
+ return "value" in descriptor ? [descriptor.value] : [];
275
+ })
276
+ : isSandboxDate(value)
277
+ ? dateDataProperties(value).flatMap(([key, descriptor]) => typeof key === "string" ? [descriptor.value] : [])
278
+ : isSandboxBox(value)
279
+ ? boxedDataProperties(value).map(([, descriptor]) => descriptor.value)
280
+ : isSandboxArguments(value)
281
+ ? getSandboxArgumentEntries(value).map(([, entry]) => entry)
282
+ : getEnumerableDataValues(value);
241
283
  for (const entry of entries) {
242
284
  collectContainerStats(entry, stats, ancestors);
243
285
  }
@@ -0,0 +1,18 @@
1
+ import { type SandboxRegex } from "../interp/values.js";
2
+ import { type SerializedSymbolProperty } from "./symbols.js";
3
+ type DataProperty<T> = {
4
+ value: T;
5
+ enumerable: boolean;
6
+ writable: boolean;
7
+ configurable: boolean;
8
+ };
9
+ export type RegexPropertyData<T> = {
10
+ properties?: Record<string, DataProperty<T>>;
11
+ symbolEntries?: Array<SerializedSymbolProperty<T>>;
12
+ extensible?: boolean;
13
+ };
14
+ export declare function hasCustomRegexProperties(value: SandboxRegex): boolean;
15
+ export declare function serializeRegexProperties<T>(value: SandboxRegex, encode: (entry: unknown) => T): RegexPropertyData<T>;
16
+ export declare function validateRegexProperties(data: RegexPropertyData<unknown>): void;
17
+ export declare function restoreRegexProperties<T>(value: SandboxRegex, data: RegexPropertyData<T>, decode: (entry: T) => unknown): void;
18
+ export {};
@@ -0,0 +1,58 @@
1
+ import { getRegexProperties } from "../interp/values.js";
2
+ import { restoreSymbolProperties, serializeSymbolProperties } from "./symbols.js";
3
+ export function hasCustomRegexProperties(value) {
4
+ const properties = getRegexProperties(value);
5
+ return Reflect.ownKeys(properties).length !== 1 || !Object.isExtensible(properties) ||
6
+ Object.getOwnPropertyDescriptor(properties, "lastIndex")?.writable !== true;
7
+ }
8
+ export function serializeRegexProperties(value, encode) {
9
+ if (!hasCustomRegexProperties(value))
10
+ return {};
11
+ const source = getRegexProperties(value);
12
+ const properties = Object.create(null);
13
+ for (const key of Object.getOwnPropertyNames(source)) {
14
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
15
+ if (!("value" in descriptor))
16
+ throw new TypeError("RegExp accessor properties cannot be serialized as data.");
17
+ properties[key] = { value: encode(descriptor.value), enumerable: descriptor.enumerable === true,
18
+ configurable: descriptor.configurable === true, writable: descriptor.writable === true };
19
+ }
20
+ const symbolEntries = serializeSymbolProperties(source, encode);
21
+ return { properties, ...(symbolEntries.length === 0 ? {} : { symbolEntries }),
22
+ ...(Object.isExtensible(source) ? {} : { extensible: false }) };
23
+ }
24
+ export function validateRegexProperties(data) {
25
+ const descriptor = (value) => {
26
+ if (typeof value !== "object" || value === null || Array.isArray(value) ||
27
+ !Object.hasOwn(value, "value") ||
28
+ !["enumerable", "configurable", "writable"].every(key => Object.hasOwn(value, key) && typeof value[key] === "boolean") ||
29
+ Object.keys(value).some(key => !["value", "enumerable", "configurable", "writable"].includes(key)))
30
+ throw new TypeError("Invalid RegExp data property descriptor.");
31
+ };
32
+ if (data.extensible !== undefined && typeof data.extensible !== "boolean")
33
+ throw new TypeError("Invalid RegExp extensibility.");
34
+ if (data.properties !== undefined) {
35
+ if (typeof data.properties !== "object" || data.properties === null || Array.isArray(data.properties))
36
+ throw new TypeError("Invalid RegExp property data.");
37
+ for (const entry of Object.values(data.properties))
38
+ descriptor(entry);
39
+ }
40
+ if (data.symbolEntries !== undefined) {
41
+ if (!Array.isArray(data.symbolEntries))
42
+ throw new TypeError("Invalid RegExp symbol properties.");
43
+ for (const entry of data.symbolEntries) {
44
+ if (!Array.isArray(entry) || entry.length !== 2)
45
+ throw new TypeError("Invalid RegExp symbol property.");
46
+ descriptor(entry[1]);
47
+ }
48
+ }
49
+ }
50
+ export function restoreRegexProperties(value, data, decode) {
51
+ validateRegexProperties(data);
52
+ const properties = getRegexProperties(value);
53
+ for (const [key, descriptor] of Object.entries(data.properties ?? {}))
54
+ Object.defineProperty(properties, key, { ...descriptor, value: decode(descriptor.value) });
55
+ restoreSymbolProperties(properties, data.symbolEntries, decode);
56
+ if (data.extensible === false)
57
+ Object.preventExtensions(properties);
58
+ }