@quillmark/wasm 0.103.0 → 0.105.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/core/wasm.d.ts CHANGED
@@ -24,17 +24,13 @@ export type PayloadItem =
24
24
  | { type: "comment"; text: string; inline?: boolean };
25
25
 
26
26
  /**
27
- * A single card block, as read back from a document: returned by
28
- * `Document.main` / `Document.cards` / `Document.removeCard` / `Quill.seedCard`
29
- * / `Document.makeCard`. To feed a card *into* a document use `CardInput`
30
- * (which `insertCard` accepts); every `Card` is a valid `CardInput`,
31
- * so a card read from one document pushes straight into another.
27
+ * A single card block, as read back from a document. Every `Card` is a valid
28
+ * `CardInput`, so a card read from one document pushes straight into another.
32
29
  *
33
30
  * `$` system entries are hoisted to named fields: `kind` (the `$kind`, empty
34
- * string when none), optional `quill` (the `$quill` `name@version`, main card
35
- * only), optional `ext` (`$ext`), and optional `seed`
36
- * (the `$seed` per-kind overlay map, main card only). `payloadItems` carries
37
- * user fields and comments in order.
31
+ * string when none), `quill` (`$quill` `name@version`, main card only), `ext`
32
+ * (`$ext`), and `seed` (the `$seed` per-kind overlay map, main card only).
33
+ * `payloadItems` carries user fields and comments in order.
38
34
  */
