@rohal12/spindle 0.59.13 → 0.59.14

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.
@@ -9893,6 +9893,86 @@ function parseMarkup(source, options = {}) {
9893
9893
  function tokenizeMarkup(source, options = {}) {
9894
9894
  return run$2(source, "Tokens", options);
9895
9895
  }
9896
+ /**
9897
+ * `tokenizeMarkup` for half-typed markup: the tokens it can read, and every
9898
+ * error, instead of throwing at the first. At a malformed tag, the tokens
9899
+ * before it are kept, its first character is read as text and tokenizing
9900
+ * resumes after it, so what follows is read as if the tag had not been
9901
+ * started. Tokens and errors have offsets in `source`; for well-formed
9902
+ * markup the tokens are those of `tokenizeMarkup` and there are no errors.
9903
+ */
9904
+ function tokenizeMarkupTolerant(source, options = {}) {
9905
+ const tokens = [];
9906
+ const errors = [];
9907
+ const lastBrace = source.lastIndexOf("}");
9908
+ const { closeBrace } = {
9909
+ ...defaultHooks,
9910
+ ...options.hooks
9911
+ };
9912
+ /** `text` shifted by `base`, tokenized: the tokens or the error. */
9913
+ const attempt = (text, base) => {
9914
+ const hooks = {
9915
+ ...options.hooks,
9916
+ closeBrace: (input, codeStart, lenientStart) => base + Math.min(codeStart, lenientStart) > lastBrace ? -1 : closeBrace(input, codeStart, lenientStart)
9917
+ };
9918
+ try {
9919
+ return tokenizeMarkup(text, {
9920
+ ...options,
9921
+ hooks
9922
+ }).map((token) => shift(token, base));
9923
+ } catch (error) {
9924
+ if (!(error instanceof MarkupError)) throw error;
9925
+ return error;
9926
+ }
9927
+ };
9928
+ let base = 0;
9929
+ while (base <= source.length) {
9930
+ const rest = source.slice(base);
9931
+ const result = attempt(rest, base);
9932
+ if (Array.isArray(result)) {
9933
+ tokens.push(...result);
9934
+ break;
9935
+ }
9936
+ const at = base + result.offset;
9937
+ const { line, column } = lineColumn(source, at);
9938
+ errors.push(new MarkupError(result.reason, at, line, column));
9939
+ let cut = result.offset;
9940
+ let prefix = attempt(rest.slice(0, cut), base);
9941
+ while (!Array.isArray(prefix)) {
9942
+ cut = prefix.offset;
9943
+ prefix = attempt(rest.slice(0, cut), base);
9944
+ }
9945
+ tokens.push(...prefix);
9946
+ if (cut < rest.length) {
9947
+ const start = base + cut;
9948
+ tokens.push({
9949
+ type: "text",
9950
+ value: rest[cut],
9951
+ start,
9952
+ end: start + 1
9953
+ });
9954
+ }
9955
+ base += cut + 1;
9956
+ }
9957
+ return {
9958
+ tokens,
9959
+ errors
9960
+ };
9961
+ }
9962
+ /** `token` with its offsets moved `by` on. */
9963
+ function shift(token, by) {
9964
+ if (by === 0) return token;
9965
+ const moved = {
9966
+ ...token,
9967
+ start: token.start + by,
9968
+ end: token.end + by
9969
+ };
9970
+ if (moved.type === "link") {
9971
+ moved.targetStart += by;
9972
+ moved.targetEnd += by;
9973
+ }
9974
+ return moved;
9975
+ }
9896
9976
  //#endregion
9897
9977
  //#region src/utils/namespace.ts
9898
9978
  /** The variable name no namespace can hold. */
@@ -10567,28 +10647,34 @@ function parseWidgetDef(rawArgs) {
10567
10647
  return widgetDef(parseMacroArgs(rawArgs, WIDGET_PARAMETERS));
10568
10648
  }
10569
10649
  /**
10570
- * A definition: its arguments (read like a {widget} macro's, see
10571
- * parseWidgetDef) and its body. Macro names are not case-sensitive.
10572
- */
10573
- var WIDGET_DEFINITION = /\{widget\s+([^}]*)\}([\s\S]*?)\{\/widget\}/gi;
10574
- /** `{@children}`, also with selectors such as `{.highlight @children}`. */
10575
- var CHILDREN_PLACEHOLDER = /\{[^{}]*@children\s*\}/;
10576
- /**
10577
10650
  * The names of the block widgets (those whose body renders `{@children}`)
10578
10651
  * that StoryInit and the passages tagged `widget` define. They must be
10579
10652
  * known as block macros before any passage is parsed, so that passages
10580
10653
  * invoking them nest their content whatever the passage order.
10654
+ *
10655
+ * The passages are read as flat tokens, which need no block macros to be
10656
+ * known, so a `{@children}` in an HTML comment or in a `{do}` body is text
10657
+ * here as it is to the parser, and does not make a block widget (#387).
10581
10658
  */
