@quillmark/wasm 0.96.0 → 0.98.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 CHANGED
@@ -1,5 +1,104 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.98.0 - 2026-07-28
4
+
5
+ Five breaking changes, all covered by `docs/migrations/0.97-to-0.98.md`.
6
+ Stored documents are unaffected: a `0.97` blob loads byte-identically and
7
+ `0.98` writes the same bytes for the same content.
8
+
9
+ - feat(core,content,wasm)!: the block vocabulary opens. A line's `kind` and a
10
+ container's name join the mark `type` and island `type` as open sets — an
11
+ unrecognized value round-trips opaque and renders as its nearest safe
12
+ neighbor instead of failing the load, so adding a block construct is no
13
+ longer a document schema-version event. `LineKind` and `Container` each gain
14
+ an `Unknown { tag, attrs }` variant (exhaustive matches need an arm) and
15
+ `Line` / `LineKind` / `Container` drop their `Eq` derive, matching `MarkKind`;
16
+ `Invariant` gains `ReservedUnknownLineKind` / `ReservedUnknownContainer`, so
17
+ an unknown may not reuse a built-in name. `ContentLine.kind` and
18
+ `ContentContainer.container` gain an open TS arm, so a bare discriminant check
19
+ no longer narrows — `isHeadingLine` / `isCodeLine` / `isListItemContainer`
20
+ join the existing guards in `@quillmark/wasm/runtime`. `Loss` describes
21
+ fidelity; it does not gate export (#1054). See
22
+ `docs/migrations/0.97-to-0.98.md`
23
+ - refactor(core,wasm,python,cli)!: `OutputFormat::Txt` is removed — the Rust
24
+ variant, Python's `OutputFormat.TXT`, the TS `'txt'` arm. No backend listed it
25
+ in `SUPPORTED_FORMATS`, so every path reaching it failed at render time;
26
+ `--format txt` and `from_str("txt")` now fail at the argument instead, and
27
+ there is no replacement format (#1058). See
28
+ `docs/migrations/0.97-to-0.98.md`
29
+ - refactor(typst,pdfform)!: `format_not_supported` was three codes for one
30
+ condition. Both backends now emit `backend::format_not_supported`, alongside
31
+ the sibling `backend::apply_unsupported` — route on the namespaced code
32
+ (#1057). See `docs/migrations/0.97-to-0.98.md`
33
+ - refactor(core)!: dead public surface is removed — `ReadValue::as_text` /
34
+ `as_value` (match the variants), `Quill::list_files` / `list_subdirectories`
35
+ (`quill.files()` owns the walk), and `QuillValue`'s eight `Deref`-shadowed
36
+ accessors (`QuillValue: Deref<Target = serde_json::Value>` resolves each call
37
+ unchanged — no action). `prescan`'s helpers and `reader::err` drop to
38
+ `pub(crate)` (#1066, #1064). See `docs/migrations/0.97-to-0.98.md`
39
+ - fix(python)!: rendered regions cross the boundary as `DocPath`. Python
40
+ returned plate-space addresses no document API accepts; the plate→`DocPath`
41
+ translation now lives in core as `regions_to_doc_path` and both bindings call
42
+ it, so `RenderResult.regions[].field` reads `main.body` /
43
+ `cards.<kind>[<i>].<field>` as WASM already did (#1063). See
44
+ `docs/migrations/0.97-to-0.98.md`
45
+ - fix(content): three codec holes close. A mark spanning an island slot no
46
+ longer swallows it; a `LineKind` that disagrees with its segment is caught
47
+ (`Invariant::LineKindMismatch`, carrying a `LineKindMismatch` reason); and
48
+ decode is bounded — `MAX_NESTING_DEPTH` is enforced at the door, and a wire
49
+ position is read checked rather than `as usize`, which on wasm32 landed
50
+ `2^32 + 5` at position `5` (#1051)
51
+ - fix(content): export's verify-and-drop safety net is bounded per line rather
52
+ than per mark; `CONVERT.md` states the import↔export coupling the net implies
53
+ (#1052)
54
+ - fix(content): markdown import keeps a literal `*` that abuts strong or
55
+ emphasis — `a***a**` imports as `a`, `*`, strong `a`, where the deleted fixup
56
+ dropped the typed star. Removing it also fixes a backslash escape or entity in
57
+ such a span re-entering the content as literal source bytes (#1053)
58
+ - feat(core): `FileTreeNode`, `QuillIgnore`, `QuillConfig`, the schema types,
59
+ and `ValidationError` are nameable from `quillmark` and from `quillmark_core`'s
60
+ root, so in-memory quill construction and schema reading need no direct core
61
+ dependency (#1055)
62
+ - refactor(core,pdfform): `From<PdfError> for RenderError` replaces two
63
+ byte-identical `map_pdf_err` copies; `lopdf`, `js-sys` and `wasm-bindgen`
64
+ hoist into `[workspace.dependencies]`, the no-op `default-features = false`
65
+ pins drop, and `publish` is explicit on every crate (#1070, #1064)
66
+ - docs(core): the card-kind validation line is documented where it lives —
67
+ construction (`Document::make_card`, `TryFrom<CardWire>`) is permissive
68
+ data-shaping and insertion is the gate, returning `edit::invalid_kind_name`
69
+ - docs(canon): `ERROR.md` stops documenting a `fmt_pretty_with_source()` that
70
+ does not exist and states what carries the source chain: serialization to
71
+ both bindings, no Rust formatter. `error.rs`'s divergent path-grammar copy
72
+ gives way to a pointer at `path.rs` and the canon anchor table (#1061, #1069)
73
+ - docs(canon): prune — canon stops re-documenting what `docs/` owns and states
74
+ each fact once; the lint checks truth rather than shape, and the CI job table
75
+ lists every job
76
+ - test: test clusters go table-driven, wrong-altitude binding tests move down to
77
+ core, tests that pin foreign behavior or assert nothing are dropped, and one
78
+ walker and one quill builder replace the per-suite copies (#1060, #1056,
79
+ #1065, #1062, #1067)
80
+ - ci: the zero-backend configuration is built and tested — `--workspace
81
+ --all-features` forces `typst` on, so that branch never compiled (#1068)
82
+
83
+
84
+ ## v0.97.0 - 2026-07-24
85
+
86
+ - core: one take_item primitive behind the three payload removers
87
+ - core: fold the incoming-card guard shared by push_card/insert_card
88
+ - tests: dense-prose fix on a muddy reinstatement comment
89
+ - canon/docs: §Card-id identity — the third twin; wasm surface follows
90
+ - core: card $id is the durable handle — unique per document, guarded
91
+ - wasm: add island/mark discriminant guards; USV docs on mapPos/rebase
92
+ - ci: lint the wasm binding's rustdoc to catch intra-doc breakage
93
+ - Canonize anchor-id policy: caller-supplied, unique, invariant
94
+ - Fix stale resolve() docs: body is a sibling, not a `$body` row
95
+ - docs: prune per pruning pass — archive old migrations, cut duplication
96
+ - docs: public integration pages for the high/medium doc gaps
97
+ - docs: dense-prose sweep and accuracy fixes in docs/ and canon
98
+ - docs(wasm): fix broken [`Delta`] intra-doc links (#1034)
99
+ - wasm: rename Document.get → getStored so the verbatim read carries its lane
100
+
101
+
3
102
  ## v0.96.0 - 2026-07-23
4
103
 
5
104
  - docs(spec): keep plate JSON out of the markdown spec
package/README.md CHANGED
@@ -255,9 +255,10 @@ Build a fresh card from a flat field map with
255
255
  `{ card?, field? }`, absent `card` = main, absent `field` = body — and a bare
256
256
  string is shorthand for `{ field }`. So `doc.storeField("qty", 3)` targets the
257
257
  main card's `qty`, `doc.storeField({ card: 2, field: "qty" }, 3)` a composable
258
- card's. Reads are total over the field axis (`get` → `undefined`, `isFill` → `false` for
258
+ card's. Reads are total over the field axis (`getStored` → `undefined`, `isFill` → `false` for
259
259
  an absent field; only an out-of-range card throws); field writes throw on a body
260
- address. `getMarkdown` is the body markdown read (a `CardAddr`; a field's
260
+ address. `getStored` is the verbatim transport read, distinct from the interpreted
261
+ `quill.reader(doc).get`; `getMarkdown` is the body markdown read (a `CardAddr`; a field's
261
262
  markdown is read through `quill.reader(doc).get(field)`). Card-scoped verbs take a
262
263
  `CardAddr` (`{ card? }`) first: `doc.getExt({ card: 2 })`, and the batch below.
263
264
 
@@ -84,25 +84,43 @@ export interface Content {
84
84
  islands: ContentIsland[];
85
85
  }
86
86
 
87
- /** One `\n`-separated segment of `Content.text`, in order. */
87
+ /** One `\n`-separated segment of `Content.text`, in order. `kind` is an open set
88
+ * (as on `ContentIsland` and `ContentMark`): a role this build does not know
89
+ * round-trips with opaque `attrs` and renders as a paragraph, so a document
90
+ * carrying a future block construct still opens. The open arm blocks discriminant
91
+ * narrowing, so read `level`/`lang` behind a check of the arm you want. */
88
92
  export type ContentLine = {
89
93
  containers: ContentContainer[];
90
94
  /** A within-block hard line break rather than a new block. Omitted (false) in the common case. */
91
95
  continues?: boolean;
92
- } & (
96
+ } & ContentLineKind;
97
+
98
+ /** A line's block role, declared once for `ContentLine` and the `setKind` op —
99
+ * a new role is one edit here, as for `ContentContainer`. */
100
+ export type ContentLineKind =
93
101
  | { kind: "para" }
94
102
  | { kind: "heading"; level: number }
95
103
  | { kind: "code"; lang?: string }
96
104
  | { kind: "island" }
97
105
  | { kind: "rule" }
98
- );
106
+ | { kind: string; attrs: unknown };
99
107
 
100
- /** An ancestor block a line nests inside, outermost first. */
108
+ /** An ancestor block a line nests inside, outermost first. Open like
109
+ * `ContentLine.kind`: an unrecognized container round-trips with opaque `attrs`
110
+ * and renders transparently (its lines sit at the enclosing level). */
101
111
  export type ContentContainer =
102
112
  | { container: "list_item"; ordered: boolean; start: number; ordinal: number }
103
- | { container: "quote" };
104
-
105
- /** A mark over char range `[start, end)` into `Content.text`. */
113
+ | { container: "quote" }
114
+ | { container: string; attrs: unknown };
115
+
116
+ /** A mark over char range `[start, end)` into `Content.text`. The open `type`
117
+ * arm blocks discriminant narrowing (as on `ContentIsland`), so read a
118
+ * payload-carrying arm behind its guard — `isLinkMark` (`url`) / `isAnchorMark`
119
+ * (`id`), from `@quillmark/wasm/runtime`; the bare arms carry no payload. An
120
+ * `anchor`'s `id` is a caller-supplied, opaque handle, unique per `Content` and
121
+ * invariant while the mark lives (positions rebase, the id never does); it has no
122
+ * markdown projection and survives only through the edit lane. See DOCUMENT_STORAGE
123
+ * § Anchor-id identity. */
106
124
  export type ContentMark = { start: number; end: number } & (
107
125
  | { type: "strong" | "emph" | "underline" | "strike" | "code" }
108
126
  | { type: "link"; url: string }
@@ -138,7 +156,8 @@ export interface ImageProps {
138
156
  * open set: the engine pins `props` as `TableProps` for `table` and `ImageProps`
139
157
  * for `image`; an island of any other type round-trips with opaque `props`. Like
140
158
  * `ContentMark`, the open `type` arm means a discriminant check does not itself
141
- * narrow `props` — key off `type` and read `props` as the matching shape. */
159
+ * narrow `props` — read `props` as the matching shape behind the `isTableIsland` /
160
+ * `isImageIsland` guards (from `@quillmark/wasm/runtime`), which narrow it. */
142
161
  export type ContentIsland = {
143
162
  id: string;
144
163
  /** How faithfully the markdown projection can carry this island. */
@@ -191,7 +210,10 @@ export type Assoc = "before" | "after";
191
210
  /**
192
211
  * A mark edit in final-text coordinates (post-delta, post-line-op). `add` /
193
212
  * `remove` carry the `ContentMark` vocabulary (`{ type, … }`); `removeAnchor`
194
- * drops one identity anchor by id.
213
+ * drops one identity anchor by id. An `add` of an `anchor` requires a non-empty
214
+ * `id` not already live in the field — a collision or the empty id throws
215
+ * (ids are caller-supplied and unique per `Content`; DOCUMENT_STORAGE
216
+ * § Anchor-id identity).
195
217
  */
196
218
  export type MarkOp =
197
219
  | ({ op: "add" | "remove"; start: number; end: number } & (
@@ -212,11 +234,7 @@ export type MarkOp =
212
234
  export type LineOp =
213
235
  | { op: "split"; at: number }
214
236
  | { op: "join"; line: number }
215
- | ({ op: "setKind"; line: number } & (
216
- | { kind: "para" | "island" | "rule" }
217
- | { kind: "heading"; level: number }
218
- | { kind: "code"; lang?: string }
219
- ))
237
+ | ({ op: "setKind"; line: number } & ContentLineKind)
220
238
  | { op: "setContainers"; line: number; containers: ContentContainer[] }
221
239
  | { op: "setContinues"; line: number; continues: boolean };
222
240
 
@@ -542,7 +560,7 @@ export interface RenderResult {
542
560
 
543
561
  export type HitGranularity = "cluster" | "segment";
544
562
 
545
- export type OutputFormat = "pdf" | "svg" | "txt" | "png";
563
+ export type OutputFormat = "pdf" | "svg" | "png";
546
564
 
547
565
  export type Severity = "error" | "warning";
548
566
 
@@ -580,10 +598,10 @@ export class Document {
580
598
  */
581
599
  card(index: number): Card;
582
600
  /**
583
- * The index of the first composable card whose `$id` equals `id`, or
584
- * `undefined` when none carries it. Resolves the canonical durable address
601
+ * The index of the composable card whose `$id` equals `id`, or
602
+ * `undefined` when none carries it. Resolves the durable card handle
585
603
  * without a hand-rolled scan over [`cards`](Self::cards); `$id` is
586
- * non-unique by design, so the first match wins.
604
+ * unique per document, so at most one card matches.
587
605
  */
588
606
  cardIndexById(id: string): number | undefined;
589
607
  clone(): Document;
@@ -625,17 +643,6 @@ export class Document {
625
643
  * Parse markdown into a typed Document. Throws on parse errors.
626
644
  */
627
645
  static fromMarkdown(markdown: string): Document;
628
- /**
629
- * Read the value at `addr` — the raw stored payload value of a field (a
630
- * content object for a richtext field, a scalar/array/object otherwise), or
631
- * the **body content** when `addr.field` is absent. A bare string is `Addr`
632
- * shorthand for `{ field }`. Reads are total over the field axis: an absent
633
- * field is `undefined`; only an out-of-range `addr.card` throws
634
- * `edit::index_out_of_range`. Reads need no schema, so they live on
635
- * `Document`, not the typed writer; for the markdown projection of a
636
- * richtext value use [`getMarkdown`](Self::get_markdown).
637
- */
638
- get(addr: Addr | string): unknown;
639
646
  /**
640
647
  * The whole `$ext` map at `addr` (a card address, absent `card` = main), or
641
648
  * `undefined` when the card carries none. The fine-grained `$ext` read —
@@ -662,6 +669,19 @@ export class Document {
662
669
  * type (#978). An out-of-range `addr.card` throws.
663
670
  */
664
671
  getMarkdown(addr?: CardAddr): string;
672
+ /**
673
+ * Read the **verbatim stored value** at `addr` — the raw payload value of a
674
+ * field (a content object for a richtext field, a scalar/array/object
675
+ * otherwise), or the **body content** when `addr.field` is absent. A bare
676
+ * string is `Addr` shorthand for `{ field }`. Reads are total over the field
677
+ * axis: an absent field is `undefined`; only an out-of-range `addr.card`
678
+ * throws `edit::index_out_of_range`. Needs no schema, so it lives on
679
+ * `Document` — the read echo of the verbatim `store*` write, distinct from
680
+ * the interpreted schema-plane [`reader.get`](Self::reader_get). For the
681
+ * markdown projection use [`getMarkdown`](Self::get_markdown) (body) or
682
+ * `reader.get` (a field's declared type).
683
+ */
684
+ getStored(addr: Addr | string): unknown;
665
685
  /**
666
686
  * Insert a card — the single insertion verb: `at` absent appends, a number
667
687
  * inserts at that index (must be in `0..=cards.length`). Accepts a
@@ -707,8 +727,16 @@ export class Document {
707
727
  * Build a fresh `Card` from a kind and a flat field map — the ergonomic
708
728
  * constructor for `insertCard`. `fields` is an optional
709
729
  * `Record<string, unknown>` (each entry becomes a card field, in
710
- * insertion order); `body` defaults to `""`. Kind validity is checked by
711
- * `insertCard`, not here.
730
+ * insertion order); `body` defaults to `""`.
731
+ *
732
+ * Sugar, not a required step: `insertCard` takes any `Card` object, and
733
+ * `removeCard` returns one, so a card round-trips without passing through
734
+ * here.
735
+ *
736
+ * Checks only what a detached card can decide alone: field-name grammar
737
+ * and value depth. Kind validity is positional — `main` is right for the
738
+ * root, reserved for a composable card — so `insertCard` is its gate, and
739
+ * any kind string is accepted here.
712
740
  */
713
741
  static makeCard(kind: string, fields?: Record<string, unknown>, body?: string): Card;
714
742
  /**
@@ -762,7 +790,7 @@ export class Document {
762
790
  removeSeedNamespace(card_kind: string): any;
763
791
  /**
764
792
  * **Revise** the richtext value at `addr` from a markdown string — **edit
765
- * semantics**, the default write path, returning the text [`Delta`]. Imports
793
+ * semantics**, the default write path, returning the text `Delta`. Imports
766
794
  * the markdown, diffs it against the current value, rebases surviving
767
795
  * identity anchors, and returns the change an editor bridge maps its own
768
796
  * positions through (`mapPos`). An absent `addr.field` targets the body, an
@@ -1029,7 +1057,8 @@ export class Quill {
1029
1057
  * The resolved-value view of `doc` against this quill's schema — for every
1030
1058
  * declared field the value the render projection would use and the
1031
1059
  * `FieldSource` rung it came from (`"authored" | "default" | "zero"`), in
1032
- * one call. The card body rides the `fields` map under the `$body` key.
1060
+ * one call. The card body is a `body` sibling on its card (row `name`
1061
+ * `"body"`), never a row in `fields` — `null` when the kind enables no body.
1033
1062
  *
1034
1063
  * Value and provenance only: completeness and errors stay `validate`'s
1035
1064
  * (a consumer merges it with its own diagnostic producers regardless), and
@@ -1178,10 +1207,11 @@ export function importMarkdown(markdown: string): Content;
1178
1207
  export function init(): void;
1179
1208
 
1180
1209
  /**
1181
- * Map a base content position through a `delta` to its new position the pure
1182
- * position-mapping codec an editor bridge composes to hold a caret stable
1183
- * across a `revise`. `assoc` decides the side of a same-position insertion
1184
- * (`"after"` moves past it). Throws on a malformed `delta`.
1210
+ * Map a base content position a USV index into `Content.text`, not a UTF-16
1211
+ * offset through a `delta` to its new USV position: the pure position-mapping
1212
+ * codec an editor bridge composes to hold a caret stable across a `revise`.
1213
+ * `assoc` decides the side of a same-position insertion (`"after"` moves past
1214
+ * it). Throws on a malformed `delta`.
1185
1215
  */
1186
1216
  export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
1187
1217
 
@@ -1197,8 +1227,9 @@ export function parseDocPath(path: string): DocPathSeg[];
1197
1227
  /**
1198
1228
  * Rebase `markdown` onto a `base` content — the pure, document-free twin of
1199
1229
  * `revise`: cold-import + `diff_import`, returning the new `content` and the
1200
- * text `delta` (surviving anchors rebased). Use it to compute a revise without
1201
- * a document in hand; `revise(addr, md)` fuses this with the store for
1202
- * atomicity. Throws on an over-nested markdown input or a non-content `base`.
1230
+ * text `delta` (its offsets USV indices into `Content.text`, surviving anchors
1231
+ * rebased). Use it to compute a revise without a document in hand; `revise(addr,
1232
+ * md)` fuses this with the store for atomicity. Throws on an over-nested
1233
+ * markdown input or a non-content `base`.
1203
1234
  */
1204
1235
  export function rebase(base: Content, markdown: string): { content: Content; delta: Delta };
@@ -131,7 +131,7 @@ export class Document {
131
131
  /**
132
132
  * Interpreted read at `addr`, resolving the field's declared `type` from
133
133
  * `quill` — the stable ABI under the runtime `reader.get` / `reader.card(i).get`.
134
- * The schema-plane twin of the quill-free [`get`](Self::get): a `richtext`
134
+ * The schema-plane twin of the quill-free [`getStored`](Self::get_stored): a `richtext`
135
135
  * field returns its markdown projection, every other declared type its
136
136
  * canonical value verbatim, so a consumer holding the quill reads by field
137
137
  * meaning rather than by wire shape.
@@ -176,7 +176,7 @@ export class Document {
176
176
  * surviving anchors rebase (as [`revise`](Self::revise)), then the diffed
177
177
  * result is schema-conformed, so a `richtext(inline)` field rejects a
178
178
  * multi-block result with `edit::field_richtext_not_inline`. Returns the
179
- * text [`Delta`].
179
+ * text `Delta`.
180
180
  *
181
181
  * `addr` must name a field (a bare string is `{ field }`); a body address
182
182
  * throws (a body carries no field schema — use [`revise`](Self::revise)). A
@@ -290,10 +290,10 @@ export class Document {
290
290
  return ret >>> 0;
291
291
  }
292
292
  /**
293
- * The index of the first composable card whose `$id` equals `id`, or
294
- * `undefined` when none carries it. Resolves the canonical durable address
293
+ * The index of the composable card whose `$id` equals `id`, or
294
+ * `undefined` when none carries it. Resolves the durable card handle
295
295
  * without a hand-rolled scan over [`cards`](Self::cards); `$id` is
296
- * non-unique by design, so the first match wins.
296
+ * unique per document, so at most one card matches.
297
297
  * @param {string} id
298
298
  * @returns {number | undefined}
299
299
  */
@@ -457,33 +457,6 @@ export class Document {
457
457
  wasm.__wbindgen_add_to_stack_pointer(16);
458
458
  }
459
459
  }
460
- /**
461
- * Read the value at `addr` — the raw stored payload value of a field (a
462
- * content object for a richtext field, a scalar/array/object otherwise), or
463
- * the **body content** when `addr.field` is absent. A bare string is `Addr`
464
- * shorthand for `{ field }`. Reads are total over the field axis: an absent
465
- * field is `undefined`; only an out-of-range `addr.card` throws
466
- * `edit::index_out_of_range`. Reads need no schema, so they live on
467
- * `Document`, not the typed writer; for the markdown projection of a
468
- * richtext value use [`getMarkdown`](Self::get_markdown).
469
- * @param {Addr | string} addr
470
- * @returns {unknown}
471
- */
472
- get(addr) {
473
- try {
474
- const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
475
- wasm.document_get(retptr, this.__wbg_ptr, addHeapObject(addr));
476
- var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
477
- var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
478
- var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
479
- if (r2) {
480
- throw takeObject(r1);
481
- }
482
- return takeObject(r0);
483
- } finally {
484
- wasm.__wbindgen_add_to_stack_pointer(16);
485
- }
486
- }
487
460
  /**
488
461
  * The whole `$ext` map at `addr` (a card address, absent `card` = main), or
489
462
  * `undefined` when the card carries none. The fine-grained `$ext` read —
@@ -561,6 +534,35 @@ export class Document {
561
534
  wasm.__wbindgen_add_to_stack_pointer(16);
562
535
  }
563
536
  }
537
+ /**
538
+ * Read the **verbatim stored value** at `addr` — the raw payload value of a
539
+ * field (a content object for a richtext field, a scalar/array/object
540
+ * otherwise), or the **body content** when `addr.field` is absent. A bare
541
+ * string is `Addr` shorthand for `{ field }`. Reads are total over the field
542
+ * axis: an absent field is `undefined`; only an out-of-range `addr.card`
543
+ * throws `edit::index_out_of_range`. Needs no schema, so it lives on
544
+ * `Document` — the read echo of the verbatim `store*` write, distinct from
545
+ * the interpreted schema-plane [`reader.get`](Self::reader_get). For the
546
+ * markdown projection use [`getMarkdown`](Self::get_markdown) (body) or
547
+ * `reader.get` (a field's declared type).
548
+ * @param {Addr | string} addr
549
+ * @returns {unknown}
550
+ */
551
+ getStored(addr) {
552
+ try {
553
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
554
+ wasm.document_getStored(retptr, this.__wbg_ptr, addHeapObject(addr));
555
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
556
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
557
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
558
+ if (r2) {
559
+ throw takeObject(r1);
560
+ }
561
+ return takeObject(r0);
562
+ } finally {
563
+ wasm.__wbindgen_add_to_stack_pointer(16);
564
+ }
565
+ }
564
566
  /**
565
567
  * Insert a card — the single insertion verb: `at` absent appends, a number
566
568
  * inserts at that index (must be in `0..=cards.length`). Accepts a
@@ -685,8 +687,16 @@ export class Document {
685
687
  * Build a fresh `Card` from a kind and a flat field map — the ergonomic
686
688
  * constructor for `insertCard`. `fields` is an optional
687
689
  * `Record<string, unknown>` (each entry becomes a card field, in
688
- * insertion order); `body` defaults to `""`. Kind validity is checked by
689
- * `insertCard`, not here.
690
+ * insertion order); `body` defaults to `""`.
691
+ *
692
+ * Sugar, not a required step: `insertCard` takes any `Card` object, and
693
+ * `removeCard` returns one, so a card round-trips without passing through
694
+ * here.
695
+ *
696
+ * Checks only what a detached card can decide alone: field-name grammar
697
+ * and value depth. Kind validity is positional — `main` is right for the
698
+ * root, reserved for a composable card — so `insertCard` is its gate, and
699
+ * any kind string is accepted here.
690
700
  * @param {string} kind
691
701
  * @param {Record<string, unknown>} [fields]
692
702
  * @param {string} [body]
@@ -917,7 +927,7 @@ export class Document {
917
927
  }
918
928
  /**
919
929
  * **Revise** the richtext value at `addr` from a markdown string — **edit
920
- * semantics**, the default write path, returning the text [`Delta`]. Imports
930
+ * semantics**, the default write path, returning the text `Delta`. Imports
921
931
  * the markdown, diffs it against the current value, rebases surviving
922
932
  * identity anchors, and returns the change an editor bridge maps its own
923
933
  * positions through (`mapPos`). An absent `addr.field` targets the body, an
@@ -1691,7 +1701,8 @@ export class Quill {
1691
1701
  * The resolved-value view of `doc` against this quill's schema — for every
1692
1702
  * declared field the value the render projection would use and the
1693
1703
  * `FieldSource` rung it came from (`"authored" | "default" | "zero"`), in
1694
- * one call. The card body rides the `fields` map under the `$body` key.
1704
+ * one call. The card body is a `body` sibling on its card (row `name`
1705
+ * `"body"`), never a row in `fields` — `null` when the kind enables no body.
1695
1706
  *
1696
1707
  * Value and provenance only: completeness and errors stay `validate`'s
1697
1708
  * (a consumer merges it with its own diagnostic producers regardless), and
@@ -2064,10 +2075,11 @@ export function init() {
2064
2075
  }
2065
2076
 
2066
2077
  /**
2067
- * Map a base content position through a `delta` to its new position the pure
2068
- * position-mapping codec an editor bridge composes to hold a caret stable
2069
- * across a `revise`. `assoc` decides the side of a same-position insertion
2070
- * (`"after"` moves past it). Throws on a malformed `delta`.
2078
+ * Map a base content position a USV index into `Content.text`, not a UTF-16
2079
+ * offset through a `delta` to its new USV position: the pure position-mapping
2080
+ * codec an editor bridge composes to hold a caret stable across a `revise`.
2081
+ * `assoc` decides the side of a same-position insertion (`"after"` moves past
2082
+ * it). Throws on a malformed `delta`.
2071
2083
  * @param {Delta} delta
2072
2084
  * @param {number} pos
2073
2085
  * @param {Assoc} assoc
@@ -2119,9 +2131,10 @@ export function parseDocPath(path) {
2119
2131
  /**
2120
2132
  * Rebase `markdown` onto a `base` content — the pure, document-free twin of
2121
2133
  * `revise`: cold-import + `diff_import`, returning the new `content` and the
2122
- * text `delta` (surviving anchors rebased). Use it to compute a revise without
2123
- * a document in hand; `revise(addr, md)` fuses this with the store for
2124
- * atomicity. Throws on an over-nested markdown input or a non-content `base`.
2134
+ * text `delta` (its offsets USV indices into `Content.text`, surviving anchors
2135
+ * rebased). Use it to compute a revise without a document in hand; `revise(addr,
2136
+ * md)` fuses this with the store for atomicity. Throws on an over-nested
2137
+ * markdown input or a non-content `base`.
2125
2138
  * @param {Content} base
2126
2139
  * @param {string} markdown
2127
2140
  * @returns {{ content: Content; delta: Delta }}
Binary file
@@ -23,10 +23,10 @@ export const document_formatDiagnostic: (a: number, b: number) => void;
23
23
  export const document_formatRules: (a: number) => void;
24
24
  export const document_fromJson: (a: number, b: number, c: number) => void;
25
25
  export const document_fromMarkdown: (a: number, b: number, c: number) => void;
26
- export const document_get: (a: number, b: number, c: number) => void;
27
26
  export const document_getExt: (a: number, b: number, c: number) => void;
28
27
  export const document_getExtNamespace: (a: number, b: number, c: number, d: number, e: number) => void;
29
28
  export const document_getMarkdown: (a: number, b: number, c: number) => void;
29
+ export const document_getStored: (a: number, b: number, c: number) => void;
30
30
  export const document_insertCard: (a: number, b: number, c: number, d: number) => void;
31
31
  export const document_install: (a: number, b: number, c: number, d: number) => void;
32
32
  export const document_isFill: (a: number, b: number, c: number) => void;