@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.
@@ -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;
package/core/wasm.d.ts CHANGED
@@ -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
 
@@ -451,10 +469,10 @@ export class Document {
451
469
  */
452
470
  card(index: number): Card;
453
471
  /**
454
- * The index of the first composable card whose `$id` equals `id`, or
455
- * `undefined` when none carries it. Resolves the canonical durable address
472
+ * The index of the composable card whose `$id` equals `id`, or
473
+ * `undefined` when none carries it. Resolves the durable card handle
456
474
  * without a hand-rolled scan over [`cards`](Self::cards); `$id` is
457
- * non-unique by design, so the first match wins.
475
+ * unique per document, so at most one card matches.
458
476
  */
459
477
  cardIndexById(id: string): number | undefined;
460
478
  clone(): Document;
@@ -496,17 +514,6 @@ export class Document {
496
514
  * Parse markdown into a typed Document. Throws on parse errors.
497
515
  */
498
516
  static fromMarkdown(markdown: string): Document;
499
- /**
500
- * Read the value at `addr` — the raw stored payload value of a field (a
501
- * content object for a richtext field, a scalar/array/object otherwise), or
502
- * the **body content** when `addr.field` is absent. A bare string is `Addr`
503
- * shorthand for `{ field }`. Reads are total over the field axis: an absent
504
- * field is `undefined`; only an out-of-range `addr.card` throws
505
- * `edit::index_out_of_range`. Reads need no schema, so they live on
506
- * `Document`, not the typed writer; for the markdown projection of a
507
- * richtext value use [`getMarkdown`](Self::get_markdown).
508
- */
509
- get(addr: Addr | string): unknown;
510
517
  /**
511
518
  * The whole `$ext` map at `addr` (a card address, absent `card` = main), or
512
519
  * `undefined` when the card carries none. The fine-grained `$ext` read —
@@ -533,6 +540,19 @@ export class Document {
533
540
  * type (#978). An out-of-range `addr.card` throws.
534
541
  */
535
542
  getMarkdown(addr?: CardAddr): string;
543
+ /**
544
+ * Read the **verbatim stored value** at `addr` — the raw payload value of a
545
+ * field (a content object for a richtext field, a scalar/array/object
546
+ * otherwise), or the **body content** when `addr.field` is absent. A bare
547
+ * string is `Addr` shorthand for `{ field }`. Reads are total over the field
548
+ * axis: an absent field is `undefined`; only an out-of-range `addr.card`
549
+ * throws `edit::index_out_of_range`. Needs no schema, so it lives on
550
+ * `Document` — the read echo of the verbatim `store*` write, distinct from
551
+ * the interpreted schema-plane [`reader.get`](Self::reader_get). For the
552
+ * markdown projection use [`getMarkdown`](Self::get_markdown) (body) or
553
+ * `reader.get` (a field's declared type).
554
+ */
555
+ getStored(addr: Addr | string): unknown;
536
556
  /**
537
557
  * Insert a card — the single insertion verb: `at` absent appends, a number
538
558
  * inserts at that index (must be in `0..=cards.length`). Accepts a
@@ -578,8 +598,16 @@ export class Document {
578
598
  * Build a fresh `Card` from a kind and a flat field map — the ergonomic
579
599
  * constructor for `insertCard`. `fields` is an optional
580
600
  * `Record<string, unknown>` (each entry becomes a card field, in
581
- * insertion order); `body` defaults to `""`. Kind validity is checked by
582
- * `insertCard`, not here.
601
+ * insertion order); `body` defaults to `""`.
602
+ *
603
+ * Sugar, not a required step: `insertCard` takes any `Card` object, and
604
+ * `removeCard` returns one, so a card round-trips without passing through
605
+ * here.
606
+ *
607
+ * Checks only what a detached card can decide alone: field-name grammar
608
+ * and value depth. Kind validity is positional — `main` is right for the
609
+ * root, reserved for a composable card — so `insertCard` is its gate, and
610
+ * any kind string is accepted here.
583
611
  */
584
612
  static makeCard(kind: string, fields?: Record<string, unknown>, body?: string): Card;
585
613
  /**
@@ -633,7 +661,7 @@ export class Document {
633
661
  removeSeedNamespace(card_kind: string): any;
634
662
  /**
635
663
  * **Revise** the richtext value at `addr` from a markdown string — **edit
636
- * semantics**, the default write path, returning the text [`Delta`]. Imports
664
+ * semantics**, the default write path, returning the text `Delta`. Imports
637
665
  * the markdown, diffs it against the current value, rebases surviving
638
666
  * identity anchors, and returns the change an editor bridge maps its own
639
667
  * positions through (`mapPos`). An absent `addr.field` targets the body, an
@@ -776,7 +804,8 @@ export class Quill {
776
804
  * The resolved-value view of `doc` against this quill's schema — for every
777
805
  * declared field the value the render projection would use and the
778
806
  * `FieldSource` rung it came from (`"authored" | "default" | "zero"`), in
779
- * one call. The card body rides the `fields` map under the `$body` key.
807
+ * one call. The card body is a `body` sibling on its card (row `name`
808
+ * `"body"`), never a row in `fields` — `null` when the kind enables no body.
780
809
  *
781
810
  * Value and provenance only: completeness and errors stay `validate`'s
782
811
  * (a consumer merges it with its own diagnostic producers regardless), and
@@ -891,10 +920,11 @@ export function importMarkdown(markdown: string): Content;
891
920
  export function init(): void;
892
921
 
893
922
  /**
894
- * Map a base content position through a `delta` to its new position the pure
895
- * position-mapping codec an editor bridge composes to hold a caret stable
896
- * across a `revise`. `assoc` decides the side of a same-position insertion
897
- * (`"after"` moves past it). Throws on a malformed `delta`.
923
+ * Map a base content position a USV index into `Content.text`, not a UTF-16
924
+ * offset through a `delta` to its new USV position: the pure position-mapping
925
+ * codec an editor bridge composes to hold a caret stable across a `revise`.
926
+ * `assoc` decides the side of a same-position insertion (`"after"` moves past
927
+ * it). Throws on a malformed `delta`.
898
928
  */
899
929
  export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
900
930
 
@@ -910,8 +940,9 @@ export function parseDocPath(path: string): DocPathSeg[];
910
940
  /**
911
941
  * Rebase `markdown` onto a `base` content — the pure, document-free twin of
912
942
  * `revise`: cold-import + `diff_import`, returning the new `content` and the
913
- * text `delta` (surviving anchors rebased). Use it to compute a revise without
914
- * a document in hand; `revise(addr, md)` fuses this with the store for
915
- * atomicity. Throws on an over-nested markdown input or a non-content `base`.
943
+ * text `delta` (its offsets USV indices into `Content.text`, surviving anchors
944
+ * rebased). Use it to compute a revise without a document in hand; `revise(addr,
945
+ * md)` fuses this with the store for atomicity. Throws on an over-nested
946
+ * markdown input or a non-content `base`.
916
947
  */
917
948
  export function rebase(base: Content, markdown: string): { content: Content; delta: Delta };