10582
10659
  function blockWidgetNames(passages) {
10583
10660
  const names = [];
10584
10661
  for (const passage of passages) {
10585
10662
  if (passage.name !== "StoryInit" && !passage.tags?.includes("widget")) continue;
10586
- for (const match of passage.content.matchAll(WIDGET_DEFINITION)) {
10587
- if (!CHILDREN_PLACEHOLDER.test(match[2])) continue;
10663
+ const open = [];
10664
+ for (const token of tokenizeMarkupTolerant(passage.content).tokens) if (token.type === "macro" && token.name.toLowerCase() === "widget") {
10665
+ if (!token.isClose) {
10666
+ open.push({
10667
+ rawArgs: token.rawArgs,
10668
+ isBlock: false
10669
+ });
10670
+ continue;
10671
+ }
10672
+ const def = open.pop();
10673
+ if (!def?.isBlock) continue;
10588
10674
  try {
10589
- names.push(parseWidgetDef(match[1]).name);
10675
+ names.push(parseWidgetDef(def.rawArgs).name);
10590
10676
  } catch {}
10591
- }
10677
+ } else if (token.type === "variable" && token.scope === "local" && token.name === "children" && open.length > 0) open[open.length - 1].isBlock = true;
10592
10678
  }
10593
10679
  return names;
10594
10680
  }
@@ -11108,6 +11194,13 @@ function wrapContent(className, id, content) {
11108
11194
  function hasInterpolation(s) {
11109
11195
  return s.includes("{") || s.includes("}");
11110
11196
  }
11197
+ /** The children context for an invocation's `children`, or null if none. */
11198
+ function widgetChildrenOf(children, outer) {
11199
+ return children?.length ? {
11200
+ nodes: children,
11201
+ outer: outer ?? null
11202
+ } : null;
11203
+ }
11111
11204
  var parseCache = /* @__PURE__ */ new Map();
11112
11205
  var PARSE_CACHE_LIMIT = 2e3;
11113
11206
  /**
@@ -11193,7 +11286,7 @@ var TextEvaluator = class {
11193
11286
  if (node.scope === "local" && node.name === "children") {
11194
11287
  const children = scope.widgetChildren;
11195
11288
  if (!children) return "";
11196
- return this.nested("children", children, scope, depth, { widgetChildren: null });
11289
+ return this.nested("children", children.nodes, scope, depth, { widgetChildren: children.outer });
11197
11290
  }
11198
11291
  return this.variable(node.scope, node.name, scope);
11199
11292
  case "expression": try {
@@ -11256,7 +11349,7 @@ var TextEvaluator = class {
11256
11349
  }
11257
11350
  return this.nested(node.name, widget.body, scope, depth, {
11258
11351
  locals,
11259
- widgetChildren: node.children.length > 0 ? node.children : null
11352
+ widgetChildren: widgetChildrenOf(node.children, scope.widgetChildren)
11260
11353
  });
11261
11354
  }
11262
11355
  const text = getMacroText(node.name);
@@ -11514,7 +11607,8 @@ function WidgetInvocation(props) {
11514
11607
  function WidgetContent({ body, params, rawArgs, invocationChildren }) {
11515
11608
  const parentValues = useRenderOptions().locals;
11516
11609
  const [mergedVars, mergedTemps, mergedLocals, mergedTrans] = useMergedLocals();
11517
- const childrenValue = invocationChildren?.length ? invocationChildren : null;
11610
+ const outerChildren = useContext(WidgetChildrenContext);
11611
+ const childrenValue = useMemo(() => widgetChildrenOf(invocationChildren, outerChildren), [invocationChildren, outerChildren]);
11518
11612
  const argExprs = rawArgs ? splitArgs(rawArgs) : [];
11519
11613
  const values = [];
11520
11614
  for (let i = 0; i < params.length; i++) {
@@ -12282,10 +12376,13 @@ function HtmlNodeRenderer({ node }) {
12282
12376
  }) : element);
12283
12377
  }
12284
12378
  function ChildrenSlot() {
12285
- const childrenAST = useContext(WidgetChildrenContext);
12379
+ const children = useContext(WidgetChildrenContext);
12286
12380
  const renderOptions = useRenderOptions();
12287
- if (!childrenAST || childrenAST.length === 0) return null;
12288
- return /* @__PURE__ */ jsx(Fragment$1, { children: renderNodes(childrenAST, renderOptions) });
12381
+ if (!children) return null;
12382
+ return /* @__PURE__ */ jsx(WidgetChildrenContext.Provider, {
12383
+ value: children.outer,
12384
+ children: renderNodes(children.nodes, renderOptions)
12385
+ });
12289
12386
  }
