rusteerio 1.1.1 → 1.1.2

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/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/README.md +37 -37
  4. package/THIRD_PARTY_NOTICES.md +1 -1
  5. package/dist/{chunk-FPRP6UIX.mjs → chunk-HN63P3U7.mjs} +625 -401
  6. package/dist/chunk-HN63P3U7.mjs.map +1 -0
  7. package/dist/index.cjs +624 -400
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.mts +3 -2
  10. package/dist/index.d.ts +3 -2
  11. package/dist/index.mjs +1 -1
  12. package/dist/slim.cjs +555 -407
  13. package/dist/slim.cjs.map +1 -1
  14. package/dist/slim.d.mts +2 -2
  15. package/dist/slim.d.ts +2 -2
  16. package/dist/slim.mjs +1 -1
  17. package/dist/{types-Cs9UP1Vv.d.mts → types-CJsc0PH-.d.mts} +12 -0
  18. package/dist/{types-Cs9UP1Vv.d.ts → types-CJsc0PH-.d.ts} +12 -0
  19. package/dist/utils.d.mts +1 -1
  20. package/dist/utils.d.ts +1 -1
  21. package/native/src/arena.rs +163 -7
  22. package/native/src/attributes.rs +18 -1
  23. package/native/src/codec.rs +79 -0
  24. package/native/src/lib.rs +117 -30
  25. package/native/src/snapshot.rs +117 -80
  26. package/native/src/tokenizer.rs +34 -20
  27. package/package.json +1 -1
  28. package/rusteerio.darwin-arm64.node +0 -0
  29. package/rusteerio.darwin-x64.node +0 -0
  30. package/rusteerio.linux-arm64-gnu.node +0 -0
  31. package/rusteerio.linux-arm64-musl.node +0 -0
  32. package/rusteerio.linux-x64-gnu.node +0 -0
  33. package/rusteerio.linux-x64-musl.node +0 -0
  34. package/rusteerio.win32-arm64-msvc.node +0 -0
  35. package/rusteerio.win32-x64-msvc.node +0 -0
  36. package/src/extract.ts +117 -24
  37. package/src/index.ts +27 -6
  38. package/src/match.ts +4 -4
  39. package/src/native.ts +4 -0
  40. package/src/nodes.ts +70 -10
  41. package/src/router.ts +80 -4
  42. package/src/selection.ts +18 -17
  43. package/src/snapshot.ts +37 -0
  44. package/src/transport.ts +1 -1
  45. package/dist/chunk-FPRP6UIX.mjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -256,7 +256,7 @@ function wrapNativeBinding(binding) {
256
256
  if (raw.NativeHookStream) wrapped.NativeHookStream = wrapConstructor(raw.NativeHookStream, ([isDocument2, options, bridge]) => [isDocument2, encodeOptions(options), wrapBridge(bridge)], wrapStream);
257
257
  if (raw.parseWithTreeAdapter) wrapped.parseWithTreeAdapter = (source, isDocument2, options, bridge) => invoke(() => raw.parseWithTreeAdapter(encodeString(source), isDocument2, encodeOptions(options), wrapBridge(bridge)));
258
258
  if (raw.parseAsync) wrapped.parseAsync = (source, isDocument2, options) => invoke(() => raw.parseAsync(encodeString(source), isDocument2, encodeOptions(options)));
259
- if (raw.parseManyAsync) wrapped.parseManyAsync = (sources, isDocument2, threads, options) => invoke(() => raw.parseManyAsync(sources.map(encodeString), isDocument2, threads, encodeOptions(options)));
259
+ if (raw.parseManyAsync) wrapped.parseManyAsync = (sources, isDocument2, threads, options, plans) => invoke(() => raw.parseManyAsync(sources.map(encodeString), isDocument2, threads, encodeOptions(options), plans === void 0 ? void 0 : encodeJson(plans)));
260
260
  if (raw.extractPlanManyAsync) wrapped.extractPlanManyAsync = async (sources, plan, threads, options) => decodeJson(await invoke(() => raw.extractPlanManyAsync(sources.map(encodeString), encodeJson(plan), threads, encodeOptions(options))));
261
261
  if (raw.extractManyAsync) wrapped.extractManyAsync = async (sources, queries, threads, isDocument2, options) => decodeJson(await invoke(() => raw.extractManyAsync(sources.map(encodeString), queries.map((query) => ({ ...query, key: encodeString(query.key), selector: encodeString(query.selector), ...query.attr === void 0 ? {} : { attr: encodeString(query.attr) } })), threads, isDocument2, encodeOptions(options))));
262
262
  return wrapped;
@@ -304,6 +304,284 @@ function nativeStatus() {
304
304
  return available ? { available } : { available, error: String(bindingError ?? "Native binding not found") };
305
305
  }
306
306
 
307
+ // src/snapshot.ts
308
+ var HEADER = 5;
309
+ var STRIDE = 15;
310
+ var ABSENT = 4294967295;
311
+ var HAS_START_INDEX = 1;
312
+ var HAS_END_INDEX = 2;
313
+ var HAS_NAMESPACE = 4;
314
+ var EMPTY_ATTRIBUTE_MAPS = 8;
315
+ var OPAQUE = 16;
316
+ var RELEASED = 32;
317
+ var noChildren = Object.freeze([]);
318
+ var noAttributes = Object.freeze({});
319
+ var SnapshotRecord = class {
320
+ constructor(snapshot, id, base) {
321
+ this.snapshot = snapshot;
322
+ this.id = id;
323
+ this.base = base;
324
+ this.decodedData = void 0;
325
+ this.decodedAttributes = void 0;
326
+ this.decodedChildren = void 0;
327
+ }
328
+ snapshot;
329
+ id;
330
+ base;
331
+ decodedData;
332
+ decodedAttributes;
333
+ decodedChildren;
334
+ get type() {
335
+ return this.snapshot.name(this.snapshot.nodes[this.base]);
336
+ }
337
+ get name() {
338
+ const name = this.snapshot.nodes[this.base + 6];
339
+ return name ? this.snapshot.name(name - 1) : void 0;
340
+ }
341
+ get data() {
342
+ if (this.decodedData !== void 0) return this.decodedData;
343
+ const data2 = this.snapshot.nodes[this.base + 7];
344
+ return data2 ? this.decodedData = this.snapshot.string(data2 - 1) : void 0;
345
+ }
346
+ get parent() {
347
+ const parent = this.snapshot.nodes[this.base + 1];
348
+ return parent ? parent - 1 : null;
349
+ }
350
+ get prev() {
351
+ const prev = this.snapshot.nodes[this.base + 2];
352
+ return prev ? prev - 1 : null;
353
+ }
354
+ get next() {
355
+ const next = this.snapshot.nodes[this.base + 3];
356
+ return next ? next - 1 : null;
357
+ }
358
+ get startIndex() {
359
+ return this.index(HAS_START_INDEX, 11);
360
+ }
361
+ get endIndex() {
362
+ return this.index(HAS_END_INDEX, 12);
363
+ }
364
+ get namespace() {
365
+ const nodes = this.snapshot.nodes;
366
+ return nodes[this.base + 10] & HAS_NAMESPACE ? this.snapshot.name(nodes[this.base + 13]) : void 0;
367
+ }
368
+ get attribsNamespace() {
369
+ return this.snapshot.nodes[this.base + 10] & EMPTY_ATTRIBUTE_MAPS ? {} : void 0;
370
+ }
371
+ get attribsPrefix() {
372
+ return this.snapshot.nodes[this.base + 10] & EMPTY_ATTRIBUTE_MAPS ? {} : void 0;
373
+ }
374
+ get attribs() {
375
+ return this.decodedAttributes ??= this.snapshot.attributesOf(this.id);
376
+ }
377
+ get children() {
378
+ return this.decodedChildren ??= this.snapshot.childrenOf(this.id);
379
+ }
380
+ index(flag, offset) {
381
+ const nodes = this.snapshot.nodes;
382
+ if (!(nodes[this.base + 10] & flag)) return void 0;
383
+ const value = nodes[this.base + offset];
384
+ return value === ABSENT ? null : value;
385
+ }
386
+ };
387
+ var recordFields = ["type", "name", "data", "parent", "prev", "next", "startIndex", "endIndex", "namespace", "attribsNamespace", "attribsPrefix", "attribs", "children"];
388
+ function plainRecord(record) {
389
+ if (!(record instanceof SnapshotRecord)) return record;
390
+ const copy = { id: record.id };
391
+ for (const field of recordFields) {
392
+ const value = record[field];
393
+ if (value !== void 0) copy[field] = value;
394
+ }
395
+ for (const key of Object.keys(record)) if (!key.startsWith("decoded") && key !== "snapshot" && key !== "base" && key !== "id") copy[key] = record[key];
396
+ return copy;
397
+ }
398
+ var NodeSnapshot = class {
399
+ size;
400
+ nodes;
401
+ children;
402
+ attributes;
403
+ strings;
404
+ text;
405
+ isEncoded;
406
+ names = [];
407
+ /** Per interned type: 1 for element types, 2 for root, 3 for text, 0 otherwise. */
408
+ typeKinds = [];
409
+ /** Node object constructors by interned type, resolved once per snapshot. */
410
+ viewConstructors = void 0;
411
+ /** Text nodes too large for the arrays, read individually while this snapshot is current. */
412
+ largeTexts = /* @__PURE__ */ new Map();
413
+ constructor(raw) {
414
+ const { data: data2 } = raw;
415
+ const childrenStart = HEADER + data2[0];
416
+ const attributesStart = childrenStart + data2[1];
417
+ const stringsStart = attributesStart + data2[2];
418
+ this.text = raw.text;
419
+ this.isEncoded = data2[4] === 1;
420
+ this.nodes = data2.subarray(HEADER, childrenStart);
421
+ this.children = data2.subarray(childrenStart, attributesStart);
422
+ this.attributes = data2.subarray(attributesStart, stringsStart);
423
+ this.strings = data2.subarray(stringsStart, stringsStart + data2[3]);
424
+ this.size = this.nodes.length / STRIDE;
425
+ }
426
+ /** The record, `null` for a released node, or `undefined` when it must be read individually. */
427
+ record(local) {
428
+ if (local >= this.size) return void 0;
429
+ const base = local * STRIDE;
430
+ const flags = this.nodes[base + 10];
431
+ if (flags & RELEASED) return null;
432
+ if (flags & OPAQUE) return void 0;
433
+ const record = new SnapshotRecord(this, local, base);
434
+ const extras = this.nodes[base + 14];
435
+ if (extras) {
436
+ const fields = JSON.parse(this.string(extras - 1));
437
+ for (const key of Object.keys(fields)) Object.defineProperty(record, key, { value: fields[key], writable: true, enumerable: true, configurable: true });
438
+ }
439
+ return record;
440
+ }
441
+ /** Whether a node's fields all live in the arrays, so callers may read them without a record. */
442
+ isPlain(local) {
443
+ if (local >= this.size) return false;
444
+ const base = local * STRIDE;
445
+ return (this.nodes[base + 10] & (OPAQUE | RELEASED)) === 0 && this.nodes[base + 14] === 0;
446
+ }
447
+ typeOf(local) {
448
+ return this.name(this.nodes[local * STRIDE]);
449
+ }
450
+ nameOf(local) {
451
+ const name = this.nodes[local * STRIDE + 6];
452
+ return name ? this.name(name - 1) : void 0;
453
+ }
454
+ dataOf(local) {
455
+ const data2 = this.nodes[local * STRIDE + 7];
456
+ return data2 ? this.string(data2 - 1) : void 0;
457
+ }
458
+ /** The parent, previous or next sibling (`offset` 1, 2 or 3), or `null`. */
459
+ linkOf(local, offset) {
460
+ const link = this.nodes[local * STRIDE + offset];
461
+ return link ? link - 1 : null;
462
+ }
463
+ namespaceOf(local) {
464
+ const base = local * STRIDE;
465
+ return this.nodes[base + 10] & HAS_NAMESPACE ? this.name(this.nodes[base + 13]) : void 0;
466
+ }
467
+ /** A node's attributes as parse5 attribute objects; plain nodes have no namespaced ones. */
468
+ attributeObjects(local) {
469
+ const base = local * STRIDE;
470
+ const count = this.nodes[base + 9];
471
+ const first = this.nodes[base + 8];
472
+ const attributes = this.attributes;
473
+ const result = new Array(count);
474
+ for (let index = 0; index < count; index++) {
475
+ const pair = (first + index) * 2;
476
+ result[index] = { name: this.name(attributes[pair]), value: this.string(attributes[pair + 1]) };
477
+ }
478
+ return result;
479
+ }
480
+ /** Copies a node's attributes onto `target`, in order. */
481
+ copyAttributes(local, target) {
482
+ const base = local * STRIDE;
483
+ const count = this.nodes[base + 9];
484
+ const first = this.nodes[base + 8];
485
+ const attributes = this.attributes;
486
+ for (let index = 0; index < count; index++) {
487
+ const pair = (first + index) * 2;
488
+ const name = this.name(attributes[pair]);
489
+ const value = this.string(attributes[pair + 1]);
490
+ if (name === "__proto__") Object.defineProperty(target, name, { value, writable: true, enumerable: true, configurable: true });
491
+ else target[name] = value;
492
+ }
493
+ }
494
+ /** The flat child id list; a node's children start at `childStart(local)`. */
495
+ get childIds() {
496
+ return this.children;
497
+ }
498
+ childStart(local) {
499
+ return this.nodes[local * STRIDE + 4];
500
+ }
501
+ /** Whether a node is an element: a tag, script or style. */
502
+ isElementAt(local) {
503
+ return this.typeKind(local) === 1;
504
+ }
505
+ isRootAt(local) {
506
+ return this.typeKind(local) === 2;
507
+ }
508
+ isTextAt(local) {
509
+ return this.typeKind(local) === 3;
510
+ }
511
+ /** Whether a node's record adds fields stored as JSON, such as source locations. */
512
+ hasExtraFields(local) {
513
+ return this.nodes[local * STRIDE + 14] !== 0;
514
+ }
515
+ /** Whether the slot holds a node, rather than one a collection released. */
516
+ isPresent(local) {
517
+ return local < this.size && (this.nodes[local * STRIDE + 10] & RELEASED) === 0;
518
+ }
519
+ typeKind(local) {
520
+ const reference = this.nodes[local * STRIDE];
521
+ let kind = this.typeKinds[reference];
522
+ if (kind === void 0) {
523
+ const type = this.name(reference);
524
+ kind = this.typeKinds[reference] = type === "tag" || type === "script" || type === "style" ? 1 : type === "root" ? 2 : type === "text" ? 3 : 0;
525
+ }
526
+ return kind;
527
+ }
528
+ /** The interned reference of a node's type, comparable across nodes of this snapshot. */
529
+ typeReference(local) {
530
+ return this.nodes[local * STRIDE];
531
+ }
532
+ /** Child ids without materializing the record. */
533
+ childCount(local) {
534
+ return this.nodes[local * STRIDE + 5];
535
+ }
536
+ childAt(local, index) {
537
+ return this.children[this.nodes[local * STRIDE + 4] + index];
538
+ }
539
+ childrenOf(local) {
540
+ const base = local * STRIDE;
541
+ const count = this.nodes[base + 5];
542
+ if (count === 0) return noChildren;
543
+ const first = this.nodes[base + 4];
544
+ return Array.from(this.children.subarray(first, first + count));
545
+ }
546
+ attributesOf(local) {
547
+ const base = local * STRIDE;
548
+ const count = this.nodes[base + 9];
549
+ if (count === 0) return noAttributes;
550
+ const first = this.nodes[base + 8];
551
+ const attributes = this.attributes;
552
+ const result = {};
553
+ for (let index = 0; index < count; index++) {
554
+ const pair = (first + index) * 2;
555
+ const name = this.name(attributes[pair]);
556
+ const value = this.string(attributes[pair + 1]);
557
+ if (name === "__proto__") Object.defineProperty(result, name, { value, writable: true, enumerable: true, configurable: true });
558
+ else result[name] = value;
559
+ }
560
+ return result;
561
+ }
562
+ /** One attribute's value, without decoding the rest. */
563
+ attributeValue(local, name) {
564
+ const base = local * STRIDE;
565
+ const count = this.nodes[base + 9];
566
+ const first = this.nodes[base + 8];
567
+ const attributes = this.attributes;
568
+ for (let index = count - 1; index >= 0; index--) {
569
+ const pair = (first + index) * 2;
570
+ if (this.name(attributes[pair]) === name) return this.string(attributes[pair + 1]);
571
+ }
572
+ return void 0;
573
+ }
574
+ string(reference) {
575
+ const strings = this.strings;
576
+ const value = this.text.slice(strings[reference * 2], strings[reference * 2 + 1]);
577
+ return this.isEncoded ? decodeString(value) : value;
578
+ }
579
+ /** Names repeat across nodes, so each decodes once. */
580
+ name(reference) {
581
+ return this.names[reference] ??= this.string(reference);
582
+ }
583
+ };
584
+
307
585
  // src/hooks.ts
308
586
  var tokenCallbacks = ["onattribdata", "onattribentity", "onattribend", "onattribname", "oncdata", "onclosetag", "oncomment", "ondeclaration", "onend", "onopentagend", "onopentagname", "onprocessinginstruction", "onselfclosingtag", "ontext", "ontextentity"];
309
587
  function createTokenizerSession(options) {
@@ -606,357 +884,110 @@ function replayIntoAdapter(document, adapter, isDocument2) {
606
884
  for (let local = 0; local < Math.min(size, 65); local++) recordAt(state, local);
607
885
  for (let local = 0; local < size; local++) {
608
886
  const snapshot = plainSnapshot(state, local);
609
- const record = snapshot ? void 0 : recordAt(state, local);
610
- if (!snapshot && !record) continue;
611
- const type = snapshot ? snapshot.typeOf(local) : record.type;
612
- if (local === root) {
613
- nodes[local] = isDocument2 ? adapter.createDocument() : adapter.createDocumentFragment();
614
- const mode = record?.["x-mode"];
615
- if (isDocument2 && mode !== void 0) adapter.setDocumentMode(nodes[local], mode);
616
- continue;
617
- }
618
- const parentLocal = snapshot ? snapshot.linkOf(local, 1) : record.parent;
619
- const parent = parentLocal == null ? void 0 : nodes[parentLocal];
620
- if (parent === void 0) return void 0;
621
- switch (type) {
622
- case "tag":
623
- case "script":
624
- case "style": {
625
- const element = snapshot ? adapter.createElement(snapshot.nameOf(local), snapshot.namespaceOf(local), snapshot.attributeObjects(local)) : adapter.createElement(record.name, record.namespace, attributeList(record));
626
- adapter.appendChild(parent, element);
627
- nodes[local] = element;
628
- break;
629
- }
630
- case "root": {
631
- const content = adapter.createDocumentFragment();
632
- adapter.setTemplateContent(parent, content);
633
- nodes[local] = content;
634
- break;
635
- }
636
- case "text": {
637
- adapter.insertText(parent, (snapshot ? snapshot.dataOf(local) : record.data) ?? "");
638
- const children = adapter.getChildNodes(parent);
639
- nodes[local] = children[children.length - 1];
640
- break;
641
- }
642
- case "comment": {
643
- const comment = adapter.createCommentNode((snapshot ? snapshot.dataOf(local) : record.data) ?? "");
644
- adapter.appendChild(parent, comment);
645
- nodes[local] = comment;
646
- break;
647
- }
648
- case "directive": {
649
- if (!record || record.name !== "!doctype" || !isDocument2) return void 0;
650
- adapter.setDocumentType(parent, record["x-name"] ?? "", record["x-publicId"] ?? "", record["x-systemId"] ?? "");
651
- nodes[local] = Array.from(adapter.getChildNodes(parent) ?? []).find((child) => adapter.isDocumentTypeNode(child));
652
- break;
653
- }
654
- default:
655
- return void 0;
656
- }
657
- }
658
- const changes = differencesFromParse(adapter, nodes, state, root);
659
- if (!changes) return void 0;
660
- for (const change of changes) operation(document, "setNode", { id: state.base + change.local, field: change.field, value: change.value });
661
- return nodes;
662
- }
663
- function attributeList(record) {
664
- const attributes = record.attribs ?? {};
665
- return Object.keys(attributes).map((name) => {
666
- const namespace = record.attribsNamespace?.[name];
667
- const prefix = record.attribsPrefix?.[name];
668
- return { name, value: attributes[name], ...namespace === void 0 ? {} : { namespace }, ...prefix === void 0 ? {} : { prefix } };
669
- });
670
- }
671
- function differencesFromParse(adapter, nodes, state, root) {
672
- const changes = [];
673
- for (let local = 0; local < nodes.length; local++) {
674
- const node = nodes[local];
675
- const snapshot = plainSnapshot(state, local);
676
- const record = snapshot ? void 0 : recordAt(state, local);
677
- if (!snapshot && !record) continue;
678
- if (!node) return void 0;
679
- const type = snapshot ? snapshot.typeOf(local) : record.type;
680
- if (local !== root && adapter.getParentNode(node) !== nodes[snapshot ? snapshot.linkOf(local, 1) : record.parent]) return void 0;
681
- if (type !== "text" && type !== "comment" && type !== "directive") {
682
- const children = adapter.getChildNodes(node) ?? [];
683
- const count = snapshot ? snapshot.childCount(local) : record.children.length;
684
- if (children.length !== count) return void 0;
685
- for (let index = 0; index < count; index++) if (children[index] !== nodes[snapshot ? snapshot.childAt(local, index) : record.children[index]]) return void 0;
686
- }
687
- if (type === "tag" || type === "script" || type === "style") {
688
- const name = snapshot ? snapshot.nameOf(local) : record.name;
689
- const namespace = snapshot ? snapshot.namespaceOf(local) : record.namespace;
690
- if (!adapter.isElementNode(node) || adapter.getTagName(node) !== name || adapter.getNamespaceURI(node) !== namespace) return void 0;
691
- const attributes = adapter.getAttrList(node) ?? [];
692
- const expected = snapshot ? snapshot.attributeObjects(local) : attributeList(record);
693
- const isSame = attributes.length === expected.length && attributes.every((attribute, index) => attribute.name === expected[index].name && attribute.value === expected[index].value);
694
- if (!isSame) {
695
- const values = /* @__PURE__ */ Object.create(null);
696
- const namespaces = /* @__PURE__ */ Object.create(null);
697
- const prefixes = /* @__PURE__ */ Object.create(null);
698
- for (const attribute of attributes) {
699
- values[attribute.name] = attribute.value;
700
- if (attribute.namespace !== void 0) namespaces[attribute.name] = attribute.namespace;
701
- if (attribute.prefix !== void 0) prefixes[attribute.name] = attribute.prefix;
702
- }
703
- changes.push({ local, field: "attribs", value: values }, { local, field: "attribsNamespace", value: namespaces }, { local, field: "attribsPrefix", value: prefixes });
704
- }
705
- } else if (type === "text" || type === "comment") {
706
- if (type === "text" ? !adapter.isTextNode(node) : !adapter.isCommentNode(node)) return void 0;
707
- const data2 = type === "text" ? adapter.getTextNodeContent(node) : adapter.getCommentNodeContent(node);
708
- if (data2 !== (snapshot ? snapshot.dataOf(local) : record.data)) changes.push({ local, field: "data", value: data2 });
709
- }
710
- }
711
- return changes;
712
- }
713
-
714
- // src/snapshot.ts
715
- var HEADER = 5;
716
- var STRIDE = 15;
717
- var ABSENT = 4294967295;
718
- var HAS_START_INDEX = 1;
719
- var HAS_END_INDEX = 2;
720
- var HAS_NAMESPACE = 4;
721
- var EMPTY_ATTRIBUTE_MAPS = 8;
722
- var OPAQUE = 16;
723
- var RELEASED = 32;
724
- var noChildren = Object.freeze([]);
725
- var noAttributes = Object.freeze({});
726
- var SnapshotRecord = class {
727
- constructor(snapshot, id, base) {
728
- this.snapshot = snapshot;
729
- this.id = id;
730
- this.base = base;
731
- this.decodedData = void 0;
732
- this.decodedAttributes = void 0;
733
- this.decodedChildren = void 0;
734
- }
735
- snapshot;
736
- id;
737
- base;
738
- decodedData;
739
- decodedAttributes;
740
- decodedChildren;
741
- get type() {
742
- return this.snapshot.name(this.snapshot.nodes[this.base]);
743
- }
744
- get name() {
745
- const name = this.snapshot.nodes[this.base + 6];
746
- return name ? this.snapshot.name(name - 1) : void 0;
747
- }
748
- get data() {
749
- if (this.decodedData !== void 0) return this.decodedData;
750
- const data2 = this.snapshot.nodes[this.base + 7];
751
- return data2 ? this.decodedData = this.snapshot.string(data2 - 1) : void 0;
752
- }
753
- get parent() {
754
- const parent = this.snapshot.nodes[this.base + 1];
755
- return parent ? parent - 1 : null;
756
- }
757
- get prev() {
758
- const prev = this.snapshot.nodes[this.base + 2];
759
- return prev ? prev - 1 : null;
760
- }
761
- get next() {
762
- const next = this.snapshot.nodes[this.base + 3];
763
- return next ? next - 1 : null;
764
- }
765
- get startIndex() {
766
- return this.index(HAS_START_INDEX, 11);
767
- }
768
- get endIndex() {
769
- return this.index(HAS_END_INDEX, 12);
770
- }
771
- get namespace() {
772
- const nodes = this.snapshot.nodes;
773
- return nodes[this.base + 10] & HAS_NAMESPACE ? this.snapshot.name(nodes[this.base + 13]) : void 0;
774
- }
775
- get attribsNamespace() {
776
- return this.snapshot.nodes[this.base + 10] & EMPTY_ATTRIBUTE_MAPS ? {} : void 0;
777
- }
778
- get attribsPrefix() {
779
- return this.snapshot.nodes[this.base + 10] & EMPTY_ATTRIBUTE_MAPS ? {} : void 0;
780
- }
781
- get attribs() {
782
- return this.decodedAttributes ??= this.snapshot.attributesOf(this.id);
783
- }
784
- get children() {
785
- return this.decodedChildren ??= this.snapshot.childrenOf(this.id);
786
- }
787
- index(flag, offset) {
788
- const nodes = this.snapshot.nodes;
789
- if (!(nodes[this.base + 10] & flag)) return void 0;
790
- const value = nodes[this.base + offset];
791
- return value === ABSENT ? null : value;
792
- }
793
- };
794
- var recordFields = ["type", "name", "data", "parent", "prev", "next", "startIndex", "endIndex", "namespace", "attribsNamespace", "attribsPrefix", "attribs", "children"];
795
- function plainRecord(record) {
796
- if (!(record instanceof SnapshotRecord)) return record;
797
- const copy = { id: record.id };
798
- for (const field of recordFields) {
799
- const value = record[field];
800
- if (value !== void 0) copy[field] = value;
801
- }
802
- for (const key of Object.keys(record)) if (!key.startsWith("decoded") && key !== "snapshot" && key !== "base" && key !== "id") copy[key] = record[key];
803
- return copy;
804
- }
805
- var NodeSnapshot = class {
806
- size;
807
- nodes;
808
- children;
809
- attributes;
810
- strings;
811
- text;
812
- isEncoded;
813
- names = [];
814
- /** Node object constructors by interned type, resolved once per snapshot. */
815
- viewConstructors = void 0;
816
- constructor(raw) {
817
- const { data: data2 } = raw;
818
- const childrenStart = HEADER + data2[0];
819
- const attributesStart = childrenStart + data2[1];
820
- const stringsStart = attributesStart + data2[2];
821
- this.text = raw.text;
822
- this.isEncoded = data2[4] === 1;
823
- this.nodes = data2.subarray(HEADER, childrenStart);
824
- this.children = data2.subarray(childrenStart, attributesStart);
825
- this.attributes = data2.subarray(attributesStart, stringsStart);
826
- this.strings = data2.subarray(stringsStart, stringsStart + data2[3]);
827
- this.size = this.nodes.length / STRIDE;
828
- }
829
- /** The record, `null` for a released node, or `undefined` when it must be read individually. */
830
- record(local) {
831
- if (local >= this.size) return void 0;
832
- const base = local * STRIDE;
833
- const flags = this.nodes[base + 10];
834
- if (flags & RELEASED) return null;
835
- if (flags & OPAQUE) return void 0;
836
- const record = new SnapshotRecord(this, local, base);
837
- const extras = this.nodes[base + 14];
838
- if (extras) {
839
- const fields = JSON.parse(this.string(extras - 1));
840
- for (const key of Object.keys(fields)) Object.defineProperty(record, key, { value: fields[key], writable: true, enumerable: true, configurable: true });
841
- }
842
- return record;
843
- }
844
- /** Whether a node's fields all live in the arrays, so callers may read them without a record. */
845
- isPlain(local) {
846
- if (local >= this.size) return false;
847
- const base = local * STRIDE;
848
- return (this.nodes[base + 10] & (OPAQUE | RELEASED)) === 0 && this.nodes[base + 14] === 0;
849
- }
850
- typeOf(local) {
851
- return this.name(this.nodes[local * STRIDE]);
852
- }
853
- nameOf(local) {
854
- const name = this.nodes[local * STRIDE + 6];
855
- return name ? this.name(name - 1) : void 0;
856
- }
857
- dataOf(local) {
858
- const data2 = this.nodes[local * STRIDE + 7];
859
- return data2 ? this.string(data2 - 1) : void 0;
860
- }
861
- /** The parent, previous or next sibling (`offset` 1, 2 or 3), or `null`. */
862
- linkOf(local, offset) {
863
- const link = this.nodes[local * STRIDE + offset];
864
- return link ? link - 1 : null;
865
- }
866
- namespaceOf(local) {
867
- const base = local * STRIDE;
868
- return this.nodes[base + 10] & HAS_NAMESPACE ? this.name(this.nodes[base + 13]) : void 0;
869
- }
870
- /** A node's attributes as parse5 attribute objects; plain nodes have no namespaced ones. */
871
- attributeObjects(local) {
872
- const base = local * STRIDE;
873
- const count = this.nodes[base + 9];
874
- const first = this.nodes[base + 8];
875
- const attributes = this.attributes;
876
- const result = new Array(count);
877
- for (let index = 0; index < count; index++) {
878
- const pair = (first + index) * 2;
879
- result[index] = { name: this.name(attributes[pair]), value: this.string(attributes[pair + 1]) };
887
+ const record = snapshot ? void 0 : recordAt(state, local);
888
+ if (!snapshot && !record) continue;
889
+ const type = snapshot ? snapshot.typeOf(local) : record.type;
890
+ if (local === root) {
891
+ nodes[local] = isDocument2 ? adapter.createDocument() : adapter.createDocumentFragment();
892
+ const mode = record?.["x-mode"];
893
+ if (isDocument2 && mode !== void 0) adapter.setDocumentMode(nodes[local], mode);
894
+ continue;
880
895
  }
881
- return result;
882
- }
883
- /** Copies a node's attributes onto `target`, in order. */
884
- copyAttributes(local, target) {
885
- const base = local * STRIDE;
886
- const count = this.nodes[base + 9];
887
- const first = this.nodes[base + 8];
888
- const attributes = this.attributes;
889
- for (let index = 0; index < count; index++) {
890
- const pair = (first + index) * 2;
891
- const name = this.name(attributes[pair]);
892
- const value = this.string(attributes[pair + 1]);
893
- if (name === "__proto__") Object.defineProperty(target, name, { value, writable: true, enumerable: true, configurable: true });
894
- else target[name] = value;
896
+ const parentLocal = snapshot ? snapshot.linkOf(local, 1) : record.parent;
897
+ const parent = parentLocal == null ? void 0 : nodes[parentLocal];
898
+ if (parent === void 0) return void 0;
899
+ switch (type) {
900
+ case "tag":
901
+ case "script":
902
+ case "style": {
903
+ const element = snapshot ? adapter.createElement(snapshot.nameOf(local), snapshot.namespaceOf(local), snapshot.attributeObjects(local)) : adapter.createElement(record.name, record.namespace, attributeList(record));
904
+ adapter.appendChild(parent, element);
905
+ nodes[local] = element;
906
+ break;
907
+ }
908
+ case "root": {
909
+ const content = adapter.createDocumentFragment();
910
+ adapter.setTemplateContent(parent, content);
911
+ nodes[local] = content;
912
+ break;
913
+ }
914
+ case "text": {
915
+ adapter.insertText(parent, (snapshot ? snapshot.dataOf(local) : record.data) ?? "");
916
+ const children = adapter.getChildNodes(parent);
917
+ nodes[local] = children[children.length - 1];
918
+ break;
919
+ }
920
+ case "comment": {
921
+ const comment = adapter.createCommentNode((snapshot ? snapshot.dataOf(local) : record.data) ?? "");
922
+ adapter.appendChild(parent, comment);
923
+ nodes[local] = comment;
924
+ break;
925
+ }
926
+ case "directive": {
927
+ if (!record || record.name !== "!doctype" || !isDocument2) return void 0;
928
+ adapter.setDocumentType(parent, record["x-name"] ?? "", record["x-publicId"] ?? "", record["x-systemId"] ?? "");
929
+ nodes[local] = Array.from(adapter.getChildNodes(parent) ?? []).find((child) => adapter.isDocumentTypeNode(child));
930
+ break;
931
+ }
932
+ default:
933
+ return void 0;
895
934
  }
896
935
  }
897
- /** The flat child id list; a node's children start at `childStart(local)`. */
898
- get childIds() {
899
- return this.children;
900
- }
901
- childStart(local) {
902
- return this.nodes[local * STRIDE + 4];
903
- }
904
- /** The interned reference of a node's type, comparable across nodes of this snapshot. */
905
- typeReference(local) {
906
- return this.nodes[local * STRIDE];
907
- }
908
- /** Child ids without materializing the record. */
909
- childCount(local) {
910
- return this.nodes[local * STRIDE + 5];
911
- }
912
- childAt(local, index) {
913
- return this.children[this.nodes[local * STRIDE + 4] + index];
914
- }
915
- childrenOf(local) {
916
- const base = local * STRIDE;
917
- const count = this.nodes[base + 5];
918
- if (count === 0) return noChildren;
919
- const first = this.nodes[base + 4];
920
- return Array.from(this.children.subarray(first, first + count));
921
- }
922
- attributesOf(local) {
923
- const base = local * STRIDE;
924
- const count = this.nodes[base + 9];
925
- if (count === 0) return noAttributes;
926
- const first = this.nodes[base + 8];
927
- const attributes = this.attributes;
928
- const result = {};
929
- for (let index = 0; index < count; index++) {
930
- const pair = (first + index) * 2;
931
- const name = this.name(attributes[pair]);
932
- const value = this.string(attributes[pair + 1]);
933
- if (name === "__proto__") Object.defineProperty(result, name, { value, writable: true, enumerable: true, configurable: true });
934
- else result[name] = value;
936
+ const changes = differencesFromParse(adapter, nodes, state, root);
937
+ if (!changes) return void 0;
938
+ for (const change of changes) operation(document, "setNode", { id: state.base + change.local, field: change.field, value: change.value });
939
+ return nodes;
940
+ }
941
+ function attributeList(record) {
942
+ const attributes = record.attribs ?? {};
943
+ return Object.keys(attributes).map((name) => {
944
+ const namespace = record.attribsNamespace?.[name];
945
+ const prefix = record.attribsPrefix?.[name];
946
+ return { name, value: attributes[name], ...namespace === void 0 ? {} : { namespace }, ...prefix === void 0 ? {} : { prefix } };
947
+ });
948
+ }
949
+ function differencesFromParse(adapter, nodes, state, root) {
950
+ const changes = [];
951
+ for (let local = 0; local < nodes.length; local++) {
952
+ const node = nodes[local];
953
+ const snapshot = plainSnapshot(state, local);
954
+ const record = snapshot ? void 0 : recordAt(state, local);
955
+ if (!snapshot && !record) continue;
956
+ if (!node) return void 0;
957
+ const type = snapshot ? snapshot.typeOf(local) : record.type;
958
+ if (local !== root && adapter.getParentNode(node) !== nodes[snapshot ? snapshot.linkOf(local, 1) : record.parent]) return void 0;
959
+ if (type !== "text" && type !== "comment" && type !== "directive") {
960
+ const children = adapter.getChildNodes(node) ?? [];
961
+ const count = snapshot ? snapshot.childCount(local) : record.children.length;
962
+ if (children.length !== count) return void 0;
963
+ for (let index = 0; index < count; index++) if (children[index] !== nodes[snapshot ? snapshot.childAt(local, index) : record.children[index]]) return void 0;
935
964
  }
936
- return result;
937
- }
938
- /** One attribute's value, without decoding the rest. */
939
- attributeValue(local, name) {
940
- const base = local * STRIDE;
941
- const count = this.nodes[base + 9];
942
- const first = this.nodes[base + 8];
943
- const attributes = this.attributes;
944
- for (let index = count - 1; index >= 0; index--) {
945
- const pair = (first + index) * 2;
946
- if (this.name(attributes[pair]) === name) return this.string(attributes[pair + 1]);
965
+ if (type === "tag" || type === "script" || type === "style") {
966
+ const name = snapshot ? snapshot.nameOf(local) : record.name;
967
+ const namespace = snapshot ? snapshot.namespaceOf(local) : record.namespace;
968
+ if (!adapter.isElementNode(node) || adapter.getTagName(node) !== name || adapter.getNamespaceURI(node) !== namespace) return void 0;
969
+ const attributes = adapter.getAttrList(node) ?? [];
970
+ const expected = snapshot ? snapshot.attributeObjects(local) : attributeList(record);
971
+ const isSame = attributes.length === expected.length && attributes.every((attribute, index) => attribute.name === expected[index].name && attribute.value === expected[index].value);
972
+ if (!isSame) {
973
+ const values = /* @__PURE__ */ Object.create(null);
974
+ const namespaces = /* @__PURE__ */ Object.create(null);
975
+ const prefixes = /* @__PURE__ */ Object.create(null);
976
+ for (const attribute of attributes) {
977
+ values[attribute.name] = attribute.value;
978
+ if (attribute.namespace !== void 0) namespaces[attribute.name] = attribute.namespace;
979
+ if (attribute.prefix !== void 0) prefixes[attribute.name] = attribute.prefix;
980
+ }
981
+ changes.push({ local, field: "attribs", value: values }, { local, field: "attribsNamespace", value: namespaces }, { local, field: "attribsPrefix", value: prefixes });
982
+ }
983
+ } else if (type === "text" || type === "comment") {
984
+ if (type === "text" ? !adapter.isTextNode(node) : !adapter.isCommentNode(node)) return void 0;
985
+ const data2 = type === "text" ? adapter.getTextNodeContent(node) : adapter.getCommentNodeContent(node);
986
+ if (data2 !== (snapshot ? snapshot.dataOf(local) : record.data)) changes.push({ local, field: "data", value: data2 });
947
987
  }
948
- return void 0;
949
988
  }
950
- string(reference) {
951
- const strings = this.strings;
952
- const value = this.text.slice(strings[reference * 2], strings[reference * 2 + 1]);
953
- return this.isEncoded ? decodeString(value) : value;
954
- }
955
- /** Names repeat across nodes, so each decodes once. */
956
- name(reference) {
957
- return this.names[reference] ??= this.string(reference);
958
- }
959
- };
989
+ return changes;
990
+ }
960
991
 
961
992
  // src/router.ts
962
993
  var STRIDE2 = 2 ** 32;
@@ -1055,7 +1086,8 @@ function stateFor(raw, options = {}) {
1055
1086
  isExposed: false,
1056
1087
  hasChildLists: false,
1057
1088
  collectionRetries: 0,
1058
- isCollectionEvicted: false
1089
+ isCollectionEvicted: false,
1090
+ hasPreparedSnapshot: false
1059
1091
  };
1060
1092
  holder[stateKey] = state;
1061
1093
  return state;
@@ -1180,7 +1212,7 @@ function readRecord(state, local) {
1180
1212
  const size = Math.max(state.snapshotSize, state.baseline);
1181
1213
  const firstSnapshotRead = state.generation === 0 ? size / 100 : 1 + size / 100;
1182
1214
  if (reads >= Math.min(minimumReadsBeforeSnapshot, firstSnapshotRead)) {
1183
- const snapshot = new NodeSnapshot(state.raw.snapshot());
1215
+ const snapshot = new NodeSnapshot(takePreparedSnapshot(state, true) ?? state.raw.snapshot());
1184
1216
  state.snapshotSize = snapshot.size;
1185
1217
  state.bulk = snapshot;
1186
1218
  const record = snapshot.record(local);
@@ -1189,10 +1221,45 @@ function readRecord(state, local) {
1189
1221
  }
1190
1222
  return rawOperation(state.raw, "node", { id: local });
1191
1223
  }
1224
+ function usePreparedSnapshot(document) {
1225
+ if (!(document instanceof RoutedDocument)) return;
1226
+ const state = document.state;
1227
+ state.hasPreparedSnapshot = state.generation === 0 && state.bulk === 0;
1228
+ }
1229
+ function takePreparedSnapshot(state, isWanted) {
1230
+ if (!state.hasPreparedSnapshot) return void 0;
1231
+ state.hasPreparedSnapshot = false;
1232
+ return state.raw.takePreparedSnapshot?.(isWanted) ?? void 0;
1233
+ }
1192
1234
  function plainSnapshot(state, local) {
1193
1235
  const bulk = state.bulk;
1194
1236
  return bulk instanceof NodeSnapshot && !(state.stale.size && state.stale.has(local)) && bulk.isPlain(local) ? bulk : void 0;
1195
1237
  }
1238
+ function structureSnapshot(state, local) {
1239
+ const bulk = state.bulk;
1240
+ return bulk instanceof NodeSnapshot && !(state.stale.size && state.stale.has(local)) && bulk.isPresent(local) ? bulk : void 0;
1241
+ }
1242
+ var snapshotTextBudget = 256;
1243
+ function textFromSnapshot(state, local) {
1244
+ let text = "";
1245
+ const stack = [local];
1246
+ for (let visited = 0; stack.length; visited++) {
1247
+ const node = stack.pop();
1248
+ const snapshot = structureSnapshot(state, node);
1249
+ if (!snapshot || visited === snapshotTextBudget) return void 0;
1250
+ if (snapshot.isTextAt(node)) {
1251
+ text += snapshot.dataOf(node) ?? largeText(state, snapshot, node);
1252
+ continue;
1253
+ }
1254
+ for (let index = snapshot.childCount(node) - 1; index >= 0; index--) stack.push(snapshot.childAt(node, index));
1255
+ }
1256
+ return text;
1257
+ }
1258
+ function largeText(state, snapshot, local) {
1259
+ let text = snapshot.largeTexts.get(local);
1260
+ if (text === void 0) snapshot.largeTexts.set(local, text = state.raw.text([local]));
1261
+ return text;
1262
+ }
1196
1263
  function attributeAt(state, local, name) {
1197
1264
  const snapshot = plainSnapshot(state, local);
1198
1265
  if (snapshot) return snapshot.attributeValue(local, name);
@@ -1201,15 +1268,18 @@ function attributeAt(state, local, name) {
1201
1268
  return typeof value === "string" ? value : void 0;
1202
1269
  }
1203
1270
  function invalidate(state) {
1271
+ takePreparedSnapshot(state, false);
1204
1272
  state.generation++;
1205
1273
  state.records = [];
1206
1274
  state.bulk = 0;
1207
1275
  state.stale.clear();
1208
1276
  }
1209
1277
  function invalidateNodes(state, locals) {
1278
+ takePreparedSnapshot(state, false);
1210
1279
  const isSnapshot = state.bulk instanceof NodeSnapshot;
1280
+ const { records } = state;
1211
1281
  for (const local of locals) {
1212
- state.records[local] = void 0;
1282
+ if (records[local] !== void 0) records[local] = void 0;
1213
1283
  if (isSnapshot) state.stale.add(local);
1214
1284
  }
1215
1285
  }
@@ -1444,6 +1514,11 @@ var RoutedDocument = class {
1444
1514
  return this.globals(group.state, found);
1445
1515
  }
1446
1516
  text(ids) {
1517
+ if (ids.length === 1) {
1518
+ const state = this.owner(ids[0]);
1519
+ const text = textFromSnapshot(state, ids[0] - state.base);
1520
+ if (text !== void 0) return text;
1521
+ }
1447
1522
  return this.groups(ids).map((group) => group.state.raw.text(group.locals)).join("");
1448
1523
  }
1449
1524
  innerHtml(id) {
@@ -1528,9 +1603,12 @@ var RoutedDocument = class {
1528
1603
  return this.globals(state, inserted);
1529
1604
  }
1530
1605
  const result2 = [];
1606
+ const isBesideTarget = args.position === "before" || args.position === "after";
1531
1607
  for (let index = 0; index < targets.length; index++) {
1532
1608
  const target = this.owner(targets[index]);
1533
1609
  const targetLocal = targets[index] - target.base;
1610
+ const record = recordAt(target, targetLocal);
1611
+ if (!record || !parentTypes.has(record.type) || isBesideTarget && record.parent == null) continue;
1534
1612
  const local = this.moveInto(target, nodes, index + 1 < targets.length);
1535
1613
  const inserted = this.call(target, command, { ...args, targetIds: [targetLocal], nodeIds: local });
1536
1614
  result2.push(...this.globals(target, inserted));
@@ -1753,6 +1831,8 @@ function traverseRecords(document, ids, kind) {
1753
1831
  for (const id of ids) {
1754
1832
  const state = stateIn(document, id);
1755
1833
  const { base } = state;
1834
+ const snapshot = state.bulk instanceof NodeSnapshot ? state.bulk : void 0;
1835
+ if (snapshot?.isPresent(id - base) && traverseSnapshot(snapshot, base, id - base, kind, result)) continue;
1756
1836
  const node = recordAt(state, id - base);
1757
1837
  if (!node) continue;
1758
1838
  const isElement2 = (local) => isElementType(recordAt(state, local)?.type);
@@ -1793,6 +1873,60 @@ function traverseRecords(document, ids, kind) {
1793
1873
  }
1794
1874
  return result;
1795
1875
  }
1876
+ function traverseSnapshot(snapshot, base, local, kind, result) {
1877
+ const pushChildren2 = (parent, isElementsOnly, except) => {
1878
+ const count = snapshot.childCount(parent);
1879
+ for (let index = 0; index < count; index++) {
1880
+ const child = snapshot.childAt(parent, index);
1881
+ if (child !== except && (!isElementsOnly || snapshot.isElementAt(child))) result.push(base + child);
1882
+ }
1883
+ };
1884
+ switch (kind) {
1885
+ case "children":
1886
+ pushChildren2(local, true, -1);
1887
+ return true;
1888
+ case "contents":
1889
+ pushChildren2(local, false, -1);
1890
+ return true;
1891
+ case "siblings": {
1892
+ const parent = snapshot.linkOf(local, 1);
1893
+ if (parent !== null && snapshot.isPresent(parent)) pushChildren2(parent, true, local);
1894
+ return true;
1895
+ }
1896
+ case "parent": {
1897
+ const parent = snapshot.linkOf(local, 1);
1898
+ if (parent !== null && snapshot.isPresent(parent) && snapshot.isElementAt(parent)) result.push(base + parent);
1899
+ return true;
1900
+ }
1901
+ case "parents":
1902
+ case "ancestors": {
1903
+ let parent = snapshot.linkOf(local, 1);
1904
+ for (let steps = 0; parent !== null && steps < snapshot.size && snapshot.isPresent(parent) && !snapshot.isRootAt(parent); steps++) {
1905
+ if (snapshot.isElementAt(parent)) result.push(base + parent);
1906
+ parent = snapshot.linkOf(parent, 1);
1907
+ }
1908
+ return true;
1909
+ }
1910
+ case "next":
1911
+ case "prev":
1912
+ case "nextAll":
1913
+ case "prevAll": {
1914
+ const offset = kind === "next" || kind === "nextAll" ? 3 : 2;
1915
+ const isSingle = kind === "next" || kind === "prev";
1916
+ let sibling = snapshot.linkOf(local, offset);
1917
+ for (let steps = 0; sibling !== null && steps < snapshot.size && snapshot.isPresent(sibling); steps++) {
1918
+ if (snapshot.isElementAt(sibling)) {
1919
+ result.push(base + sibling);
1920
+ if (isSingle) break;
1921
+ }
1922
+ sibling = snapshot.linkOf(sibling, offset);
1923
+ }
1924
+ return true;
1925
+ }
1926
+ default:
1927
+ return false;
1928
+ }
1929
+ }
1796
1930
  function viewAt(state, local) {
1797
1931
  const existing = state.views[local] ?? cachedView(state, local);
1798
1932
  if (existing) return existing;
@@ -1801,9 +1935,9 @@ function viewAt(state, local) {
1801
1935
  state.adapterNodes[local] = void 0;
1802
1936
  return adoptObject(state, local, parsed);
1803
1937
  }
1804
- const snapshot = plainSnapshot(state, local);
1938
+ const snapshot = structureSnapshot(state, local);
1805
1939
  let constructor;
1806
- if (snapshot) constructor = viewConstructor(snapshot.typeOf(local), false);
1940
+ if (snapshot && !snapshot.hasExtraFields(local)) constructor = viewConstructor(snapshot.typeOf(local), false);
1807
1941
  else {
1808
1942
  const record = recordAt(state, local);
1809
1943
  if (!record) return void 0;
@@ -2002,14 +2136,17 @@ function fillChildren(view) {
2002
2136
  if (!target) return;
2003
2137
  const state = view[VIEW_STATE];
2004
2138
  const local = view[VIEW_LOCAL];
2005
- const snapshot = plainSnapshot(state, local);
2139
+ const snapshot = structureSnapshot(state, local);
2006
2140
  if (snapshot && state.stale.size === 0) {
2007
2141
  fillFromSnapshot(state, snapshot, local, target);
2008
2142
  return;
2009
2143
  }
2010
2144
  const locals = viewRecord(view).children ?? [];
2011
- target.length = locals.length;
2012
2145
  for (let index = 0; index < locals.length; index++) target[index] = viewAt(state, locals[index]);
2146
+ truncate(target, locals.length);
2147
+ }
2148
+ function truncate(target, length) {
2149
+ if (target.length !== length) target.length = length;
2013
2150
  }
2014
2151
  function fillFromSnapshot(state, snapshot, local, target) {
2015
2152
  const count = snapshot.childCount(local);
@@ -2018,17 +2155,17 @@ function fillFromSnapshot(state, snapshot, local, target) {
2018
2155
  const views = state.views;
2019
2156
  const isStrong = !state.adapterNodes;
2020
2157
  const constructors = snapshotConstructors(snapshot);
2021
- target.length = count;
2022
2158
  for (let index = 0; index < count; index++) {
2023
2159
  const child = childIds2[first + index];
2024
2160
  let node = views[child] ?? (state.weakViews.size ? cachedView(state, child) : void 0);
2025
2161
  if (node === void 0) {
2026
- const constructor = isStrong && child < state.baseline && snapshot.isPlain(child) ? constructors(snapshot.typeReference(child)) : void 0;
2162
+ const constructor = isStrong && child < state.baseline && snapshot.isPresent(child) && !snapshot.hasExtraFields(child) ? constructors(snapshot.typeReference(child)) : void 0;
2027
2163
  if (constructor) views[child] = node = new constructor(state, child);
2028
2164
  else node = viewAt(state, child);
2029
2165
  }
2030
2166
  target[index] = node;
2031
2167
  }
2168
+ truncate(target, count);
2032
2169
  }
2033
2170
  function snapshotConstructors(snapshot) {
2034
2171
  let lookup = snapshot.viewConstructors;
@@ -2051,7 +2188,7 @@ var snapshotField = (field) => ({
2051
2188
  get() {
2052
2189
  const state = this[VIEW_STATE];
2053
2190
  const local = this[VIEW_LOCAL];
2054
- const snapshot = plainSnapshot(state, local);
2191
+ const snapshot = field === "data" ? plainSnapshot(state, local) : structureSnapshot(state, local);
2055
2192
  if (snapshot) return field === "type" ? snapshot.typeOf(local) : field === "name" ? snapshot.nameOf(local) : snapshot.dataOf(local);
2056
2193
  return viewRecord(this)[field];
2057
2194
  },
@@ -2064,7 +2201,7 @@ var pointerField = (field) => ({
2064
2201
  get() {
2065
2202
  const state = this[VIEW_STATE];
2066
2203
  const local = this[VIEW_LOCAL];
2067
- const snapshot = plainSnapshot(state, local);
2204
+ const snapshot = structureSnapshot(state, local);
2068
2205
  return linkedView(this, snapshot ? snapshot.linkOf(local, linkOffsets[field]) : viewRecord(this)[field]);
2069
2206
  },
2070
2207
  set(value) {
@@ -2917,11 +3054,11 @@ function selectSimpleWithin(document, contexts, selector, options) {
2917
3054
  }
2918
3055
  var isElementType2 = (type) => type === "tag" || type === "script" || type === "style";
2919
3056
  function typeAt(state, local) {
2920
- const snapshot = plainSnapshot(state, local);
3057
+ const snapshot = structureSnapshot(state, local);
2921
3058
  return snapshot ? snapshot.typeOf(local) : recordAt(state, local)?.type;
2922
3059
  }
2923
3060
  function pushChildren(state, local, stack) {
2924
- const snapshot = plainSnapshot(state, local);
3061
+ const snapshot = structureSnapshot(state, local);
2925
3062
  if (snapshot) {
2926
3063
  for (let index = snapshot.childCount(local) - 1; index >= 0; index--) stack.push(snapshot.childAt(local, index));
2927
3064
  return;
@@ -2937,7 +3074,7 @@ function sameMatchOptions(documentOptions, isXml, isLowerCaseTags, isLowerCaseAt
2937
3074
  return isDocumentXml === isXml && (documentOptions.lowerCaseTags ?? !isDocumentXml) === isLowerCaseTags && (documentOptions.lowerCaseAttributeNames ?? !isDocumentXml) === isLowerCaseAttributes;
2938
3075
  }
2939
3076
  function matchesCompound(state, local, compound, isLowerCaseTags, isLowerCaseAttributes) {
2940
- const snapshot = plainSnapshot(state, local);
3077
+ const snapshot = structureSnapshot(state, local);
2941
3078
  const record = snapshot ? void 0 : recordAt(state, local);
2942
3079
  if (!snapshot && !record) return false;
2943
3080
  if (!isElementType2(snapshot ? snapshot.typeOf(local) : record.type)) return false;
@@ -3517,26 +3654,25 @@ var Cheerio = class {
3517
3654
  }
3518
3655
  /** @internal */
3519
3656
  _insert(position, content) {
3657
+ if (typeof content[0] !== "function") {
3658
+ const ids = this.ids;
3659
+ if (ids.length) this.op("insert", { targetIds: ids, nodeIds: this._domIds(content), position });
3660
+ return this;
3661
+ }
3520
3662
  const targets = this.ids.filter((id) => ["root", "tag", "script", "style", "cdata"].includes(nodeRecord(this[DOCUMENT], id).type));
3521
3663
  if (!targets.length)
3522
3664
  return this;
3523
- if (typeof content[0] === "function")
3524
- return this.each((index, node) => {
3525
- if (!targets.includes(this.ids[index]))
3526
- return;
3527
- if ((position === "before" || position === "after") && nodeRecord(this[DOCUMENT], this.ids[index]).parent == null)
3528
- return;
3529
- const value = content[0].call(node, index, this[DOCUMENT].innerHtml(this.ids[index]));
3530
- let nodes2 = this._domIds(value);
3531
- if (index < this.length - 1)
3532
- nodes2 = this.op("clone", { ids: nodes2, deep: true });
3533
- this.op("insert", { targetIds: [this.ids[index]], nodeIds: nodes2, position });
3534
- });
3535
- let nodes = this._domIds(content);
3536
- if (targets[targets.length - 1] !== this.ids[this.ids.length - 1])
3537
- nodes = this.op("clone", { ids: nodes, deep: true });
3538
- this.op("insert", { targetIds: targets, nodeIds: nodes, position });
3539
- return this;
3665
+ return this.each((index, node) => {
3666
+ if (!targets.includes(this.ids[index]))
3667
+ return;
3668
+ if ((position === "before" || position === "after") && nodeRecord(this[DOCUMENT], this.ids[index]).parent == null)
3669
+ return;
3670
+ const value = content[0].call(node, index, this[DOCUMENT].innerHtml(this.ids[index]));
3671
+ let nodes = this._domIds(value);
3672
+ if (index < this.length - 1)
3673
+ nodes = this.op("clone", { ids: nodes, deep: true });
3674
+ this.op("insert", { targetIds: [this.ids[index]], nodeIds: nodes, position });
3675
+ });
3540
3676
  }
3541
3677
  append(...content) {
3542
3678
  return this._insert("append", content);
@@ -3716,28 +3852,30 @@ function plainKeys(map) {
3716
3852
  return !import_node_util2.types.isProxy(map) && (Object.getPrototypeOf(map) === Object.prototype || Object.getPrototypeOf(map) === null) ? Object.keys(map) : void 0;
3717
3853
  }
3718
3854
  function nativePrefix(scope, map, keys, key) {
3719
- if (!keys || scope._make !== nativeExtractionMethods.make || scope._findBySelector !== nativeExtractionMethods.find || scope.prop !== nativeExtractionMethods.prop || scope.extract !== nativeExtractionMethods.extract) return void 0;
3855
+ if (!keys || !hasNativeExtraction(scope)) return void 0;
3856
+ const prefix = prefixPlan(map, keys, key, scope.options);
3857
+ if (!prefix) return void 0;
3858
+ return { keys: prefix.names, values: runPlan(scope, prefix.plan) };
3859
+ }
3860
+ function prefixPlan(map, keys, key, options) {
3720
3861
  const prefix = /* @__PURE__ */ Object.create(null);
3721
- let count = 0;
3722
3862
  let plan;
3723
- const index = keys.indexOf(key);
3724
- for (let position = index; position < keys.length; position++) {
3863
+ for (let position = keys.indexOf(key); position < keys.length; position++) {
3725
3864
  const name = keys[position];
3726
3865
  const slot = Object.getOwnPropertyDescriptor(map, name);
3727
3866
  if (!slot || !slot.enumerable || !("value" in slot)) break;
3728
3867
  Object.defineProperty(prefix, name, { value: slot.value, enumerable: true, configurable: true });
3729
- const compiled2 = compileExtractPlan(prefix, scope.options);
3868
+ const compiled2 = compileExtractPlan(prefix, options);
3730
3869
  if (!compiled2) {
3731
3870
  delete prefix[name];
3732
3871
  break;
3733
3872
  }
3734
3873
  plan = compiled2;
3735
- count++;
3736
3874
  }
3737
- if (!plan || !count) return void 0;
3738
- const values = scope.op("extractPlan", { ids: scope.ids, plan });
3739
- hydrateExtractResult(values, plan.entries);
3740
- return { keys: Object.keys(prefix), values };
3875
+ return plan ? { names: Object.keys(prefix), plan } : void 0;
3876
+ }
3877
+ function hasNativeExtraction(scope) {
3878
+ return scope._make === nativeExtractionMethods.make && scope._findBySelector === nativeExtractionMethods.find && scope.prop === nativeExtractionMethods.prop && scope.extract === nativeExtractionMethods.extract;
3741
3879
  }
3742
3880
  function extract(map) {
3743
3881
  const result = {};
@@ -3782,8 +3920,17 @@ function extract(map) {
3782
3920
  return result;
3783
3921
  }
3784
3922
  function nativeRows(scope, key, all, selector, map) {
3923
+ if (!hasNativeExtraction(scope)) return void 0;
3924
+ const planned = rowsPlan(key, all, selector, map, scope.options);
3925
+ if (!planned) return void 0;
3926
+ const writes = writeCount();
3927
+ const extracted = runPlan(scope, planned.plan)[key];
3928
+ const rows = all ? extracted : extracted === void 0 ? [] : [extracted];
3929
+ return Array.isArray(rows) ? { keys: planned.nativeKeys, rows, writes } : void 0;
3930
+ }
3931
+ function rowsPlan(key, all, selector, map, options) {
3785
3932
  const keys = plainKeys(map);
3786
- if (!keys || scope._make !== nativeExtractionMethods.make || scope._findBySelector !== nativeExtractionMethods.find || scope.prop !== nativeExtractionMethods.prop || scope.extract !== nativeExtractionMethods.extract) return void 0;
3933
+ if (!keys) return void 0;
3787
3934
  const subset = /* @__PURE__ */ Object.create(null);
3788
3935
  const nativeKeys = /* @__PURE__ */ new Set();
3789
3936
  for (const name of keys) {
@@ -3791,21 +3938,81 @@ function nativeRows(scope, key, all, selector, map) {
3791
3938
  if (!slot || !slot.enumerable || !("value" in slot)) continue;
3792
3939
  const single = /* @__PURE__ */ Object.create(null);
3793
3940
  Object.defineProperty(single, name, { value: slot.value, enumerable: true, configurable: true });
3794
- if (!compileExtractPlan(single, scope.options)) continue;
3941
+ if (!compileExtractPlan(single, options)) continue;
3795
3942
  Object.defineProperty(subset, name, { value: slot.value, enumerable: true, configurable: true });
3796
3943
  nativeKeys.add(name);
3797
3944
  }
3798
3945
  if (!nativeKeys.size || nativeKeys.size === keys.length) return void 0;
3799
3946
  const outer = /* @__PURE__ */ Object.create(null);
3800
3947
  Object.defineProperty(outer, key, { value: all ? [{ selector, value: subset }] : { selector, value: subset }, enumerable: true, configurable: true });
3801
- const plan = compileExtractPlan(outer, scope.options);
3802
- if (!plan) return void 0;
3803
- const writes = writeCount();
3804
- const values = scope.op("extractPlan", { ids: scope.ids, plan });
3948
+ const plan = compileExtractPlan(outer, options);
3949
+ return plan ? { nativeKeys, plan } : void 0;
3950
+ }
3951
+ function rootExtractPlans(map, options) {
3952
+ if (!isPlainData(map, 0) || hasInheritedEntryFields()) return [];
3953
+ try {
3954
+ return collectRootExtractPlans(map, options);
3955
+ } catch {
3956
+ return [];
3957
+ }
3958
+ }
3959
+ function isPlainData(value, depth) {
3960
+ if (typeof value !== "object" || value === null) return true;
3961
+ if (depth > 32 || import_node_util2.types.isProxy(value)) return false;
3962
+ const prototype = Object.getPrototypeOf(value);
3963
+ if (prototype !== Object.prototype && prototype !== Array.prototype && prototype !== null) return false;
3964
+ for (const key of Reflect.ownKeys(value)) {
3965
+ const slot = Object.getOwnPropertyDescriptor(value, key);
3966
+ if (!("value" in slot) || !isPlainData(slot.value, depth + 1)) return false;
3967
+ }
3968
+ return true;
3969
+ }
3970
+ function hasInheritedEntryFields() {
3971
+ return "selector" in Object.prototype || "value" in Object.prototype || "0" in Array.prototype;
3972
+ }
3973
+ function collectRootExtractPlans(map, options) {
3974
+ const plans = [];
3975
+ const keys = plainKeys(map);
3976
+ const processed = /* @__PURE__ */ new Set();
3977
+ for (const key in map) {
3978
+ if (processed.has(key)) continue;
3979
+ const prefix = keys ? prefixPlan(map, keys, key, options) : void 0;
3980
+ if (prefix) {
3981
+ for (const name of prefix.names) processed.add(name);
3982
+ plans.push(prefix.plan);
3983
+ continue;
3984
+ }
3985
+ const entry = map[key];
3986
+ const all = Array.isArray(entry);
3987
+ const descriptor = all ? entry[0] : entry;
3988
+ const value = typeof descriptor === "string" ? "textContent" : descriptor.value ?? "textContent";
3989
+ if (typeof value !== "object" || value === null) continue;
3990
+ const planned = rowsPlan(key, all, typeof descriptor === "string" ? descriptor : descriptor.selector, value, options);
3991
+ if (planned) plans.push(planned.plan);
3992
+ }
3993
+ return plans.filter((plan) => !JSON.stringify(plan).includes("innerHTML"));
3994
+ }
3995
+ var preparedPlans = /* @__PURE__ */ new WeakMap();
3996
+ function prepareExtractPlans(document, plans, results) {
3997
+ if (!results) return;
3998
+ const prepared = /* @__PURE__ */ new Map();
3999
+ for (let index = 0; index < plans.length; index++) {
4000
+ const result = results[index];
4001
+ if (typeof result === "string") prepared.set(JSON.stringify(plans[index]), decodeJson(result));
4002
+ }
4003
+ preparedPlans.set(document, { writes: writeCount(), results: prepared });
4004
+ }
4005
+ function releaseExtractPlans(document) {
4006
+ preparedPlans.delete(document);
4007
+ }
4008
+ function runPlan(scope, plan) {
4009
+ const document = scope[DOCUMENT];
4010
+ const prepared = preparedPlans.get(document);
4011
+ const isRoot = prepared !== void 0 && prepared.writes === writeCount() && scope.ids.length === 1 && scope.ids[0] === document.rootId();
4012
+ const result = isRoot ? prepared.results.get(JSON.stringify(plan)) : void 0;
4013
+ const values = result !== void 0 ? JSON.parse(result) : scope.op("extractPlan", { ids: scope.ids, plan });
3805
4014
  hydrateExtractResult(values, plan.entries);
3806
- const extracted = values[key];
3807
- const rows = all ? extracted : extracted === void 0 ? [] : [extracted];
3808
- return Array.isArray(rows) ? { keys: nativeKeys, rows, writes } : void 0;
4015
+ return values;
3809
4016
  }
3810
4017
  function completeRow(scope, map, rows, index) {
3811
4018
  const row = {};
@@ -4476,7 +4683,9 @@ var dom = {
4476
4683
  isCDATA,
4477
4684
  hasChildren
4478
4685
  };
4686
+ var defaultOptions = { _useHtmlParser2: false };
4479
4687
  function flattenOptions(options, base) {
4688
+ if (!options) return base ?? defaultOptions;
4480
4689
  const result = { _useHtmlParser2: false, ...base, ...options };
4481
4690
  if (options?.xml) {
4482
4691
  result._useHtmlParser2 = true;
@@ -4636,13 +4845,23 @@ function parallelOptions(options) {
4636
4845
  return { options: flattenOptions(opts), threads };
4637
4846
  }
4638
4847
  async function loadAll(content, options, isDocument2 = true) {
4848
+ return loadAllWithPlans(content, options, isDocument2);
4849
+ }
4850
+ async function loadAllWithPlans(content, options, isDocument2, plans) {
4639
4851
  const { options: opts, threads } = parallelOptions(options);
4640
4852
  if (!Array.isArray(content) || content.some((item) => typeof item !== "string")) throw new TypeError("loadAll expects an array of HTML strings");
4641
4853
  if (!content.length) return [];
4642
4854
  const binding = requireNative();
4643
4855
  if (!binding.parseManyAsync || hasParserHooks(opts)) return Promise.all(content.map((html) => loadAsync(html, opts, isDocument2)));
4644
- const documents = await binding.parseManyAsync([...content], isDocument2, threads, JSON.stringify(opts));
4645
- return documents.map((document) => createAPI(routeDocument(document, opts), opts));
4856
+ const documents = await binding.parseManyAsync([...content], isDocument2, threads, JSON.stringify(opts), plans && JSON.stringify(plans));
4857
+ return documents.map((document) => {
4858
+ const routed = routeDocument(document, opts);
4859
+ if (plans) {
4860
+ usePreparedSnapshot(routed);
4861
+ prepareExtractPlans(routed, plans, document.takePreparedPlans?.());
4862
+ }
4863
+ return createAPI(routed, opts);
4864
+ });
4646
4865
  }
4647
4866
  function nativeQueries(map, options) {
4648
4867
  const queries = [];
@@ -4678,11 +4897,16 @@ async function extractAll(content, map, options) {
4678
4897
  }
4679
4898
  return results2;
4680
4899
  }
4681
- const documents = await loadAll(content, { ...opts, threads });
4900
+ const documents = await loadAllWithPlans(content, { ...opts, threads }, true, hasParserHooks(opts) ? void 0 : rootExtractPlans(map, opts));
4682
4901
  const results = [];
4683
4902
  let yieldedAt = performance.now();
4684
4903
  for (const document of documents) {
4685
- results.push((await extractCooperatively(document.root(), map)).value);
4904
+ const root = document.root();
4905
+ try {
4906
+ results.push((await extractCooperatively(root, map)).value);
4907
+ } finally {
4908
+ releaseExtractPlans(root[DOCUMENT]);
4909
+ }
4686
4910
  if (performance.now() - yieldedAt >= 4) {
4687
4911
  await (0, import_promises.setImmediate)();
4688
4912
  yieldedAt = performance.now();