39
35
  export interface Card {
40
36
  kind: string;
@@ -43,21 +39,16 @@ export interface Card {
43
39
  seed?: Record<string, unknown>;
44
40
  payloadItems: PayloadItem[];
45
41
  /**
46
- * The card body as canonical `Content`: the source-of-truth content model.
47
- * Always this content shape on read, never a markdown string. For the markdown
48
- * projection call the codec `exportMarkdown(card.body)`. Write a body back
49
- * with `doc.overwrite(addr, rt)` / `doc.revise(addr, md)`, or via `CardInput.body`.
42
+ * The card body as canonical `Content`, never a markdown string. For the
43
+ * markdown projection call `exportMarkdown(card.body)`.
50
44
  */
51
45
  body: Content;
52
46
  }
53
47
 
54
48
  /**
55
- * A card written *into* a document: the input twin of `Card`, accepted by
56
- * `Document.insertCard`. Like `Card` but `body` also
57
- * takes a markdown `string` (imported to the content, so a markdown / LLM writer
58
- * needn't build the `Content` shape), and every field but `kind` is optional:
59
- * an absent field defaults (no payload items, an empty body). Write one inline
60
- * (`{ kind, body }`) or build it with `Document.makeCard`.
49
+ * A card written *into* a document, accepted by `Document.insertCard`. Like
50
+ * `Card`, but `body` also takes a markdown `string`, and every field but `kind`
51
+ * is optional (defaulting to no payload items and an empty body).
61
52
  */
62
53
  export interface CardInput {
63
54
  kind: string;
@@ -69,11 +60,10 @@ export interface CardInput {
69
60
  }
70
61
 
71
62
  /**
72
- * Canonical richtext content: the content model for a card body (and richtext
73
- * fields). One text sequence over a single coordinate space (Unicode scalar
74
- * values): `text` plus line attributes, anchored `marks`, and embedded
75
- * `islands`. Every edit is a splice; markdown is a projection, not the model.
76
- * Mirrors `quillmark_content::serial`'s canonical JSON encoding.
63
+ * Canonical richtext content: the model behind a card body and richtext fields.
64
+ * One text sequence over a single coordinate space (Unicode scalar values):
65
+ * `text` plus line attributes, anchored `marks`, and embedded `islands`. Every
66
+ * edit is a splice; markdown is a projection, not the model.
77
67
  */
78
68
  export interface Content {
79
69
  text: string;
@@ -82,19 +72,17 @@ export interface Content {
82
72
  islands: ContentIsland[];
83
73
  }
84
74
 
85
- /** One `\n`-separated segment of `Content.text`, in order. `kind` is an open set
86
- * (as on `ContentIsland` and `ContentMark`): a role this build does not know
87
- * round-trips with opaque `attrs` and renders as a paragraph, so a document
88
- * carrying a future block construct still opens. The open arm blocks discriminant
89
- * narrowing, so read `level`/`lang` behind a check of the arm you want. */
75
+ /** One `\n`-separated segment of `Content.text`, in order. `kind` is an open set:
76
+ * an unknown role round-trips with opaque `attrs` and renders as a paragraph.
77
+ * The open arm blocks discriminant narrowing, so read `level`/`lang` behind a
78
+ * check of the arm you want. */
90
79
  export type ContentLine = {
91
80
  containers: ContentContainer[];
92
81
  /** A within-block hard line break rather than a new block. Omitted (false) in the common case. */
93
82
  continues?: boolean;
94
83
  } & ContentLineKind;
95
84
 
96
- /** A line's block role, declared once for `ContentLine` and the `setKind` op:
97
- * a new role is one edit here, as for `ContentContainer`. */
85
+ /** A line's block role, shared by `ContentLine` and the `setKind` op. */
98
86
  export type ContentLineKind =
99
87
  | { kind: "para" }
100
88
  | { kind: "heading"; level: number }
@@ -112,13 +100,12 @@ export type ContentContainer =
112
100
  | { container: string; attrs: unknown };
113
101
 
114
102
  /** A mark over char range `[start, end)` into `Content.text`. The open `type`
115
- * arm blocks discriminant narrowing (as on `ContentIsland`), so read a
116
- * payload-carrying arm behind its guard: `isLinkMark` (`url`) / `isAnchorMark`
117
- * (`id`), from `@quillmark/wasm/runtime`; the bare arms carry no payload. An
118
- * `anchor`'s `id` is a caller-supplied, opaque handle, unique per `Content` and
119
- * invariant while the mark lives (positions rebase, the id never does); it has no
120
- * markdown projection and survives only through the edit lane. See DOCUMENT_STORAGE
121
- * § Anchor-id identity. */
103
+ * arm blocks discriminant narrowing, so read a payload-carrying arm behind its
104
+ * guard: `isLinkMark` (`url`) / `isAnchorMark` (`id`), from
105
+ * `@quillmark/wasm/runtime`. An `anchor`'s `id` is a caller-supplied opaque
106
+ * handle, unique per `Content` and invariant while the mark lives (positions
107
+ * rebase, the id never does); it has no markdown projection and survives only
108
+ * through the edit lane. */
122
109
  export type ContentMark = { start: number; end: number } & (
123
110
  | { type: "strong" | "emph" | "underline" | "strike" | "code" }
124
111
  | { type: "link"; url: string }
@@ -126,9 +113,8 @@ export type ContentMark = { start: number; end: number } & (
126
113
  | { type: string; attrs: unknown }
127
114
  );
128
115
 
129
- /** A cell in a `TableProps`: its plain `text` plus the `marks` over it. `marks`
130
- * rides the same wire shape as prose `ContentMark`, but each mark's `start`/`end`
131
- * are USV offsets into this cell's `text` (`0..text.length`), not into
116
+ /** A cell in a `TableProps`. `marks` rides the prose `ContentMark` shape, but
117
+ * each mark's `start`/`end` are USV offsets into this cell's `text`, not into
132
118
  * `Content.text`. */
133
119
  export interface TableCell {
134
120
  text: string;
@@ -150,17 +136,15 @@ export interface ImageProps {
150
136
  alt: string;
151
137
  }
152
138
 
153
- /** A structured object occupying one island slot in `Content.text`. `type` is an
154
- * open set: the engine pins `props` as `TableProps` for `table` and `ImageProps`
155
- * for `image`; an island of any other type round-trips with opaque `props`. Like
156
- * `ContentMark`, the open `type` arm means a discriminant check does not itself
157
- * narrow `props`: read `props` as the matching shape behind the `isTableIsland` /
158
- * `isImageIsland` guards (from `@quillmark/wasm/runtime`), which narrow it. */
159
- /** How faithfully the markdown projection can carry an island. Open like an
160
- * island `type`: a class this build does not know round-trips verbatim, and
161
- * reads as `unrepresentable`. */
139
+ /** How faithfully the markdown projection can carry an island. Open: an unknown
140
+ * class round-trips verbatim and reads as `unrepresentable`. */
162
141
  export type ContentLossClass = "lossless" | "degraded" | "unrepresentable" | (string & {});
163
142
 
143
+ /** A structured object occupying one island slot in `Content.text`. `type` is an
144
+ * open set: `props` is `TableProps` for `table` and `ImageProps` for `image`,
145
+ * and any other type round-trips with opaque `props`. The open arm blocks
146
+ * narrowing, so read `props` behind the `isTableIsland` / `isImageIsland`
147
+ * guards (from `@quillmark/wasm/runtime`). */
164
148
  export type ContentIsland = {
165
149
  id: string;
166
150
  loss: ContentLossClass;
@@ -178,9 +162,16 @@ export type ContentIsland = {
178
162
  * field: "intro" }` a card field.
179
163
  *
180
164
  * On the `Addr`-taking verbs a **bare string** is shorthand for `{ field: name }`
181
- * (`doc.storeField("qty", 3)`, `doc.revise("intro", md)`) the one coercion
182
- * rule. A bare number is *not* an addr (`{ card: 2 }` is the self-documenting
183
- * spelling), so no third navigation idiom re-fragments the surface.
165
+ * (`doc.storeField("qty", 3)`); a bare number is *not* an addr.
166
+ *
167
+ * `doc.pathFor(addr)` mints the address as its canonical `DocPath` string, the
168
+ * anchor `Diagnostic.path` carries and `session.locate` / `session.fieldBoxes`
169
+ * take. A card path is kind-qualified, so a hand-built one needs the card's
170
+ * `$kind`, and a wrong-kind path matches nothing silently.
171
+ *
172
+ * An `Addr` names a field, never a value inside one: every verb that takes one
173
+ * would then carry an element axis it cannot answer. The one read that reaches
174
+ * inside takes the path as its own argument, `reader.getContentAt(addr, path)`.
184
175
  */
185
176
  export interface Addr {
186
177
  card?: number;
@@ -188,10 +179,9 @@ export interface Addr {
188
179
  }
189
180
 
190
181
  /**
191
- * A card-only address: the axis the card-scoped verbs (`storeFields`,
192
- * `storeExt`, `getExt`, `commitFields`, …) take. An absent `card` targets the
193
- * main card. A present `field` throws: a card address takes only `card`, and a
194
- * would-be nested write is a bug the error names rather than silently ignores.
182
+ * A card-only address, taken by the card-scoped verbs (`storeFields`,
183
+ * `storeExt`, `getExt`, `commitFields`, …). An absent `card` targets the main
184
+ * card; a present `field` throws.
195
185
  */
196
186
  export interface CardAddr {
197
187
  card?: number;
@@ -199,8 +189,8 @@ export interface CardAddr {
199
189
 
200
190
  /**
201
191
  * A text-splice change set over the USV content (CodeMirror `ChangeSet`
202
- * semantics): plain, structured-clone-able data. Returned by `revise` and by
203
- * the `rebase` codec; map a stored position through it with `mapPos`.
192
+ * semantics), returned by `revise` and by the `rebase` codec. Map a stored
193
+ * position through it with `mapPos`.
204
194
  */
205
195
  export interface Delta {
206
196
  ops: ({ retain: number } | { insert: string } | { delete: number })[];
@@ -211,11 +201,9 @@ export type Assoc = "before" | "after";
211
201
 
212
202
  /**
213
203
  * A mark edit in final-text coordinates (post-delta, post-line-op). `add` /
214
- * `remove` carry the `ContentMark` vocabulary (`{ type, }`); `removeAnchor`
215
- * drops one identity anchor by id. An `add` of an `anchor` requires a non-empty
216
- * `id` not already live in the field: a collision or the empty id throws
217
- * (ids are caller-supplied and unique per `Content`; DOCUMENT_STORAGE
218
- * § Anchor-id identity).
204
+ * `remove` carry the `ContentMark` vocabulary; `removeAnchor` drops one identity
205
+ * anchor by id. An `add` of an `anchor` requires a non-empty `id` not already
206
+ * live in the field; a collision or the empty id throws.
219
207
  */
220
208
  export type MarkOp =
221
209
  | ({ op: "add" | "remove"; start: number; end: number } & (
@@ -227,12 +215,10 @@ export type MarkOp =
227
215
  | { op: "removeAnchor"; id: string };
228
216
 
229
217
  /**
230
- * A line/block edit. `split`/`join` splice `\n` in post-`delta`, post-`islandOps`
231
- * coordinates; `setKind`/`setContainers`/`setContinues` touch metadata.
232
- * `setContinues` sets/clears a line's within-block hard-break flag
233
- * (`ContentLine.continues`): the op-grained way to lower a Shift+Enter hard
234
- * break or a new code-fence interior line; `continues: true` on line 0 is
235
- * rejected (nothing precedes it to continue).
218
+ * A line/block edit. `split`/`join` splice `\n` in post-`delta`,
219
+ * post-`islandOps` coordinates; `setKind`/`setContainers`/`setContinues` touch
220
+ * metadata. `setContinues` sets or clears a line's within-block hard-break flag
221
+ * (`ContentLine.continues`); `continues: true` on line 0 is rejected.
236
222
  */
237
223
  export type LineOp =
238
224
  | { op: "split"; at: number }
@@ -243,34 +229,25 @@ export type LineOp =
243
229
 
244
230
  /**
245
231
  * An island edit: the only channel that reaches an island's payload, a table's
246
- * cells or an image's url.
247
- *
248
- * Both ops move one island entry and leave the field's text and marks alone, so
249
- * an island edit keeps every identity anchor in the field. That is why a table
250
- * edit lowers to `applyChange` rather than `overwrite`, which drops them all.
232
+ * cells or an image's url. Both ops leave the field's text and marks alone, so
233
+ * an island edit keeps every identity anchor in the field.
251
234
  *
252
- * `set` addresses an existing island by `id`; an `id` no island carries throws
253
- * rather than passing silently. `insert` places a new island's slot at `at`
254
- * together with its entry, so a slot never exists without an island behind it;
255
- * its `id` must be non-empty and unused. `at` is a position in the text the
256
- * `delta` and this bundle's earlier island ops left: each insert splices its
257
- * slot before the next op reads the text, so slots after `a` and `b` of `abc`
258
- * go in at 1 and 3. A stale frame misplaces slots and never throws.
235
+ * `set` addresses an existing island by `id`; an unknown `id` throws. `insert`
236
+ * places a new island's slot at `at` together with its entry, so a slot never
237
+ * exists without an island behind it; its `id` must be non-empty and unused.
238
+ * `at` is a position in the text the `delta` and this bundle's earlier island
239
+ * ops left, so slots after `a` and `b` of `abc` go in at 1 and 3. A stale frame
240
+ * misplaces slots and never throws. A `delta` insert string may not carry a
241
+ * slot, which would orphan: split such a splice into the slot-free `delta` plus
242
+ * one `insert` per slot.
259
243
  *
260
- * A `delta` insert string may not carry a slot, which would orphan. A producer
261
- * that computes one splice over the whole field text carries slots in it on any
262
- * paste of an island or undo of a deletion; that splice splits into the
263
- * slot-free `delta` plus one `insert` per slot.
244
+ * Deleting an island needs no op: a `delta` that removes its slot drops the
245
+ * island whole, and a block island's line demotes to `para`. Re-landing it is an
246
+ * `insert` of the full island under its original id; a pasted copy of a live
247
+ * island mints a fresh one.
264
248
  *
265
- * Deleting an island needs no op, and the drop is whole: a `delta` that removes
266
- * its slot drops the island from the store. Re-landing it is an `insert` of the
267
- * full island under its original id, and only the producer that deleted it
268
- * still holds that value. A pasted copy of a live island is new and mints fresh
269
- * (DOCUMENT_STORAGE § Island-id determinism). A block island's line demotes to
270
- * `para` when its slot goes, so re-landing one re-tags the line too.
271
- *
272
- * A `set` stores the `loss` it is given: nothing re-derives the class from the
273
- * new `props`, so a write that changes what markdown can carry must say so.
249
+ * A `set` stores the `loss` it is given; nothing re-derives the class from the
250
+ * new `props`.
274
251
  *
275
252
  * An island is *inline* (a slot inside a paragraph) unless its line says
276
253
  * otherwise. A **block** island is one bundle of all three channels, in the
@@ -283,13 +260,12 @@ export type IslandOp =
283
260
  | ({ op: "insert"; at: number } & ContentIsland);
284
261
 
285
262
  /**
286
- * A committed content edit bundle for `applyChange`: a text `delta` (default no
287
- * text change), then `islandOps`, then `lineOps`, then `markOps` (mark ranges
288
- * are in final-text coordinates: every earlier channel applied). Every field is
289
- * optional.
263
+ * A committed content edit bundle for `applyChange`, applied in order: a text
264
+ * `delta`, then `islandOps`, then `lineOps`, then `markOps` (mark ranges are in
265
+ * final-text coordinates). Every field is optional.
290
266
  *
291
- * Within each channel ops apply in sequence, each against the state the earlier
292
- * ones left: an island `insert`'s `at` counts earlier ops' slots, and `lineOps`
267
+ * Within each channel ops apply in sequence against the state the earlier ones
268
+ * left: an island `insert`'s `at` counts earlier ops' slots, and `lineOps`
293
269
  * positions and indices renumber through earlier `split`/`join`.
294
270
  */
295
271
  export interface ChangeBundle {
@@ -317,14 +293,12 @@ export type DocPathSeg =
317
293
 
318
294
 
319
295
  /** The commitment-ladder rung that produced a `ResolvedField.value`. */
320
- export type FieldSource = "authored" | "default" | "zero";
296
+ export type FieldSource = "authored" | "default" | "blank";
321
297
 
322
298
  /**
323
299
  * One resolved row: its `name`, the value the render projection would use, and
324
- * the `FieldSource` rung it came from. Rows are an ordered array: declaration
325
- * order is structural, not object-key order. The card body is a `body` sibling
326
- * on its card, never a row in `fields`. Diagnostics stay `Quill.validate`'s;
327
- * schema guidance (`example:`, labels) reads from `Quill.schema`.
300
+ * the `FieldSource` rung it came from. Rows are an ordered array, so declaration
301
+ * order is structural rather than object-key order.
328
302
  */
329
303
  export interface ResolvedField {
330
304
  name: string;
@@ -354,9 +328,8 @@ export interface ResolvedCard {
354
328
  }
355
329
 
356
330
  /**
357
- * The resolved-value view (`Quill.resolve`): the main card and every
358
- * composable card. Value and provenance only: completeness and errors stay
359
- * `Quill.validate`.
331
+ * The resolved-value view (`Quill.resolve`): the main card and every composable
332
+ * card. Value and provenance only; completeness stays `Quill.validate`'s.
360
333
  */
361
334
  export interface Resolved {
362
335
  main: ResolvedMain;
@@ -365,20 +338,21 @@ export interface Resolved {
365
338
 
366
339
 
367
340
 
368
- /** UI layout hints for a single field. Field display order is not a hint:
369
- * key order in the schema's `fields`/`properties` objects is declaration
370
- * order, the ordering contract. */
341
+ /** UI layout hints for a single field. Display order is not a hint: key order
342
+ * in the schema's `fields`/`properties` objects is the ordering contract. */
371
343
  export interface QuillFieldUi {
372
344
  title?: string;
373
345
  group?: string;
374
346
  compact?: boolean;
375
347
  multiline?: boolean;
348
+ /** Label for an `enum`'s blank option. Absent, the consumer supplies a
349
+ * conventional label of its own. */
350
+ blank_title?: string;
376
351
  }
377
352
 
378
353
  /** One entry in a card's `ui.groups` registry: a display-label override for the
379
- * group id (the map key). An empty object carries no override: the consumer
380
- * derives the label from the id (`memo_for` → "Memo For"), as it does a field
381
- * label from its key. */
354
+ * group id (the map key). An empty object carries no override, and the consumer
355
+ * derives the label from the id (`memo_for` → "Memo For"). */
382
356
  export interface QuillGroupUi {
383
357
  title?: string;
384
358
  }
@@ -386,48 +360,61 @@ export interface QuillGroupUi {
386
360
  /** UI layout hints for a card (main or named card kind). */
387
361
  export interface QuillCardUi {
388
362
  title?: string;
389
- /** The card's group registry: the ordered table of contents naming every
390
- * group a field's `ui.group` may reference. The map key is the group id, and
391
- * key order is declaration order: the display-order contract, the same one
392
- * `fields` key order carries. Absent when the card declares no groups (or
393
- * uses the deprecated implicit-group form). */
363
+ /** The groups a field's `ui.group` may reference, keyed by group id. Key
364
+ * order is the display-order contract, as with `fields`. Absent when the
365
+ * card declares no groups. */
394
366
  groups?: Record<string, QuillGroupUi>;
395
367
  }
396
368
 
369
+ /** A block construct a body can hold. `paragraph` is the floor and cannot be
370
+ * declined, so it is absent. */
371
+ export type QuillBlockConstruct =
372
+ | "heading"
373
+ | "rule"
374
+ | "code"
375
+ | "list"
376
+ | "quote"
377
+ | "table"
378
+ | "image";
379
+
397
380
  /** Body namespace for a card (main or named card kind). */
398
381
  export interface QuillCardBody {
399
382
  /** When false, consumers must not accept or store body content for this card kind. Defaults to true. */
400
383
  enabled?: boolean;
401
384
  /** Example body content embedded verbatim in the blueprint body region. Fallback is "Write <card> body here." */
402
385
  example?: string;
386
+ /** Block constructs this quill's plate does not typeset in this body;
387
+ * absent or empty declines nothing. A body that holds one anyway draws a
388
+ * non-fatal `plate::unsupported_construct` warning. Nothing verifies the
389
+ * claim: absence from this list is not a promise the plate typesets it. */
390
+ unsupported?: QuillBlockConstruct[];
403
391
  }
404
392
 
405
393
  /** Schema entry for a single field declared in a quill's `Quill.yaml`.
406
394
  *
407
- * A field's *cell* is determined by `default`: a field with a `default`
408
- * is **Endorsed** (the rendered value is shippable as-is), while a field
409
- * without a `default` is **Unendorsed** (the blueprint carries a
410
- * `!must_fill` marker; a marker left in the document yields the non-fatal
411
- * `validation::must_fill` warning from validate, and the render path
412
- * zero-fills the field). There is no separate `required` axis.
395
+ * Two independent axes, and no separate `required` one. `default` and
396
+ * `example` say what the cell holds; `must_fill` says whether a human must
397
+ * author it, deriving from `default`'s absence when left unset. An obliged
398
+ * field carries a `!must_fill` marker in the blueprint and warns
399
+ * `validation::must_fill` while the document leaves it unauthored. Neither
400
+ * axis gates render: an absent field blank-fills.
413
401
  */
414
402
  export interface QuillFieldSchema {
415
403
  type: "string" | "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "richtext" | "plaintext" | "enum";
416
404
  description?: string;
417
405
  default?: unknown;
418
406
  example?: unknown;
419
- /** Closed value domain. On `type: "enum"` declared as `values`; the
420
- * deprecated `enum` modifier on `type: "string"` is accepted for one
421
- * release. Both round-trip through this field. */
422
- enum?: string[];
423
- /** Required on `type: "enum"`: the closed set of allowed string values. */
407
+ /** The closed set of allowed values. Required on `type: "enum"`, and valid
408
+ * nowhere else. */
424
409
  values?: string[];
410
+ /** Whether a human must author the field. Absent, it derives from
411
+ * `default`: a defaulted field is unobliged, a defaultless one obliged. */
412
+ must_fill?: boolean;
425
413
  ui?: QuillFieldUi;
426
414
  properties?: Record<string, QuillFieldSchema>;
427
415
  items?: QuillFieldSchema;
428
- /** Present (and `true`) on a `richtext` or `plaintext` field declared
429
- * `inline`: the single-paragraph, container-free, island-free constraint.
430
- * Core serializes `inline: true` into the schema JSON; absent otherwise. */
416
+ /** `true` on a `richtext` or `plaintext` field declared `inline`: the
417
+ * single-paragraph, container-free, island-free constraint. */
431
418
  inline?: boolean;
432
419
  }
433
420
 
@@ -440,10 +427,8 @@ export interface QuillCardSchema {
440
427
  }
441
428
 
442
429
  /**
443
- * Document schema returned by `Quill.schema`. Includes optional `ui` keys.
444
- *
445
- * Describes only the user-fillable fields. The quill reference
446
- * (constructed as `${metadata.name}@${metadata.version}`) and card-kind
430
+ * Document schema returned by `Quill.schema`: the user-fillable fields only.
431
+ * The quill reference (`${metadata.name}@${metadata.version}`) and card-kind
447
432
  * discriminators are document-level metadata, not schema fields.
448
433
  */
449
434
  export interface QuillSchema {
@@ -453,10 +438,9 @@ export interface QuillSchema {
453
438
  }
454
439
 
455
440
  /**
456
- * Identity snapshot mirroring the `quill:` section of `Quill.yaml`.
457
- * The schema lives on `Quill.schema`; the backend's output formats are a
458
- * resolved-backend capability read from the engine (`Quillmark.supportedFormats`),
459
- * not part of this pure-config snapshot.
441
+ * Identity snapshot mirroring the `quill:` section of `Quill.yaml`. The schema
442
+ * lives on `Quill.schema`; output formats are a resolved-backend capability read
443
+ * from `Quillmark.supportedFormats`, not part of this config snapshot.
460
444
  */
461
445
  export interface QuillMetadata {
462
446
  name: string;
@@ -476,31 +460,22 @@ export interface Diagnostic {
476
460
  message: string;
477
461
  location?: Location;
478
462
  /**
479
- * Document-model path anchor (e.g. `\"cards.indorsement[0].signature_block\"`).
480
- *
481
- * Set on schema validation diagnostics; `undefined` otherwise. See the
482
- * Rust `quillmark_core::error` module docs for the path grammar.
463
+ * Document-model path anchor (e.g. `\"cards.indorsement[0].signature_block\"`),
464
+ * set on schema validation diagnostics and `undefined` otherwise.
483
465
  */
484
466
  path?: string;
485
467
  hint?: string;
486
468
  /**
487
- * The facts `message` interpolates, keyed by name. With `code`, the
488
- * substitution unit needed to word this diagnostic in another language;
489
- * `prose/canon/ERROR.md` § \"Diagnostic args\" tabulates the keys per code.
469
+ * The facts `message` interpolates, keyed by name. With `code`, enough to
470
+ * word this diagnostic in another language.
490
471
  *
491
- * Declared optional explicitly because `tsify` does not read
492
- * `skip_serializing_if`: without this, a field the runtime omits is
493
- * declared required. `sourceChain` carries that mismatch.
472
+ * Declared optional explicitly: `tsify` does not read
473
+ * `skip_serializing_if`, so an omitted field would be declared required.
494
474
  */
495
475
  args?: Record<string, unknown>;
496
476
  sourceChain?: string[];
497
477
  }
498
478
 
499
- /**
500
- * Severity levels for diagnostics
501
- */
502
- export type Severity = "error" | "warning";
503
-
504
479
  /**
505
480
  * Source location for errors and warnings
506
481
  */
@@ -510,6 +485,8 @@ export interface Location {
510
485
  column: number;
511
486
  }
512
487
 
488
+ export type Severity = "error" | "warning";
489
+
513
490
 
514
491
  /**
515
492
  * Typed in-memory Quillmark document.
@@ -518,79 +495,69 @@ export class Document {
518
495
  free(): void;
519
496
  [Symbol.dispose](): void;
520
497
  /**
521
- * **Apply** a committed content edit `bundle`
522
- * (`{ delta?, islandOps?, lineOps?, markOps? }`) at `addr`, the editor
523
- * splice: text delta first, then island ops, then line ops, then mark ops
524
- * (mark ranges in final-text coordinates), each all-or-nothing. An absent
525
- * `addr.field` targets the body, an absent `addr.card` the main card.
526
- *
527
- * The island channel keeps a table or image edit on the op path: it moves
528
- * the island alone, so the anchors elsewhere in the field survive an edit
498
+ * **Apply** a committed content edit `bundle` at `addr`, the editor splice:
499
+ * text delta first, then island ops, then line ops, then mark ops (mark
500
+ * ranges in final-text coordinates), all-or-nothing. An absent `addr.field`
501
+ * targets the body, an absent `addr.card` the main card. The island channel
502
+ * moves an island alone, so anchors elsewhere in the field survive an edit
529
503
  * `overwrite` would clear.
530
504
  *
531
505
  * Throws on an out-of-range card, a field that is not richtext, a malformed
532
- * bundle, or an op that applies out of bounds (the value is unchanged on a
533
- * failed apply).
506
+ * bundle, or an op that applies out of bounds; the value is unchanged on a
507
+ * failed apply.
534
508
  */
535
509
  applyChange(addr: Addr | string, bundle: ChangeBundle): void;
536
510
  /**
537
- * Authoring-ergonomics header introducing a blueprint to an LLM/MCP
538
- * consumer for the given `quillName`. Re-exposes core's canonical text for
539
- * JS consumers; any surface that draws from the same core source stays
540
- * uniform.
511
+ * Authoring-ergonomics header introducing a blueprint to an LLM/MCP consumer
512
+ * for the given `quillName`, re-exposed from core.
541
513
  */
542
514
  static blueprintInstruction(quill_name: string): string;
543
515
  /**
544
- * The **body** markdown projection (the main body, or a composable card's
545
- * body (`{ card }`)) the on-demand, lossy export (content-only marks do not
546
- * survive markdown). A body's type is a format fact, not a schema fact, so
547
- * this read stays quill-free; a body is never absent.
516
+ * The **body** markdown projection: an on-demand, lossy export (content-only
517
+ * marks do not survive markdown). A body's type is a format fact, not a
518
+ * schema fact, so this read stays quill-free, and a body is never absent.
548
519
  *
549
- * `addr` is an optional **card address** (`{ card }`, absent = main). A
550
- * present `field` throws: a field's markdown is read through the
551
- * schema-plane `quill.reader(doc).get(field)`, which interprets by declared
552
- * type. An out-of-range `addr.card` throws.
520
+ * `addr` is an optional card address (absent = main). A present `field`
521
+ * throws: read a field's markdown through `quill.reader(doc).get(field)`,
522
+ * which interprets by declared type. An out-of-range `addr.card` throws.
553
523
  */
554
524
  bodyMarkdown(addr?: CardAddr): string;
555
525
  /**
556
- * A single composable card by index: the whole `Card`, the card-indexed
557
- * twin of the [`main`](Self::main) getter, so reading one card need not
558
- * materialize every card via [`cards`](Self::cards). An out-of-range
559
- * `index` throws `edit::index_out_of_range`, matching the card write
560
- * verbs.
526
+ * A single composable card by index, so reading one need not materialize
527
+ * every card via [`cards`](Self::cards). An out-of-range `index` throws
528
+ * `edit::index_out_of_range`.
561
529
  */
562
530
  card(index: number): Card;
531
+ /**
532
+ * The composable card's own path, `cards.<kind>[index]`: the root
533
+ * [`pathFor`](Self::path_for) extends, for anchoring the card rather than
534
+ * one of its fields. Total on the index axis; out of range renders
535
+ * `cards[index]`.
536
+ */
537
+ cardPath(index: number): string;
563
538
  clone(): Document;
564
539
  /**
565
- * Storage version this build writes via [`toJson`](Document::to_json).
566
- * Tracks the `Document` model version (not the running crate version):
567
- * the tag advances only when the wire format changes, not on every release.
540
+ * Storage version this build writes via [`toJson`](Document::to_json). The
541
+ * tag advances only when the wire format changes, not on every release.
568
542
  */
569
543
  static currentStorageVersion(): string;
570
544
  /**
571
- * Structural equality (parse-time `warnings` excluded). Use to debounce
572
- * upstream prop updates instead of re-parsing on every keystroke.
545
+ * Structural equality, excluding parse-time `warnings`.
573
546
  */
574
547
  equals(other: Document): boolean;
575
548
  /**
576
- * Render a Diagnostic as the canonical pretty-printed text (core's
577
- * `Diagnostic::fmt_pretty`). Single source of truth so a Diagnostic looks
578
- * identical no matter which consumer surfaces it.
549
+ * Render a Diagnostic as the canonical pretty-printed text, so it looks
550
+ * identical whichever consumer surfaces it.
579
551
  */
580
552
  static formatDiagnostic(diag: Diagnostic): string;
581
553
  /**
582
- * Authoring-format rules for the card-yaml markdown surface. The canonical
583
- * text is core's (`quillmark_core::document::FORMAT_RULES`), re-exposed
584
- * here for JS consumers so it matches any other surface that draws from the
585
- * same source. Read once at startup and cache; the value never changes
586
- * between calls.
554
+ * Authoring-format rules for the card-yaml markdown surface, re-exposed from
555
+ * core. Constant across calls; read once and cache.
587
556
  */
588
557
  static formatRules(): string;
589
558
  /**
590
- * Reconstruct a `Document` from a versioned storage DTO string produced
591
- * by [`toJson`](Document::to_json). Unknown `schema` tags are rejected.
592
- * The result carries no parse-time warnings (`.warnings` is always empty).
593
- *
559
+ * Reconstruct a `Document` from a versioned storage DTO string produced by
560
+ * [`toJson`](Document::to_json). The result carries no parse-time warnings.
594
561
  * Throws if `json` is not a valid storage DTO (malformed JSON, unknown
595
562
  * `schema`, missing fields, or unparseable quill reference).
596
563
  */
@@ -601,81 +568,66 @@ export class Document {
601
568
  static fromMarkdown(markdown: string): Document;
602
569
  /**
603
570
  * The whole `$ext` map at `addr` (a card address, absent `card` = main), or
604
- * `undefined` when the card carries none. The fine-grained `$ext` read:
605
- * your own state without serializing the whole card. Throws on a present
606
- * `field` (a card address takes only `card`) or an out-of-range card.
571
+ * `undefined` when the card carries none: the `$ext` read that avoids
572
+ * serializing the whole card. Throws on a present `field` or an
573
+ * out-of-range card.
607
574
  */
608
575
  getExt(addr?: CardAddr): Record<string, unknown> | undefined;
609
576
  /**
610
577
  * The value stored under `$ext[ns]` at `addr` (a card address, absent `card`
611
- * = main), or `undefined`. The namespace-scoped `$ext` read: your own slot
612
- * without a whole-card serialize, and non-destructive (unlike
613
- * `removeExtNamespace`). Throws on a present `field` or an out-of-range card.
578
+ * = main), or `undefined`. Throws on a present `field` or an out-of-range
579
+ * card.
614
580
  */
615
581
  getExtNamespace(addr: CardAddr, ns: string): unknown;
616
582
  /**
617
- * Read the **verbatim stored value** at `addr`: the raw payload value of a
618
- * field, or the **body content** when `addr.field` is absent. A bare
619
- * string is `Addr` shorthand for `{ field }`. Reads are total over the field
620
- * axis: an absent field is `undefined`; only an out-of-range `addr.card`
621
- * throws `edit::index_out_of_range`. Needs no schema, so it lives on
622
- * `Document`: the read echo of the verbatim `store*` write, distinct from
623
- * the interpreted schema-plane [`reader.get`](Self::reader_get). For the
624
- * markdown projection use [`bodyMarkdown`](Self::get_markdown) (body) or
625
- * `reader.get` (a field's declared type).
583
+ * Read the **verbatim stored value** at `addr`: a field's raw payload value,
584
+ * or the body content when `addr.field` is absent. A bare string is `Addr`
585
+ * shorthand for `{ field }`. Needs no schema: the read echo of the verbatim
586
+ * `store*` write, distinct from the interpreted
587
+ * [`reader.get`](Self::reader_get). Reads are total over the field axis — an
588
+ * absent field is `undefined` and only an out-of-range `addr.card` throws
589
+ * `edit::index_out_of_range`.
626
590
  *
627
- * **A content field at rest has one stored form per codec**: a `richtext`
628
- * field holds the canonical content object, a `plaintext` field its literal
629
- * string. A document that came through the bound door (`quill.parse` /
630
- * `quill.conform`) is at rest, so this read no longer depends on which lane
631
- * built it. A document that came through the transport door
632
- * (`Document.fromMarkdown`, a legacy stored row) may rest as authored until
633
- * it is conformed, and this read reports what is there. For the `Content`
634
- * either way, use the schema-plane `reader.getContent`, which decodes
635
- * through the codec the field's declared type names.
591
+ * A content field at rest has one stored form per codec: a `richtext` field
592
+ * holds the canonical content object, a `plaintext` field its literal
593
+ * string. A document from the bound door (`quill.parse` / `quill.conform`)
594
+ * is at rest; one from the transport door may rest as authored until it is
595
+ * conformed, and this read reports what is there. For the `Content` either
596
+ * way use `reader.getContent`.
636
597
  */
637
598
  getStored(addr: Addr | string): unknown;
638
599
  /**
639
- * Insert a card, the single insertion verb: `at` absent appends, a number
640
- * inserts at that index (must be in `0..=cards.length`). Accepts a
641
- * `CardInput`: a card read back (`cards` / `removeCard` / `quill.seedCard`),
642
- * a [`makeCard`](Document::make_card) result, or a bare `{ kind, body }`
643
- * (every returned `Card` is a valid `CardInput`). Throws if `card.kind` is
644
- * not a valid kind name, or if `at` is out of range.
600
+ * Insert a card: `at` absent appends, a number inserts at that index (in
601
+ * `0..=cards.length`). Accepts any `CardInput`, including a card read back
602
+ * out of a document. Throws if `card.kind` is not a valid kind name, or if
603
+ * `at` is out of range.
645
604
  */
646
605
  insertCard(card: CardInput, at?: number): void;
647
606
  /**
648
607
  * Whether the field at `addr` is marked `!must_fill`. A bare string is `Addr`
649
- * shorthand for `{ field }`. `false` for an absent field (truthful: it isn't
650
- * marked) and for a body address (a body is never a fill). Only an
651
- * out-of-range `addr.card` throws.
608
+ * shorthand for `{ field }`. `false` for an absent field and for a body
609
+ * address; only an out-of-range `addr.card` throws.
652
610
  */
653
611
  isFill(addr: Addr | string): boolean;
654
612
  /**
655
- * Replace this document's contents **in place** from a versioned storage
656
- * DTO string: the mutating twin of the static
657
- * [`fromJson`](Document::from_json) constructor. Parse-time `warnings` are
658
- * cleared. Throws (leaving the document unchanged) on an invalid DTO.
613
+ * Replace this document's contents **in place** from a versioned storage DTO
614
+ * string: the mutating twin of [`fromJson`](Document::from_json). Parse-time
615
+ * `warnings` are cleared. Throws on an invalid DTO, leaving the document
616
+ * unchanged.
659
617
  *
660
618
  * The cross-WASM-memory `Document` bridge: mutate a document on a
661
- * backend-memory clone, then write the mutated state back into the caller's
662
- * canonical document with this, the one way to update a live handle across
663
- * the linear-memory seam without the caller re-binding its variable.
619
+ * backend-memory clone, then write the state back into the caller's
620
+ * canonical document, without the caller re-binding its variable.
664
621
  */
665
622
  loadJson(json: string): void;
666
623
  /**
667
624
  * Build a fresh `Card` from a kind and a flat field map: the ergonomic
668
- * constructor for `insertCard`. `fields` is an optional
669
- * `Record<string, unknown>` (each entry becomes a card field, in
670
- * insertion order); `body` defaults to `""`.
625
+ * constructor for `insertCard`, which also takes any `Card` object
626
+ * directly. Each `fields` entry becomes a card field in insertion order;
627
+ * `body` defaults to `""`.
671
628
  *
672
- * Sugar, not a required step: `insertCard` takes any `Card` object, and
673
- * `removeCard` returns one, so a card round-trips without passing through
674
- * here.
675
- *
676
- * Checks only what a detached card can decide alone: field-name grammar
677
- * and value depth. Kind validity is positional (`main` is right for the
678
- * root, reserved for a composable card) so `insertCard` is its gate, and
629
+ * Checks only what a detached card can decide alone: field-name grammar and
630
+ * value depth. Kind validity is positional, so `insertCard` is its gate and
679
631
  * any kind string is accepted here.
680
632
  */
681
633
  static makeCard(kind: string, fields?: Record<string, unknown>, body?: string): Card;
@@ -684,43 +636,51 @@ export class Document {
684
636
  */
685
637
  moveCard(from: number, to: number): void;
686
638
  /**
687
- * `new Document(quillRef)`, a blank document: a main card carrying only
688
- * `$quill`, an empty body, and no composable cards. The programmatic
689
- * blank canvas: absent fields resolve at render time (`default`, else
690
- * type-empty zero), so nothing the caller did not set reaches the
691
- * output. For an example-filled starter use `Quill.seedDocument()`.
692
- * Throws on an invalid quill reference. Mirrors Python `Document(quill_ref)`.
639
+ * A blank document: a main card carrying only `$quill`, an empty body, and
640
+ * no composable cards. Absent fields resolve at render time (`default`, else
641
+ * the field's blank), so nothing the caller did not set reaches the output.
642
+ * For an example-filled starter use `Quill.seedDocument()`. Throws on an
643
+ * invalid quill reference.
693
644
  */
694
645
  constructor(quill_ref: string);
695
646
  /**
696
- * **Overwrite** the content value at `addr`: **value semantics**, content
697
- * only. Stores exactly `rt` (a canonical `Content` content object); the
698
- * identity anchors of any previous value are gone. The bottom rung of the
699
- * content lane's ladder by anchor fate: `overwrite` destroys,
700
- * [`revise`](Document::revise) rebases,
701
- * [`applyChange`](Document::apply_change) preserves. An absent `addr.field`
702
- * targets the body, an absent `addr.card` the main card. Cold-importing
703
- * markdown is spelled `overwrite(addr, importMarkdown(md))` at the call
704
- * site, where the anchor loss is visible.
647
+ * **Overwrite** the content value at `addr` with exactly `rt`: value
648
+ * semantics, so the identity anchors of any previous value are gone. By
649
+ * anchor fate `overwrite` destroys, [`revise`](Document::revise) rebases,
650
+ * and [`applyChange`](Document::apply_change) preserves. An absent
651
+ * `addr.field` targets the body, an absent `addr.card` the main card.
705
652
  *
706
653
  * Throws on an out-of-range card, a malformed field name, or an `rt` that is
707
654
  * not a canonical content object.
708
655
  */
709
656
  overwrite(addr: Addr | string, rt: Content): void;
710
657
  /**
711
- * The canonical `$quill` reference grammar as author-facing text. Core is
712
- * the single source of truth: drive schema `describe` and validation
713
- * messages from this instead of re-stating the rule; it matches the
714
- * `hint` on `parse::invalid_quill_reference`. Cache it; the value never
715
- * changes.
658
+ * `addr`'s canonical `DocPath` string, the anchor `Diagnostic.path` carries:
659
+ * `pathFor()` is `main.body`, `pathFor("intro")` `main.intro`,
660
+ * `pathFor({card: 2})` `cards.<kind>[2].body`.
661
+ *
662
+ * The kind is the card's stored `$kind` verbatim, not `validate`'s
663
+ * declared-kind filter, since a `Document` holds a `$quill` reference and no
664
+ * schema. That is the one edge where this path and a `validate` diagnostic
665
+ * path differ for the same card.
666
+ *
667
+ * **Total on the index axis**, unlike the `Addr` reads, which throw there:
668
+ * an out-of-range `{card: 7, field: "from"}` renders `cards[7].from`, which
669
+ * parses back and resolves to nothing rather than mis-targeting. Only a
670
+ * malformed address throws.
671
+ */
672
+ pathFor(addr: Addr | string): string;
673
+ /**
674
+ * The canonical `$quill` reference grammar as author-facing text: the same
675
+ * text the `parse::invalid_quill_reference` hint carries. Drive validation
676
+ * messages from this instead of re-stating the rule.
716
677
  */
717
678
  static quillRefHint(): string;
718
679
  removeCard(index: number): Card | undefined;
719
680
  /**
720
- * Remove the `$ext` map on the card `addr` targets *entirely*, returning the
721
- * previous map or `undefined`: a blunt escape hatch that discards every
722
- * namespace at once (prefer `removeExtNamespace`). `addr` is a card address
723
- * (absent = main). Throws on a present `field` or an out-of-range card.
681
+ * Remove the `$ext` map on the card `addr` targets entirely, returning the
682
+ * previous map or `undefined`. Discards every namespace at once; prefer
683
+ * `removeExtNamespace`. Throws on a present `field` or an out-of-range card.
724
684
  */
725
685
  removeExt(addr?: CardAddr): Record<string, unknown> | undefined;
726
686
  /**
@@ -732,35 +692,31 @@ export class Document {
732
692
  removeExtNamespace(addr: CardAddr, ns: string): any;
733
693
  /**
734
694
  * Remove a field at `addr`, returning the removed value or `undefined`. A
735
- * bare string is `Addr` shorthand for `{ field }`. One `remove` verb serves
736
- * every write lane. A body address throws; throws on an out-of-range card or
737
- * a malformed name.
695
+ * bare string is `Addr` shorthand for `{ field }`. A body address throws, as
696
+ * does an out-of-range card or a malformed name.
738
697
  */
739
698
  removeField(addr: Addr | string): any;
740
699
  /**
741
- * Remove `cardKind` from the main card's `$seed` map, returning its
742
- * overlay or `undefined`; drops `$seed` entirely once empty. Sibling kinds
743
- * survive. `$seed` is main-only, so this takes no address.
700
+ * Remove `cardKind` from the main card's `$seed` map, returning its overlay
701
+ * or `undefined`; drops `$seed` entirely once empty. Sibling kinds survive.
744
702
  */
745
703
  removeSeedOverlay(card_kind: string): any;
746
704
  /**
747
- * **Revise** the richtext value at `addr` from a markdown string: **edit
748
- * semantics**, the default write path, returning the text `Delta`. Imports
749
- * the markdown, diffs it against the current value, rebases surviving
750
- * identity anchors, and returns the change an editor bridge maps its own
751
- * positions through (`mapPos`). An absent `addr.field` targets the body, an
752
- * absent `addr.card` the main card; an absent field cold-imports from empty.
705
+ * **Revise** the richtext value at `addr` from a markdown string: the
706
+ * default write path. Imports the markdown, diffs it against the current
707
+ * value, rebases surviving identity anchors, and returns the text `Delta` an
708
+ * editor bridge maps its own positions through (`mapPos`). An absent
709
+ * `addr.field` targets the body, an absent `addr.card` the main card; an
710
+ * absent field cold-imports from empty.
753
711
  *
754
712
  * Throws on an out-of-range card, a malformed field name, a present
755
713
  * non-content field value, or an over-nested markdown input.
756
714
  */
757
715
  revise(addr: Addr | string, markdown: string): Delta;
758
716
  /**
759
- * The main card's `$seed` overlay object for `kind` (the `$seed[kind]`
760
- * entry), or `undefined` when absent. The cheap read that feeds
761
- * `quill.seedCard(kind, overlay)` without serializing the whole main card
762
- * via [`main`](Self::main) to fish out one key, and it keeps `seedCard`
763
- * pure: the quill still never reads the document.
717
+ * The main card's `$seed[kind]` overlay object, or `undefined`. Feeds
718
+ * `quill.seedCard(kind, overlay)` without serializing the whole main card,
719
+ * and keeps `seedCard` pure: the quill never reads the document.
764
720
  */
765
721
  seedOverlay(kind: string): Record<string, unknown> | undefined;
766
722
  /**
@@ -774,78 +730,60 @@ export class Document {
774
730
  */
775
731
  setQuillRef(ref_str: string): void;
776
732
  /**
777
- * Read the storage version tag from a raw storage DTO string without a
778
- * full parse, or `undefined`. Returns unknown future versions as-is:
779
- * useful to distinguish "build too old" from "payload corrupt" when
780
- * `fromJson` throws.
781
- *
782
- * The storage version, not a field schema ([`schema`](Quill::schema) is the
783
- * quill's field declarations). The JSON key is spelled `"schema"`: it is
784
- * the DTO's serde tag, and retagging it would break the version dispatch
785
- * it drives.
733
+ * Read the storage version tag from a raw storage DTO string without a full
734
+ * parse, or `undefined`. Unknown future versions come back as-is, which
735
+ * distinguishes "build too old" from "payload corrupt" when `fromJson`
736
+ * throws. This is the storage version, not a field schema, though the JSON
737
+ * key is spelled `"schema"`: that is the DTO's serde tag.
786
738
  */
787
739
  static storageVersionOf(json: string): string | undefined;
788
740
  /**
789
- * Replace the opaque `$ext` map on the card `addr` targets (a card address,
790
- * absent `card` = main). `value` must be a plain object. `$ext` carries
791
- * out-of-band consumer state and never reaches the rendered output; pass
792
- * `{}` for an explicit empty `$ext`. Quill-free and verbatim: an opaque
793
- * `store` verb. Throws on a present `field` or an out-of-range card.
741
+ * Replace the opaque `$ext` map on the card `addr` targets (absent `card` =
742
+ * main). `value` must be a plain object. `$ext` carries out-of-band consumer
743
+ * state and never reaches the rendered output. Throws on a present `field`
744
+ * or an out-of-range card.
794
745
  */
795
746
  storeExt(addr: CardAddr, value: any): void;
796
747
  /**
797
748
  * Merge `value` into `$ext[ns]` on the card `addr` targets, preserving
798
- * sibling namespaces: the recommended `$ext` write. `addr` is a card
799
- * address (absent = main). Quill-free and verbatim: an opaque `store` verb.
800
- * Throws on a present `field` or an out-of-range card.
749
+ * sibling namespaces: the recommended `$ext` write. Throws on a present
750
+ * `field` or an out-of-range card.
801
751
  */
802
752
  storeExtNamespace(addr: CardAddr, ns: string, value: any): void;
803
753
  /**
804
- * Store a field verbatim at `addr`: the opaque store (**store** = verbatim,
805
- * coercion deferred to render; the typed write is
806
- * [`commitField`](Document::commit_field)). A bare string is `Addr`
807
- * shorthand for `{ field }`, so `doc.storeField("qty", 3)` reads as written;
808
- * `{ card: 2, field: "qty" }` targets a composable card. Clears any
809
- * `!must_fill` marker. A body address (no `field`) throws: a body is never
810
- * opaque; write it with `revise` / `overwrite` / `writer.reviseBody`. Throws on
811
- * an out-of-range card or a malformed name.
754
+ * Store a field verbatim at `addr`, deferring coercion to render; the typed
755
+ * write is [`commitField`](Document::commit_field). A bare string is `Addr`
756
+ * shorthand for `{ field }`; `{ card: 2, field: "qty" }` targets a
757
+ * composable card. Clears any `!must_fill` marker. A body address throws:
758
+ * write a body with `revise` / `overwrite`. Throws on an out-of-range card
759
+ * or a malformed name.
812
760
  */
813
761
  storeField(addr: Addr | string, value: any): void;
814
762
  /**
815
- * Store several fields verbatim and atomically on the card `addr` targets:
816
- * the opaque store's batch. `addr` is a **card address** (`{ card }`, absent
817
- * = main); a present `field` throws. The batch verb takes the address first
818
- * and is never shape-overloaded, because `card` is a legal field name:
819
- * `storeFields({}, fields)` is the main card, `storeFields({ card: 2 },
820
- * fields)` a composable one, never ambiguous with "set field `card`".
821
- * Nothing is applied on error; the thrown error's `diagnostics` carry one
822
- * entry per offending field. Throws on an out-of-range card.
763
+ * Store several fields verbatim and atomically on the card `addr` targets.
764
+ * `addr` is a **card address** (`{ card }`, absent = main) and comes first
765
+ * because `card` is itself a legal field name; a present `field` throws.
766
+ * Nothing is applied on error, and the thrown error's `diagnostics` carry
767
+ * one entry per offending field. Throws on an out-of-range card.
823
768
  */
824
769
  storeFields(addr: CardAddr, fields: Record<string, unknown>): void;
825
770
  /**
826
- * Store a field verbatim at `addr` and mark it `!must_fill`: the opaque
827
- * store's fill variant, card-capable (a bare string or `{ field }` for main,
828
- * `{ card, field }` for a composable card). A body address throws. Same
829
- * validation as [`storeField`](Document::store_field).
771
+ * Store a field verbatim at `addr` and mark it `!must_fill`. A body address
772
+ * throws; same validation as [`storeField`](Document::store_field).
830
773
  */
831
774
  storeFill(addr: Addr | string, value: any): void;
832
775
  /**
833
776
  * Merge a card-kind's seed `overlay` into the **main** card's `$seed` map
834
- * under `cardKind`, preserving sibling kinds: `$seed` lives on the main
835
- * card by model, so this takes no address. Sets the starting values new
836
- * cards of that kind spawn with. Quill-free and verbatim: an opaque `store`
837
- * verb. Throws if `overlay` cannot be serialized or nests too deep.
777
+ * under `cardKind`, preserving sibling kinds; `$seed` is main-only, so this
778
+ * takes no address. Sets the starting values new cards of that kind spawn
779
+ * with. Throws if `overlay` cannot be serialized or nests too deep.
838
780
  */
839
781
  storeSeedOverlay(card_kind: string, overlay: any): void;
840
782
  /**
841
- * Serialize this document to a versioned storage DTO string.
842
- *
843
- * Prefer this over `toMarkdown` for persistence across restarts or crate
844
- * upgrades: the wire format is frozen per `schema` version. Parse-time
845
- * `warnings` are excluded from the DTO.
846
- *
847
- * Output is **byte-deterministic** within a `schema` version: equal
848
- * documents produce byte-equal output, safe for content-hash use cases.
783
+ * Serialize this document to a versioned storage DTO string. Prefer it over
784
+ * `toMarkdown` for persistence: the wire format is frozen per `schema`
785
+ * version and the output is byte-deterministic within one, so equal
786
+ * documents hash equal. Parse-time `warnings` are excluded.
849
787
  */
850
788
  toJson(): string;
851
789
  /**
@@ -854,29 +792,26 @@ export class Document {
854
792
  */
855
793
  toMarkdown(): string;
856
794
  /**
857
- * Like [`fromJson`](Document::from_json) but returns `undefined` instead
858
- * of throwing when `json` is not a valid storage DTO: use to
859
- * discriminate format without exceptions as control flow.
860
- * `undefined` means "not a storage DTO"; `fromMarkdown` still throws on
861
- * genuinely malformed markdown.
795
+ * Like [`fromJson`](Document::from_json) but returns `undefined` instead of
796
+ * throwing when `json` is not a valid storage DTO, to discriminate format
797
+ * without exceptions as control flow.
862
798
  */
863
799
  static tryFromJson(json: string): Document | undefined;
864
800
  /**
865
- * Number of composable cards (excludes the main card). O(1).
801
+ * Number of composable cards, excluding the main card.
866
802
  */
867
803
  readonly cardCount: number;
868
804
  readonly cards: Card[];
869
805
  /**
870
- * The document's main (entry) card. Allocates and serializes on each
871
- * call: cache locally if read in a hot loop.
806
+ * The document's main (entry) card. Allocates and serializes on each call.
872
807
  */
873
808
  readonly main: Card;
874
809
  readonly quillRef: string;
875
810
  /**
876
811
  * The non-fatal diagnostics of the load that produced this document: parse
877
- * warnings, plus the `conform::*` warnings when it came through
878
- * `quill.parse`. Session state, not document value: `equals` and the
879
- * storage DTO exclude it, and `fromJson` / `loadJson` clear it.
812
+ * warnings, plus `conform::*` warnings when it came through `quill.parse`.
813
+ * Session state, not document value: `equals` and the storage DTO exclude
814
+ * it, and `fromJson` / `loadJson` clear it.
880
815
  */
881
816
  readonly warnings: Diagnostic[];
882
817
  }
@@ -887,196 +822,152 @@ export class Quill {
887
822
  [Symbol.dispose](): void;
888
823
  /**
889
824
  * Land `doc`'s declared content fields at their canonical rest **in
890
- * place**, returning the `conform::*` diagnostics for the values that would
891
- * not commit (an empty array when everything rested).
892
- *
893
- * The read-repair verb: a document that arrived through the transport door
894
- * (`fromMarkdown`, `fromJson`, a stored row) converges here, and is then
895
- * eligible for rewrite under its current schema tag. Idempotent, and a
896
- * no-op on an already-canonical document: an equal value is not rewritten,
897
- * so YAML comments and stored bytes survive.
825
+ * place**, returning the `conform::*` diagnostics for values that would not
826
+ * commit. The read-repair verb for a document that arrived through the
827
+ * transport door (`fromMarkdown`, `fromJson`, a stored row).
898
828
  *
899
- * A `!must_fill` marker anywhere in a field's value skips that field (the
900
- * marker is the state), and a value the strict write refuses stays as
901
- * authored with a diagnostic. Throws when `doc` declares a different
902
- * `$quill`, before any mutation.
829
+ * Idempotent: an equal value is not rewritten, so YAML comments and stored
830
+ * bytes survive. A `!must_fill` marker anywhere in a field's value skips
831
+ * that field, and a value the strict write refuses stays as authored with a
832
+ * diagnostic. Throws when `doc` declares a different `$quill`, before any
833
+ * mutation.
903
834
  */
904
835
  conform(doc: Document): Diagnostic[];
905
836
  /**
906
- * Build a quill from a file tree. Pure: no backend, no engine; the
907
- * declared backend is resolved later, at render time.
908
- *
909
- * Accepts either a `Map<string, Uint8Array>` or a plain object
910
- * (`Record<string, Uint8Array>`). Plain objects are walked via
911
- * `Object.entries` at the boundary; the Rust side sees a single
912
- * canonical shape.
837
+ * Build a quill from a file tree. Pure: the declared backend is resolved
838
+ * later, at render time. Accepts a `Map<string, Uint8Array>` or a plain
839
+ * object.
913
840
  */
914
841
  static fromTree(tree: Map<string, Uint8Array>): Quill;
915
842
  /**
916
- * Parse `markdown` and conform it against this quill: the **primary
917
- * ingestion path**, and the bound twin of the schema-free
918
- * `Document.fromMarkdown`. The returned document rests at its canonical
919
- * form (a `richtext` field as a content object, a `plaintext` field as its
920
- * literal string), so `getStored` answers "content object or string?" by the
921
- * field's declared codec rather than by how the document was built.
843
+ * Parse `markdown` and conform it against this quill: the primary ingestion
844
+ * path, and the bound twin of the schema-free `Document.fromMarkdown`. The
845
+ * returned document rests at its canonical form (a `richtext` field as a
846
+ * content object, a `plaintext` field as its literal string), so `getStored`
847
+ * answers by the field's declared codec, not by how the document was built.
922
848
  *
923
849
  * Parse warnings and the `conform::*` diagnostics both land on
924
850
  * `doc.warnings`. Throws on a parse failure, or when `markdown` declares a
925
- * `$quill` this quill does not answer to: nothing conforms under the wrong
926
- * schema. To open a document whose `$quill` is stale, use the transport
927
- * door (`Document.fromMarkdown`, `setQuillRef`, then `quill.conform`).
851
+ * `$quill` this quill does not answer to. To open a document whose `$quill`
852
+ * is stale, use `Document.fromMarkdown`, `setQuillRef`, then `quill.conform`.
928
853
  */
929
854
  parse(markdown: string): Document;
930
855
  /**
931
- * The resolved-value view of `doc` against this quill's schema: for every
932
- * declared field the value the render projection would use and the
933
- * `FieldSource` rung it came from (`"authored" | "default" | "zero"`), in
934
- * one call. The card body is a `body` sibling on its card (row `name`
935
- * `"body"`), never a row in `fields`: `null` when the kind enables no body.
936
- *
937
- * Value and provenance only: completeness and errors stay `validate`'s
938
- * (a consumer merges it with its own diagnostic producers regardless), and
939
- * schema guidance reads from `Quill.schema`.
856
+ * The resolved-value view of `doc`: for every declared field, the value the
857
+ * render projection would use and the `FieldSource` rung it came from
858
+ * (`"authored" | "default" | "blank"`). The card body is a `body` sibling on
859
+ * its card, never a row in `fields`, and `null` when the kind enables no
860
+ * body. Value and provenance only; completeness stays `validate`'s.
940
861
  */
941
862
  resolve(doc: Document): Resolved;
942
863
  /**
943
864
  * Seed a starter composable `Card` of the given kind (carries `$kind`),
944
- * layering an optional per-kind seed `overlay` over the schema-example
945
- * base (`overlay › example › absent`). Returns `undefined` if `cardKind`
946
- * is not declared in this quill's schema, else a `Card` that feeds
947
- * straight into `Document.insertCard`.
865
+ * layering an optional per-kind seed `overlay` over the schema-example base
866
+ * (`overlay › example › absent`). `undefined` when `cardKind` is not
867
+ * declared in this quill's schema.
948
868
  *
949
869
  * Pass `document.seedOverlay(cardKind)` as `overlay` so a card added to a
950
870
  * template-derived document inherits its curated starting values; omit it
951
- * (or pass `undefined` / `null`) for the bare schema seed. `overlay` is a
952
- * plain object: this reads the document, it does not mutate it.
871
+ * for the bare schema seed.
953
872
  */
954
873
  seedCard(card_kind: string, overlay: Record<string, unknown> | undefined): Card | undefined;
955
874
  /**
956
- * Seed a starter `Document` from the schema, the main card plus one
957
- * instance of each composable card kind, each committing its fields'
958
- * `example:` values and leaving every other field absent (interpolated at
959
- * render: `default:`, else type-empty zero). Illustration-first: a field
960
- * with both an `example` and a `default` renders its example. See
961
- * `prose/canon/SCHEMAS.md` § "Document seeding".
875
+ * Seed a starter `Document` from the schema: the main card plus one instance
876
+ * of each composable card kind, each committing its fields' `example:`
877
+ * values and leaving every other field absent (interpolated at render as
878
+ * `default:`, else the field's blank). A field with both renders its example.
962
879
  */
963
880
  seedDocument(): Document;
964
881
  /**
965
882
  * Seed a starter main `Card` (carries `$quill`) from the schema: the
966
- * `$kind: main` card of [`seedDocument`](Self::seed_document) in
967
- * isolation, committing each field's `example:` value. Returns the same
968
- * `Card` shape as the `Document.main` getter.
883
+ * `$kind: main` card of [`seedDocument`](Self::seed_document) alone.
969
884
  */
970
885
  seedMain(): Card;
971
886
  /**
972
- * Flatten this quill back into its canonical file tree: the inverse of
973
- * [`fromTree`](Self::from_tree). Round-trips: `Quill.fromTree(q.toTree())`
974
- * reproduces an equivalent quill.
887
+ * Flatten this quill back into its canonical file tree, the inverse of
888
+ * [`fromTree`](Self::from_tree). Keys are `"/"`-joined relative paths.
975
889
  *
976
890
  * This is how a quill crosses a WASM linear-memory boundary as data: a
977
- * `Quill` built in one build (e.g. the Typst-less `@quillmark/wasm/core`)
978
- * cannot be passed to an engine in another (separate linear memories), so
891
+ * `Quill` built in one build cannot be passed to an engine in another, so
979
892
  * `@quillmark/wasm/runtime` re-feeds this tree to the backend build's
980
- * `Quill.fromTree` on demand. Keys are `"/"`-joined relative paths,
981
- * matching what `fromTree` accepts.
893
+ * `Quill.fromTree` on demand.
982
894
  */
983
895
  toTree(): Map<string, Uint8Array>;
984
896
  /**
985
897
  * Validate `doc` against this quill's schema, returning every diagnostic
986
- * (an empty array when the document is valid).
987
- *
988
- * Forwards the canonical `validation::*` diagnostics (same `code`,
989
- * `path`, and `hint` the engine emits) including the non-fatal
990
- * `validation::must_fill` warning for each `!must_fill` marker left in
991
- * the document. Field values, defaults, and order are not part of this
992
- * surface: read them from the `Document` payload and `Quill.schema`
993
- * (schema key order is display order).
898
+ * (empty when the document is valid). Forwards the canonical
899
+ * `validation::*` diagnostics the engine emits, including the non-fatal
900
+ * `validation::must_fill` warning per `!must_fill` marker left behind.
994
901
  */
995
902
  validate(doc: Document): Diagnostic[];
996
903
  /**
997
- * The *declared* backend identifier (`config.backend`, e.g. `"typst"`).
998
- * Intent, not a resolved capability: capability (`supportedFormats` /
999
- * `supportsCanvas`) is read from the engine.
904
+ * The *declared* backend identifier (e.g. `"typst"`): intent, not a
905
+ * resolved capability. Capability is read from the engine.
1000
906
  */
1001
907
  readonly backendId: string;
1002
908
  readonly blueprint: string;
1003
909
  /**
1004
910
  * Identity snapshot of the `quill:` section of `Quill.yaml` plus any extra
1005
- * `quill:` keys. Pure config: the backend's output formats are a
1006
- * resolved-backend capability read from the engine
1007
- * (`Quillmark.supportedFormats`), not part of this snapshot.
911
+ * `quill:` keys. Pure config: output formats are a resolved-backend
912
+ * capability read from `Quillmark.supportedFormats`, not part of this.
1008
913
  */
1009
914
  readonly metadata: QuillMetadata;
1010
915
  /**
1011
- * Document schema for the quill: the user-fillable fields plus their
1012
- * `ui` hints (title / group / compact / multiline). The single
1013
- * field-metadata surface: drives form editors and LLM/MCP consumers
1014
- * alike. Key order in `fields`/`properties` is declaration order: the
1015
- * ordering contract. Returns the `QuillSchema` shape.
916
+ * Document schema for the quill: the user-fillable fields plus their `ui`
917
+ * hints. Key order in `fields`/`properties` is declaration order, the
918
+ * ordering contract.
1016
919
  */
1017
920
  readonly schema: QuillSchema;
1018
921
  }
1019
922
 
1020
923
  /**
1021
- * Export a canonical `Content` content to its markdown projection: the pure
1022
- * on-demand codec behind `exportMarkdown(card.body)`. Throws if `rt` is not a
924
+ * Export canonical `Content` to its markdown projection. Throws if `rt` is not
1023
925
  * canonical content.
1024
926
  */
1025
927
  export function exportMarkdown(rt: Content): string;
1026
928
 
1027
929
  /**
1028
930
  * Serialize structured [`DocPathSeg`] segments back to the canonical path
1029
- * string: the inverse of `parseDocPath`, for a consumer that builds a path
1030
- * rather than reads one. Throws on a segment array the deserializer rejects,
1031
- * and on an empty segment array (symmetric with `parseDocPath("")`, which
1032
- * throws "empty path").
931
+ * string: the inverse of `parseDocPath`. Throws on a segment array the
932
+ * deserializer rejects, and on an empty one.
1033
933
  */
1034
934
  export function formatDocPath(segs: DocPathSeg[]): string;
1035
935
 
1036
936
  /**
1037
- * Import a markdown string to a canonical `Content` content: the pure,
1038
- * document-free codec. Pair with `overwrite(addr, importMarkdown(md))` to spell
1039
- * the cold (anchor-losing) write at the call site; prefer `revise` for edit
1040
- * semantics. Throws on an over-nested input.
937
+ * Import a markdown string to canonical `Content`: the pure, document-free
938
+ * codec. `overwrite(addr, importMarkdown(md))` spells the cold, anchor-losing
939
+ * write; prefer `revise` for edit semantics. Throws on an over-nested input.
1041
940
  */
1042
941
  export function importMarkdown(markdown: string): Content;
1043
942
 
1044
943
  /**
1045
944
  * Map a base content position (a USV index into `Content.text`, not a UTF-16
1046
- * offset) through a `delta` to its new USV position: the pure position-mapping
1047
- * codec an editor bridge composes to hold a caret stable across a `revise`.
1048
- * `assoc` decides the side of a same-position insertion (`"after"` moves past
1049
- * it). Throws on a malformed `delta`.
945
+ * offset) through a `delta` to its new position, holding a caret stable across
946
+ * a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
947
+ * moves past it). Throws on a malformed `delta`.
1050
948
  */
1051
949
  export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
1052
950
 
1053
951
  /**
1054
- * Parse a canonical document-model `Diagnostic.path`
1055
- * (`cards.<kind>[<i>].<field>`, `main.body`, `recipients[0].name`) into its
1056
- * structured [`DocPathSeg`] segments: the exported inverse of the engine's
1057
- * one path serializer, so a consumer routes on segments instead of regexing
1058
- * the string. Throws on a malformed path.
952
+ * Parse a canonical document-model `Diagnostic.path` (`cards.<kind>[<i>].<field>`,
953
+ * `main.body`, `recipients[0].name`) into structured [`DocPathSeg`] segments, so
954
+ * a consumer routes on segments instead of regexing the string. Throws on a
955
+ * malformed path.
1059
956
  */
1060
957
  export function parseDocPath(path: string): DocPathSeg[];
1061
958
 
1062
959
  /**
1063
- * Rebase `markdown` onto a `base` content, the pure, document-free twin of
1064
- * `revise`: cold-import + `diff_import`, returning the new `content` and the
1065
- * text `delta` (its offsets USV indices into `Content.text`, surviving anchors
1066
- * rebased). Use it to compute a revise without a document in hand; `revise(addr,
1067
- * md)` fuses this with the store for atomicity. Throws on an over-nested
1068
- * markdown input or a non-content `base`.
960
+ * Rebase `markdown` onto a `base` content: the document-free twin of `revise`,
961
+ * returning the new `content` and the text `delta` (offsets are USV indices into
962
+ * `Content.text`, surviving anchors rebased). Throws on an over-nested markdown
963
+ * input or a non-content `base`.
1069
964
  */
1070
965
  export function rebase(base: Content, markdown: string): { content: Content; delta: Delta };
1071
966
 
1072
967
  /**
1073
- * Runs at instantiation (the wasm-bindgen start section): installs the panic
1074
- * hook, so a Rust panic reaches the console as a stack trace rather than
1075
- * `unreachable`.
1076
- *
1077
- * Not the package's `init`. That name belongs to the hand-written runtime,
1078
- * which owns instantiation itself (`runtime/runtime.js`); this runs as part of
1079
- * the instantiation it awaits.
968
+ * Runs at instantiation, so a Rust panic reaches the console as a stack trace
969
+ * rather than `unreachable`. Not the package's `init` that name belongs to
970
+ * the hand-written runtime, which owns instantiation itself.
1080
971
  */
1081
972
  export function start(): void;
1082
973
 
@@ -1091,12 +982,14 @@ export interface InitOutput {
1091
982
  readonly document__commitFields: (a: number, b: number, c: number, d: number, e: number) => void;
1092
983
  readonly document__readerGet: (a: number, b: number, c: number, d: number) => void;
1093
984
  readonly document__readerGetContent: (a: number, b: number, c: number, d: number) => void;
985
+ readonly document__readerGetContentAt: (a: number, b: number, c: number, d: number, e: number) => void;
1094
986
  readonly document__reviseField: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1095
987
  readonly document_applyChange: (a: number, b: number, c: number, d: number) => void;
1096
988
  readonly document_blueprintInstruction: (a: number, b: number, c: number) => void;
1097
989
  readonly document_bodyMarkdown: (a: number, b: number, c: number) => void;
1098
990
  readonly document_card: (a: number, b: number, c: number) => void;
1099
991
  readonly document_cardCount: (a: number) => number;
992
+ readonly document_cardPath: (a: number, b: number, c: number) => void;
1100
993
  readonly document_cards: (a: number, b: number) => void;
1101
994
  readonly document_clone: (a: number) => number;
1102
995
  readonly document_currentStorageVersion: (a: number) => void;
@@ -1116,6 +1009,7 @@ export interface InitOutput {
1116
1009
  readonly document_moveCard: (a: number, b: number, c: number, d: number) => void;
1117
1010
  readonly document_new: (a: number, b: number, c: number) => void;
1118
1011
  readonly document_overwrite: (a: number, b: number, c: number, d: number) => void;
1012
+ readonly document_pathFor: (a: number, b: number, c: number) => void;
1119
1013
  readonly document_quillRef: (a: number, b: number) => void;
1120
1014
  readonly document_quillRefHint: (a: number) => void;
1121
1015
  readonly document_removeCard: (a: number, b: number, c: number) => void;