12290
12387
  /**
12291
12388
  * Stable per-node keys. Keying rendered children by AST node identity (not
@@ -13194,6 +13291,39 @@ function stringify_primitive(thing) {
13194
13291
  return String(thing);
13195
13292
  }
13196
13293
  //#endregion
13294
+ //#region src/utils/value-kinds.ts
13295
+ var toStringTag = (value) => Object.prototype.toString.call(value).slice(8, -1);
13296
+ /** Temporal values are immutable: copies may share them. */
13297
+ var isTemporal = (value) => toStringTag(value).startsWith("Temporal.");
13298
+ /** new Number(), new String(), new Boolean(), Object(1n). */
13299
+ var isBoxed = (value) => value instanceof Number || value instanceof String || value instanceof Boolean || toStringTag(value) === "BigInt";
13300
+ /**
13301
+ * Values compared and copied as a whole, by their content: they have no
13302
+ * own keys to merge by, or their keys are not the data (a property written
13303
+ * into one would be dropped by clones and saves, and by Immer for Map and
13304
+ * Set).
13305
+ */
13306
+ function isAtomic(value) {
13307
+ return value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || ArrayBuffer.isView(value) || value instanceof ArrayBuffer || value instanceof URL || value instanceof URLSearchParams || value instanceof Error || isBoxed(value) || isTemporal(value);
13308
+ }
13309
+ /** "a Map", "an Error", ... for an atomic value (see isAtomic), else undefined. */
13310
+ function atomicName(value) {
13311
+ if (!isAtomic(value)) return void 0;
13312
+ const tag = value instanceof Error ? "Error" : toStringTag(value);
13313
+ return `${/^[AEIOU]/.test(tag) ? "an" : "a"} ${tag}`;
13314
+ }
13315
+ /** Whether `key` is an index of an array element. */
13316
+ var isIndexKey = (key) => key !== "4294967295" && String(Number(key) >>> 0) === key;
13317
+ /**
13318
+ * The own enumerable string keys of a collection (an Array, Map or Set) that
13319
+ * are not its elements: what an instance of a subclass may hold besides
13320
+ * them.
13321
+ */
13322
+ function extraKeys(collection) {
13323
+ const keys = Object.keys(collection);
13324
+ return Array.isArray(collection) ? keys.filter((k) => !isIndexKey(k)) : keys;
13325
+ }
13326
+ //#endregion
13197
13327
  //#region src/class-registry.ts
13198
13328
  var registry = /* @__PURE__ */ new Map();
13199
13329
  var ctorToName = /* @__PURE__ */ new Map();
@@ -13233,6 +13363,9 @@ var ERROR_HIDDEN_KEYS$1 = [
13233
13363
  "errors"
13234
13364
  ];
13235
13365
  var isObject = (v) => typeof v === "object" && v !== null;
