brookmd 0.27.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +96 -0
- package/README.md +100 -0
- package/dist/client.d.ts +139 -0
- package/dist/client.js +251 -2
- package/dist/dom.d.ts +17 -0
- package/dist/dom.js +196 -18
- package/dist/element.js +3 -1
- package/dist/hi-inc.d.ts +25 -0
- package/dist/hi-inc.js +6 -0
- package/dist/html-to-react.d.ts +2 -0
- package/dist/html-to-react.js +7 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/react-splice.d.ts +32 -0
- package/dist/react-splice.js +33 -0
- package/dist/react.d.ts +11 -0
- package/dist/react.js +29 -8
- package/dist/renderers/CodeBlock.d.ts +13 -1
- package/dist/renderers/CodeBlock.js +51 -8
- package/dist/server.js +1 -0
- package/dist/splice.d.ts +126 -0
- package/dist/splice.js +212 -0
- package/dist/types-core.d.ts +16 -0
- package/dist/wasm/brook_md_core.d.ts +13 -0
- package/dist/wasm/brook_md_core.js +15 -0
- package/dist/wasm/brook_md_core_bg.wasm +0 -0
- package/dist/wasm/brook_md_core_bg.wasm.d.ts +1 -0
- package/dist/worker.js +1 -0
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import { warnOnce } from "./warn.js";
|
|
2
2
|
import { createWorker } from "./asset-urls.js";
|
|
3
|
+
import { noteSplice } from "./splice.js";
|
|
3
4
|
function emptyBlockStore() {
|
|
4
5
|
return { committed: /* @__PURE__ */ new Map(), committedOrder: [], active: [], snapshot: [] };
|
|
5
6
|
}
|
|
7
|
+
const HYDRATE_VERSION = 1;
|
|
8
|
+
function sourceFingerprint(source) {
|
|
9
|
+
let h = 2166136261;
|
|
10
|
+
for (let i = 0; i < source.length; i++) {
|
|
11
|
+
h ^= source.charCodeAt(i);
|
|
12
|
+
h = Math.imul(h, 16777619);
|
|
13
|
+
}
|
|
14
|
+
return (h >>> 0).toString(16).padStart(8, "0");
|
|
15
|
+
}
|
|
16
|
+
function isPersistedBlock(b) {
|
|
17
|
+
if (typeof b !== "object" || b === null) return false;
|
|
18
|
+
const x = b;
|
|
19
|
+
return typeof x.id === "number" && Number.isFinite(x.id) && typeof x.html === "string" && typeof x.start === "number" && typeof x.end === "number" && typeof x.open === "boolean" && typeof x.speculative === "boolean" && typeof x.kind === "object" && x.kind !== null && typeof x.kind.type === "string";
|
|
20
|
+
}
|
|
6
21
|
function htmlToText(html) {
|
|
7
22
|
return html.replace(/<[^>]*>/g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&").replace(/\s+/g, " ").trim();
|
|
8
23
|
}
|
|
@@ -18,7 +33,9 @@ function applyPatch(store, patch) {
|
|
|
18
33
|
const { html_delta, ...rest } = entry;
|
|
19
34
|
const prev = store.active.find((b) => b.id === entry.id);
|
|
20
35
|
if (!prev) throw new Error(`brookmd: html_delta for block ${entry.id} without a base`);
|
|
21
|
-
|
|
36
|
+
const next2 = { ...rest, html: prev.html.slice(0, html_delta.keep_units) + html_delta.append };
|
|
37
|
+
active[i] = next2;
|
|
38
|
+
noteSplice(next2, prev, html_delta.keep_units);
|
|
22
39
|
} else {
|
|
23
40
|
active[i] = entry;
|
|
24
41
|
}
|
|
@@ -341,6 +358,12 @@ class BrookClient {
|
|
|
341
358
|
// same-length-or-shorter replacement the growth check misses). Also cleared on
|
|
342
359
|
// an explicit caller reset().
|
|
343
360
|
recoveryAttempted = false;
|
|
361
|
+
// Whether any chunk has been appended in this generation. Read only by
|
|
362
|
+
// getPersistable's source fallback, to tell "nothing was ever driven" (whose
|
|
363
|
+
// source is the empty document) from "driven, but this client did not retain
|
|
364
|
+
// it" — the one configuration, recovery off plus manual appends, where the
|
|
365
|
+
// source is genuinely unknown and the caller must supply it.
|
|
366
|
+
appendedAny = false;
|
|
344
367
|
// Opt-in rAF coalescing (see constructor `coalesce`). When on AND
|
|
345
368
|
// requestAnimationFrame exists, intra-frame emit()s collapse into ONE
|
|
346
369
|
// rAF-scheduled flush to listeners — the React useSyncExternalStore path then
|
|
@@ -385,6 +408,24 @@ class BrookClient {
|
|
|
385
408
|
/** Set by mergeStale when it had to compact a hole out of the view, so
|
|
386
409
|
* getSnapshot skips caching a view whose indices no longer track `base`. */
|
|
387
410
|
mergeDropped = false;
|
|
411
|
+
// --- Hydration (see getPersistable / hydrate) ---
|
|
412
|
+
// Set by hydrate(): this store was FILLED FROM JSON, never parsed, so no
|
|
413
|
+
// worker has ever seen this document. Sticky until reset() — a resumed client
|
|
414
|
+
// is still "a hydrated client" as far as getPersistable is concerned.
|
|
415
|
+
hydrated = false;
|
|
416
|
+
// The restored envelope's own fields, kept so the resume can re-check
|
|
417
|
+
// staleness and so getPersistable() can re-emit an untouched snapshot exactly.
|
|
418
|
+
hydratedDone = false;
|
|
419
|
+
hydratedLength = 0;
|
|
420
|
+
hydratedHash = "";
|
|
421
|
+
// The original markdown behind a hydrated snapshot (hydrate's `source`), held
|
|
422
|
+
// ONLY until the resume re-feed consumes it. Null for a `done` snapshot, which
|
|
423
|
+
// is terminal and needs none.
|
|
424
|
+
hydratedSource = null;
|
|
425
|
+
// Latch: the resume re-feed has been issued, or the caller took over by
|
|
426
|
+
// driving the whole document itself. From here append()/finalize() behave
|
|
427
|
+
// exactly as on any other client.
|
|
428
|
+
resumed = false;
|
|
388
429
|
// Perf
|
|
389
430
|
appendedBytes = 0;
|
|
390
431
|
patchCount = 0;
|
|
@@ -489,8 +530,15 @@ class BrookClient {
|
|
|
489
530
|
this.resetParser();
|
|
490
531
|
}
|
|
491
532
|
get ready() {
|
|
533
|
+
if (this.hydrationPending) return true;
|
|
492
534
|
return this.pw?.ready ?? false;
|
|
493
535
|
}
|
|
536
|
+
/** True while a hydrated document has never been handed to a parser: the
|
|
537
|
+
* store holds restored blocks and no worker exists. Cleared once the resume
|
|
538
|
+
* re-feed is issued (or the caller re-drives the whole document itself). */
|
|
539
|
+
get hydrationPending() {
|
|
540
|
+
return this.hydrated && !this.resumed;
|
|
541
|
+
}
|
|
494
542
|
/**
|
|
495
543
|
* The fatal error that killed this client's worker, or `null` if healthy.
|
|
496
544
|
*
|
|
@@ -505,6 +553,7 @@ class BrookClient {
|
|
|
505
553
|
return this.failedError;
|
|
506
554
|
}
|
|
507
555
|
whenReady() {
|
|
556
|
+
if (this.hydrationPending) return Promise.resolve();
|
|
508
557
|
const pw = this.ensureAcquired();
|
|
509
558
|
return this.pool.whenWorkerReady(pw);
|
|
510
559
|
}
|
|
@@ -517,9 +566,11 @@ class BrookClient {
|
|
|
517
566
|
return this.config;
|
|
518
567
|
}
|
|
519
568
|
append(chunk) {
|
|
569
|
+
if (this.hydrationPending) this.beginResume();
|
|
520
570
|
const pw = this.ensureAcquired();
|
|
521
571
|
this.settleRebind();
|
|
522
572
|
if (this.firstAppendMs === 0) this.firstAppendMs = performance.now();
|
|
573
|
+
this.appendedAny = true;
|
|
523
574
|
if (this.recovery) {
|
|
524
575
|
this.recoveryBuffer += chunk;
|
|
525
576
|
if (this.recoveryAttempted && this.recoveryBuffer.length > this.recoveredLen) {
|
|
@@ -529,6 +580,10 @@ class BrookClient {
|
|
|
529
580
|
this.pool.send(pw, { type: "append", streamId: this.streamId, chunk, config: this.firstConfig(), epoch: this.epoch });
|
|
530
581
|
}
|
|
531
582
|
finalize() {
|
|
583
|
+
if (this.hydrationPending) {
|
|
584
|
+
if (this.hydratedDone) return;
|
|
585
|
+
this.beginResume();
|
|
586
|
+
}
|
|
532
587
|
const pw = this.ensureAcquired();
|
|
533
588
|
this.settleRebind();
|
|
534
589
|
this.finalizePending = true;
|
|
@@ -630,12 +685,14 @@ class BrookClient {
|
|
|
630
685
|
if (!this.contentDone && content.startsWith(this.lastContent)) {
|
|
631
686
|
if (this.lastContent === "" && content.length > 0 && this.getSnapshot().length > 0) {
|
|
632
687
|
this.softReset(this.getSnapshot());
|
|
688
|
+
this.retireHydration();
|
|
633
689
|
}
|
|
634
690
|
this.append(content.slice(this.lastContent.length));
|
|
635
691
|
} else {
|
|
636
692
|
const displayed = this.getSnapshot();
|
|
637
693
|
if (content.length > 0 && displayed.length > 0) this.softReset(displayed);
|
|
638
694
|
else this.reset();
|
|
695
|
+
this.retireHydration();
|
|
639
696
|
this.append(content);
|
|
640
697
|
}
|
|
641
698
|
this.lastContent = content;
|
|
@@ -646,6 +703,195 @@ class BrookClient {
|
|
|
646
703
|
this.contentDone = true;
|
|
647
704
|
}
|
|
648
705
|
}
|
|
706
|
+
/**
|
|
707
|
+
* Capture everything this client currently renders as a plain-JSON
|
|
708
|
+
* {@link PersistableSnapshot}: `JSON.stringify` it, store it beside the
|
|
709
|
+
* thread, and {@link hydrate} it back later to repaint with **no parse at
|
|
710
|
+
* all**. This is the persistence half of instant thread reopen.
|
|
711
|
+
*
|
|
712
|
+
* The committed wire is already a complete serialization — a committed block
|
|
713
|
+
* is emitted exactly once and is final (WIRE.md §2) — so there is nothing to
|
|
714
|
+
* re-derive: the snapshot IS the document. Cost here is one pass to
|
|
715
|
+
* fingerprint the source; cost on the way back in is a `Map` fill.
|
|
716
|
+
*
|
|
717
|
+
* @param source the markdown driven into this stream, used ONLY to compute
|
|
718
|
+
* {@link PersistableSnapshot.sourceHash}. Optional, because a client with
|
|
719
|
+
* `recovery` on (the default) already retains it, as does a
|
|
720
|
+
* `setContent`-driven one; required in the single configuration that holds
|
|
721
|
+
* neither — `recovery: false` plus manual `append()` — where omitting it
|
|
722
|
+
* throws rather than persisting a snapshot no one can check for staleness.
|
|
723
|
+
*/
|
|
724
|
+
getPersistable(source) {
|
|
725
|
+
const blocks = this.getSnapshot();
|
|
726
|
+
if (source === void 0 && this.hydrationPending) {
|
|
727
|
+
return {
|
|
728
|
+
hydrateVersion: HYDRATE_VERSION,
|
|
729
|
+
blocks,
|
|
730
|
+
sourceLength: this.hydratedLength,
|
|
731
|
+
sourceHash: this.hydratedHash,
|
|
732
|
+
done: this.hydratedDone
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
const src = source ?? this.retainedSource();
|
|
736
|
+
if (src === null) {
|
|
737
|
+
throw new Error(
|
|
738
|
+
"brookmd: getPersistable() needs the source markdown to fingerprint, and this client retains none (constructed with `recovery: false` and driven with append()). Pass it explicitly: client.getPersistable(source)."
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
hydrateVersion: HYDRATE_VERSION,
|
|
743
|
+
blocks,
|
|
744
|
+
sourceLength: src.length,
|
|
745
|
+
sourceHash: sourceFingerprint(src),
|
|
746
|
+
done: this.contentDone
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
/** The full driven document when this client happens to hold it: the recovery
|
|
750
|
+
* buffer is exactly that whenever `recovery` is on (the default), and
|
|
751
|
+
* setContent's baseline covers the recovery-off controlled-string mode.
|
|
752
|
+
* `null` means genuinely unknown — reachable only with recovery off AND
|
|
753
|
+
* manual appends. */
|
|
754
|
+
retainedSource() {
|
|
755
|
+
if (this.recovery) return this.recoveryBuffer;
|
|
756
|
+
if (this.lastContent.length > 0) return this.lastContent;
|
|
757
|
+
if (!this.appendedAny) return "";
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Restore a {@link PersistableSnapshot} into an untouched client. The blocks
|
|
762
|
+
* land in the store as ordinary committed blocks and `getSnapshot()` returns
|
|
763
|
+
* them immediately, so the first paint already has the whole document —
|
|
764
|
+
* **no worker is created, no WASM loads, nothing is parsed**. Reopening a
|
|
765
|
+
* thread costs O(blocks) of JSON handling instead of O(source) of parsing.
|
|
766
|
+
*
|
|
767
|
+
* Call it on a fresh client before anything is appended and, ideally, before
|
|
768
|
+
* the renderer mounts (hydrating an already-mounted client works — it
|
|
769
|
+
* notifies subscribers — but costs an extra render). Hydrating a client that
|
|
770
|
+
* already holds content throws.
|
|
771
|
+
*
|
|
772
|
+
* **Resuming a live thread.** A snapshot with `done: false` was still
|
|
773
|
+
* streaming. Pass `source` — the markdown behind the snapshot — and the first
|
|
774
|
+
* {@link append} rebuilds parser state in the background: the parser's
|
|
775
|
+
* internals are an `Rc` graph with no serialized form, so continuing a
|
|
776
|
+
* document genuinely requires re-parsing what came before it, but that cost
|
|
777
|
+
* moves OFF the critical path. The hydrated blocks stay on screen and the
|
|
778
|
+
* reader scrolls them while the worker catches up; new chunks queue behind the
|
|
779
|
+
* re-feed and land the moment it does. Without `source` the thread is
|
|
780
|
+
* view-only and appending throws — continuing a document correctly is not
|
|
781
|
+
* possible without the text that precedes it.
|
|
782
|
+
*
|
|
783
|
+
* **Hydration does not verify the blocks against the source.** The snapshot is
|
|
784
|
+
* trusted as produced; `sourceHash` exists so the CALLER can notice its stored
|
|
785
|
+
* source moved on and re-stream instead of painting stale HTML. The resume
|
|
786
|
+
* path re-checks it, lets the fresh parse win, and warns in dev.
|
|
787
|
+
*
|
|
788
|
+
* @throws if the envelope version is unknown, the snapshot is malformed, or
|
|
789
|
+
* this client already holds content. Validation completes before anything is
|
|
790
|
+
* written, so a rejected snapshot leaves the client exactly as it was.
|
|
791
|
+
*/
|
|
792
|
+
hydrate(snapshot, opts) {
|
|
793
|
+
if (this.hydrated || this.pw !== null || this.getSnapshot().length > 0) {
|
|
794
|
+
throw new Error(
|
|
795
|
+
"brookmd: hydrate() must be called on an untouched client, before any append()/setContent()/finalize(). Construct a new BrookClient (or reset() this one)."
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
if (typeof snapshot !== "object" || snapshot === null) {
|
|
799
|
+
throw new Error("brookmd: hydrate() expects a PersistableSnapshot object.");
|
|
800
|
+
}
|
|
801
|
+
if (snapshot.hydrateVersion !== HYDRATE_VERSION) {
|
|
802
|
+
throw new Error(
|
|
803
|
+
`brookmd: cannot hydrate a version ${String(snapshot.hydrateVersion)} snapshot \u2014 this build reads version ${HYDRATE_VERSION}. Discard it and re-stream the source.`
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
if (!Array.isArray(snapshot.blocks) || typeof snapshot.done !== "boolean" || typeof snapshot.sourceHash !== "string" || typeof snapshot.sourceLength !== "number" || !Number.isFinite(snapshot.sourceLength) || snapshot.sourceLength < 0) {
|
|
807
|
+
throw new Error(
|
|
808
|
+
"brookmd: malformed PersistableSnapshot (bad blocks / done / sourceHash / sourceLength)."
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
const committed = /* @__PURE__ */ new Map();
|
|
812
|
+
const committedOrder = new Array(snapshot.blocks.length);
|
|
813
|
+
for (let i = 0; i < snapshot.blocks.length; i++) {
|
|
814
|
+
const b = snapshot.blocks[i];
|
|
815
|
+
if (!isPersistedBlock(b)) {
|
|
816
|
+
throw new Error(
|
|
817
|
+
`brookmd: malformed PersistableSnapshot \u2014 block at index ${i} is not a Block.`
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
if (committed.has(b.id)) {
|
|
821
|
+
throw new Error(`brookmd: malformed PersistableSnapshot \u2014 duplicate block id ${b.id}.`);
|
|
822
|
+
}
|
|
823
|
+
committedOrder[i] = b.id;
|
|
824
|
+
committed.set(b.id, b);
|
|
825
|
+
}
|
|
826
|
+
this.store = { committed, committedOrder, active: [], snapshot: snapshot.blocks.slice() };
|
|
827
|
+
this.hydrated = true;
|
|
828
|
+
this.hydratedDone = snapshot.done;
|
|
829
|
+
this.hydratedLength = snapshot.sourceLength;
|
|
830
|
+
this.hydratedHash = snapshot.sourceHash;
|
|
831
|
+
this.hydratedSource = snapshot.done ? null : opts?.source ?? null;
|
|
832
|
+
if (this.hydratedSource !== null) this.lastContent = this.hydratedSource;
|
|
833
|
+
this.contentDone = snapshot.done;
|
|
834
|
+
this.emit(true);
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* The first content-bearing op on a hydrated thread: give the parser back the
|
|
838
|
+
* state it could not be handed.
|
|
839
|
+
*
|
|
840
|
+
* There is no way around re-parsing — the core keeps `Rc` graphs with no
|
|
841
|
+
* `Deserialize` — but every part of that cost sits off the critical path. The
|
|
842
|
+
* hydrated blocks are already painted and STAY painted: `softReset` preserves
|
|
843
|
+
* them exactly as the setContent divergence swap does, so the reader keeps
|
|
844
|
+
* reading and scrolling while the worker chews through the history on its own
|
|
845
|
+
* thread. The caller's new chunks need no buffer of ours — `postMessage` is
|
|
846
|
+
* FIFO per worker, so they queue behind the re-feed and are parsed the instant
|
|
847
|
+
* it catches up. When the re-parse's patch lands, `mergeStale` adopts every
|
|
848
|
+
* unchanged block BY REFERENCE (same object, same id), so the swap re-renders
|
|
849
|
+
* and remounts nothing, and the live tail streams on from there.
|
|
850
|
+
*/
|
|
851
|
+
beginResume() {
|
|
852
|
+
if (this.hydratedLength === 0 && this.store.snapshot.length === 0) {
|
|
853
|
+
this.retireHydration();
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
if (this.hydratedDone) {
|
|
857
|
+
throw new Error(
|
|
858
|
+
"brookmd: this client was hydrated from a FINALIZED snapshot (done: true), and a completed thread cannot be appended to. Call reset() to start a new stream, or hydrate a `done: false` snapshot with its `source` to resume one."
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
const source = this.hydratedSource;
|
|
862
|
+
if (source === null) {
|
|
863
|
+
throw new Error(
|
|
864
|
+
"brookmd: cannot append to a hydrated client without its source. Continuing a document means re-parsing the text that came before it \u2014 pass it at hydrate time: client.hydrate(snapshot, { source })."
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
this.retireHydration();
|
|
868
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV !== "production" && (source.length !== this.hydratedLength || sourceFingerprint(source) !== this.hydratedHash)) {
|
|
869
|
+
warnOnce(
|
|
870
|
+
"hydrate-stale",
|
|
871
|
+
"brookmd: the source passed to hydrate() does not match the snapshot's fingerprint, so the resume re-parse will replace the hydrated blocks. Compare `snapshot.sourceHash` with `sourceFingerprint(source)` before hydrating and re-stream when they differ."
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
this.softReset(this.getSnapshot());
|
|
875
|
+
this.append(source);
|
|
876
|
+
}
|
|
877
|
+
/** The resume re-feed is no longer wanted: either it has just been issued, or
|
|
878
|
+
* the caller took over by driving the whole document itself. Releases the
|
|
879
|
+
* retained source so one document, not two, stays in memory. */
|
|
880
|
+
retireHydration() {
|
|
881
|
+
this.resumed = true;
|
|
882
|
+
this.hydratedSource = null;
|
|
883
|
+
}
|
|
884
|
+
/** Drop every trace of a hydrate. An explicit reset() is a brand-new
|
|
885
|
+
* document, so a restored view must not keep intercepting appends or re-feed
|
|
886
|
+
* a source that is no longer the one being streamed. */
|
|
887
|
+
clearHydration() {
|
|
888
|
+
this.hydrated = false;
|
|
889
|
+
this.resumed = false;
|
|
890
|
+
this.hydratedDone = false;
|
|
891
|
+
this.hydratedSource = null;
|
|
892
|
+
this.hydratedLength = 0;
|
|
893
|
+
this.hydratedHash = "";
|
|
894
|
+
}
|
|
649
895
|
reset() {
|
|
650
896
|
const hadContent = this.getSnapshot().length > 0;
|
|
651
897
|
this.staleSnapshot = null;
|
|
@@ -654,6 +900,7 @@ class BrookClient {
|
|
|
654
900
|
this.failedError = null;
|
|
655
901
|
this.recoveryAttempted = false;
|
|
656
902
|
this.pendingRebind = false;
|
|
903
|
+
this.clearHydration();
|
|
657
904
|
this.resetParser();
|
|
658
905
|
if (hadContent) this.emit(true);
|
|
659
906
|
}
|
|
@@ -709,6 +956,7 @@ class BrookClient {
|
|
|
709
956
|
this.firstAppendMs = 0;
|
|
710
957
|
this.retainedBytes = 0;
|
|
711
958
|
this.wasmMemoryBytes = 0;
|
|
959
|
+
this.appendedAny = false;
|
|
712
960
|
this.lastContent = "";
|
|
713
961
|
this.contentDone = false;
|
|
714
962
|
this.recoveryBuffer = "";
|
|
@@ -1032,5 +1280,6 @@ export {
|
|
|
1032
1280
|
__resetDefaultPool,
|
|
1033
1281
|
applyPatch,
|
|
1034
1282
|
emptyBlockStore,
|
|
1035
|
-
getDefaultPool
|
|
1283
|
+
getDefaultPool,
|
|
1284
|
+
sourceFingerprint
|
|
1036
1285
|
};
|
package/dist/dom.d.ts
CHANGED
|
@@ -129,7 +129,24 @@ export interface MountOptions {
|
|
|
129
129
|
* `client.getMetrics().rebuildCount`.
|
|
130
130
|
*/
|
|
131
131
|
onRenderMetrics?: RenderMetricsHook;
|
|
132
|
+
/**
|
|
133
|
+
* @internal TEST-ONLY. Turn off the incremental apply fast paths — the open
|
|
134
|
+
* code block's frozen/tail mirror and the delta-driven child splice — so every
|
|
135
|
+
* changed open block rebuilds its whole node, exactly as it did before those
|
|
136
|
+
* existed. The DOM-parity fuzz mounts one renderer with this on and one with it
|
|
137
|
+
* off over the same patch stream and asserts their `innerHTML` matches after
|
|
138
|
+
* every sync: correctness must never depend on a fast path firing. Not part of
|
|
139
|
+
* the supported API and never useful in an app.
|
|
140
|
+
*/
|
|
141
|
+
__fullRebuild?: boolean;
|
|
132
142
|
}
|
|
143
|
+
/** @internal Test-only. */
|
|
144
|
+
export declare function __keyedStats(): {
|
|
145
|
+
attempts: number;
|
|
146
|
+
hits: number;
|
|
147
|
+
};
|
|
148
|
+
/** @internal Test-only. */
|
|
149
|
+
export declare function __resetKeyedStats(): void;
|
|
133
150
|
export declare function mountBrookMarkdown(client: BrookClient, container: HTMLElement, options?: MountOptions): MountHandle;
|
|
134
151
|
/**
|
|
135
152
|
* Derive the streaming tail's block id from an ordered snapshot: the id of the
|