brookmd 0.22.1 → 0.23.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 +67 -17
- package/dist/client.js +14 -2
- package/dist/types-core.d.ts +22 -1
- package/dist/wasm/brook_md_core.d.ts +10 -0
- package/dist/wasm/brook_md_core.js +12 -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/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,73 @@ Notable changes to brookmd (formerly `flux-md`). Format based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/); this project aims to follow
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## 0.23.0 — 2026-07-21
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Wire delta mode — the streaming re-emit floor is retired** (wire contract
|
|
12
|
+
**v1.2.0**, additive). Previously, every `append` re-emitted each open
|
|
13
|
+
block's full HTML across the WASM→worker→main-thread boundary, making total
|
|
14
|
+
emitted bytes O(n²/chunk) for a block that grows across many chunks — the
|
|
15
|
+
documented "re-emit floor". Now the parser can emit a **verified splice**
|
|
16
|
+
(`html_delta: { keep_bytes, keep_units, append }`) against the block's
|
|
17
|
+
previous emit instead; splices are established by byte comparison, never by
|
|
18
|
+
structural inference, so reconstruction is byte-exact by construction.
|
|
19
|
+
|
|
20
|
+
Measured at 200 KB / 256-byte chunks: a streaming list's total patch JSON
|
|
21
|
+
drops **119.6 MB → 0.78 MB (153× less)** with **2.8× faster** end-to-end
|
|
22
|
+
parse+serialize; an unclosed code fence **80.1 MB → 0.58 MB (137×, 4.9×
|
|
23
|
+
faster)**. Fast-committing shapes are unchanged (no regression). The curve
|
|
24
|
+
is now linear, so the gap keeps widening with document size.
|
|
25
|
+
|
|
26
|
+
- The npm package enables this **automatically and invisibly** — the worker
|
|
27
|
+
opts in, and the client reconstructs full blocks before anything else sees
|
|
28
|
+
them. No API change; `Block.html` is always complete.
|
|
29
|
+
- `brookmd-react-native` does the same across the JSI boundary.
|
|
30
|
+
- Raw-boundary consumers (`BrookParser`, native `BrookSession`, C ABI): the
|
|
31
|
+
mode is **opt-in** (`setWireDelta(true)` / `wire_delta: true`) and the
|
|
32
|
+
default wire is byte-identical to contract v1.1.0. Enabling it obliges you
|
|
33
|
+
to reconstruct per [WIRE.md §11](../../crates/brookmd-core/WIRE.md).
|
|
34
|
+
- Pinned by: reconstruction-parity suites (Rust corpus × chunkings, dual
|
|
35
|
+
UTF-8/UTF-16 offset verification), wire goldens in all five language
|
|
36
|
+
surfaces (core, FFI, C-ABI, Kotlin, Swift), a fuzzer extension asserting
|
|
37
|
+
per-patch reconstruction on arbitrary inputs, and a new scaling-gate test
|
|
38
|
+
that *fails CI* if delta-mode emitted bytes regress from linear.
|
|
39
|
+
|
|
40
|
+
## 0.22.2 — 2026-07-21
|
|
41
|
+
|
|
42
|
+
### Performance
|
|
43
|
+
|
|
44
|
+
- **7–24% faster streaming across every benchmarked document shape** (native
|
|
45
|
+
and WASM builds alike), from a final profiling pass over the Rust core:
|
|
46
|
+
- the scanner's newline search (`line_end`) — the hottest primitive, re-run
|
|
47
|
+
by every block probe on every line — now scans 8 bytes per step (SWAR)
|
|
48
|
+
instead of one;
|
|
49
|
+
- the inline renderer copies runs of plain text with a single `push_str`
|
|
50
|
+
instead of per-byte dispatch + escape calls.
|
|
51
|
+
|
|
52
|
+
Rendered HTML is byte-identical (spec goldens, wire-envelope goldens,
|
|
53
|
+
chunk-independence property tests, and the streaming-parity fuzzer all
|
|
54
|
+
pass); the WASM binary grows 338 bytes (+0.16%). Ships as `brookmd-core`
|
|
55
|
+
0.22.1 on crates.io.
|
|
56
|
+
|
|
57
|
+
## 0.22.1 — 2026-07-21
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
- **React Native / Metro compatibility**, both issues found by the new
|
|
62
|
+
app-level e2e gate (a real RN app on an emulator + simulator asserting the
|
|
63
|
+
wire goldens through the native parser):
|
|
64
|
+
- every conditional-exports subpath now ships a `default` condition, so
|
|
65
|
+
Metro's stock package-exports resolution works without custom
|
|
66
|
+
`unstable_conditionNames` (which could poison `@babel/runtime` helper
|
|
67
|
+
resolution and crash release bundles at startup);
|
|
68
|
+
- the browser worker bootstrap moved behind a `react-native` field map to
|
|
69
|
+
an `import.meta`-free shim, so Hermes can parse release bundles (Hermes
|
|
70
|
+
rejects `import.meta` at parse time even in unreachable code). Web
|
|
71
|
+
behavior is unchanged — the `new Worker(new URL(...))` pattern bundlers
|
|
72
|
+
analyze stays intact.
|
|
73
|
+
|
|
7
74
|
## 0.22.0 — 2026-07-20
|
|
8
75
|
|
|
9
76
|
### Changed
|
|
@@ -35,23 +102,6 @@ Notable changes to brookmd (formerly `flux-md`). Format based on
|
|
|
35
102
|
- The old `flux-md` npm package is **deprecated and frozen at 0.21.0**; all future
|
|
36
103
|
releases ship as `brookmd`.
|
|
37
104
|
|
|
38
|
-
## 0.22.1 — 2026-07-21
|
|
39
|
-
|
|
40
|
-
### Fixed
|
|
41
|
-
|
|
42
|
-
- **React Native / Metro compatibility**, both issues found by the new
|
|
43
|
-
app-level e2e gate (a real RN app on an emulator + simulator asserting the
|
|
44
|
-
wire goldens through the native parser):
|
|
45
|
-
- every conditional-exports subpath now ships a `default` condition, so
|
|
46
|
-
Metro's stock package-exports resolution works without custom
|
|
47
|
-
`unstable_conditionNames` (which could poison `@babel/runtime` helper
|
|
48
|
-
resolution and crash release bundles at startup);
|
|
49
|
-
- the browser worker bootstrap moved behind a `react-native` field map to
|
|
50
|
-
an `import.meta`-free shim, so Hermes can parse release bundles (Hermes
|
|
51
|
-
rejects `import.meta` at parse time even in unreachable code). Web
|
|
52
|
-
behavior is unchanged — the `new Worker(new URL(...))` pattern bundlers
|
|
53
|
-
analyze stays intact.
|
|
54
|
-
|
|
55
105
|
## 0.21.0 — 2026-07-20
|
|
56
106
|
|
|
57
107
|
### Added
|
package/dist/client.js
CHANGED
|
@@ -10,7 +10,19 @@ function applyPatch(store, patch) {
|
|
|
10
10
|
if (!store.committed.has(b.id)) store.committedOrder.push(b.id);
|
|
11
11
|
store.committed.set(b.id, b);
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
const active = new Array(patch.active.length);
|
|
14
|
+
for (let i = 0; i < patch.active.length; i++) {
|
|
15
|
+
const entry = patch.active[i];
|
|
16
|
+
if ("html_delta" in entry) {
|
|
17
|
+
const { html_delta, ...rest } = entry;
|
|
18
|
+
const prev = store.active.find((b) => b.id === entry.id);
|
|
19
|
+
if (!prev) throw new Error(`brookmd: html_delta for block ${entry.id} without a base`);
|
|
20
|
+
active[i] = { ...rest, html: prev.html.slice(0, html_delta.keep_units) + html_delta.append };
|
|
21
|
+
} else {
|
|
22
|
+
active[i] = entry;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
store.active = active;
|
|
14
26
|
const next = new Array(store.committedOrder.length + store.active.length);
|
|
15
27
|
for (let i = 0; i < store.committedOrder.length; i++) {
|
|
16
28
|
next[i] = store.committed.get(store.committedOrder[i]);
|
|
@@ -679,13 +691,13 @@ class BrookClient {
|
|
|
679
691
|
let patch;
|
|
680
692
|
try {
|
|
681
693
|
patch = JSON.parse(msg.patch);
|
|
694
|
+
applyPatch(this.store, patch);
|
|
682
695
|
} catch (e) {
|
|
683
696
|
const message = e instanceof Error ? e.message : String(e);
|
|
684
697
|
if (this.onError) this.onError({ message: `brookmd: malformed patch (${message})` });
|
|
685
698
|
else console.error("brookmd: malformed patch:", message);
|
|
686
699
|
break;
|
|
687
700
|
}
|
|
688
|
-
applyPatch(this.store, patch);
|
|
689
701
|
if (msg.final === true && this.staleSnapshot) {
|
|
690
702
|
this.staleTrimmed = true;
|
|
691
703
|
this.mergeCache = null;
|
package/dist/types-core.d.ts
CHANGED
|
@@ -164,9 +164,30 @@ export interface Block {
|
|
|
164
164
|
open: boolean;
|
|
165
165
|
speculative: boolean;
|
|
166
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Wire delta mode (WIRE.md §11): the splice an active block carries in place
|
|
169
|
+
* of `html` when it was already emitted in the previous patch. Reconstruct
|
|
170
|
+
* with `prev.slice(0, keep_units) + append` (JS strings are UTF-16, so
|
|
171
|
+
* `keep_units` is the right offset here; `keep_bytes` is the same prefix for
|
|
172
|
+
* byte-oriented consumers).
|
|
173
|
+
*/
|
|
174
|
+
export interface WireHtmlDelta {
|
|
175
|
+
keep_bytes: number;
|
|
176
|
+
keep_units: number;
|
|
177
|
+
append: string;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* An `active` array entry as serialized: a full {@link Block}, or (wire delta
|
|
181
|
+
* mode only) every Block field except `html` plus an `html_delta` splice.
|
|
182
|
+
* {@link applyPatch} reconstructs deltas into full Blocks — nothing past the
|
|
183
|
+
* store ever sees this union.
|
|
184
|
+
*/
|
|
185
|
+
export type WireActiveBlock = Block | (Omit<Block, "html"> & {
|
|
186
|
+
html_delta: WireHtmlDelta;
|
|
187
|
+
});
|
|
167
188
|
export interface Patch {
|
|
168
189
|
newly_committed: Block[];
|
|
169
|
-
active:
|
|
190
|
+
active: WireActiveBlock[];
|
|
170
191
|
}
|
|
171
192
|
/**
|
|
172
193
|
* Per-block render-churn sample passed to an {@link RenderMetricsHook}. Lets you
|
|
@@ -112,6 +112,15 @@ export class BrookParser {
|
|
|
112
112
|
* when rendering untrusted input — bypasses XSS protection.
|
|
113
113
|
*/
|
|
114
114
|
setUnsafeHtml(on: boolean): void;
|
|
115
|
+
/**
|
|
116
|
+
* Opt-in wire delta mode (WIRE.md §11): active blocks re-emitted across
|
|
117
|
+
* appends serialize as verified `html_delta` splices against their previous
|
|
118
|
+
* emit instead of full `html`, making total emitted bytes O(n) for a block
|
|
119
|
+
* that grows across many appends. Off by default (wire byte-identical to
|
|
120
|
+
* pre-delta releases). A consumer that enables this must reconstruct
|
|
121
|
+
* active `html` per WIRE.md §11 — the npm package's client does.
|
|
122
|
+
*/
|
|
123
|
+
setWireDelta(on: boolean): void;
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
|
@@ -137,6 +146,7 @@ export interface InitOutput {
|
|
|
137
146
|
readonly brookparser_setHtmlSanitize: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
|
|
138
147
|
readonly brookparser_setInlineComponentTags: (a: number, b: number, c: number) => void;
|
|
139
148
|
readonly brookparser_setUnsafeHtml: (a: number, b: number) => void;
|
|
149
|
+
readonly brookparser_setWireDelta: (a: number, b: number) => void;
|
|
140
150
|
readonly __wbindgen_export: (a: number, b: number) => number;
|
|
141
151
|
readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
|
|
142
152
|
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
|
|
@@ -255,6 +255,18 @@ export class BrookParser {
|
|
|
255
255
|
setUnsafeHtml(on) {
|
|
256
256
|
wasm.brookparser_setUnsafeHtml(this.__wbg_ptr, on);
|
|
257
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* Opt-in wire delta mode (WIRE.md §11): active blocks re-emitted across
|
|
260
|
+
* appends serialize as verified `html_delta` splices against their previous
|
|
261
|
+
* emit instead of full `html`, making total emitted bytes O(n) for a block
|
|
262
|
+
* that grows across many appends. Off by default (wire byte-identical to
|
|
263
|
+
* pre-delta releases). A consumer that enables this must reconstruct
|
|
264
|
+
* active `html` per WIRE.md §11 — the npm package's client does.
|
|
265
|
+
* @param {boolean} on
|
|
266
|
+
*/
|
|
267
|
+
setWireDelta(on) {
|
|
268
|
+
wasm.brookparser_setWireDelta(this.__wbg_ptr, on);
|
|
269
|
+
}
|
|
258
270
|
}
|
|
259
271
|
if (Symbol.dispose) BrookParser.prototype[Symbol.dispose] = BrookParser.prototype.free;
|
|
260
272
|
function __wbg_get_imports() {
|
|
Binary file
|
|
@@ -20,6 +20,7 @@ export const brookparser_setGfmTagfilter: (a: number, b: number) => void;
|
|
|
20
20
|
export const brookparser_setHtmlSanitize: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
|
|
21
21
|
export const brookparser_setInlineComponentTags: (a: number, b: number, c: number) => void;
|
|
22
22
|
export const brookparser_setUnsafeHtml: (a: number, b: number) => void;
|
|
23
|
+
export const brookparser_setWireDelta: (a: number, b: number) => void;
|
|
23
24
|
export const __wbindgen_export: (a: number, b: number) => number;
|
|
24
25
|
export const __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
|
|
25
26
|
export const __wbindgen_add_to_stack_pointer: (a: number) => number;
|
package/dist/worker.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brookmd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Zero-dep streaming markdown for the browser. Rust→WASM core, Web Worker per stream, incremental parse with speculative closure.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": ["./dist/worker.js", "./dist/styles.css"],
|