rusteerio 1.1.0 → 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 +144 -89
  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
@@ -219,7 +219,7 @@ function wrapNativeBinding(binding) {
219
219
  if (raw.NativeHookStream) wrapped.NativeHookStream = wrapConstructor(raw.NativeHookStream, ([isDocument2, options, bridge]) => [isDocument2, encodeOptions(options), wrapBridge(bridge)], wrapStream);
220
220
  if (raw.parseWithTreeAdapter) wrapped.parseWithTreeAdapter = (source, isDocument2, options, bridge) => invoke(() => raw.parseWithTreeAdapter(encodeString(source), isDocument2, encodeOptions(options), wrapBridge(bridge)));
221
221
  if (raw.parseAsync) wrapped.parseAsync = (source, isDocument2, options) => invoke(() => raw.parseAsync(encodeString(source), isDocument2, encodeOptions(options)));
222
- if (raw.parseManyAsync) wrapped.parseManyAsync = (sources, isDocument2, threads, options) => invoke(() => raw.parseManyAsync(sources.map(encodeString), isDocument2, threads, encodeOptions(options)));
222
+ 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)));
223
223
  if (raw.extractPlanManyAsync) wrapped.extractPlanManyAsync = async (sources, plan, threads, options) => decodeJson(await invoke(() => raw.extractPlanManyAsync(sources.map(encodeString), encodeJson(plan), threads, encodeOptions(options))));
224
224
  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))));
225
225
  return wrapped;
@@ -266,6 +266,284 @@ function nativeStatus() {
266
266
  return available ? { available } : { available, error: String(bindingError ?? "Native binding not found") };
267
267
  }
268
268
 
269
+ // src/snapshot.ts
270
+ var HEADER = 5;
271
+ var STRIDE = 15;
272
+ var ABSENT = 4294967295;
273
+ var HAS_START_INDEX = 1;
274
+ var HAS_END_INDEX = 2;
275
+ var HAS_NAMESPACE = 4;
276
+ var EMPTY_ATTRIBUTE_MAPS = 8;
277
+ var OPAQUE = 16;
278
+ var RELEASED = 32;
279
+ var noChildren = Object.freeze([]);
280
+ var noAttributes = Object.freeze({});
281
+ var SnapshotRecord = class {
282
+ constructor(snapshot, id, base) {
283
+ this.snapshot = snapshot;
284
+ this.id = id;
285
+ this.base = base;
286
+ this.decodedData = void 0;
287
+ this.decodedAttributes = void 0;
288
+ this.decodedChildren = void 0;
289
+ }
290
+ snapshot;
291
+ id;
292
+ base;
293
+ decodedData;
294
+ decodedAttributes;
295
+ decodedChildren;
296
+ get type() {
297
+ return this.snapshot.name(this.snapshot.nodes[this.base]);
298
+ }
299
+ get name() {
300
+ const name = this.snapshot.nodes[this.base + 6];
301
+ return name ? this.snapshot.name(name - 1) : void 0;
302
+ }
303
+ get data() {
304
+ if (this.decodedData !== void 0) return this.decodedData;
305
+ const data2 = this.snapshot.nodes[this.base + 7];
306
+ return data2 ? this.decodedData = this.snapshot.string(data2 - 1) : void 0;
307
+ }
308
+ get parent() {
309
+ const parent = this.snapshot.nodes[this.base + 1];
310
+ return parent ? parent - 1 : null;
311
+ }
312
+ get prev() {
313
+ const prev = this.snapshot.nodes[this.base + 2];
314
+ return prev ? prev - 1 : null;
315
+ }
316
+ get next() {
317
+ const next = this.snapshot.nodes[this.base + 3];
318
+ return next ? next - 1 : null;
319
+ }
320
+ get startIndex() {
321
+ return this.index(HAS_START_INDEX, 11);
322
+ }
323
+ get endIndex() {
324
+ return this.index(HAS_END_INDEX, 12);
325
+ }
326
+ get namespace() {
327
+ const nodes = this.snapshot.nodes;
328
+ return nodes[this.base + 10] & HAS_NAMESPACE ? this.snapshot.name(nodes[this.base + 13]) : void 0;
329
+ }
330
+ get attribsNamespace() {
331
+ return this.snapshot.nodes[this.base + 10] & EMPTY_ATTRIBUTE_MAPS ? {} : void 0;
332
+ }
333
+ get attribsPrefix() {
334
+ return this.snapshot.nodes[this.base + 10] & EMPTY_ATTRIBUTE_MAPS ? {} : void 0;
335
+ }
336
+ get attribs() {
337
+ return this.decodedAttributes ??= this.snapshot.attributesOf(this.id);
338
+ }
339
+ get children() {
340
+ return this.decodedChildren ??= this.snapshot.childrenOf(this.id);
341
+ }
342
+ index(flag, offset) {
343
+ const nodes = this.snapshot.nodes;
344
+ if (!(nodes[this.base + 10] & flag)) return void 0;
345
+ const value = nodes[this.base + offset];
346
+ return value === ABSENT ? null : value;
347
+ }
348
+ };
349
+ var recordFields = ["type", "name", "data", "parent", "prev", "next", "startIndex", "endIndex", "namespace", "attribsNamespace", "attribsPrefix", "attribs", "children"];
350
+ function plainRecord(record) {
351
+ if (!(record instanceof SnapshotRecord)) return record;
352
+ const copy = { id: record.id };
353
+ for (const field of recordFields) {
354
+ const value = record[field];
355
+ if (value !== void 0) copy[field] = value;
356
+ }
357
+ for (const key of Object.keys(record)) if (!key.startsWith("decoded") && key !== "snapshot" && key !== "base" && key !== "id") copy[key] = record[key];
358
+ return copy;
359
+ }
360
+ var NodeSnapshot = class {
361
+ size;
362
+ nodes;
363
+ children;
364
+ attributes;
365
+ strings;
366
+ text;
367
+ isEncoded;
368
+ names = [];
369
+ /** Per interned type: 1 for element types, 2 for root, 3 for text, 0 otherwise. */
370
+ typeKinds = [];
371
+ /** Node object constructors by interned type, resolved once per snapshot. */
372
+ viewConstructors = void 0;
373
+ /** Text nodes too large for the arrays, read individually while this snapshot is current. */
374
+ largeTexts = /* @__PURE__ */ new Map();
375
+ constructor(raw) {
376
+ const { data: data2 } = raw;
377
+ const childrenStart = HEADER + data2[0];
378
+ const attributesStart = childrenStart + data2[1];
379
+ const stringsStart = attributesStart + data2[2];
380
+ this.text = raw.text;
381
+ this.isEncoded = data2[4] === 1;
382
+ this.nodes = data2.subarray(HEADER, childrenStart);
383
+ this.children = data2.subarray(childrenStart, attributesStart);
384
+ this.attributes = data2.subarray(attributesStart, stringsStart);
385
+ this.strings = data2.subarray(stringsStart, stringsStart + data2[3]);
386
+ this.size = this.nodes.length / STRIDE;
387
+ }
388
+ /** The record, `null` for a released node, or `undefined` when it must be read individually. */
389
+ record(local) {
390
+ if (local >= this.size) return void 0;
391
+ const base = local * STRIDE;
392
+ const flags = this.nodes[base + 10];
393
+ if (flags & RELEASED) return null;
394
+ if (flags & OPAQUE) return void 0;
395
+ const record = new SnapshotRecord(this, local, base);
396
+ const extras = this.nodes[base + 14];
397
+ if (extras) {
398
+ const fields = JSON.parse(this.string(extras - 1));
399
+ for (const key of Object.keys(fields)) Object.defineProperty(record, key, { value: fields[key], writable: true, enumerable: true, configurable: true });
400
+ }
401
+ return record;
402
+ }
403
+ /** Whether a node's fields all live in the arrays, so callers may read them without a record. */
404
+ isPlain(local) {
405
+ if (local >= this.size) return false;
406
+ const base = local * STRIDE;
407
+ return (this.nodes[base + 10] & (OPAQUE | RELEASED)) === 0 && this.nodes[base + 14] === 0;
408
+ }
409
+ typeOf(local) {
410
+ return this.name(this.nodes[local * STRIDE]);
411
+ }
412
+ nameOf(local) {
413
+ const name = this.nodes[local * STRIDE + 6];
414
+ return name ? this.name(name - 1) : void 0;
415
+ }
416
+ dataOf(local) {
417
+ const data2 = this.nodes[local * STRIDE + 7];
418
+ return data2 ? this.string(data2 - 1) : void 0;
419
+ }
420
+ /** The parent, previous or next sibling (`offset` 1, 2 or 3), or `null`. */
421
+ linkOf(local, offset) {
422
+ const link = this.nodes[local * STRIDE + offset];
423
+ return link ? link - 1 : null;
424
+ }
425
+ namespaceOf(local) {
426
+ const base = local * STRIDE;
427
+ return this.nodes[base + 10] & HAS_NAMESPACE ? this.name(this.nodes[base + 13]) : void 0;
428
+ }
429
+ /** A node's attributes as parse5 attribute objects; plain nodes have no namespaced ones. */
430
+ attributeObjects(local) {
431
+ const base = local * STRIDE;
432
+ const count = this.nodes[base + 9];
433
+ const first = this.nodes[base + 8];
434
+ const attributes = this.attributes;
435
+ const result = new Array(count);
436
+ for (let index = 0; index < count; index++) {
437
+ const pair = (first + index) * 2;
438
+ result[index] = { name: this.name(attributes[pair]), value: this.string(attributes[pair + 1]) };
439
+ }
440
+ return result;
441
+ }
442
+ /** Copies a node's attributes onto `target`, in order. */
443
+ copyAttributes(local, target) {
444
+ const base = local * STRIDE;
445
+ const count = this.nodes[base + 9];
446
+ const first = this.nodes[base + 8];
447
+ const attributes = this.attributes;
448
+ for (let index = 0; index < count; index++) {
449
+ const pair = (first + index) * 2;
450
+ const name = this.name(attributes[pair]);
451
+ const value = this.string(attributes[pair + 1]);
452
+ if (name === "__proto__") Object.defineProperty(target, name, { value, writable: true, enumerable: true, configurable: true });
453
+ else target[name] = value;
454
+ }
455
+ }
456
+ /** The flat child id list; a node's children start at `childStart(local)`. */
457
+ get childIds() {
458
+ return this.children;
459
+ }
460
+ childStart(local) {
461
+ return this.nodes[local * STRIDE + 4];
462
+ }
463
+ /** Whether a node is an element: a tag, script or style. */
464
+ isElementAt(local) {
465
+ return this.typeKind(local) === 1;
466
+ }
467
+ isRootAt(local) {
468
+ return this.typeKind(local) === 2;
469
+ }
470
+ isTextAt(local) {
471
+ return this.typeKind(local) === 3;
472
+ }
473
+ /** Whether a node's record adds fields stored as JSON, such as source locations. */
474
+ hasExtraFields(local) {
475
+ return this.nodes[local * STRIDE + 14] !== 0;
476
+ }
477
+ /** Whether the slot holds a node, rather than one a collection released. */
478
+ isPresent(local) {
479
+ return local < this.size && (this.nodes[local * STRIDE + 10] & RELEASED) === 0;
480
+ }
481
+ typeKind(local) {
482
+ const reference = this.nodes[local * STRIDE];
483
+ let kind = this.typeKinds[reference];
484
+ if (kind === void 0) {
485
+ const type = this.name(reference);
486
+ kind = this.typeKinds[reference] = type === "tag" || type === "script" || type === "style" ? 1 : type === "root" ? 2 : type === "text" ? 3 : 0;
487
+ }
488
+ return kind;
489
+ }
490
+ /** The interned reference of a node's type, comparable across nodes of this snapshot. */
491
+ typeReference(local) {
492
+ return this.nodes[local * STRIDE];
493
+ }
494
+ /** Child ids without materializing the record. */
495
+ childCount(local) {
496
+ return this.nodes[local * STRIDE + 5];
497
+ }
498
+ childAt(local, index) {
499
+ return this.children[this.nodes[local * STRIDE + 4] + index];
500
+ }
501
+ childrenOf(local) {
502
+ const base = local * STRIDE;
503
+ const count = this.nodes[base + 5];
504
+ if (count === 0) return noChildren;
505
+ const first = this.nodes[base + 4];
506
+ return Array.from(this.children.subarray(first, first + count));
507
+ }
508
+ attributesOf(local) {
509
+ const base = local * STRIDE;
510
+ const count = this.nodes[base + 9];
511
+ if (count === 0) return noAttributes;
512
+ const first = this.nodes[base + 8];
513
+ const attributes = this.attributes;
514
+ const result = {};
515
+ for (let index = 0; index < count; index++) {
516
+ const pair = (first + index) * 2;
517
+ const name = this.name(attributes[pair]);
518
+ const value = this.string(attributes[pair + 1]);
519
+ if (name === "__proto__") Object.defineProperty(result, name, { value, writable: true, enumerable: true, configurable: true });
520
+ else result[name] = value;
521
+ }
522
+ return result;
523
+ }
524
+ /** One attribute's value, without decoding the rest. */
525
+ attributeValue(local, name) {
526
+ const base = local * STRIDE;
527
+ const count = this.nodes[base + 9];
528
+ const first = this.nodes[base + 8];
529
+ const attributes = this.attributes;
530
+ for (let index = count - 1; index >= 0; index--) {
531
+ const pair = (first + index) * 2;
532
+ if (this.name(attributes[pair]) === name) return this.string(attributes[pair + 1]);
533
+ }
534
+ return void 0;
535
+ }
536
+ string(reference) {
537
+ const strings = this.strings;
538
+ const value = this.text.slice(strings[reference * 2], strings[reference * 2 + 1]);
539
+ return this.isEncoded ? decodeString(value) : value;
540
+ }
541
+ /** Names repeat across nodes, so each decodes once. */
542
+ name(reference) {
543
+ return this.names[reference] ??= this.string(reference);
544
+ }
545
+ };
546
+
269
547
  // src/hooks.ts
