@quillmark/wasm 0.95.1 → 0.97.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,58 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.97.0 - 2026-07-24
4
+
5
+ - core: one take_item primitive behind the three payload removers
6
+ - core: fold the incoming-card guard shared by push_card/insert_card
7
+ - tests: dense-prose fix on a muddy reinstatement comment
8
+ - canon/docs: §Card-id identity — the third twin; wasm surface follows
9
+ - core: card $id is the durable handle — unique per document, guarded
10
+ - wasm: add island/mark discriminant guards; USV docs on mapPos/rebase
11
+ - ci: lint the wasm binding's rustdoc to catch intra-doc breakage
12
+ - Canonize anchor-id policy: caller-supplied, unique, invariant
13
+ - Fix stale resolve() docs: body is a sibling, not a `$body` row
14
+ - docs: prune per pruning pass — archive old migrations, cut duplication
15
+ - docs: public integration pages for the high/medium doc gaps
16
+ - docs: dense-prose sweep and accuracy fixes in docs/ and canon
17
+ - docs(wasm): fix broken [`Delta`] intra-doc links (#1034)
18
+ - wasm: rename Document.get → getStored so the verbatim read carries its lane
19
+
20
+
21
+ ## v0.96.0 - 2026-07-23
22
+
23
+ - docs(spec): keep plate JSON out of the markdown spec
24
+ - docs(core): fix doc-lint and mkdocs-strict CI failures
25
+ - core: gate plate $body at construction, not post-hoc strip
26
+ - docs(core): trim duplicated $kind comment in to_plate_json
27
+ - core: plate $body/$kind absent on undefined (#1030)
28
+ - core: compile_data conforms once — ladder consumes the gate's coerced output
29
+ - rename: view()→reader(), fieldStates()→resolve() across core and bindings
30
+ - core: one shared resolver behind compile_data and field_states
31
+ - content: dense-prose + simplify pass on the #1002 decision
32
+ - content: decide island-id ↔ content-hash determinism (#1002)
33
+ - Re-key runtime.d.ts geometry docs onto the canonical DocPath form
34
+ - Canon lint cleanup: dedup spine restatements, fold CI job, shape-based anchor check
35
+ - Canon docs infra: single-source the spine in prose/README.md, enforce in CI
36
+ - release: strip the seed coverage comment by construction; fix blank-notes fallback
37
+ - Reject signed years in date/datetime grammar (#1008)
38
+ - core: silence unused_must_use on Payload::insert in tests
39
+ - DocPath: root every main-field address at `main`
40
+ - fieldStates: ordered rows carrying `name`, body as a sibling (drop `$body`)
41
+ - Descope phase 5: cut the conformance suite, contractVersion, and phase-plan docs
42
+ - Contract phase 5 — conformance suite + contractVersion
43
+ - Contract phase 4 — typed-surface completion: QuillCardUi.groups + island props
44
+ - Phase 3 doc: separate tested value-parity from structural source-rung claim
45
+ - Fix rustdoc intra-doc links on EditError::doc_path / RenderedRegion.field
46
+ - simplify pass: hoist kinds vec, unify render-sidecar addressing, merge doc_path arms
47
+ - Contract phases 2–3: geometry DocPath unification, mutator paths, lean fieldStates
48
+ - Repivot the contract rework around consumer evidence (phase-doc rewrite)
49
+ - Qualify card fill diagnostics by schema-declared kind only (#1014)
50
+ - Contract phase 2 — canonical DocPath (+ phase 1 wasm test fix) (#1012)
51
+ - edit:: diagnostic codes on mutator failures (contract phase 1) (#1006)
52
+ - Add document-contract rework phase plan (#1005)
53
+ - content: centralize island type dispatch behind KnownIslandType (#985) (#1001)
54
+
55
+
3
56
  ## v0.95.1 - 2026-07-19
4
57
 
5
58
 
@@ -30,49 +83,6 @@
30
83
  - feat(content,wasm,python): `LineOp::SetContinues { line, continues }` — hard breaks lower op-wise. Split, join, and a text-delta `\n` all mint `continues: false` lines, so a within-block hard break (a paragraph hard break, a code fence's interior line) had no op and fell back to a whole-install, losing that edit's identity anchors. Threaded through the wire codec into WASM `applyChange` (TS union updated) and Python; `continues: true` on line 0 is rejected with `ApplyError::FirstLineContinues` before the write, leaving the content untouched (#949)
31
84
  - feat(wasm): the runtime root re-exports the edit vocabulary its own signatures reference — `Content` / `ContentLine` / `ContentContainer` / `ContentMark` / `ContentIsland`, `Addr` / `Delta` / `Assoc` / `LineOp` / `MarkOp` / `ChangeBundle`, `CardInput` / `PathStep` — as type-only exports (single entry point preserved; no `/core` subpath), with a presence guard so a dropped re-export fails `npm run typecheck` (#948)
32
85
 
33
- <!-- seed: commits since v0.94.0 — confirm the entries above cover them, then delete this comment
34
- - chore: prune redundant logic and duplicate tests (post-0.94.0 residue scan) (#996)
35
- - release: unbreak the crates.io publish lane; fold curated notes into the seed (#995)
36
- - Emit date fields as click-to-edit value-objects (#990) (#994)
37
- - Split `datetime` into strict `date` and `datetime` types (#991) (#993)
38
- - Python binding: commit to the Tier-1 surface (#970) (#992)
39
- - audit #982: complete the Content-genus residual sweep (retire "corpus") (#989)
40
- - feat: schema-bound read view — `quill.view(doc)` and `TypedReader` (#988)
41
- - Rebase marks through line ops; collapse bundle normalize (#987)
42
- - core: retire the V0_81_0 and V0_82_0 storage read shims (#929) (#986)
43
- - Document binding build performance guidance in CLAUDE.md (#984)
44
- - richtext: to_markdown projects a value, not a file — no trailing newline (#965) (#977)
45
- - docs(markdown-spec): scope $body wire claim, fix lossless→lossy projection (#983)
46
- - Delete prose/review directory
47
- - rename: content genus off its codec's name — RichText → Content, crate → quillmark-content (#976) (#981)
48
- - Add note to not run cargo fmt (#980)
49
- - fix(core,wasm,python): getMarkdown surfaces present-but-not-richtext instead of blanking (#968) (#979)
50
- - docs: purge rogue .qmd file-extension mentions (#975)
51
- - Rewrite CLAUDE.md for density (#974)
52
- - docs: fix mkdocs strict build — drop cross-tree link to prose/canon
53
- - wasm: name the main-card address (MAIN_CARD_ADDR), reject unknown addr keys (#969)
54
- - core: collapse the two parse functions into one `Document::parse` -> `Parsed` (#964)
55
- - core,wasm: writer-level reviseField; hide the quill-taking Document ABI (#966)
56
- - core,wasm,docs: dense-prose pass over the #963 write surface
57
- - docs: document the write-surface reshape (#955, #957, #960)
58
- - python: rename opaque store verbs set_* → store_* (#960)
59
- - wasm: unify Document on Addr addressing; store_* verbs; reviseChecked (#955, #960, #957)
60
- - core: rename opaque store verbs set_* → store_*; add revise_field_checked (#960, #957)
61
- - core: collapse the Payload insert helpers; tighten prose
62
- - core: use plain code spans for pub(crate) refs in Payload::insert docs
63
- - docs: 0.94→0.95 migration guide and BINDINGS parity refresh (#956, #958, #959, #961)
64
- - wasm,core: writer/card-surface parity cleanups (#961)
65
- - wasm,core: single-card, $id, and seed-overlay reads (#956)
66
- - core: enforce field invariants at the Payload::insert boundary (#958)
67
- - core: make ParseOutput the single owner of parse warnings (#959)
68
- - docs(bindings): densify the card-read doc comments
69
- - feat(wasm,python): keyed card reads mirroring the card write verbs (#953)
70
- - docs: remove prose/simplifications backlog for greenfield re-analysis
71
- - Add setContinues line op so hard breaks lower op-wise (#949)
72
- - Re-export corpus edit vocabulary from @quillmark/wasm root (#948)
73
- -->
74
-
75
-
76
86
  ## v0.94.0 - 2026-07-15
77
87
 
78
88
  These notes cover everything since v0.92.1. No 0.93.x was separately
package/README.md CHANGED
@@ -255,10 +255,11 @@ 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
261
- markdown is read through `quill.view(doc).get(field)`). Card-scoped verbs take a
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
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
 
264
265
  Batch mutation: `doc.storeFields({}, {...})` / `doc.storeFields({ card: index }, {...})`
@@ -462,10 +463,11 @@ compilation failures. The same shape applies to every throw site:
462
463
 
463
464
  - `Document.fromMarkdown` — parse errors (missing root `$quill` metadata, YAML
464
465
  errors, `parse::input_too_large` for inputs > 10 MB).
465
- - `Document` mutators (`storeField`, the writer's `set`, etc.) — `EditError`
466
- variants (`InvalidFieldName`, `InvalidKindName`, `ReservedKind`,
467
- `IndexOutOfRange`, `ValueTooDeep`, `Import`) appear in `diagnostics[0].message`
468
- with the `[EditError::<Variant>]` prefix.
466
+ - `Document` mutators (`storeField`, the writer's `set`, etc.) — mutator
467
+ failures carry a namespaced `edit::*` `code` on `diagnostics[0]`
468
+ (`edit::invalid_field_name`, `edit::unknown_field`, `edit::index_out_of_range`,
469
+ `edit::field_conform`, …). Route on `diagnostics[0].code`, never on message
470
+ text.
469
471
  - `engine.render` / `session.render` — backend compilation failures and
470
472
  validation errors.
471
473
 
@@ -102,7 +102,14 @@ export type ContentContainer =
102
102
  | { container: "list_item"; ordered: boolean; start: number; ordinal: number }
103
103
  | { container: "quote" };
104
104
 
105
- /** A mark over char range `[start, end)` into `Content.text`. */
105
+ /** A mark over char range `[start, end)` into `Content.text`. The open `type`
106
+ * arm blocks discriminant narrowing (as on `ContentIsland`), so read a
107
+ * payload-carrying arm behind its guard — `isLinkMark` (`url`) / `isAnchorMark`
108
+ * (`id`), from `@quillmark/wasm/runtime`; the bare arms carry no payload. An
109
+ * `anchor`'s `id` is a caller-supplied, opaque handle, unique per `Content` and
110
+ * invariant while the mark lives (positions rebase, the id never does); it has no
111
+ * markdown projection and survives only through the edit lane. See DOCUMENT_STORAGE
112
+ * § Anchor-id identity. */
106
113
  export type ContentMark = { start: number; end: number } & (
107
114
  | { type: "strong" | "emph" | "underline" | "strike" | "code" }
108
115
  | { type: "link"; url: string }
@@ -110,14 +117,45 @@ export type ContentMark = { start: number; end: number } & (
110
117
  | { type: string; attrs: unknown }
111
118
  );
112
119
 
113
- /** A structured object (table, figure, …) occupying one island slot in `Content.text`. */
114
- export interface ContentIsland {
120
+ /** A cell in a `TableProps` its plain `text` plus the `marks` over it. `marks`
121
+ * rides the same wire shape as prose `ContentMark`, but each mark's `start`/`end`
122
+ * are USV offsets into this cell's `text` (`0..text.length`), not into
123
+ * `Content.text`. */
124
+ export interface TableCell {
125
+ text: string;
126
+ marks: ContentMark[];
127
+ }
128
+
129
+ /** `props` of a `type: "table"` island: a pipe table normalized to one column
130
+ * count that `header`, every row of `rows`, and `aligns` all share. */
131
+ export interface TableProps {
132
+ header: TableCell[];
133
+ rows: TableCell[][];
134
+ /** Per-column alignment, one entry per column. */
135
+ aligns: ("none" | "left" | "center" | "right")[];
136
+ }
137
+
138
+ /** `props` of a `type: "image"` island. */
139
+ export interface ImageProps {
140
+ url: string;
141
+ alt: string;
142
+ }
143
+
144
+ /** A structured object occupying one island slot in `Content.text`. `type` is an
145
+ * open set: the engine pins `props` as `TableProps` for `table` and `ImageProps`
146
+ * for `image`; an island of any other type round-trips with opaque `props`. Like
147
+ * `ContentMark`, the open `type` arm means a discriminant check does not itself
148
+ * narrow `props` — read `props` as the matching shape behind the `isTableIsland` /
149
+ * `isImageIsland` guards (from `@quillmark/wasm/runtime`), which narrow it. */
150
+ export type ContentIsland = {
115
151
  id: string;
116
- type: string;
117
- props: unknown;
118
152
  /** How faithfully the markdown projection can carry this island. */
119
153
  loss: "lossless" | "degraded" | "unrepresentable";
120
- }
154
+ } & (
155
+ | { type: "table"; props: TableProps }
156
+ | { type: "image"; props: ImageProps }
157
+ | { type: string; props: unknown }
158
+ );
121
159
 
122
160
  /**
123
161
  * A write address — one navigation concept for the whole `Document` surface. An
@@ -161,7 +199,10 @@ export type Assoc = "before" | "after";
161
199
  /**
162
200
  * A mark edit in final-text coordinates (post-delta, post-line-op). `add` /
163
201
  * `remove` carry the `ContentMark` vocabulary (`{ type, … }`); `removeAnchor`
164
- * drops one identity anchor by id.
202
+ * drops one identity anchor by id. An `add` of an `anchor` requires a non-empty
203
+ * `id` not already live in the field — a collision or the empty id throws
204
+ * (ids are caller-supplied and unique per `Content`; DOCUMENT_STORAGE
205
+ * § Anchor-id identity).
165
206
  */
166
207
  export type MarkOp =
167
208
  | ({ op: "add" | "remove"; start: number; end: number } & (
@@ -203,6 +244,21 @@ export interface ChangeBundle {
203
244
 
204
245
 
205
246
 
247
+ /**
248
+ * One segment of a parsed `Diagnostic.path` (see `parseDocPath`). The head
249
+ * carries the document-model root — `main` (only before `body`), a `card`
250
+ * (`kind: null` is the unknown-kind `cards[i]` form), or a `field`; the tail is
251
+ * `field` / `index` / a terminal `body`.
252
+ */
253
+ export type DocPathSeg =
254
+ | { seg: "main" }
255
+ | { seg: "card"; kind: string | null; index: number }
256
+ | { seg: "field"; name: string }
257
+ | { seg: "index"; index: number }
258
+ | { seg: "body" };
259
+
260
+
261
+
206
262
  /**
207
263
  * Page dimensions in points (1 pt = 1/72 inch). Typst measures in Typst
208
264
  * points; pdfform measures in PDF points — the same unit.
@@ -288,6 +344,55 @@ export interface PaintResult {
288
344
 
289
345
 
290
346
 
347
+ /** The commitment-ladder rung that produced a `ResolvedField.value`. */
348
+ export type FieldSource = "authored" | "default" | "zero";
349
+
350
+ /**
351
+ * One resolved row: its `name`, the value the render projection would use, and
352
+ * the `FieldSource` rung it came from. Rows are an ordered array — declaration
353
+ * order is structural, not object-key order. The card body is a `body` sibling
354
+ * on its card, never a row in `fields`. Diagnostics stay `Quill.validate`'s;
355
+ * schema guidance (`example:`, labels) reads from `Quill.schema`.
356
+ */
357
+ export interface ResolvedField {
358
+ name: string;
359
+ value: unknown;
360
+ source: FieldSource;
361
+ }
362
+
363
+ /**
364
+ * The main card's resolved rows in declaration order, plus its body row —
365
+ * `null` when the main enables no body.
366
+ */
367
+ export interface ResolvedMain {
368
+ fields: ResolvedField[];
369
+ body: ResolvedField | null;
370
+ }
371
+
372
+ /**
373
+ * One composable card's resolved rows in declaration order, with its authored
374
+ * `kind` (`null` for an unknown-kind card), its document-array `index`, and its
375
+ * body row — `null` when the kind enables no body.
376
+ */
377
+ export interface ResolvedCard {
378
+ kind: string | null;
379
+ index: number;
380
+ fields: ResolvedField[];
381
+ body: ResolvedField | null;
382
+ }
383
+
384
+ /**
385
+ * The resolved-value view (`Quill.resolve`): the main card and every
386
+ * composable card. Value and provenance only — completeness and errors stay
387
+ * `Quill.validate`.
388
+ */
389
+ export interface Resolved {
390
+ main: ResolvedMain;
391
+ cards: ResolvedCard[];
392
+ }
393
+
394
+
395
+
291
396
  /** UI layout hints for a single field. Field display order is not a hint:
292
397
  * key order in the schema's `fields`/`properties` objects is declaration
293
398
  * order, the ordering contract. */
@@ -298,9 +403,23 @@ export interface QuillFieldUi {
298
403
  multiline?: boolean;
299
404
  }
300
405
 
406
+ /** One entry in a card's `ui.groups` registry: a display-label override for the
407
+ * group id (the map key). An empty object carries no override — the consumer
408
+ * derives the label from the id (`memo_for` → "Memo For"), as it does a field
409
+ * label from its key. */
410
+ export interface QuillGroupUi {
411
+ title?: string;
412
+ }
413
+
301
414
  /** UI layout hints for a card (main or named card kind). */
302
415
  export interface QuillCardUi {
303
416
  title?: string;
417
+ /** The card's group registry: the ordered table of contents naming every
418
+ * group a field's `ui.group` may reference. The map key is the group id, and
419
+ * key order is declaration order — the display-order contract, the same one
420
+ * `fields` key order carries. Absent when the card declares no groups (or
421
+ * uses the deprecated implicit-group form). */
422
+ groups?: Record<string, QuillGroupUi>;
304
423
  }
305
424
 
306
425
  /** Body namespace for a card (main or named card kind). */
@@ -467,15 +586,15 @@ export class Document {
467
586
  * A single composable card by index — the whole `Card`, the card-indexed
468
587
  * twin of the [`main`](Self::main) getter, so reading one card need not
469
588
  * materialize every card via [`cards`](Self::cards). An out-of-range
470
- * `index` throws `[EditError::IndexOutOfRange]`, matching the card write
589
+ * `index` throws `edit::index_out_of_range`, matching the card write
471
590
  * verbs.
472
591
  */
473
592
  card(index: number): Card;
474
593
  /**
475
- * The index of the first composable card whose `$id` equals `id`, or
476
- * `undefined` when none carries it. Resolves the canonical durable address
594
+ * The index of the composable card whose `$id` equals `id`, or
595
+ * `undefined` when none carries it. Resolves the durable card handle
477
596
  * without a hand-rolled scan over [`cards`](Self::cards); `$id` is
478
- * non-unique by design, so the first match wins.
597
+ * unique per document, so at most one card matches.
479
598
  */
480
599
  cardIndexById(id: string): number | undefined;
481
600
  clone(): Document;
@@ -517,17 +636,6 @@ export class Document {
517
636
  * Parse markdown into a typed Document. Throws on parse errors.
518
637
  */
519
638
  static fromMarkdown(markdown: string): Document;
520
- /**
521
- * Read the value at `addr` — the raw stored payload value of a field (a
522
- * content object for a richtext field, a scalar/array/object otherwise), or
523
- * the **body content** when `addr.field` is absent. A bare string is `Addr`
524
- * shorthand for `{ field }`. Reads are total over the field axis: an absent
525
- * field is `undefined`; only an out-of-range `addr.card` throws
526
- * `[EditError::IndexOutOfRange]`. Reads need no schema, so they live on
527
- * `Document`, not the typed writer; for the markdown projection of a
528
- * richtext value use [`getMarkdown`](Self::get_markdown).
529
- */
530
- get(addr: Addr | string): unknown;
531
639
  /**
532
640
  * The whole `$ext` map at `addr` (a card address, absent `card` = main), or
533
641
  * `undefined` when the card carries none. The fine-grained `$ext` read —
@@ -550,10 +658,23 @@ export class Document {
550
658
  *
551
659
  * `addr` is an optional **card address** (`{ card }`, absent = main). A
552
660
  * present `field` throws — a field's markdown is read through the
553
- * schema-plane `quill.view(doc).get(field)`, which interprets by declared
661
+ * schema-plane `quill.reader(doc).get(field)`, which interprets by declared
554
662
  * type (#978). An out-of-range `addr.card` throws.
555
663
  */
556
664
  getMarkdown(addr?: CardAddr): string;
665
+ /**
666
+ * Read the **verbatim stored value** at `addr` — the raw payload value of a
667
+ * field (a content object for a richtext field, a scalar/array/object
668
+ * otherwise), or the **body content** when `addr.field` is absent. A bare
669
+ * string is `Addr` shorthand for `{ field }`. Reads are total over the field
670
+ * axis: an absent field is `undefined`; only an out-of-range `addr.card`
671
+ * throws `edit::index_out_of_range`. Needs no schema, so it lives on
672
+ * `Document` — the read echo of the verbatim `store*` write, distinct from
673
+ * the interpreted schema-plane [`reader.get`](Self::reader_get). For the
674
+ * markdown projection use [`getMarkdown`](Self::get_markdown) (body) or
675
+ * `reader.get` (a field's declared type).
676
+ */
677
+ getStored(addr: Addr | string): unknown;
557
678
  /**
558
679
  * Insert a card — the single insertion verb: `at` absent appends, a number
559
680
  * inserts at that index (must be in `0..=cards.length`). Accepts a
@@ -654,7 +775,7 @@ export class Document {
654
775
  removeSeedNamespace(card_kind: string): any;
655
776
  /**
656
777
  * **Revise** the richtext value at `addr` from a markdown string — **edit
657
- * semantics**, the default write path, returning the text [`Delta`]. Imports
778
+ * semantics**, the default write path, returning the text `Delta`. Imports
658
779
  * the markdown, diffs it against the current value, rebases surviving
659
780
  * identity anchors, and returns the change an editor bridge maps its own
660
781
  * positions through (`mapPos`). An absent `addr.field` targets the body, an
@@ -808,8 +929,8 @@ export class LiveSession {
808
929
  /**
809
930
  * The schema field whose content is under a point on `page` — the
810
931
  * forward (click → field) direction: hit-test a click against the
811
- * compiled document and get back the field address to focus in the
812
- * editor, or `undefined` off any field's ink. `x`/`y` are PDF points
932
+ * compiled document and get back the `DocPath` field address to focus in
933
+ * the editor, or `undefined` off any field's ink. `x`/`y` are PDF points
813
934
  * with a **bottom-left** origin, the same space as `FieldRegion.rect` —
814
935
  * from a canvas click, invert the overlay transform documented on
815
936
  * `FieldRegion`: `x = clickPx.x / renderScale`,
@@ -873,11 +994,12 @@ export class LiveSession {
873
994
  /**
874
995
  * Schema-field geometry for this compiled session — each content field's
875
996
  * **first placement** (one region per page it touches) plus widget and
876
- * scalar-reference-site regions, keyed on the quill schema field path; a
877
- * field may still appear more than once (group by `field`, see
878
- * `FieldRegion`). A session-level query: no render, no byte artifact. An
879
- * interactive preview reads it to scroll to / highlight the focused
880
- * field over a `paint`-ed canvas; the click direction is `fieldAt`.
997
+ * scalar-reference-site regions, keyed on the canonical `DocPath` address
998
+ * (`parseDocPath`-routable; the session resolves the backend's plate-space
999
+ * per-kind ordinal to it); a field may still appear more than once (group
1000
+ * by `field`, see `FieldRegion`). A session-level query: no render, no byte
1001
+ * artifact. An interactive preview reads it to scroll to / highlight the
1002
+ * focused field over a `paint`-ed canvas; the click direction is `fieldAt`.
881
1003
  * Empty for backends that place no schema fields.
882
1004
  */
883
1005
  regions(): FieldRegion[];
@@ -916,6 +1038,18 @@ export class Quill {
916
1038
  * canonical shape.
917
1039
  */
918
1040
  static fromTree(tree: Map<string, Uint8Array>): Quill;
1041
+ /**
1042
+ * The resolved-value view of `doc` against this quill's schema — for every
1043
+ * declared field the value the render projection would use and the
1044
+ * `FieldSource` rung it came from (`"authored" | "default" | "zero"`), in
1045
+ * one call. The card body is a `body` sibling on its card (row `name`
1046
+ * `"body"`), never a row in `fields` — `null` when the kind enables no body.
1047
+ *
1048
+ * Value and provenance only: completeness and errors stay `validate`'s
1049
+ * (a consumer merges it with its own diagnostic producers regardless), and
1050
+ * schema guidance reads from `Quill.schema`.
1051
+ */
1052
+ resolve(doc: Document): Resolved;
919
1053
  /**
920
1054
  * Seed a starter composable `Card` of the given kind (carries `$kind`),
921
1055
  * layering an optional per-kind seed `overlay` over the schema-example
@@ -1035,6 +1169,15 @@ export class Quillmark {
1035
1169
  */
1036
1170
  export function exportMarkdown(rt: Content): string;
1037
1171
 
1172
+ /**
1173
+ * Serialize structured [`DocPathSeg`] segments back to the canonical path
1174
+ * string — the inverse of `parseDocPath`, for a consumer that builds a path
1175
+ * rather than reads one. Throws on a segment array the deserializer rejects,
1176
+ * and on an empty segment array (symmetric with `parseDocPath("")`, which
1177
+ * throws "empty path").
1178
+ */
1179
+ export function formatDocPath(segs: DocPathSeg[]): string;
1180
+
1038
1181
  /**
1039
1182
  * Import a markdown string to a canonical `Content` content — the pure,
1040
1183
  * document-free codec. Pair with `install(addr, importMarkdown(md))` to spell
@@ -1049,18 +1192,29 @@ export function importMarkdown(markdown: string): Content;
1049
1192
  export function init(): void;
1050
1193
 
1051
1194
  /**
1052
- * Map a base content position through a `delta` to its new position the pure
1053
- * position-mapping codec an editor bridge composes to hold a caret stable
1054
- * across a `revise`. `assoc` decides the side of a same-position insertion
1055
- * (`"after"` moves past it). Throws on a malformed `delta`.
1195
+ * Map a base content position a USV index into `Content.text`, not a UTF-16
1196
+ * offset through a `delta` to its new USV position: the pure position-mapping
1197
+ * codec an editor bridge composes to hold a caret stable across a `revise`.
1198
+ * `assoc` decides the side of a same-position insertion (`"after"` moves past
1199
+ * it). Throws on a malformed `delta`.
1056
1200
  */
1057
1201
  export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
1058
1202
 
1203
+ /**
1204
+ * Parse a canonical document-model `Diagnostic.path`
1205
+ * (`cards.<kind>[<i>].<field>`, `main.body`, `recipients[0].name`) into its
1206
+ * structured [`DocPathSeg`] segments — the exported inverse of the engine's
1207
+ * one path serializer, so a consumer routes on segments instead of regexing
1208
+ * the string. Throws on a malformed path.
1209
+ */
1210
+ export function parseDocPath(path: string): DocPathSeg[];
1211
+
1059
1212
  /**
1060
1213
  * Rebase `markdown` onto a `base` content — the pure, document-free twin of
1061
1214
  * `revise`: cold-import + `diff_import`, returning the new `content` and the
1062
- * text `delta` (surviving anchors rebased). Use it to compute a revise without
1063
- * a document in hand; `revise(addr, md)` fuses this with the store for
1064
- * atomicity. Throws on an over-nested markdown input or a non-content `base`.
1215
+ * text `delta` (its offsets USV indices into `Content.text`, surviving anchors
1216
+ * rebased). Use it to compute a revise without a document in hand; `revise(addr,
1217
+ * md)` fuses this with the store for atomicity. Throws on an over-nested
1218
+ * markdown input or a non-content `base`.
1065
1219
  */
1066
1220
  export function rebase(base: Content, markdown: string): { content: Content; delta: Delta };
@@ -5,5 +5,5 @@ import { __wbg_set_wasm } from "./wasm_bg.js";
5
5
  __wbg_set_wasm(wasm);
6
6
  wasm.__wbindgen_start();
7
7
  export {
8
- Document, LiveSession, Quill, Quillmark, exportMarkdown, importMarkdown, init, mapPos, rebase
8
+ Document, LiveSession, Quill, Quillmark, exportMarkdown, formatDocPath, importMarkdown, init, mapPos, parseDocPath, rebase
9
9
  } from "./wasm_bg.js";