13366
+ /** Whether `v` is a plain object (as devalue revives one). */
13367
+ var isPlainData = (v) => isObject(v) && Object.getPrototypeOf(v) === Object.prototype;
13368
+ var malformedClass = (name) => /* @__PURE__ */ new TypeError(`spindle: Malformed data for class "${name}"`);
13236
13369
  var defineData = (target, key, value) => {
13237
13370
  Object.defineProperty(target, key, {
13238
13371
  value,
@@ -13252,15 +13385,49 @@ function refuseSymbolKeys(value) {
13252
13385
  * Own enumerable keys of `value`, as a plain object; for an error also its
13253
13386
  * message, cause and (AggregateError) errors, which are not enumerable.
13254
13387
  */
13255
- function ownData(value) {
13388
+ function ownData(value, keys = Object.keys(value)) {
13256
13389
  refuseSymbolKeys(value);
13257
13390
  const data = {};
13258
13391
  if (value instanceof Error) {
13259
13392
  for (const key of ERROR_HIDDEN_KEYS$1) if (hasOwn(value, key)) defineData(data, key, value[key]);
13260
13393
  }
13261
- for (const key of Object.keys(value)) defineData(data, key, value[key]);
13394
+ for (const key of keys) defineData(data, key, value[key]);
13262
13395
  return data;
13263
13396
  }
13397
+ /** The kinds of collection a registered class may extend. */
13398
+ var COLLECTION_KINDS = {
13399
+ Array,
13400
+ Map,
13401
+ Set
13402
+ };
13403
+ /**
13404
+ * The saved form of a registered class instance: its own data (see
13405
+ * ownData), or for a collection its kind, elements and extra keys, read
13406
+ * with the built-in methods, which a subclass may override (#391).
13407
+ */
13408
+ function classData(value) {
13409
+ const extra = () => ownData(value, extraKeys(value));
13410
+ if (Array.isArray(value)) {
13411
+ const items = new Array(value.length);
13412
+ for (let i = 0; i < value.length; i++) if (i in value) items[i] = value[i];
13413
+ return [
13414
+ "Array",
13415
+ items,
13416
+ extra()
13417
+ ];
13418
+ }
13419
+ if (value instanceof Map) return [
13420
+ "Map",
13421
+ Array.from(Map.prototype.entries.call(value)),
13422
+ extra()
13423
+ ];
13424
+ if (value instanceof Set) return [
13425
+ "Set",
13426
+ Array.from(Set.prototype.values.call(value)),
13427
+ extra()
13428
+ ];
13429
+ return ownData(value);
13430
+ }
13264
13431
  /** Make an error's message, cause and errors non-enumerable again. */
13265
13432
  function hideErrorKeys(value) {
13266
13433
  for (const key of ERROR_HIDDEN_KEYS$1) if (hasOwn(value, key)) Object.defineProperty(value, key, { enumerable: false });
@@ -13269,7 +13436,7 @@ function hideErrorKeys(value) {
13269
13436
  var isEscapedOneCharKey = (key) => key.length === 1 && JSON.stringify(key).length > 3;
13270
13437
  function reducers() {
13271
13438
  const out = {};
13272
- for (const [name, ctor] of registry) out[CLASS_PREFIX + name] = (v) => isObject(v) && Object.getPrototypeOf(v) === ctor.prototype && ownData(v);
13439
+ for (const [name, ctor] of registry) out[CLASS_PREFIX + name] = (v) => isObject(v) && Object.getPrototypeOf(v) === ctor.prototype && classData(v);
13273
13440
  for (const [proto, name] of BUILTIN_ERRORS) out[ERROR_PREFIX + name] = (v) => isObject(v) && Object.getPrototypeOf(v) === proto && ownData(v);
13274
13441
  out[SYMBOL_TAG] = (v) => {
13275
13442
  if (typeof v !== "symbol") return false;
@@ -13282,7 +13449,7 @@ function reducers() {
13282
13449
  v.lastIndex
13283
13450
  ];
13284
13451
  out[KEYS_TAG] = (v) => {
13285
- if (!isObject(v) || Object.getPrototypeOf(v) !== Object.prototype) return false;
13452
+ if (!isPlainData(v)) return false;
13286
13453
  const keys = Object.keys(v);
13287
13454
  if (!keys.some(isEscapedOneCharKey)) return false;
13288
13455
  refuseSymbolKeys(v);
@@ -13291,27 +13458,71 @@ function reducers() {
13291
13458
  };
13292
13459
  return out;
13293
13460
  }
13461
+ /**
13462
+ * The collection of a registered class that a `[kind, contents, keys]`
13463
+ * list holds (see classData). A cycle through the collection revives it
13464
+ * before its list is complete (devalue holds unread parts as holes), so the
13465
+ * collection is kept per list and filled with what the list holds so far.
13466
+ */
13467
+ var collections = /* @__PURE__ */ new WeakMap();
13468
+ function reviveCollection(name, ctor, list) {
13469
+ const [kind, contents, keys] = list;
13470
+ const base = typeof kind === "string" && hasOwn(COLLECTION_KINDS, kind) ? COLLECTION_KINDS[kind] : void 0;
13471
+ if (list.length !== 3 || !base || !(ctor.prototype instanceof base) || hasOwn(list, 1) && !Array.isArray(contents) || hasOwn(list, 2) && !isPlainData(keys)) throw malformedClass(name);
13472
+ let made = collections.get(list);
13473
+ if (!made) {
13474
+ made = Object.setPrototypeOf(new base(), ctor.prototype);
13475
+ collections.set(list, made);
13476
+ }
13477
+ if (Array.isArray(contents)) fillCollection(made, contents);
13478
+ if (isPlainData(keys)) for (const key of Object.keys(keys)) defineData(made, key, keys[key]);
13479
+ return made;
13480
+ }
13481
+ /** Set the elements of a revived collection, with the built-in methods. */
13482
+ function fillCollection(made, contents) {
13483
+ if (Array.isArray(made)) {
13484
+ made.length = contents.length;
13485
+ for (let i = 0; i < contents.length; i++) if (hasOwn(contents, i)) made[i] = contents[i];
13486
+ else delete made[i];
13487
+ } else if (made instanceof Map) {
13488
+ Map.prototype.clear.call(made);
13489
+ for (const entry of contents) {
13490
+ if (!Array.isArray(entry) || entry.length !== 2) throw new TypeError("spindle: Malformed map entries");
13491
+ Map.prototype.set.call(made, entry[0], entry[1]);
13492
+ }
13493
+ } else {
13494
+ Set.prototype.clear.call(made);
13495
+ for (const member of contents) Set.prototype.add.call(made, member);
13496
+ }
13497
+ }
13294
13498
  /** Restore a registered class instance in place, so cycles through it hold. */
13295
13499
  function reviveClass(name) {
13296
13500
  return (data) => {
13297
- if (!isObject(data) || Object.getPrototypeOf(data) !== Object.prototype) {
13501
+ if (Array.isArray(data)) return reviveCollection(name, registeredCtor(name), data);
13502
+ if (!isPlainData(data)) {
13298
13503
  if (isObject(data) && registeredClassName(data) === name) return data;
13299
- throw new TypeError(`spindle: Malformed data for class "${name}"`);
13504
+ throw malformedClass(name);
13300
13505
  }
13301
- const ctor = registry.get(name);
13302
- if (!ctor) throw new TypeError(`spindle: The save holds an instance of class "${name}", which is not registered`);
13506
+ const ctor = registeredCtor(name);
13507
+ if (Object.values(COLLECTION_KINDS).some((c) => ctor.prototype instanceof c)) throw malformedClass(name);
13303
13508
  Object.setPrototypeOf(data, ctor.prototype);
13304
13509
  if (data instanceof Error) hideErrorKeys(data);
13305
13510
  return data;
13306
13511
  };
13307
13512
  }
13513
+ /** The class registered as `name`; throws if there is none. */
13514
+ function registeredCtor(name) {
13515
+ const ctor = registry.get(name);
13516
+ if (!ctor) throw new TypeError(`spindle: The save holds an instance of class "${name}", which is not registered`);
13517
+ return ctor;
13518
+ }
13308
13519
  /** Restore a built-in error in place, like a class instance. */
13309
13520
  function reviveError(name) {
13310
13521
  const ctor = ERROR_CTORS.get(name);
13311
13522
  if (!ctor) return void 0;
13312
13523
  return (data) => {
13313
13524
  if (Object.getPrototypeOf(data) === ctor.prototype) return data;
13314
- if (!isObject(data) || Object.getPrototypeOf(data) !== Object.prototype) throw new TypeError(`spindle: Malformed data for "${name}"`);
13525
+ if (!isPlainData(data)) throw new TypeError(`spindle: Malformed data for "${name}"`);
13315
13526
  Object.setPrototypeOf(data, ctor.prototype);
13316
13527
  hideErrorKeys(data);
13317
13528
  return data;
@@ -13908,28 +14119,6 @@ function getBackendType() {
13908
14119
  return _backend?.type ?? "memory";
13909
14120
  }
13910
14121
  //#endregion
13911
- //#region src/utils/value-kinds.ts
13912
- var toStringTag = (value) => Object.prototype.toString.call(value).slice(8, -1);
13913
- /** Temporal values are immutable: copies may share them. */
13914
- var isTemporal = (value) => toStringTag(value).startsWith("Temporal.");
13915
- /** new Number(), new String(), new Boolean(), Object(1n). */
13916
- var isBoxed = (value) => value instanceof Number || value instanceof String || value instanceof Boolean || toStringTag(value) === "BigInt";
13917
- /**
13918
- * Values compared and copied as a whole, by their content: they have no
13919
- * own keys to merge by, or their keys are not the data (a property written
13920
- * into one would be dropped by clones and saves, and by Immer for Map and
13921
- * Set).
13922
- */
13923
- function isAtomic(value) {
13924
- return value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || ArrayBuffer.isView(value) || value instanceof ArrayBuffer || value instanceof URL || value instanceof URLSearchParams || value instanceof Error || isBoxed(value) || isTemporal(value);
13925
- }
13926
- /** "a Map", "an Error", ... for an atomic value (see isAtomic), else undefined. */
13927
- function atomicName(value) {
13928
- if (!isAtomic(value)) return void 0;
13929
- const tag = value instanceof Error ? "Error" : toStringTag(value);
13930
- return `${/^[AEIOU]/.test(tag) ? "an" : "a"} ${tag}`;
13931
- }
13932
- //#endregion
13933
14122
  //#region src/utils/object-path.ts
13934
14123
  /**
13935
14124
  * Traverse dot-path segments on an object and return the nested value.
@@ -14058,6 +14247,20 @@ function isPlainObject(value) {
14058
14247
  const proto = Object.getPrototypeOf(value);
14059
14248
  return proto === Object.prototype || proto === null;
14060
14249
  }
14250
+ /**
14251
+ * `empty`, a new collection (Array, Map or Set) standing for `obj`, made
14252
+ * like it: an instance of a registered subclass keeps its class and its
14253
+ * extra keys (#391), with the values `value` gives for them; other
14254
+ * collections are plain. `made` gets it before its keys are filled, so
14255
+ * that cycles through it find it.
14256
+ */
14257
+ function collectionLike(obj, empty, made, value) {
14258
+ made(empty);
14259
+ if (registeredClassName(obj) === void 0) return empty;
14260
+ Object.setPrototypeOf(empty, Object.getPrototypeOf(obj));
14261
+ for (const key of extraKeys(obj)) setOwn(empty, key, value(key));
14262
+ return empty;
14263
+ }
14061
14264
  /** Own keys of an error that are not enumerable but are data. */
14062
14265
  var ERROR_HIDDEN_KEYS = [
14063
14266
  "message",
@@ -14119,23 +14322,25 @@ function deepClone(value, options = {}) {
14119
14322
  const settle = options.settle;
14120
14323
  return settle && isObjectValue(copy) && isObjectValue(original) ? settle(copy, original) : copy;
14121
14324
  }
14325
+ /** `empty`, made the copy of the collection `obj` (see collectionLike). */
14326
+ const collection = (obj, empty) => collectionLike(obj, empty, (copy) => keep(obj, copy), (key) => clone(obj[key]));
14122
14327
  function clone(val) {
14123
14328
  if (val === null || typeof val !== "object") return val;
14124
14329
  const obj = val;
14125
14330
  if (seen.has(obj)) return seen.get(obj);
14126
14331
  if (Array.isArray(val)) {
14127
- const arr = keep(obj, new Array(val.length));
14332
+ const arr = collection(obj, new Array(val.length));
14128
14333
  for (let i = 0; i < val.length; i++) if (i in val) arr[i] = clone(val[i]);
14129
14334
  return arr;
14130
14335
  }
14131
14336
  if (val instanceof Map) {
14132
- const copy = keep(obj, /* @__PURE__ */ new Map());
14133
- for (const [k, v] of val) copy.set(settled(clone(k), k), clone(v));
14337
+ const copy = collection(obj, /* @__PURE__ */ new Map());
14338
+ for (const [k, v] of val) Map.prototype.set.call(copy, settled(clone(k), k), clone(v));
14134
14339
  return copy;
14135
14340
  }
14136
14341
  if (val instanceof Set) {
14137
- const copy = keep(obj, /* @__PURE__ */ new Set());
14138
- for (const v of val) copy.add(settled(clone(v), v));
14342
+ const copy = collection(obj, /* @__PURE__ */ new Set());
14343
+ for (const v of val) Set.prototype.add.call(copy, settled(clone(v), v));
14139
14344
  return copy;
14140
14345
  }
14141
14346
  const builtin = cloneBuiltin(obj);
@@ -14238,23 +14443,26 @@ function shareEqual(prev, curr) {
14238
14443
  if (placed !== void 0) return placed;
14239
14444
  if (!isObjectValue(p)) return fresh(c);
14240
14445
  if (reuse(p, c)) return p;
14446
+ const pr = p;
14447
+ const cr = c;
14448
+ /** What the result holds at `key` of `c`. */
14449
+ const shareKey = (key) => share(hasOwn(pr, key) ? pr[key] : void 0, cr[key]);
14450
+ /** `empty`, made the result for the collection `c` (see collectionLike). */
14451
+ const collection = (empty) => collectionLike(c, empty, (out) => done.set(c, out), shareKey);
14241
14452
  if (c instanceof Map && p instanceof Map) {
14242
- const out = /* @__PURE__ */ new Map();
14243
- done.set(c, out);
14244
- for (const [k, v] of c) out.set(fresh(k), share(p.get(k), v));
14453
+ const out = collection(/* @__PURE__ */ new Map());
14454
+ for (const [k, v] of c) Map.prototype.set.call(out, fresh(k), share(p.get(k), v));
14245
14455
  return out;
14246
14456
  }
14247
14457
  if (Array.isArray(c) && Array.isArray(p)) {
14248
- const out = new Array(c.length);
14249
- done.set(c, out);
14458
+ const out = collection(new Array(c.length));
14250
14459
  for (let i = 0; i < c.length; i++) if (i in c) out[i] = share(p[i], c[i]);
14251
14460
  return out;
14252
14461
  }
14253
14462
  if (mergesWith(p, c, false)) {
14254
14463
  const out = Object.create(Object.getPrototypeOf(c));
14255
14464
  done.set(c, out);
14256
- const cr = c;
14257
- for (const key of Object.keys(cr)) setOwn(out, key, share(hasOwn(p, key) ? p[key] : void 0, cr[key]));
14465
+ for (const key of Object.keys(cr)) setOwn(out, key, shareKey(key));
14258
14466
  return out;
14259
14467
  }
14260
14468
  return fresh(c);
@@ -14283,14 +14491,27 @@ function equalBuiltin(a, b, assumed) {
14283
14491
  if (isTemporal(a)) return String(a) === String(b);
14284
14492
  if (a instanceof Error) return ERROR_HIDDEN_KEYS.every((key) => hasOwn(a, key) === hasOwn(b, key) && equal(a[key], b[key], assumed)) && equalKeys(a, b, assumed);
14285
14493
  }
14286
- function equalKeys(a, b, assumed) {
14494
+ function equalKeys(a, b, assumed, keysOf = Object.keys) {
14287
14495
  const ao = a;
14288
14496
  const bo = b;
14289
- const keys = Object.keys(ao);
14290
- if (keys.length !== Object.keys(bo).length) return false;
14497
+ const keys = keysOf(ao);
14498
+ if (keys.length !== keysOf(bo).length) return false;
14291
14499
  for (const key of keys) if (!hasOwn(bo, key) || !equal(ao[key], bo[key], assumed)) return false;
14292
14500
  return true;
14293
14501
  }
14502
+ /** Built-in collection prototypes, whose instances hold no extra keys. */
14503
+ var COLLECTION_PROTOS = new Set([
14504
+ Array.prototype,
14505
+ Map.prototype,
14506
+ Set.prototype
14507
+ ]);
14508
+ /**
14509
+ * Equality of the extra keys (see extraKeys) of two collections of one
14510
+ * class: those of a subclass instance are part of its value.
14511
+ */
14512
+ function equalExtraKeys(a, b, assumed) {
14513
+ return COLLECTION_PROTOS.has(Object.getPrototypeOf(a)) || equalKeys(a, b, assumed, extraKeys);
14514
+ }
14294
14515
  /** `assumed` tracks the pairs of objects met (see Pairs). */
14295
14516
  function equal(a, b, assumed) {
14296
14517
  if (Object.is(a, b) && !(assumed.strict && isObjectValue(a))) return true;
@@ -14304,7 +14525,7 @@ function equal(a, b, assumed) {
14304
14525
  const ai = a.entries();
14305
14526
  const bi = bc.entries();
14306
14527
  for (let x = ai.next(), y = bi.next(); !x.done; x = ai.next(), y = bi.next()) if (!equal(x.value, y.value, assumed)) return false;
14307
- return true;
14528
+ return equalExtraKeys(a, b, assumed);
14308
14529
  }
14309
14530
  const builtin = equalBuiltin(a, b, assumed);
14310
14531
  if (builtin !== void 0) return builtin;
@@ -14312,7 +14533,7 @@ function equal(a, b, assumed) {
14312
14533
  const bc = b;
14313
14534
  if (a.length !== bc.length) return false;
14314
14535
  for (let i = 0; i < a.length; i++) if (i in a !== i in bc || !equal(a[i], bc[i], assumed)) return false;
14315
- return true;
14536
+ return equalExtraKeys(a, b, assumed);
14316
14537
  }
14317
14538
  return equalKeys(a, b, assumed);
14318
14539
  }
@@ -14331,7 +14552,7 @@ function isMergeable(value) {
14331
14552
  * one class (see isMergeable), or, with `arrays`, both are arrays.
14332
14553
  */
14333
14554
  function mergesWith(a, b, arrays) {
14334
- if (arrays && (Array.isArray(a) || Array.isArray(b))) return Array.isArray(a) && Array.isArray(b);
14555
+ if (arrays && (Array.isArray(a) || Array.isArray(b))) return Array.isArray(a) && Array.isArray(b) && Object.getPrototypeOf(a) === Object.getPrototypeOf(b);
14335
14556
  return isMergeable(a) && isMergeable(b) && Object.getPrototypeOf(a) === Object.getPrototypeOf(b);
14336
14557
  }
14337
14558
  /**
@@ -15605,6 +15826,11 @@ function deeplyFrozen(value, seen = /* @__PURE__ */ new Set()) {
15605
15826
  if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return true;
15606
15827
  if (!Object.isFrozen(value)) return false;
15607
15828
  seen.add(value);
15829
+ if (value instanceof Map) {
15830
+ for (const [key, entry] of value) if (!deeplyFrozen(key, seen) || !deeplyFrozen(entry, seen)) return false;
15831
+ } else if (value instanceof Set) {
15832
+ for (const member of value) if (!deeplyFrozen(member, seen)) return false;
15833
+ }
15608
15834
  return Reflect.ownKeys(value).every((key) => {
15609
15835
  const desc = Object.getOwnPropertyDescriptor(value, key);
15610
15836
  return !desc || !("value" in desc) || deeplyFrozen(desc.value, seen);
@@ -17307,15 +17533,34 @@ var FOCUSABLE = [
17307
17533
  "[tabindex]"
17308
17534
  ].join(",");
17309
17535
  /**
17536
+ * Whether `el` is in a closed `<details>`, other than in the summary that
17537
+ * toggles it: the browser renders none of that content (#389).
17538
+ */
17539
+ function inClosedDetails(el) {
17540
+ for (let e = el.parentElement; e; e = e.parentElement) {
17541
+ if (e.localName !== "details" || e.hasAttribute("open")) continue;
17542
+ if (!Array.from(e.children).find((c) => c.localName === "summary")?.contains(el)) return true;
17543
+ }
17544
+ return false;
17545
+ }
17546
+ /**
17310
17547
  * Whether CSS leaves `el` in the tab sequence: not `visibility: hidden`
17311
- * (inherited, so the computed value covers ancestors) and not inside a
17312
- * `display: none` element.
17548
+ * (inherited, so the computed value covers ancestors), not inside a
17549
+ * `display: none` element and not in the hidden content of a closed
17550
+ * `<details>`.
17313
17551
  */
17314
17552
  function isRendered(el) {
17315
17553
  const { visibility } = getComputedStyle(el);
17316
17554
  if (visibility === "hidden" || visibility === "collapse") return false;
17317
17555
  for (let e = el; e; e = e.parentElement) if (getComputedStyle(e).display === "none") return false;
17318
- return true;
17556
+ return !inClosedDetails(el);
17557
+ }
17558
+ /**
17559
+ * Whether `el` can be focused at all: shown, rendered, enabled (also by its
17560
+ * fieldset) and not inert.
17561
+ */
17562
+ function isAvailable(el) {
17563
+ return !el.hidden && !el.closest("[hidden], [inert]") && !el.matches(":disabled") && isRendered(el);
17319
17564
  }
17320
17565
  /** The `tabindex` of `el` as an integer, or null when absent or invalid. */
17321
17566
  function tabIndexAttr(el) {
@@ -17324,9 +17569,10 @@ function tabIndexAttr(el) {
17324
17569
  return parseInt(raw, 10);
17325
17570
  }
17326
17571
  /**
17327
- * Whether the tab key can reach `el`: a negative tabindex only allows
17328
- * scripted focus, and of a radio group the browser tabs to the checked
17329
- * member only (the first one when none is checked).
17572
+ * Whether the tab key can reach `el`, an available element (see
17573
+ * isAvailable): a negative tabindex only allows scripted focus, and of a
17574
+ * radio group the browser tabs to the checked member only (the first one
17575
+ * when none is checked), of the members it can reach.
17330
17576
  */
17331
17577
  function isTabStop(el) {
17332
17578
  const tabIndex = tabIndexAttr(el);
@@ -17337,7 +17583,7 @@ function isTabStop(el) {
17337
17583
  if (details?.localName !== "details" || Array.from(details.children).find((c) => c.localName === "summary") !== el) return tabIndex !== null;
17338
17584
  }
17339
17585
  if (el instanceof HTMLInputElement && el.type === "radio" && el.name) {
17340
- const group = Array.from((el.form ?? el.getRootNode()).querySelectorAll("input[type=\"radio\"]")).filter((r) => r.name === el.name && r.form === el.form && !r.disabled);
17586
+ const group = Array.from((el.form ?? el.getRootNode()).querySelectorAll("input[type=\"radio\"]")).filter((r) => r.name === el.name && r.form === el.form && (tabIndexAttr(r) ?? 0) >= 0 && isAvailable(r));
17341
17587
  return el === (group.find((r) => r.checked) ?? group[0]);
17342
17588
  }
17343
17589
  return true;
@@ -17347,7 +17593,7 @@ function isTabStop(el) {
17347
17593
  * first, ascending, then the others in document order.
17348
17594
  */
17349
17595
  function focusables(root) {
17350
- const stops = Array.from(root.querySelectorAll(FOCUSABLE)).filter((el) => !el.hidden && !el.closest("[hidden], [inert]") && !el.matches(":disabled") && isRendered(el) && isTabStop(el));
17596
+ const stops = Array.from(root.querySelectorAll(FOCUSABLE)).filter((el) => isAvailable(el) && isTabStop(el));
17351
17597
  const positive = (el) => Math.max(tabIndexAttr(el) ?? 0, 0);
17352
17598
  return [...stops.filter((el) => positive(el) > 0).sort((a, b) => positive(a) - positive(b)), ...stops.filter((el) => positive(el) === 0)];
17353
17599
  }