@poe-platform/safe-js 0.1.196 → 0.1.198

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.
@@ -27,7 +27,7 @@ import {
27
27
  validateMigrationSemantics,
28
28
  validateSnapshotData,
29
29
  validateSnapshotMigration
30
- } from "./chunk-KMQUDIYF.js";
30
+ } from "./chunk-T43UKNMF.js";
31
31
 
32
32
  // packages/safe-js/src/migrate.ts
33
33
  import { createHash } from "node:crypto";
@@ -8249,4 +8249,4 @@ export {
8249
8249
  parseMcpConfig,
8250
8250
  makeMcpModule
8251
8251
  };
8252
- //# sourceMappingURL=chunk-UODKI7OO.js.map
8252
+ //# sourceMappingURL=chunk-4WIZV3GZ.js.map
@@ -711,7 +711,7 @@ var RegexCompileGuard = class {
711
711
  this.work(flags.length);
712
712
  }
713
713
  retain(pattern, valueUnits = 0) {
714
- const units = 10 + pattern.source.length + measureNode(pattern.body);
714
+ const units = measurePattern(pattern);
715
715
  if (this.ticket !== void 0) {
716
716
  this.ticket.owner.budget.resizeCompileTicket(this.ticket, units + valueUnits);
717
717
  }
@@ -728,7 +728,15 @@ var RegexCompileGuard = class {
728
728
  }
729
729
  };
730
730
  function regexCompiledData(pattern) {
731
- return compiledData.get(pattern) ?? { units: 10 + pattern.source.length + measureNode(pattern.body) };
731
+ return compiledData.get(pattern) ?? { units: measurePattern(pattern) };
732
+ }
733
+ function measurePattern(pattern) {
734
+ let units = 10 + pattern.source.length + measureNode(pattern.body);
735
+ if (pattern.groups !== void 0) {
736
+ units += 1;
737
+ for (const [name, indices] of Object.entries(pattern.groups)) units += name.length + 2 + indices.length;
738
+ }
739
+ return units;
732
740
  }