270
548
  var tokenCallbacks = ["onattribdata", "onattribentity", "onattribend", "onattribname", "oncdata", "onclosetag", "oncomment", "ondeclaration", "onend", "onopentagend", "onopentagname", "onprocessinginstruction", "onselfclosingtag", "ontext", "ontextentity"];
271
549
  function createTokenizerSession(options) {
@@ -568,357 +846,110 @@ function replayIntoAdapter(document, adapter, isDocument2) {
568
846
  for (let local = 0; local < Math.min(size, 65); local++) recordAt(state, local);
569
847
  for (let local = 0; local < size; local++) {
570
848
  const snapshot = plainSnapshot(state, local);
571
- const record = snapshot ? void 0 : recordAt(state, local);
572
- if (!snapshot && !record) continue;
573
- const type = snapshot ? snapshot.typeOf(local) : record.type;
574
- if (local === root) {
575
- nodes[local] = isDocument2 ? adapter.createDocument() : adapter.createDocumentFragment();
576
- const mode = record?.["x-mode"];
577
- if (isDocument2 && mode !== void 0) adapter.setDocumentMode(nodes[local], mode);
578
- continue;
579
- }
580
- const parentLocal = snapshot ? snapshot.linkOf(local, 1) : record.parent;
581
- const parent = parentLocal == null ? void 0 : nodes[parentLocal];
582
- if (parent === void 0) return void 0;
583
- switch (type) {
584
- case "tag":
585
- case "script":
586
- case "style": {
587
- const element = snapshot ? adapter.createElement(snapshot.nameOf(local), snapshot.namespaceOf(local), snapshot.attributeObjects(local)) : adapter.createElement(record.name, record.namespace, attributeList(record));
588
- adapter.appendChild(parent, element);
589
- nodes[local] = element;
590
- break;
591
- }
592
- case "root": {
593
- const content = adapter.createDocumentFragment();
594
- adapter.setTemplateContent(parent, content);
595
- nodes[local] = content;
596
- break;
597
- }
598
- case "text": {
599
- adapter.insertText(parent, (snapshot ? snapshot.dataOf(local) : record.data) ?? "");
600
- const children = adapter.getChildNodes(parent);
601
- nodes[local] = children[children.length - 1];
602
- break;
603
- }
604
- case "comment": {
605
- const comment = adapter.createCommentNode((snapshot ? snapshot.dataOf(local) : record.data) ?? "");
606
- adapter.appendChild(parent, comment);
607
- nodes[local] = comment;
608
- break;
609
- }
610
- case "directive": {
611
- if (!record || record.name !== "!doctype" || !isDocument2) return void 0;
612
- adapter.setDocumentType(parent, record["x-name"] ?? "", record["x-publicId"] ?? "", record["x-systemId"] ?? "");
613
- nodes[local] = Array.from(adapter.getChildNodes(parent) ?? []).find((child) => adapter.isDocumentTypeNode(child));
614
- break;
615
- }
616
- default:
617
- return void 0;
618
- }
619
- }
620
- const changes = differencesFromParse(adapter, nodes, state, root);
621
- if (!changes) return void 0;
622
- for (const change of changes) operation(document, "setNode", { id: state.base + change.local, field: change.field, value: change.value });
623
- return nodes;
624
- }
625
- function attributeList(record) {
626
- const attributes = record.attribs ?? {};
627
- return Object.keys(attributes).map((name) => {
628
- const namespace = record.attribsNamespace?.[name];
629
- const prefix = record.attribsPrefix?.[name];
630
- return { name, value: attributes[name], ...namespace === void 0 ? {} : { namespace }, ...prefix === void 0 ? {} : { prefix } };
631
- });
632
- }
633
- function differencesFromParse(adapter, nodes, state, root) {
634
- const changes = [];
635
- for (let local = 0; local < nodes.length; local++) {
636
- const node = nodes[local];
637
- const snapshot = plainSnapshot(state, local);
638
- const record = snapshot ? void 0 : recordAt(state, local);
639
- if (!snapshot && !record) continue;
640
- if (!node) return void 0;
641
- const type = snapshot ? snapshot.typeOf(local) : record.type;
642
- if (local !== root && adapter.getParentNode(node) !== nodes[snapshot ? snapshot.linkOf(local, 1) : record.parent]) return void 0;
643
- if (type !== "text" && type !== "comment" && type !== "directive") {
644
- const children = adapter.getChildNodes(node) ?? [];
645
- const count = snapshot ? snapshot.childCount(local) : record.children.length;
646
- if (children.length !== count) return void 0;
647
- for (let index = 0; index < count; index++) if (children[index] !== nodes[snapshot ? snapshot.childAt(local, index) : record.children[index]]) return void 0;
648
- }
649
- if (type === "tag" || type === "script" || type === "style") {
650
- const name = snapshot ? snapshot.nameOf(local) : record.name;
651
- const namespace = snapshot ? snapshot.namespaceOf(local) : record.namespace;
652
- if (!adapter.isElementNode(node) || adapter.getTagName(node) !== name || adapter.getNamespaceURI(node) !== namespace) return void 0;
653
- const attributes = adapter.getAttrList(node) ?? [];
654
- const expected = snapshot ? snapshot.attributeObjects(local) : attributeList(record);
655
- const isSame = attributes.length === expected.length && attributes.every((attribute, index) => attribute.name === expected[index].name && attribute.value === expected[index].value);
656
- if (!isSame) {
657
- const values = /* @__PURE__ */ Object.create(null);
658
- const namespaces = /* @__PURE__ */ Object.create(null);
659
- const prefixes = /* @__PURE__ */ Object.create(null);
660
- for (const attribute of attributes) {
661
- values[attribute.name] = attribute.value;
662
- if (attribute.namespace !== void 0) namespaces[attribute.name] = attribute.namespace;
663
- if (attribute.prefix !== void 0) prefixes[attribute.name] = attribute.prefix;
664
- }
665
- changes.push({ local, field: "attribs", value: values }, { local, field: "attribsNamespace", value: namespaces }, { local, field: "attribsPrefix", value: prefixes });
666
- }
667
- } else if (type === "text" || type === "comment") {
668
- if (type === "text" ? !adapter.isTextNode(node) : !adapter.isCommentNode(node)) return void 0;
669
- const data2 = type === "text" ? adapter.getTextNodeContent(node) : adapter.getCommentNodeContent(node);
670
- if (data2 !== (snapshot ? snapshot.dataOf(local) : record.data)) changes.push({ local, field: "data", value: data2 });
671
- }
672
- }
673
- return changes;
674
- }
675
-
676
- // src/snapshot.ts
677
- var HEADER = 5;
678
- var STRIDE = 15;
679
- var ABSENT = 4294967295;
680
- var HAS_START_INDEX = 1;
681
- var HAS_END_INDEX = 2;
682
- var HAS_NAMESPACE = 4;
683
- var EMPTY_ATTRIBUTE_MAPS = 8;
684
- var OPAQUE = 16;
685
- var RELEASED = 32;
686
- var noChildren = Object.freeze([]);
687
- var noAttributes = Object.freeze({});
688
- var SnapshotRecord = class {
689
- constructor(snapshot, id, base) {
690
- this.snapshot = snapshot;
691
- this.id = id;
692
- this.base = base;
693
- this.decodedData = void 0;
694
- this.decodedAttributes = void 0;
695
- this.decodedChildren = void 0;
696
- }
697
- snapshot;
698
- id;
699
- base;
700
- decodedData;
701
- decodedAttributes;
702
- decodedChildren;
703
- get type() {
704
- return this.snapshot.name(this.snapshot.nodes[this.base]);
705
- }
706
- get name() {
707
- const name = this.snapshot.nodes[this.base + 6];
708
- return name ? this.snapshot.name(name - 1) : void 0;
709
- }
710
- get data() {
711
- if (this.decodedData !== void 0) return this.decodedData;
712
- const data2 = this.snapshot.nodes[this.base + 7];
713
- return data2 ? this.decodedData = this.snapshot.string(data2 - 1) : void 0;
714
- }
715
- get parent() {
716
- const parent = this.snapshot.nodes[this.base + 1];
717
- return parent ? parent - 1 : null;
718
- }
719
- get prev() {
720
- const prev = this.snapshot.nodes[this.base + 2];
721
- return prev ? prev - 1 : null;
722
- }
723
- get next() {
724
- const next = this.snapshot.nodes[this.base + 3];
725
- return next ? next - 1 : null;
726
- }
727
- get startIndex() {
728
- return this.index(HAS_START_INDEX, 11);
729
- }
730
- get endIndex() {
731
- return this.index(HAS_END_INDEX, 12);
732
- }
733
- get namespace() {
734
- const nodes = this.snapshot.nodes;
735
- return nodes[this.base + 10] & HAS_NAMESPACE ? this.snapshot.name(nodes[this.base + 13]) : void 0;
736
- }
737
- get attribsNamespace() {
738
- return this.snapshot.nodes[this.base + 10] & EMPTY_ATTRIBUTE_MAPS ? {} : void 0;
739
- }
740
- get attribsPrefix() {
741
- return this.snapshot.nodes[this.base + 10] & EMPTY_ATTRIBUTE_MAPS ? {} : void 0;
742
- }
743
- get attribs() {
744
- return this.decodedAttributes ??= this.snapshot.attributesOf(this.id);
745
- }
746
- get children() {
747
- return this.decodedChildren ??= this.snapshot.childrenOf(this.id);
748
- }
749
- index(flag, offset) {
750
- const nodes = this.snapshot.nodes;
751
- if (!(nodes[this.base + 10] & flag)) return void 0;
752
- const value = nodes[this.base + offset];
753
- return value === ABSENT ? null : value;
754
- }
755
- };
756
- var recordFields = ["type", "name", "data", "parent", "prev", "next", "startIndex", "endIndex", "namespace", "attribsNamespace", "attribsPrefix", "attribs", "children"];
757
- function plainRecord(record) {
758
- if (!(record instanceof SnapshotRecord)) return record;
759
- const copy = { id: record.id };
760
- for (const field of recordFields) {
761
- const value = record[field];
762
- if (value !== void 0) copy[field] = value;
763
- }
764
- for (const key of Object.keys(record)) if (!key.startsWith("decoded") && key !== "snapshot" && key !== "base" && key !== "id") copy[key] = record[key];
765
- return copy;
766
- }
767
- var NodeSnapshot = class {
768
- size;
769
- nodes;
770
- children;
771
- attributes;
772
- strings;
773
- text;
774
- isEncoded;
775
- names = [];
776
- /** Node object constructors by interned type, resolved once per snapshot. */
777
- viewConstructors = void 0;
778
- constructor(raw) {
779
- const { data: data2 } = raw;
780
- const childrenStart = HEADER + data2[0];
781
- const attributesStart = childrenStart + data2[1];
782
- const stringsStart = attributesStart + data2[2];
783
- this.text = raw.text;
784
- this.isEncoded = data2[4] === 1;
785
- this.nodes = data2.subarray(HEADER, childrenStart);
786
- this.children = data2.subarray(childrenStart, attributesStart);
787
- this.attributes = data2.subarray(attributesStart, stringsStart);
788
- this.strings = data2.subarray(stringsStart, stringsStart + data2[3]);
789
- this.size = this.nodes.length / STRIDE;
790
- }
791
- /** The record, `null` for a released node, or `undefined` when it must be read individually. */
792
- record(local) {
793
- if (local >= this.size) return void 0;
794
- const base = local * STRIDE;
795
- const flags = this.nodes[base + 10];
796
- if (flags & RELEASED) return null;
797
- if (flags & OPAQUE) return void 0;
798
- const record = new SnapshotRecord(this, local, base);
799
- const extras = this.nodes[base + 14];
800
- if (extras) {
801
- const fields = JSON.parse(this.string(extras - 1));
802
- for (const key of Object.keys(fields)) Object.defineProperty(record, key, { value: fields[key], writable: true, enumerable: true, configurable: true });
803
- }
804
- return record;
805
- }
806
- /** Whether a node's fields all live in the arrays, so callers may read them without a record. */
807
- isPlain(local) {
808
- if (local >= this.size) return false;
809
- const base = local * STRIDE;
810
- return (this.nodes[base + 10] & (OPAQUE | RELEASED)) === 0 && this.nodes[base + 14] === 0;
811
- }
812
- typeOf(local) {
813
- return this.name(this.nodes[local * STRIDE]);
814
- }
815
- nameOf(local) {
816
- const name = this.nodes[local * STRIDE + 6];
817
- return name ? this.name(name - 1) : void 0;
818
- }
819
- dataOf(local) {
820
- const data2 = this.nodes[local * STRIDE + 7];
821
- return data2 ? this.string(data2 - 1) : void 0;
822
- }
823
- /** The parent, previous or next sibling (`offset` 1, 2 or 3), or `null`. */
824
- linkOf(local, offset) {
825
- const link = this.nodes[local * STRIDE + offset];
826
- return link ? link - 1 : null;
827
- }
828
- namespaceOf(local) {
829
- const base = local * STRIDE;
830
- return this.nodes[base + 10] & HAS_NAMESPACE ? this.name(this.nodes[base + 13]) : void 0;
831
- }
832
- /** A node's attributes as parse5 attribute objects; plain nodes have no namespaced ones. */
833
- attributeObjects(local) {
834
- const base = local * STRIDE;
835
- const count = this.nodes[base + 9];
836
- const first = this.nodes[base + 8];
837
- const attributes = this.attributes;
838
- const result = new Array(count);
839
- for (let index = 0; index < count; index++) {
840
- const pair = (first + index) * 2;
841
- result[index] = { name: this.name(attributes[pair]), value: this.string(attributes[pair + 1]) };
849
+ const record = snapshot ? void 0 : recordAt(state, local);
850
+ if (!snapshot && !record) continue;
851
+ const type = snapshot ? snapshot.typeOf(local) : record.type;
852
+ if (local === root) {
853
+ nodes[local] = isDocument2 ? adapter.createDocument() : adapter.createDocumentFragment();
854
+ const mode = record?.["x-mode"];
855
+ if (isDocument2 && mode !== void 0) adapter.setDocumentMode(nodes[local], mode);
856
+ continue;
842
857
  }
843
- return result;
844
- }
845
- /** Copies a node's attributes onto `target`, in order. */
846
- copyAttributes(local, target) {
847
- const base = local * STRIDE;
848
- const count = this.nodes[base + 9];
849
- const first = this.nodes[base + 8];
850
- const attributes = this.attributes;
851
- for (let index = 0; index < count; index++) {
852
- const pair = (first + index) * 2;
853
- const name = this.name(attributes[pair]);
854
- const value = this.string(attributes[pair + 1]);
855
- if (name === "__proto__") Object.defineProperty(target, name, { value, writable: true, enumerable: true, configurable: true });
856
- else target[name] = value;
858
+ const parentLocal = snapshot ? snapshot.linkOf(local, 1) : record.parent;
859
+ const parent = parentLocal == null ? void 0 : nodes[parentLocal];
860
+ if (parent === void 0) return void 0;
861
+ switch (type) {
862
+ case "tag":
863
+ case "script":
864
+ case "style": {
865
+ const element = snapshot ? adapter.createElement(snapshot.nameOf(local), snapshot.namespaceOf(local), snapshot.attributeObjects(local)) : adapter.createElement(record.name, record.namespace, attributeList(record));
866
+ adapter.appendChild(parent, element);
867
+ nodes[local] = element;
868
+ break;
869
+ }
870
+ case "root": {
871
+ const content = adapter.createDocumentFragment();
872
+ adapter.setTemplateContent(parent, content);
873
+ nodes[local] = content;
874
+ break;
875
+ }
876
+ case "text": {
877
+ adapter.insertText(parent, (snapshot ? snapshot.dataOf(local) : record.data) ?? "");
878
+ const children = adapter.getChildNodes(parent);
879
+ nodes[local] = children[children.length - 1];
880
+ break;
881
+ }
882
+ case "comment": {
883
+ const comment = adapter.createCommentNode((snapshot ? snapshot.dataOf(local) : record.data) ?? "");
884
+ adapter.appendChild(parent, comment);
885
+ nodes[local] = comment;
886
+ break;
887
+ }
888
+ case "directive": {
889
+ if (!record || record.name !== "!doctype" || !isDocument2) return void 0;
890
+ adapter.setDocumentType(parent, record["x-name"] ?? "", record["x-publicId"] ?? "", record["x-systemId"] ?? "");
891
+ nodes[local] = Array.from(adapter.getChildNodes(parent) ?? []).find((child) => adapter.isDocumentTypeNode(child));
892
+ break;
893
+ }
894
+ default:
895
+ return void 0;
857
896
  }
858
897
  }
859
- /** The flat child id list; a node's children start at `childStart(local)`. */
860
- get childIds() {
861
- return this.children;
862
- }
863
- childStart(local) {
864
- return this.nodes[local * STRIDE + 4];
865
- }
866
- /** The interned reference of a node's type, comparable across nodes of this snapshot. */
867
- typeReference(local) {
868
- return this.nodes[local * STRIDE];
869
- }
870
- /** Child ids without materializing the record. */
871
- childCount(local) {
872
- return this.nodes[local * STRIDE + 5];
873
- }
874
- childAt(local, index) {
875
- return this.children[this.nodes[local * STRIDE + 4] + index];
876
- }
877
- childrenOf(local) {
878
- const base = local * STRIDE;
879
- const count = this.nodes[base + 5];
880
- if (count === 0) return noChildren;
881
- const first = this.nodes[base + 4];
882
- return Array.from(this.children.subarray(first, first + count));
883
- }
884
- attributesOf(local) {
885
- const base = local * STRIDE;
886
- const count = this.nodes[base + 9];
887
- if (count === 0) return noAttributes;
888
- const first = this.nodes[base + 8];
889
- const attributes = this.attributes;
890
- const result = {};
891
- for (let index = 0; index < count; index++) {
892
- const pair = (first + index) * 2;
893
- const name = this.name(attributes[pair]);
894
- const value = this.string(attributes[pair + 1]);
895
- if (name === "__proto__") Object.defineProperty(result, name, { value, writable: true, enumerable: true, configurable: true });
896
- else result[name] = value;
898
+ const changes = differencesFromParse(adapter, nodes, state, root);
899
+ if (!changes) return void 0;
900
+ for (const change of changes) operation(document, "setNode", { id: state.base + change.local, field: change.field, value: change.value });
901
+ return nodes;
902
+ }
903
+ function attributeList(record) {
904
+ const attributes = record.attribs ?? {};
905
+ return Object.keys(attributes).map((name) => {
906
+ const namespace = record.attribsNamespace?.[name];
907
+ const prefix = record.attribsPrefix?.[name];
908
+ return { name, value: attributes[name], ...namespace === void 0 ? {} : { namespace }, ...prefix === void 0 ? {} : { prefix } };
909
+ });
910
+ }
911
+ function differencesFromParse(adapter, nodes, state, root) {
912
+ const changes = [];
913
+ for (let local = 0; local < nodes.length; local++) {
914
+ const node = nodes[local];
915
+ const snapshot = plainSnapshot(state, local);
916
+ const record = snapshot ? void 0 : recordAt(state, local);
917
+ if (!snapshot && !record) continue;
918
+ if (!node) return void 0;
919
+ const type = snapshot ? snapshot.typeOf(local) : record.type;
920
+ if (local !== root && adapter.getParentNode(node) !== nodes[snapshot ? snapshot.linkOf(local, 1) : record.parent]) return void 0;
921
+ if (type !== "text" && type !== "comment" && type !== "directive") {
922
+ const children = adapter.getChildNodes(node) ?? [];
923
+ const count = snapshot ? snapshot.childCount(local) : record.children.length;
924
+ if (children.length !== count) return void 0;
925
+ for (let index = 0; index < count; index++) if (children[index] !== nodes[snapshot ? snapshot.childAt(local, index) : record.children[index]]) return void 0;
897
926
  }
898
- return result;
899
- }
900
- /** One attribute's value, without decoding the rest. */
901
- attributeValue(local, name) {
902
- const base = local * STRIDE;
903
- const count = this.nodes[base + 9];
904
- const first = this.nodes[base + 8];
905
- const attributes = this.attributes;
906
- for (let index = count - 1; index >= 0; index--) {
907
- const pair = (first + index) * 2;
908
- if (this.name(attributes[pair]) === name) return this.string(attributes[pair + 1]);
927
+ if (type === "tag" || type === "script" || type === "style") {
928
+ const name = snapshot ? snapshot.nameOf(local) : record.name;
929
+ const namespace = snapshot ? snapshot.namespaceOf(local) : record.namespace;
930
+ if (!adapter.isElementNode(node) || adapter.getTagName(node) !== name || adapter.getNamespaceURI(node) !== namespace) return void 0;
931
+ const attributes = adapter.getAttrList(node) ?? [];
932
+ const expected = snapshot ? snapshot.attributeObjects(local) : attributeList(record);
933
+ const isSame = attributes.length === expected.length && attributes.every((attribute, index) => attribute.name === expected[index].name && attribute.value === expected[index].value);
934
+ if (!isSame) {
935
+ const values = /* @__PURE__ */ Object.create(null);
936
+ const namespaces = /* @__PURE__ */ Object.create(null);
937
+ const prefixes = /* @__PURE__ */ Object.create(null);
938
+ for (const attribute of attributes) {
939
+ values[attribute.name] = attribute.value;
940
+ if (attribute.namespace !== void 0) namespaces[attribute.name] = attribute.namespace;
941
+ if (attribute.prefix !== void 0) prefixes[attribute.name] = attribute.prefix;
942
+ }
943
+ changes.push({ local, field: "attribs", value: values }, { local, field: "attribsNamespace", value: namespaces }, { local, field: "attribsPrefix", value: prefixes });
944
+ }
945
+ } else if (type === "text" || type === "comment") {
946
+ if (type === "text" ? !adapter.isTextNode(node) : !adapter.isCommentNode(node)) return void 0;
947
+ const data2 = type === "text" ? adapter.getTextNodeContent(node) : adapter.getCommentNodeContent(node);
948
+ if (data2 !== (snapshot ? snapshot.dataOf(local) : record.data)) changes.push({ local, field: "data", value: data2 });
909
949
  }
910
- return void 0;
911
950
  }
912
- string(reference) {
913
- const strings = this.strings;
914
- const value = this.text.slice(strings[reference * 2], strings[reference * 2 + 1]);
915
- return this.isEncoded ? decodeString(value) : value;
916
- }
917
- /** Names repeat across nodes, so each decodes once. */
918
- name(reference) {
919
- return this.names[reference] ??= this.string(reference);
920
- }
921
- };
951
+ return changes;
952
+ }
922
953
 
923
954
  // src/router.ts
924
955
  var STRIDE2 = 2 ** 32;
@@ -1017,7 +1048,8 @@ function stateFor(raw, options = {}) {
1017
1048
  isExposed: false,
1018
1049
  hasChildLists: false,
1019
1050
  collectionRetries: 0,
1020
- isCollectionEvicted: false
1051
+ isCollectionEvicted: false,
1052
+ hasPreparedSnapshot: false
1021
1053
  };
1022
1054
  holder[stateKey] = state;
1023
1055
  return state;
@@ -1142,7 +1174,7 @@ function readRecord(state, local) {
1142
1174
  const size = Math.max(state.snapshotSize, state.baseline);
1143
1175
  const firstSnapshotRead = state.generation === 0 ? size / 100 : 1 + size / 100;
1144
1176
  if (reads >= Math.min(minimumReadsBeforeSnapshot, firstSnapshotRead)) {
1145
- const snapshot = new NodeSnapshot(state.raw.snapshot());
1177
+ const snapshot = new NodeSnapshot(takePreparedSnapshot(state, true) ?? state.raw.snapshot());
1146
1178
  state.snapshotSize = snapshot.size;
1147
1179
  state.bulk = snapshot;
1148
1180
  const record = snapshot.record(local);
@@ -1151,10 +1183,45 @@ function readRecord(state, local) {
1151
1183
  }
1152
1184
  return rawOperation(state.raw, "node", { id: local });
1153
1185
  }
1186
+ function usePreparedSnapshot(document) {
1187
+ if (!(document instanceof RoutedDocument)) return;
1188
+ const state = document.state;
1189
+ state.hasPreparedSnapshot = state.generation === 0 && state.bulk === 0;
1190
+ }
1191
+ function takePreparedSnapshot(state, isWanted) {
1192
+ if (!state.hasPreparedSnapshot) return void 0;
1193
+ state.hasPreparedSnapshot = false;
1194
+ return state.raw.takePreparedSnapshot?.(isWanted) ?? void 0;
1195
+ }
1154
1196
  function plainSnapshot(state, local) {
1155
1197
  const bulk = state.bulk;
1156
1198
  return bulk instanceof NodeSnapshot && !(state.stale.size && state.stale.has(local)) && bulk.isPlain(local) ? bulk : void 0;
1157
1199
  }
1200
+ function structureSnapshot(state, local) {
1201
+ const bulk = state.bulk;
1202
+ return bulk instanceof NodeSnapshot && !(state.stale.size && state.stale.has(local)) && bulk.isPresent(local) ? bulk : void 0;
1203
+ }
1204
+ var snapshotTextBudget = 256;
1205
+ function textFromSnapshot(state, local) {
1206
+ let text = "";
1207
+ const stack = [local];
1208
+ for (let visited = 0; stack.length; visited++) {
1209
+ const node = stack.pop();
1210
+ const snapshot = structureSnapshot(state, node);
1211
+ if (!snapshot || visited === snapshotTextBudget) return void 0;
1212
+ if (snapshot.isTextAt(node)) {
1213
+ text += snapshot.dataOf(node) ?? largeText(state, snapshot, node);
1214
+ continue;
1215
+ }
1216
+ for (let index = snapshot.childCount(node) - 1; index >= 0; index--) stack.push(snapshot.childAt(node, index));
1217
+ }
1218
+ return text;
1219
+ }
1220
+ function largeText(state, snapshot, local) {
1221
+ let text = snapshot.largeTexts.get(local);
1222
+ if (text === void 0) snapshot.largeTexts.set(local, text = state.raw.text([local]));
1223
+ return text;
1224
+ }
1158
1225
  function attributeAt(state, local, name) {
1159
1226
  const snapshot = plainSnapshot(state, local);
1160
1227
  if (snapshot) return snapshot.attributeValue(local, name);
@@ -1163,15 +1230,18 @@ function attributeAt(state, local, name) {
1163
1230
  return typeof value === "string" ? value : void 0;
1164
1231
  }
1165
1232
  function invalidate(state) {
1233
+ takePreparedSnapshot(state, false);
1166
1234
  state.generation++;
1167
1235
  state.records = [];
1168
1236
  state.bulk = 0;
1169
1237
  state.stale.clear();
1170
1238
  }
1171
1239
  function invalidateNodes(state, locals) {
1240
+ takePreparedSnapshot(state, false);
1172
1241
  const isSnapshot = state.bulk instanceof NodeSnapshot;
1242
+ const { records } = state;
1173
1243
  for (const local of locals) {
1174
- state.records[local] = void 0;
1244
+ if (records[local] !== void 0) records[local] = void 0;
1175
1245
  if (isSnapshot) state.stale.add(local);
1176
1246
  }
1177
1247
  }
@@ -1406,6 +1476,11 @@ var RoutedDocument = class {
1406
1476
  return this.globals(group.state, found);
1407
1477
  }
1408
1478
  text(ids) {
1479
+ if (ids.length === 1) {
1480
+ const state = this.owner(ids[0]);
1481
+ const text = textFromSnapshot(state, ids[0] - state.base);
1482
+ if (text !== void 0) return text;
1483
+ }
1409
1484
  return this.groups(ids).map((group) => group.state.raw.text(group.locals)).join("");
1410
1485
  }
1411
1486
  innerHtml(id) {
@@ -1490,9 +1565,12 @@ var RoutedDocument = class {
1490
1565
  return this.globals(state, inserted);
1491
1566
  }
1492
1567
  const result2 = [];
1568
+ const isBesideTarget = args.position === "before" || args.position === "after";
1493
1569
  for (let index = 0; index < targets.length; index++) {
1494
1570
  const target = this.owner(targets[index]);
1495
1571
  const targetLocal = targets[index] - target.base;
1572
+ const record = recordAt(target, targetLocal);
1573
+ if (!record || !parentTypes.has(record.type) || isBesideTarget && record.parent == null) continue;
1496
1574
  const local = this.moveInto(target, nodes, index + 1 < targets.length);
1497
1575
  const inserted = this.call(target, command, { ...args, targetIds: [targetLocal], nodeIds: local });
1498
1576
  result2.push(...this.globals(target, inserted));
@@ -1715,6 +1793,8 @@ function traverseRecords(document, ids, kind) {
1715
1793
  for (const id of ids) {
1716
1794
  const state = stateIn(document, id);
1717
1795
  const { base } = state;
1796
+ const snapshot = state.bulk instanceof NodeSnapshot ? state.bulk : void 0;
1797
+ if (snapshot?.isPresent(id - base) && traverseSnapshot(snapshot, base, id - base, kind, result)) continue;
1718
1798
  const node = recordAt(state, id - base);
1719
1799
  if (!node) continue;
1720
1800
  const isElement2 = (local) => isElementType(recordAt(state, local)?.type);
@@ -1755,6 +1835,60 @@ function traverseRecords(document, ids, kind) {
1755
1835
  }
1756
1836
  return result;
1757
1837
  }
1838
+ function traverseSnapshot(snapshot, base, local, kind, result) {
1839
+ const pushChildren2 = (parent, isElementsOnly, except) => {
1840
+ const count = snapshot.childCount(parent);
1841
+ for (let index = 0; index < count; index++) {
1842
+ const child = snapshot.childAt(parent, index);
1843
+ if (child !== except && (!isElementsOnly || snapshot.isElementAt(child))) result.push(base + child);
1844
+ }
1845
+ };
1846
+ switch (kind) {
1847
+ case "children":
1848
+ pushChildren2(local, true, -1);
1849
+ return true;
1850
+ case "contents":
1851
+ pushChildren2(local, false, -1);
1852
+ return true;
1853
+ case "siblings": {
1854
+ const parent = snapshot.linkOf(local, 1);
1855
+ if (parent !== null && snapshot.isPresent(parent)) pushChildren2(parent, true, local);
1856
+ return true;
1857
+ }
1858
+ case "parent": {
1859
+ const parent = snapshot.linkOf(local, 1);
1860
+ if (parent !== null && snapshot.isPresent(parent) && snapshot.isElementAt(parent)) result.push(base + parent);
1861
+ return true;
1862
+ }
1863
+ case "parents":
1864
+ case "ancestors": {
1865
+ let parent = snapshot.linkOf(local, 1);
1866
+ for (let steps = 0; parent !== null && steps < snapshot.size && snapshot.isPresent(parent) && !snapshot.isRootAt(parent); steps++) {
1867
+ if (snapshot.isElementAt(parent)) result.push(base + parent);
1868
+ parent = snapshot.linkOf(parent, 1);
1869
+ }
1870
+ return true;
1871
+ }
1872
+ case "next":
1873
+ case "prev":
1874
+ case "nextAll":
1875
+ case "prevAll": {
1876
+ const offset = kind === "next" || kind === "nextAll" ? 3 : 2;
1877
+ const isSingle = kind === "next" || kind === "prev";
1878
+ let sibling = snapshot.linkOf(local, offset);
1879
+ for (let steps = 0; sibling !== null && steps < snapshot.size && snapshot.isPresent(sibling); steps++) {
1880
+ if (snapshot.isElementAt(sibling)) {
1881
+ result.push(base + sibling);
1882
+ if (isSingle) break;
1883
+ }
1884
+ sibling = snapshot.linkOf(sibling, offset);
1885
+ }
1886
+ return true;
1887
+ }
1888
+ default:
1889
+ return false;
1890
+ }
1891
+ }
1758
1892
  function viewAt(state, local) {
1759
1893
  const existing = state.views[local] ?? cachedView(state, local);
1760
1894
  if (existing) return existing;
@@ -1763,9 +1897,9 @@ function viewAt(state, local) {
1763
1897
  state.adapterNodes[local] = void 0;
1764
1898
  return adoptObject(state, local, parsed);
1765
1899
  }
1766
- const snapshot = plainSnapshot(state, local);
1900
+ const snapshot = structureSnapshot(state, local);
1767
1901
  let constructor;
1768
- if (snapshot) constructor = viewConstructor(snapshot.typeOf(local), false);
1902
+ if (snapshot && !snapshot.hasExtraFields(local)) constructor = viewConstructor(snapshot.typeOf(local), false);
1769
1903
  else {
1770
1904
  const record = recordAt(state, local);
1771
1905
  if (!record) return void 0;
@@ -1964,14 +2098,17 @@ function fillChildren(view) {
1964
2098
  if (!target) return;
1965
2099
  const state = view[VIEW_STATE];
1966
2100
  const local = view[VIEW_LOCAL];
1967
- const snapshot = plainSnapshot(state, local);
2101
+ const snapshot = structureSnapshot(state, local);
1968
2102
  if (snapshot && state.stale.size === 0) {
1969
2103
  fillFromSnapshot(state, snapshot, local, target);
1970
2104
  return;
1971
2105
  }
1972
2106
  const locals = viewRecord(view).children ?? [];
1973
- target.length = locals.length;
1974
2107
  for (let index = 0; index < locals.length; index++) target[index] = viewAt(state, locals[index]);
2108
+ truncate(target, locals.length);
2109
+ }
2110
+ function truncate(target, length) {
2111
+ if (target.length !== length) target.length = length;
1975
2112
  }
1976
2113
  function fillFromSnapshot(state, snapshot, local, target) {
1977
2114
  const count = snapshot.childCount(local);
@@ -1980,17 +2117,17 @@ function fillFromSnapshot(state, snapshot, local, target) {
1980
2117
  const views = state.views;
1981
2118
  const isStrong = !state.adapterNodes;
1982
2119
  const constructors = snapshotConstructors(snapshot);
1983
- target.length = count;
1984
2120
  for (let index = 0; index < count; index++) {
1985
2121
  const child = childIds2[first + index];
1986
2122
  let node = views[child] ?? (state.weakViews.size ? cachedView(state, child) : void 0);
1987
2123
  if (node === void 0) {
1988
- const constructor = isStrong && child < state.baseline && snapshot.isPlain(child) ? constructors(snapshot.typeReference(child)) : void 0;
2124
+ const constructor = isStrong && child < state.baseline && snapshot.isPresent(child) && !snapshot.hasExtraFields(child) ? constructors(snapshot.typeReference(child)) : void 0;
1989
2125
  if (constructor) views[child] = node = new constructor(state, child);
1990
2126
  else node = viewAt(state, child);
1991
2127
  }
1992
2128
  target[index] = node;
1993
2129
  }
2130
+ truncate(target, count);
1994
2131
  }
1995
2132
  function snapshotConstructors(snapshot) {
1996
2133
  let lookup = snapshot.viewConstructors;
@@ -2013,7 +2150,7 @@ var snapshotField = (field) => ({
2013
2150
  get() {
2014
2151
  const state = this[VIEW_STATE];
2015
2152
  const local = this[VIEW_LOCAL];
2016
- const snapshot = plainSnapshot(state, local);
2153
+ const snapshot = field === "data" ? plainSnapshot(state, local) : structureSnapshot(state, local);
2017
2154
  if (snapshot) return field === "type" ? snapshot.typeOf(local) : field === "name" ? snapshot.nameOf(local) : snapshot.dataOf(local);
2018
2155
  return viewRecord(this)[field];
2019
2156
  },
@@ -2026,7 +2163,7 @@ var pointerField = (field) => ({
2026
2163
  get() {
2027
2164
  const state = this[VIEW_STATE];
2028
2165
  const local = this[VIEW_LOCAL];
2029
- const snapshot = plainSnapshot(state, local);
2166
+ const snapshot = structureSnapshot(state, local);
2030
2167
  return linkedView(this, snapshot ? snapshot.linkOf(local, linkOffsets[field]) : viewRecord(this)[field]);
2031
2168
  },
2032
2169
  set(value) {
@@ -2879,11 +3016,11 @@ function selectSimpleWithin(document, contexts, selector, options) {
2879
3016
  }
2880
3017
  var isElementType2 = (type) => type === "tag" || type === "script" || type === "style";
2881
3018
  function typeAt(state, local) {
2882
- const snapshot = plainSnapshot(state, local);
3019
+ const snapshot = structureSnapshot(state, local);
2883
3020
  return snapshot ? snapshot.typeOf(local) : recordAt(state, local)?.type;
2884
3021
  }
2885
3022
  function pushChildren(state, local, stack) {
2886
- const snapshot = plainSnapshot(state, local);
3023
+ const snapshot = structureSnapshot(state, local);
2887
3024
  if (snapshot) {
2888
3025
  for (let index = snapshot.childCount(local) - 1; index >= 0; index--) stack.push(snapshot.childAt(local, index));
2889
3026
  return;
@@ -2899,7 +3036,7 @@ function sameMatchOptions(documentOptions, isXml, isLowerCaseTags, isLowerCaseAt
2899
3036
  return isDocumentXml === isXml && (documentOptions.lowerCaseTags ?? !isDocumentXml) === isLowerCaseTags && (documentOptions.lowerCaseAttributeNames ?? !isDocumentXml) === isLowerCaseAttributes;
2900
3037
  }
2901
3038
  function matchesCompound(state, local, compound, isLowerCaseTags, isLowerCaseAttributes) {
2902
- const snapshot = plainSnapshot(state, local);
3039
+ const snapshot = structureSnapshot(state, local);
2903
3040
  const record = snapshot ? void 0 : recordAt(state, local);
2904
3041
  if (!snapshot && !record) return false;
2905
3042
  if (!isElementType2(snapshot ? snapshot.typeOf(local) : record.type)) return false;
@@ -3479,26 +3616,25 @@ var Cheerio = class {
3479
3616
  }
3480
3617
  /** @internal */
3481
3618
  _insert(position, content) {
3619
+ if (typeof content[0] !== "function") {
3620
+ const ids = this.ids;
3621
+ if (ids.length) this.op("insert", { targetIds: ids, nodeIds: this._domIds(content), position });
3622
+ return this;
3623
+ }
3482
3624
  const targets = this.ids.filter((id) => ["root", "tag", "script", "style", "cdata"].includes(nodeRecord(this[DOCUMENT], id).type));
3483
3625
  if (!targets.length)
3484
3626
  return this;
3485
- if (typeof content[0] === "function")
3486
- return this.each((index, node) => {
3487
- if (!targets.includes(this.ids[index]))
3488
- return;
3489
- if ((position === "before" || position === "after") && nodeRecord(this[DOCUMENT], this.ids[index]).parent == null)
3490
- return;
3491
- const value = content[0].call(node, index, this[DOCUMENT].innerHtml(this.ids[index]));
3492
- let nodes2 = this._domIds(value);
3493
- if (index < this.length - 1)
3494
- nodes2 = this.op("clone", { ids: nodes2, deep: true });
3495
- this.op("insert", { targetIds: [this.ids[index]], nodeIds: nodes2, position });
3496
- });
3497
- let nodes = this._domIds(content);
3498
- if (targets[targets.length - 1] !== this.ids[this.ids.length - 1])
3499
- nodes = this.op("clone", { ids: nodes, deep: true });
3500
- this.op("insert", { targetIds: targets, nodeIds: nodes, position });
3501
- return this;
3627
+ return this.each((index, node) => {
3628
+ if (!targets.includes(this.ids[index]))
3629
+ return;
3630
+ if ((position === "before" || position === "after") && nodeRecord(this[DOCUMENT], this.ids[index]).parent == null)
3631
+ return;
3632
+ const value = content[0].call(node, index, this[DOCUMENT].innerHtml(this.ids[index]));
3633
+ let nodes = this._domIds(value);
3634
+ if (index < this.length - 1)
3635
+ nodes = this.op("clone", { ids: nodes, deep: true });
3636
+ this.op("insert", { targetIds: [this.ids[index]], nodeIds: nodes, position });
3637
+ });
3502
3638
  }
3503
3639
  append(...content) {
3504
3640
  return this._insert("append", content);
@@ -3678,28 +3814,30 @@ function plainKeys(map) {
3678
3814
  return !types2.isProxy(map) && (Object.getPrototypeOf(map) === Object.prototype || Object.getPrototypeOf(map) === null) ? Object.keys(map) : void 0;
3679
3815
  }
3680
3816
  function nativePrefix(scope, map, keys, key) {
3681
- if (!keys || scope._make !== nativeExtractionMethods.make || scope._findBySelector !== nativeExtractionMethods.find || scope.prop !== nativeExtractionMethods.prop || scope.extract !== nativeExtractionMethods.extract) return void 0;
3817
+ if (!keys || !hasNativeExtraction(scope)) return void 0;
3818
+ const prefix = prefixPlan(map, keys, key, scope.options);
3819
+ if (!prefix) return void 0;
3820
+ return { keys: prefix.names, values: runPlan(scope, prefix.plan) };
3821
+ }
3822
+ function prefixPlan(map, keys, key, options) {
3682
3823
  const prefix = /* @__PURE__ */ Object.create(null);
3683
- let count = 0;
3684
3824
  let plan;
3685
- const index = keys.indexOf(key);
3686
- for (let position = index; position < keys.length; position++) {
3825
+ for (let position = keys.indexOf(key); position < keys.length; position++) {
3687
3826
  const name = keys[position];
3688
3827
  const slot = Object.getOwnPropertyDescriptor(map, name);
3689
3828
  if (!slot || !slot.enumerable || !("value" in slot)) break;
3690
3829
  Object.defineProperty(prefix, name, { value: slot.value, enumerable: true, configurable: true });
3691
- const compiled2 = compileExtractPlan(prefix, scope.options);
3830
+ const compiled2 = compileExtractPlan(prefix, options);
3692
3831
  if (!compiled2) {
3693
3832
  delete prefix[name];
3694
3833
  break;
3695
3834
  }
3696
3835
  plan = compiled2;
3697
- count++;
3698
3836
  }
3699
- if (!plan || !count) return void 0;
3700
- const values = scope.op("extractPlan", { ids: scope.ids, plan });
3701
- hydrateExtractResult(values, plan.entries);
3702
- return { keys: Object.keys(prefix), values };
3837
+ return plan ? { names: Object.keys(prefix), plan } : void 0;
3838
+ }
3839
+ function hasNativeExtraction(scope) {
3840
+ return scope._make === nativeExtractionMethods.make && scope._findBySelector === nativeExtractionMethods.find && scope.prop === nativeExtractionMethods.prop && scope.extract === nativeExtractionMethods.extract;
3703
3841
  }
3704
3842
  function extract(map) {
3705
3843
  const result = {};
@@ -3744,8 +3882,17 @@ function extract(map) {
3744
3882
  return result;
3745
3883
  }
3746
3884
  function nativeRows(scope, key, all, selector, map) {
3885
+ if (!hasNativeExtraction(scope)) return void 0;
3886
+ const planned = rowsPlan(key, all, selector, map, scope.options);
3887
+ if (!planned) return void 0;
3888
+ const writes = writeCount();
3889
+ const extracted = runPlan(scope, planned.plan)[key];
3890
+ const rows = all ? extracted : extracted === void 0 ? [] : [extracted];
3891
+ return Array.isArray(rows) ? { keys: planned.nativeKeys, rows, writes } : void 0;
3892
+ }
3893
+ function rowsPlan(key, all, selector, map, options) {
3747
3894
  const keys = plainKeys(map);
3748
- if (!keys || scope._make !== nativeExtractionMethods.make || scope._findBySelector !== nativeExtractionMethods.find || scope.prop !== nativeExtractionMethods.prop || scope.extract !== nativeExtractionMethods.extract) return void 0;
3895
+ if (!keys) return void 0;
3749
3896
  const subset = /* @__PURE__ */ Object.create(null);
3750
3897
  const nativeKeys = /* @__PURE__ */ new Set();
3751
3898
  for (const name of keys) {
@@ -3753,21 +3900,81 @@ function nativeRows(scope, key, all, selector, map) {
3753
3900
  if (!slot || !slot.enumerable || !("value" in slot)) continue;
3754
3901
  const single = /* @__PURE__ */ Object.create(null);
3755
3902
  Object.defineProperty(single, name, { value: slot.value, enumerable: true, configurable: true });
3756
- if (!compileExtractPlan(single, scope.options)) continue;
3903
+ if (!compileExtractPlan(single, options)) continue;
3757
3904
  Object.defineProperty(subset, name, { value: slot.value, enumerable: true, configurable: true });
3758
3905
  nativeKeys.add(name);
3759
3906
  }
3760
3907
  if (!nativeKeys.size || nativeKeys.size === keys.length) return void 0;
3761
3908
  const outer = /* @__PURE__ */ Object.create(null);
3762
3909
  Object.defineProperty(outer, key, { value: all ? [{ selector, value: subset }] : { selector, value: subset }, enumerable: true, configurable: true });
3763
- const plan = compileExtractPlan(outer, scope.options);
3764
- if (!plan) return void 0;
3765
- const writes = writeCount();
3766
- const values = scope.op("extractPlan", { ids: scope.ids, plan });
3910
+ const plan = compileExtractPlan(outer, options);
3911
+ return plan ? { nativeKeys, plan } : void 0;
3912
+ }
3913
+ function rootExtractPlans(map, options) {
3914
+ if (!isPlainData(map, 0) || hasInheritedEntryFields()) return [];
3915
+ try {
3916
+ return collectRootExtractPlans(map, options);
3917
+ } catch {
3918
+ return [];
3919
+ }
3920
+ }
3921
+ function isPlainData(value, depth) {
3922
+ if (typeof value !== "object" || value === null) return true;
3923
+ if (depth > 32 || types2.isProxy(value)) return false;
3924
+ const prototype = Object.getPrototypeOf(value);
3925
+ if (prototype !== Object.prototype && prototype !== Array.prototype && prototype !== null) return false;
3926
+ for (const key of Reflect.ownKeys(value)) {
3927
+ const slot = Object.getOwnPropertyDescriptor(value, key);
3928
+ if (!("value" in slot) || !isPlainData(slot.value, depth + 1)) return false;
3929
+ }
3930
+ return true;
3931
+ }
3932
+ function hasInheritedEntryFields() {
3933
+ return "selector" in Object.prototype || "value" in Object.prototype || "0" in Array.prototype;
3934
+ }
3935
+ function collectRootExtractPlans(map, options) {
3936
+ const plans = [];
3937
+ const keys = plainKeys(map);
3938
+ const processed = /* @__PURE__ */ new Set();
3939
+ for (const key in map) {
3940
+ if (processed.has(key)) continue;
3941
+ const prefix = keys ? prefixPlan(map, keys, key, options) : void 0;
3942
+ if (prefix) {
3943
+ for (const name of prefix.names) processed.add(name);
3944
+ plans.push(prefix.plan);
3945
+ continue;
3946
+ }
3947
+ const entry = map[key];
3948
+ const all = Array.isArray(entry);
3949
+ const descriptor = all ? entry[0] : entry;
3950
+ const value = typeof descriptor === "string" ? "textContent" : descriptor.value ?? "textContent";
3951
+ if (typeof value !== "object" || value === null) continue;
3952
+ const planned = rowsPlan(key, all, typeof descriptor === "string" ? descriptor : descriptor.selector, value, options);
3953
+ if (planned) plans.push(planned.plan);
3954
+ }
3955
+ return plans.filter((plan) => !JSON.stringify(plan).includes("innerHTML"));
3956
+ }
3957
+ var preparedPlans = /* @__PURE__ */ new WeakMap();
3958
+ function prepareExtractPlans(document, plans, results) {
3959
+ if (!results) return;
3960
+ const prepared = /* @__PURE__ */ new Map();
3961
+ for (let index = 0; index < plans.length; index++) {
3962
+ const result = results[index];
3963
+ if (typeof result === "string") prepared.set(JSON.stringify(plans[index]), decodeJson(result));
3964
+ }
3965
+ preparedPlans.set(document, { writes: writeCount(), results: prepared });
3966
+ }
3967
+ function releaseExtractPlans(document) {
3968
+ preparedPlans.delete(document);
3969
+ }
3970
+ function runPlan(scope, plan) {
3971
+ const document = scope[DOCUMENT];
3972
+ const prepared = preparedPlans.get(document);
3973
+ const isRoot = prepared !== void 0 && prepared.writes === writeCount() && scope.ids.length === 1 && scope.ids[0] === document.rootId();
3974
+ const result = isRoot ? prepared.results.get(JSON.stringify(plan)) : void 0;
3975
+ const values = result !== void 0 ? JSON.parse(result) : scope.op("extractPlan", { ids: scope.ids, plan });
3767
3976
  hydrateExtractResult(values, plan.entries);
3768
- const extracted = values[key];
3769
- const rows = all ? extracted : extracted === void 0 ? [] : [extracted];
3770
- return Array.isArray(rows) ? { keys: nativeKeys, rows, writes } : void 0;
3977
+ return values;
3771
3978
  }
3772
3979
  function completeRow(scope, map, rows, index) {
3773
3980
  const row = {};
@@ -4431,7 +4638,9 @@ var dom = {
4431
4638
  isCDATA,
4432
4639
  hasChildren
4433
4640
  };
4641
+ var defaultOptions = { _useHtmlParser2: false };
4434
4642
  function flattenOptions(options, base) {
4643
+ if (!options) return base ?? defaultOptions;
4435
4644
  const result = { _useHtmlParser2: false, ...base, ...options };
4436
4645
  if (options?.xml) {
4437
4646
  result._useHtmlParser2 = true;
@@ -4591,13 +4800,23 @@ function parallelOptions(options) {
4591
4800
  return { options: flattenOptions(opts), threads };
4592
4801
  }
4593
4802
  async function loadAll(content, options, isDocument2 = true) {
4803
+ return loadAllWithPlans(content, options, isDocument2);
4804
+ }
4805
+ async function loadAllWithPlans(content, options, isDocument2, plans) {
4594
4806
  const { options: opts, threads } = parallelOptions(options);
4595
4807
  if (!Array.isArray(content) || content.some((item) => typeof item !== "string")) throw new TypeError("loadAll expects an array of HTML strings");
4596
4808
  if (!content.length) return [];
4597
4809
  const binding = requireNative();
4598
4810
  if (!binding.parseManyAsync || hasParserHooks(opts)) return Promise.all(content.map((html) => loadAsync(html, opts, isDocument2)));
4599
- const documents = await binding.parseManyAsync([...content], isDocument2, threads, JSON.stringify(opts));
4600
- return documents.map((document) => createAPI(routeDocument(document, opts), opts));
4811
+ const documents = await binding.parseManyAsync([...content], isDocument2, threads, JSON.stringify(opts), plans && JSON.stringify(plans));
4812
+ return documents.map((document) => {
4813
+ const routed = routeDocument(document, opts);
4814
+ if (plans) {
4815
+ usePreparedSnapshot(routed);
4816
+ prepareExtractPlans(routed, plans, document.takePreparedPlans?.());
4817
+ }
4818
+ return createAPI(routed, opts);
4819
+ });
4601
4820
  }
4602
4821
  function nativeQueries(map, options) {
4603
4822
  const queries = [];
@@ -4633,11 +4852,16 @@ async function extractAll(content, map, options) {
4633
4852
  }
4634
4853
  return results2;
4635
4854
  }
4636
- const documents = await loadAll(content, { ...opts, threads });
4855
+ const documents = await loadAllWithPlans(content, { ...opts, threads }, true, hasParserHooks(opts) ? void 0 : rootExtractPlans(map, opts));
4637
4856
  const results = [];
4638
4857
  let yieldedAt = performance.now();
4639
4858
  for (const document of documents) {
4640
- results.push((await extractCooperatively(document.root(), map)).value);
4859
+ const root = document.root();
4860
+ try {
4861
+ results.push((await extractCooperatively(root, map)).value);
4862
+ } finally {
4863
+ releaseExtractPlans(root[DOCUMENT]);
4864
+ }
4641
4865
  if (performance.now() - yieldedAt >= 4) {
4642
4866
  await yieldToEventLoop();
4643
4867
  yieldedAt = performance.now();
@@ -4664,4 +4888,4 @@ export {
4664
4888
  loadAll,
4665
4889
  extractAll
4666
4890
  };
4667
- //# sourceMappingURL=chunk-FPRP6UIX.mjs.map
4891
+ //# sourceMappingURL=chunk-HN63P3U7.mjs.map