@poe-platform/safe-js 0.1.288 → 0.1.290

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.
package/README.md CHANGED
@@ -103,8 +103,6 @@ Current-time reads are recorded for replay; replay does not call `now()` again.
103
103
 
104
104
  Date values copy by value across host bindings, preserving aliases within a graph. Checkpoints preserve epoch values, invalid dates and mutations rather than converting dates to strings. Parsing is limited to 4,096 characters and consumes the work/string budgets; retained values consume data budget. Local methods and non-ISO parsing follow the host timezone/runtime, so use explicit-zone ISO strings and UTC methods for portable output.
105
105
 
106
- Unsupported: locale formatting, legacy `getYear`/`setYear`/`toGMTString`, subclassing, custom argument coercion, Date-instance own properties and prototype modification. These are restrictions, not stubs; no native constructor or prototype is exposed to guest code.
107
-
108
106
  </details>
109
107
 
110
108
  ## Add a host capability
@@ -6966,6 +6966,9 @@ var methodNames = [
6966
6966
  "setUTCSeconds",
6967
6967
  "toDateString",
6968
6968
  "toISOString",
6969
+ "toLocaleString",
6970
+ "toLocaleDateString",
6971
+ "toLocaleTimeString",
6969
6972
  "toString",
6970
6973
  "toTimeString",
6971
6974
  "toUTCString",
@@ -10498,6 +10501,96 @@ function formatNumber(value, methodName, argument, budget) {
10498
10501
 
10499
10502
  // packages/safe-js/src/interp/methods/string.ts
10500
10503
  import { types as types5 } from "node:util";
10504
+
10505
+ // packages/safe-js/src/interp/intl-options.ts
10506
+ var canonicalLocales = Intl.getCanonicalLocales;
10507
+ function readIntlProperty(value, key, budget, context) {
10508
+ budget.visitNode();
10509
+ if (context?.getProperty !== void 0) return context.getProperty(value, key);
10510
+ const descriptor = getSandboxPropertyDescriptor(value, key, budget);
10511
+ return descriptor === void 0 ? void 0 : readPropertyDescriptor(descriptor, value, context);
10512
+ }
10513
+ function intlOptionsObject(value, budget) {
10514
+ if (value === void 0) return /* @__PURE__ */ Object.create(null);
10515
+ if (value === null) throw new TypeError("Cannot convert null to an object.");
10516
+ if (typeof value === "object") return value;
10517
+ const box = createSandboxBox(value);
10518
+ allocateProducedSandboxValue(box, budget);
10519
+ return box;
10520
+ }
10521
+ async function canonicalizeGuestLocales(input, budget, context) {
10522
+ const locales = [];
10523
+ if (input === void 0) return locales;
10524
+ if (input === null) return Reflect.apply(canonicalLocales, Intl, [input]);
10525
+ const release = retainValues(budget, () => [locales]);
10526
+ try {
10527
+ const list2 = typeof input === "string" ? [input] : intlOptionsObject(input, budget);
10528
+ const count = await sandboxNumber(await readIntlProperty(list2, "length", budget, context), budget, context);
10529
+ const length = Number.isNaN(count) || count <= 0 ? 0 : Math.min(Math.floor(count), Number.MAX_SAFE_INTEGER);
10530
+ for (let index = 0; index < length; index++) {
10531
+ budget.visitNode();
10532
+ const key = String(index);
10533
+ if (getSandboxPropertyDescriptor(list2, key, budget) === void 0 && !(isFloat32Array(list2) && Object.hasOwn(list2, key))) continue;
10534
+ const value = await readIntlProperty(list2, key, budget, context);
10535
+ if (typeof value !== "string" && (typeof value !== "object" || value === null))
10536
+ return Reflect.apply(canonicalLocales, Intl, [[value]]);
10537
+ const tag = await sandboxString(value, budget, context);
10538
+ budget.visitNode(tag.length);
10539
+ const canonical = canonicalLocales(tag)[0];
10540
+ budget.visitNode(locales.length);
10541
+ if (!locales.includes(canonical)) locales.push(budget.allocateString(canonical));
10542
+ }
10543
+ return locales;
10544
+ } finally {
10545
+ release();
10546
+ }
10547
+ }
10548
+ async function convertIntlOption(value, key, type, budget, context, invalid) {
10549
+ if (type === "boolean") return Boolean(value);
10550
+ const text = await sandboxString(value, budget, context);
10551
+ budget.visitNode(text.length);
10552
+ if (type === "unicodeType") {
10553
+ const valid = text.split("-").every((part) => part.length >= 3 && part.length <= 8 && [...part].every((char) => char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char >= "0" && char <= "9"));
10554
+ if (valid) return text;
10555
+ } else if (type.includes(text)) return text;
10556
+ invalid?.(text);
10557
+ throw new RangeError(`Invalid ${key} option.`);
10558
+ }
10559
+
10560
+ // packages/safe-js/src/interp/methods/string-locale.ts
10561
+ var collatorOptions = [
10562
+ ["usage", ["sort", "search"]],
10563
+ ["localeMatcher", ["lookup", "best fit"]],
10564
+ ["collation", "unicodeType"],
10565
+ ["numeric", "boolean"],
10566
+ ["caseFirst", ["upper", "lower", "false"]],
10567
+ ["sensitivity", ["base", "accent", "case", "variant"]],
10568
+ ["ignorePunctuation", "boolean"]
10569
+ ];
10570
+ async function compareStringLocale(value, args, budget, context) {
10571
+ let comparison = "";
10572
+ let locales = [];
10573
+ const options = /* @__PURE__ */ Object.create(null);
10574
+ const release = retainValues(budget, () => [value, comparison, locales, options]);
10575
+ try {
10576
+ comparison = await sandboxString(args[0], budget, context);
10577
+ locales = await canonicalizeGuestLocales(args[1], budget, context);
10578
+ if (args[2] === null) return Reflect.apply(String.prototype.localeCompare, value, [comparison, locales, null]);
10579
+ const inputOptions = intlOptionsObject(args[2], budget);
10580
+ for (const [key, type] of collatorOptions) {
10581
+ const option = await readIntlProperty(inputOptions, key, budget, context);
10582
+ if (option !== void 0) options[key] = await convertIntlOption(option, key, type, budget, context, (text) => {
10583
+ Reflect.apply(String.prototype.localeCompare, "", ["", [], { [key]: text }]);
10584
+ });
10585
+ }
10586
+ budget.visitNode(value.length + comparison.length);
10587
+ return Reflect.apply(String.prototype.localeCompare, value, [comparison, locales, options]);
10588
+ } finally {
10589
+ release();
10590
+ }
10591
+ }
10592
+
10593
+ // packages/safe-js/src/interp/methods/string.ts
10501
10594
  var stringMethodNames = /* @__PURE__ */ new Set([
10502
10595
  "at",
10503
10596
  "charAt",
@@ -10682,6 +10775,8 @@ function callStringMethodBody(value, methodName, args, budget, parent, context)
10682
10775
  if ((methodName === "match" || methodName === "matchAll" || methodName === "search") && !isSandboxRegex(args[0])) {
10683
10776
  return callStringPattern(value, methodName, args[0], budget, parent, context);
10684
10777
  }
10778
+ if (methodName === "localeCompare" && context?.getProperty !== void 0)
10779
+ return compareStringLocale(value, args, budget, context);
10685
10780
  const operation = budget.acquireCompileOwner(false, parent?.owner);
10686
10781
  const compilation = new CompileScope(operation.owner);
10687
10782
  try {
@@ -20366,6 +20461,73 @@ async function parseJsonWithReviver(text, reviver, budget, context) {
20366
20461
  }
20367
20462
  }
20368
20463
 
20464
+ // packages/safe-js/src/interp/date-locale.ts
20465
+ var NativeDateTimeFormat = Intl.DateTimeFormat;
20466
+ var dateTimeOptions = [
20467
+ ["localeMatcher", ["lookup", "best fit"]],
20468
+ ["calendar", "unicodeType"],
20469
+ ["numberingSystem", "unicodeType"],
20470
+ ["hour12", "boolean"],
20471
+ ["hourCycle", ["h11", "h12", "h23", "h24"]],
20472
+ ["timeZone", "zone"],
20473
+ ["weekday", ["narrow", "short", "long"]],
20474
+ ["era", ["narrow", "short", "long"]],
20475
+ ["year", ["2-digit", "numeric"]],
20476
+ ["month", ["2-digit", "numeric", "narrow", "short", "long"]],
20477
+ ["day", ["2-digit", "numeric"]],
20478
+ ["dayPeriod", ["narrow", "short", "long"]],
20479
+ ["hour", ["2-digit", "numeric"]],
20480
+ ["minute", ["2-digit", "numeric"]],
20481
+ ["second", ["2-digit", "numeric"]],
20482
+ ["fractionalSecondDigits", "number"],
20483
+ ["timeZoneName", ["short", "long", "shortOffset", "longOffset", "shortGeneric", "longGeneric"]],
20484
+ ["formatMatcher", ["basic", "best fit"]],
20485
+ ["dateStyle", ["full", "long", "medium", "short"]],
20486
+ ["timeStyle", ["full", "long", "medium", "short"]]
20487
+ ];
20488
+ async function formatDateLocale(name, time, args, budget, context) {
20489
+ if (Number.isNaN(time)) return budget.allocateString("Invalid Date");
20490
+ let locales = [];
20491
+ const options = /* @__PURE__ */ Object.create(null);
20492
+ const release = retainValues(budget, () => [locales, options]);
20493
+ try {
20494
+ locales = await canonicalizeGuestLocales(args[0], budget, context);
20495
+ const inputOptions = intlOptionsObject(args[1], budget);
20496
+ for (const [key, type] of dateTimeOptions) {
20497
+ const value = await readIntlProperty(inputOptions, key, budget, context);
20498
+ if (value === void 0) continue;
20499
+ if (type === "number") {
20500
+ const number = await sandboxNumber(value, budget, context);
20501
+ if (!Number.isFinite(number) || number < 1 || number > 3)
20502
+ throw new RangeError("fractionalSecondDigits must be between 1 and 3.");
20503
+ options[key] = Math.floor(number);
20504
+ } else if (type === "zone") {
20505
+ const text = await sandboxString(value, budget, context);
20506
+ budget.visitNode(text.length);
20507
+ new NativeDateTimeFormat("en", { timeZone: text });
20508
+ options[key] = text;
20509
+ } else options[key] = await convertIntlOption(value, key, type, budget, context);
20510
+ }
20511
+ const date = name !== "toLocaleTimeString";
20512
+ const clock = name !== "toLocaleDateString";
20513
+ if (options.dateStyle !== void 0 || options.timeStyle !== void 0) {
20514
+ if (!date && options.dateStyle !== void 0 || !clock && options.timeStyle !== void 0)
20515
+ throw new TypeError("Date/time style is incompatible with this locale method.");
20516
+ } else {
20517
+ const hasDate = date && ["weekday", "year", "month", "day"].some((key) => options[key] !== void 0);
20518
+ const hasTime = clock && ["dayPeriod", "hour", "minute", "second", "fractionalSecondDigits"].some((key) => options[key] !== void 0);
20519
+ if (!hasDate && !hasTime) {
20520
+ if (date) for (const key of ["year", "month", "day"]) options[key] = "numeric";
20521
+ if (clock) for (const key of ["hour", "minute", "second"]) options[key] = "numeric";
20522
+ }
20523
+ }
20524
+ const formatter = new NativeDateTimeFormat(locales, options);
20525
+ return budget.allocateString(formatter.format(time));
20526
+ } finally {
20527
+ release();
20528
+ }
20529
+ }
20530
+
20369
20531
  // packages/safe-js/src/snapshot/replay-data.ts
20370
20532
  var MissingReplayCapabilityError = class extends TypeError {
20371
20533
  };
@@ -21943,6 +22105,7 @@ function createDateGlobal(options) {
21943
22105
  if (!isSandboxDate(receiver))
21944
22106
  throw new TypeError(`Date#${name} requires a Date receiver.`);
21945
22107
  options.budget.visitNode();
22108
+ if (name.startsWith("toLocale")) return formatDateLocale(name, dateTime(receiver), args, options.budget, context);
21946
22109
  if (name.startsWith("set")) return coerceDateSetter(name, receiver, args, options.budget, context);
21947
22110
  const value = method.invoke(receiver, args);
21948
22111
  return typeof value === "string" ? options.budget.allocateString(value) : value;
@@ -40340,4 +40503,4 @@ export {
40340
40503
  FileSnapshotBackend,
40341
40504
  run
40342
40505
  };
40343
- //# sourceMappingURL=chunk-K45MDTVS.js.map
40506
+ //# sourceMappingURL=chunk-GK6XA4H4.js.map