733
741
  function measureNode(node) {
734
742
  switch (node.type) {
@@ -737,6 +745,8 @@ function measureNode(node) {
737
745
  return 2;
738
746
  case "literal":
739
747
  return 3 + node.value.length;
748
+ case "namedBackreference":
749
+ return 3 + node.name.length;
740
750
  case "anchor":
741
751
  case "wordBoundary":
742
752
  case "backreference":
@@ -756,7 +766,7 @@ function measureNode(node) {
756
766
  return usage;
757
767
  }
758
768
  case "group":
759
- return 5 + measureNode(node.body);
769
+ return 5 + measureNode(node.body) + (node.name === void 0 ? 0 : 1 + node.name.length);
760
770
  case "lookahead":
761
771
  case "lookbehind":
762
772
  return 4 + measureNode(node.body);
@@ -2049,6 +2059,8 @@ function createIdentifier(token, name) {
2049
2059
  }
2050
2060
 
2051
2061
  // packages/safe-js/src/interp/regex/parse.ts
2062
+ var groupNameStart = /^[$_\p{ID_Start}]$/u;
2063
+ var groupNamePart = /^[$\u200c\u200d\p{ID_Continue}]$/u;
2052
2064
  function parseRegex(source, flags = "", compilation, valueUnits = 0) {
2053
2065
  const guard = new RegexCompileGuard(compilation);
2054
2066
  try {
@@ -2060,6 +2072,7 @@ function parseRegex(source, flags = "", compilation, valueUnits = 0) {
2060
2072
  const body = parser.parse();
2061
2073
  guard.allocate(5 + source.length);
2062
2074
  const pattern = { source, flags: parsedFlags, captureCount: parser.captureCount, body };
2075
+ if (Object.keys(parser.namedGroups).length > 0) pattern.groups = parser.namedGroups;
2063
2076
  guard.retain(pattern, valueUnits);
2064
2077
  return pattern;
2065
2078
  } finally {
@@ -2076,6 +2089,8 @@ var RegexParser = class {
2076
2089
  captureCount = 0;
2077
2090
  cursor = 0;
2078
2091
  totalCaptureCount;
2092
+ hasNamedCaptures = false;
2093
+ namedGroups = /* @__PURE__ */ Object.create(null);
2079
2094
  get position() {
2080
2095
  return this.cursor;
2081
2096
  }
@@ -2091,6 +2106,7 @@ var RegexParser = class {
2091
2106
  }
2092
2107
  this.fail(`Unexpected character '${this.peek()}'`);
2093
2108
  }
2109
+ if (Object.keys(this.namedGroups).length > 0) this.validateNamedGroups(body);
2094
2110
  return body;
2095
2111
  }
2096
2112
  parseAlternation() {
@@ -2167,6 +2183,7 @@ var RegexParser = class {
2167
2183
  }
2168
2184
  parseGroup(start) {
2169
2185
  let capturing = true;
2186
+ let name;
2170
2187
  let assertionNegated;
2171
2188
  let lookbehind = false;
2172
2189
  if (this.peek() === "?") {
@@ -2186,13 +2203,20 @@ var RegexParser = class {
2186
2203
  lookbehind = true;
2187
2204
  this.position += 3;
2188
2205
  } else if (extension.startsWith("?<")) {
2189
- this.fail("Named groups are not supported", start);
2206
+ this.position += 2;
2207
+ name = this.parseGroupName(start);
2190
2208
  } else {
2191
2209
  this.fail("Unsupported group construct", start);
2192
2210
  }
2193
2211
  }
2194
2212
  if (capturing) this.guard.allocate(1);
2195
2213
  const index = capturing ? ++this.captureCount : void 0;
2214
+ if (name !== void 0) {
2215
+ this.guard.allocate(name.length + 2);
2216
+ const indices = this.namedGroups[name] ??= [];
2217
+ this.guard.array(indices.length + 1);
2218
+ indices.push(index);
2219
+ }
2196
2220
  this.guard.enterGroup();
2197
2221
  let body;
2198
2222
  try {
@@ -2209,7 +2233,7 @@ var RegexParser = class {
2209
2233
  return { type: lookbehind ? "lookbehind" : "lookahead", negated: assertionNegated, body };
2210
2234
  }
2211
2235
  this.guard.allocate(5);
2212
- return { type: "group", capturing, index, body };
2236
+ return { type: "group", capturing, index, body, ...name === void 0 ? {} : { name } };
2213
2237
  }
2214
2238
  parseCharacterClass(start) {
2215
2239
  const negated = this.peek() === "^";
@@ -2269,6 +2293,15 @@ var RegexParser = class {
2269
2293
  this.fail("Trailing escape", start);
2270
2294
  }
2271
2295
  const escaped = this.take();
2296
+ if (escaped === "k") {
2297
+ this.totalCaptureCount ??= this.countAllCaptures();
2298
+ if (this.hasNamedCaptures) {
2299
+ if (inCharacterClass || this.take() !== "<") this.fail("Invalid named backreference", start);
2300
+ const name = this.parseGroupName(start);
2301
+ this.guard.allocate(3 + name.length);
2302
+ return { type: "namedBackreference", name };
2303
+ }
2304
+ }
2272
2305
  if (isDecimalDigit2(escaped)) {
2273
2306
  if (!inCharacterClass && escaped !== "0") {
2274
2307
  let end = this.position;
@@ -2358,10 +2391,62 @@ var RegexParser = class {
2358
2391
  else if (character === "\\") escaped = true;
2359
2392
  else if (character === "[") characterClass = true;
2360
2393
  else if (character === "]") characterClass = false;
2361
- else if (!characterClass && character === "(" && this.source[index + 1] !== "?") count++;
2394
+ else if (!characterClass && character === "(") {
2395
+ const named = this.source[index + 1] === "?" && this.source[index + 2] === "<" && this.source[index + 3] !== "=" && this.source[index + 3] !== "!";
2396
+ if (named) this.hasNamedCaptures = true;
2397
+ if (named || this.source[index + 1] !== "?") count++;
2398
+ }
2362
2399
  }
2363
2400
  return count;
2364
2401
  }
2402
+ parseGroupName(start) {
2403
+ let name = "";
2404
+ while (!this.atEnd() && this.peek() !== ">") {
2405
+ let character = this.take();
2406
+ if (character === "\\") {
2407
+ if (this.take() !== "u") this.fail("Invalid group name escape", start);
2408
+ if (this.peek() === "{") {
2409
+ this.position++;
2410
+ const begin = this.position;
2411
+ while (!this.atEnd() && this.peek() !== "}") this.position++;
2412
+ this.guard.allocate(this.position - begin);
2413
+ const digits = this.source.slice(begin, this.position);
2414
+ const point = Number.parseInt(digits, 16);
2415
+ if (this.take() !== "}" || digits.length === 0 || !allHexDigits(digits) || point > 1114111)
2416
+ this.fail("Invalid group name escape", start);
2417
+ character = String.fromCodePoint(point);
2418
+ } else character = this.parseHexEscape(4, "Unicode", start);
2419
+ }
2420
+ this.guard.allocate(character.length);
2421
+ name += character;
2422
+ }
2423
+ if (this.take() !== ">" || name.length === 0) this.fail("Invalid group name", start);
2424
+ let first = true;
2425
+ for (const character of name) {
2426
+ this.guard.work(1);
2427
+ if (!(first ? groupNameStart : groupNamePart).test(character)) this.fail("Invalid group name", start);
2428
+ first = false;
2429
+ }
2430
+ return name;
2431
+ }
2432
+ validateNamedGroups(node) {
2433
+ this.guard.work(1);
2434
+ this.guard.allocate(1);
2435
+ const names = /* @__PURE__ */ new Set();
2436
+ if (node.type === "namedBackreference" && !Object.hasOwn(this.namedGroups, node.name))
2437
+ this.fail("Unknown named backreference");
2438
+ if (node.type === "group" && node.name !== void 0) names.add(node.name);
2439
+ 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] : [];
2440
+ for (const child of children) {
2441
+ for (const name of this.validateNamedGroups(child)) {
2442
+ this.guard.work(1);
2443
+ if (names.has(name) && node.type !== "alternation") this.fail("Duplicate capture group name");
2444
+ this.guard.allocate(1);
2445
+ names.add(name);
2446
+ }
2447
+ }
2448
+ return names;
2449
+ }
2365
2450
  parseQuantifier() {
2366
2451
  const character = this.peek();
2367
2452
  if (character === "*") {
@@ -7694,6 +7779,9 @@ function getSandboxPrototype(value, budget) {
7694
7779
  function hasExplicitSandboxPrototype(value) {
7695
7780
  return prototypes.has(value);
7696
7781
  }
7782
+ function hasNullObjectPrototype(value) {
7783
+ return prototypes.get(value) === null && !Array.isArray(value) && !isSandboxClosure(value) && !isSandboxRegex(value);
7784
+ }
7697
7785
  function getSandboxPropertyDescriptor(value, key, budget) {
7698
7786
  const hostProperties = isSandboxClosure(value) ? value.properties : void 0;
7699
7787
  if (isSandboxClosure(value) && !isGuestClosure(value))
@@ -7775,7 +7863,7 @@ function hasGuestObjectState(value) {
7775
7863
  const intrinsicUnchanged = intrinsicConstructors.get(value);
7776
7864
  if (intrinsicUnchanged !== void 0) return !intrinsicUnchanged();
7777
7865
  if (isLiveCapability(value)) return true;
7778
- if (functionProperties.has(value) || prototypes.has(value)) return true;
7866
+ if (functionProperties.has(value) || prototypes.has(value) && !hasNullObjectPrototype(value)) return true;
7779
7867
  if (isSandboxBox(value) || isSandboxDate(value)) return false;
7780
7868
  return descriptorObjects.has(value) && Object.values(Object.getOwnPropertyDescriptors(value)).some(
7781
7869
  (descriptor) => !descriptor.enumerable || !descriptor.configurable || !descriptor.writable
@@ -8096,6 +8184,7 @@ function serializeHeapReference(value, path, state) {
8096
8184
  });
8097
8185
  state.heap[String(id)] = {
8098
8186
  kind: "object",
8187
+ ...hasNullObjectPrototype(value) ? { sandboxNullPrototype: true } : {},
8099
8188
  entries: serializeObjectEntries(value, path, state),
8100
8189
  ...symbolEntries.length === 0 ? {} : { symbolEntries },
8101
8190
  ...errorType === void 0 ? {} : { errorType }
@@ -8129,7 +8218,7 @@ function indexHeapContainers(snapshot) {
8129
8218
  const heapIds = /* @__PURE__ */ new Map();
8130
8219
  let nextId = 1;
8131
8220
  for (const [value, stat2] of stats.entries()) {
8132
- 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)) {
8221
+ 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)) {
8133
8222
  heapIds.set(value, nextId);
8134
8223
  nextId += 1;
8135
8224
  }
@@ -8315,6 +8404,8 @@ function validateDumpHeap(root, state) {
8315
8404
  continue;
8316
8405
  }
8317
8406
  if (entry.kind === "object") {
8407
+ if (Object.hasOwn(entry, "sandboxNullPrototype") && entry.sandboxNullPrototype !== true)
8408
+ fail("invalidValue", `${path}.sandboxNullPrototype`, "invalid object prototype");
8318
8409
  requireRecord(entry.entries, `${path}.entries`);
8319
8410
  continue;
8320
8411
  }
@@ -8435,7 +8526,11 @@ function validateTaggedValue(record2, path, state) {
8435
8526
  return;
8436
8527
  case "array":
8437
8528
  case "arguments":
8529
+ return;
8438
8530
  case "object":
8531
+ if (Object.hasOwn(record2, "sandboxNullPrototype") && record2.sandboxNullPrototype !== true)
8532
+ fail("invalidValue", `${path}.sandboxNullPrototype`, "invalid object prototype");
8533
+ return;
8439
8534
  case "map":
8440
8535
  case "set":
8441
8536
  case "collection-iterator":
@@ -9363,7 +9458,7 @@ function matchRegexFrom(pattern, input, startIndex) {
9363
9458
  return null;
9364
9459
  }
9365
9460
  for (let attempt = startIndex; attempt <= input.length; attempt += 1) {
9366
- const context = { input, flags: pattern.flags, direction: 1, work: { steps: 0 } };
9461
+ const context = { input, flags: pattern.flags, groups: pattern.groups, direction: 1, work: { steps: 0 } };
9367
9462
  charge(context);
9368
9463
  const initialState = {
9369
9464
  position: attempt,
@@ -9371,7 +9466,7 @@ function matchRegexFrom(pattern, input, startIndex) {
9371
9466
  };
9372
9467
  const result = matchNode(pattern.body, initialState, context).next();
9373
9468
  if (!result.done) {
9374
- return toRegexMatch(input, attempt, result.value, pattern.flags.hasIndices);
9469
+ return toRegexMatch(input, attempt, result.value, pattern.flags.hasIndices, pattern.groups);
9375
9470
  }
9376
9471
  if (pattern.flags.sticky) break;
9377
9472
  }
@@ -9389,8 +9484,19 @@ function* matchNode(node, state, context) {
9389
9484
  yield { ...state, position: state.position + context.direction };
9390
9485
  }
9391
9486
  return;
9392
- case "backreference": {
9393
- const capture = state.captures[node.index - 1];
9487
+ case "backreference":
9488
+ case "namedBackreference": {
9489
+ let capture;
9490
+ if (node.type === "backreference") capture = state.captures[node.index - 1];
9491
+ else {
9492
+ for (const index of context.groups?.[node.name] ?? []) {
9493
+ charge(context);
9494
+ if (state.captures[index - 1] !== void 0) {
9495
+ capture = state.captures[index - 1];
9496
+ break;
9497
+ }
9498
+ }
9499
+ }
9394
9500
  if (capture === void 0) {
9395
9501
  yield state;
9396
9502
  return;
@@ -9535,7 +9641,7 @@ function matchesCharacterClassItem(character, item, ignoreCase) {
9535
9641
  const matched = item.kind === "digit" ? isDigit(character) : item.kind === "word" ? isWordCharacter(character) : isSpaceCharacter(character);
9536
9642
  return item.negated ? !matched : matched;
9537
9643
  }
9538
- function toRegexMatch(input, start, state, hasIndices) {
9644
+ function toRegexMatch(input, start, state, hasIndices, groups) {
9539
9645
  const match = {
9540
9646
  index: start,
9541
9647
  text: input.slice(start, state.position),
@@ -9546,6 +9652,15 @@ function toRegexMatch(input, start, state, hasIndices) {
9546
9652
  if (hasIndices) {
9547
9653
  match.indices = [[start, state.position], ...state.captures.map((capture) => capture === void 0 ? void 0 : [capture.start, capture.end])];
9548
9654
  }
9655
+ if (groups !== void 0) {
9656
+ match.groups = /* @__PURE__ */ Object.create(null);
9657
+ if (hasIndices) match.indicesGroups = /* @__PURE__ */ Object.create(null);
9658
+ for (const [name, indices] of Object.entries(groups)) {
9659
+ const index = indices.find((index2) => state.captures[index2 - 1] !== void 0);
9660
+ match.groups[name] = index === void 0 ? void 0 : match.captures[index - 1];
9661
+ if (match.indicesGroups !== void 0) match.indicesGroups[name] = index === void 0 ? void 0 : match.indices[index];
9662
+ }
9663
+ }
9549
9664
  return match;
9550
9665
  }
9551
9666
  function cloneState(state) {
@@ -10168,7 +10283,7 @@ function callReplaceLikeMethod(value, methodName, args, budget, callClosure, use
10168
10283
  );
10169
10284
  }
10170
10285
  async function replaceRegex(value, regex, replacement, budget, callClosure, context) {
10171
- if (hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget))
10286
+ if (getSandboxRegexPattern(regex).groups !== void 0 || hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget))
10172
10287
  return regexReplace(value, regex, replacement, budget, callClosure, context);
10173
10288
  if (regex.flags.includes("g")) regex.lastIndex = 0;
10174
10289
  const cursor = regex.lastIndex;
@@ -10218,7 +10333,7 @@ async function regexReplace(input, regex, replacementInput, budget, callClosure,
10218
10333
  input = void 0;
10219
10334
  replacement = isSandboxClosure(replacementInput) ? replacementInput : await sandboxString(replacementInput, budget, context);
10220
10335
  replacementInput = void 0;
10221
- if (isSandboxRegex(regex) && !hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget)) {
10336
+ if (isSandboxRegex(regex) && getSandboxRegexPattern(regex).groups === void 0 && !hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget)) {
10222
10337
  release();
10223
10338
  return await replaceRegex(value, regex, replacement, budget, callClosure, context);
10224
10339
  }
@@ -16819,11 +16934,13 @@ function toMatchArray(match, input, budget) {
16819
16934
  }
16820
16935
  budget?.allocateArrayLength(match.captures.length + 1);
16821
16936
  const result = [match.text, ...match.captures];
16822
- Object.assign(result, { index: match.index, input, groups: void 0 });
16937
+ if (match.groups !== void 0) setSandboxPrototype(match.groups, null, budget);
16938
+ if (match.indicesGroups !== void 0) setSandboxPrototype(match.indicesGroups, null, budget);
16939
+ Object.assign(result, { index: match.index, input, groups: match.groups });
16823
16940
  if (match.indices !== void 0) {
16824
16941
  budget?.allocateArrayLength(match.indices.length);
16825
16942
  budget?.allocateArrayLength(2);
16826
- Object.assign(match.indices, { groups: void 0 });
16943
+ Object.assign(match.indices, { groups: match.indicesGroups });
16827
16944
  Object.assign(result, { indices: match.indices });
16828
16945
  }
16829
16946
  return result;
@@ -17970,6 +18087,7 @@ function encodeReplayData(value, options = {}) {
17970
18087
  nodes[id] = {
17971
18088
  kind: Array.isArray(entry) ? "array" : "object",
17972
18089
  nullPrototype: prototype === null,
18090
+ ...hasNullObjectPrototype(entry) ? { sandboxNullPrototype: true } : {},
17973
18091
  ...errorType === void 0 ? {} : { errorType },
17974
18092
  extensible: Object.isExtensible(entry),
17975
18093
  properties,
@@ -18192,6 +18310,10 @@ function decodeReplayData(input, options = {}, parent) {
18192
18310
  throw new TypeError("Invalid replay object metadata.");
18193
18311
  }
18194
18312
  const result2 = kind === "array" ? [] : Object.create(node.nullPrototype ? null : Object.prototype);
18313
+ if (Object.hasOwn(node, "sandboxNullPrototype")) {
18314
+ if (kind !== "object" || node.sandboxNullPrototype !== true) throw new TypeError("Invalid replay object prototype.");
18315
+ setSandboxPrototype(result2, null);
18316
+ }
18195
18317
  if (Object.hasOwn(node, "errorType")) {
18196
18318
  sandboxErrorTypes.set(result2, node.errorType);
18197
18319
  }
@@ -21827,6 +21949,7 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
21827
21949
  const copy = createPlainObject(
21828
21950
  !cloneSandboxCollections || Object.getPrototypeOf(value) === null
21829
21951
  );
21952
+ if (!state.structuredClone && hasNullObjectPrototype(value)) setSandboxPrototype(copy, null);
21830
21953
  state.seen.set(value, copy);
21831
21954
  const errorType = sandboxErrorTypes.get(value);
21832
21955
  if (errorType !== void 0) sandboxErrorTypes.set(copy, errorType);
@@ -22038,7 +22161,7 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
22038
22161
  if (existing !== void 0) {
22039
22162
  return existing;
22040
22163
  }
22041
- const copy = createPlainObject(Object.getPrototypeOf(value) === null);
22164
+ const copy = createPlainObject(hasNullObjectPrototype(value) || Object.getPrototypeOf(value) === null);
22042
22165
  state.seen.set(value, copy);
22043
22166
  for (const entry of getEnumerableObjectEntries(value, path)) {
22044
22167
  defineOwnDataProperty(
@@ -37166,4 +37289,4 @@ export {
37166
37289
  FileSnapshotBackend,
37167
37290
  run
37168
37291
  };
37169
- //# sourceMappingURL=chunk-KMQUDIYF.js.map
37292
+ //# sourceMappingURL=chunk-T43UKNMF.js.map