@quillmark/wasm 0.92.1 → 0.95.1

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
@@ -17,17 +17,18 @@ export type PayloadItem =
17
17
  /**
18
18
  * Paths to `!must_fill` markers nested *inside* `value` (the `value`
19
19
  * projection itself is fill-free). Absent when the field has no nested
20
- * placeholders. Preserved across `pushCard` / `makeCard`.
20
+ * placeholders. Preserved across `insertCard` / `makeCard`.
21
21
  */
22
22
  nestedFills?: PathStep[][];
23
23
  }
24
24
  | { type: "comment"; text: string; inline?: boolean };
25
25
 
26
26
  /**
27
- * A single card block. The one shape exchanged in both directions: returned by
28
- * `Document.main` / `Document.cards` / `Document.removeCard` / `Quill.seedCard`,
29
- * and accepted by `Document.pushCard` / `Document.insertCard`. Build a fresh
30
- * one with `Document.makeCard`.
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.
31
32
  *
32
33
  * `$` system entries are hoisted to named fields: `kind` (the `$kind`, empty
33
34
  * string when none), optional `quill` (the `$quill` `name@version`, main card
@@ -42,15 +43,172 @@ export interface Card {
42
43
  ext?: Record<string, unknown>;
43
44
  seed?: Record<string, unknown>;
44
45
  payloadItems: PayloadItem[];
45
- body: string;
46
+ /**
47
+ * The card body as canonical `Content` — the source-of-truth content model.
48
+ * Always this content shape on read, never a markdown string. For the markdown
49
+ * projection call the codec `exportMarkdown(card.body)`. Write a body back
50
+ * with `doc.install(addr, rt)` / `doc.revise(addr, md)`, or via `CardInput.body`.
51
+ */
52
+ body: Content;
53
+ }
54
+
55
+ /**
56
+ * A card written *into* a document — the input twin of `Card`, accepted by
57
+ * `Document.insertCard`. Like `Card` but `body` also
58
+ * takes a markdown `string` (imported to the content, so a markdown / LLM writer
59
+ * needn't build the `Content` shape), and every field but `kind` is optional —
60
+ * an absent field defaults (no payload items, an empty body). Write one inline
61
+ * (`{ kind, body }`) or build it with `Document.makeCard`.
62
+ */
63
+ export interface CardInput {
64
+ kind: string;
65
+ quill?: string;
66
+ id?: string;
67
+ ext?: Record<string, unknown>;
68
+ seed?: Record<string, unknown>;
69
+ payloadItems?: PayloadItem[];
70
+ body?: Content | string;
71
+ }
72
+
73
+ /**
74
+ * Canonical richtext content — the content model for a card body (and richtext
75
+ * fields). One text sequence over a single coordinate space (Unicode scalar
76
+ * values): `text` plus line attributes, anchored `marks`, and embedded
77
+ * `islands`. Every edit is a splice; markdown is a projection, not the model.
78
+ * Mirrors `quillmark_content::serial`'s canonical JSON encoding.
79
+ */
80
+ export interface Content {
81
+ text: string;
82
+ lines: ContentLine[];
83
+ marks: ContentMark[];
84
+ islands: ContentIsland[];
85
+ }
86
+
87
+ /** One `\n`-separated segment of `Content.text`, in order. */
88
+ export type ContentLine = {
89
+ containers: ContentContainer[];
90
+ /** A within-block hard line break rather than a new block. Omitted (false) in the common case. */
91
+ continues?: boolean;
92
+ } & (
93
+ | { kind: "para" }
94
+ | { kind: "heading"; level: number }
95
+ | { kind: "code"; lang?: string }
96
+ | { kind: "island" }
97
+ | { kind: "rule" }
98
+ );
99
+
100
+ /** An ancestor block a line nests inside, outermost first. */
101
+ export type ContentContainer =
102
+ | { container: "list_item"; ordered: boolean; start: number; ordinal: number }
103
+ | { container: "quote" };
104
+
105
+ /** A mark over char range `[start, end)` into `Content.text`. */
106
+ export type ContentMark = { start: number; end: number } & (
107
+ | { type: "strong" | "emph" | "underline" | "strike" | "code" }
108
+ | { type: "link"; url: string }
109
+ | { type: "anchor"; id: string }
110
+ | { type: string; attrs: unknown }
111
+ );
112
+
113
+ /** A structured object (table, figure, …) occupying one island slot in `Content.text`. */
114
+ export interface ContentIsland {
115
+ id: string;
116
+ type: string;
117
+ props: unknown;
118
+ /** How faithfully the markdown projection can carry this island. */
119
+ loss: "lossless" | "degraded" | "unrepresentable";
120
+ }
121
+
122
+ /**
123
+ * A write address — one navigation concept for the whole `Document` surface. An
124
+ * absent `field` targets the card body; an absent `card` targets the main card.
125
+ * `{}` is the main-card body; `{ card: 2 }` the body of the composable card at
126
+ * index 2; `{ field: "intro" }` the main card's `intro` field; `{ card: 2,
127
+ * field: "intro" }` a card field.
128
+ *
129
+ * On the `Addr`-taking verbs a **bare string** is shorthand for `{ field: name }`
130
+ * — `doc.storeField("qty", 3)`, `doc.revise("intro", md)` — the one coercion
131
+ * rule. A bare number is *not* an addr (`{ card: 2 }` is the self-documenting
132
+ * spelling), so no third navigation idiom re-fragments the surface.
133
+ */
134
+ export interface Addr {
135
+ card?: number;
136
+ field?: string;
46
137
  }
47
138
 
139
+ /**
140
+ * A card-only address — the axis the card-scoped verbs (`storeFields`,
141
+ * `storeExt`, `getExt`, `commitFields`, …) take. An absent `card` targets the
142
+ * main card. A present `field` throws: a card address takes only `card`, and a
143
+ * would-be nested write is a bug the error names rather than silently ignores.
144
+ */
145
+ export interface CardAddr {
146
+ card?: number;
147
+ }
148
+
149
+ /**
150
+ * A text-splice change set over the USV content (CodeMirror `ChangeSet`
151
+ * semantics) — plain, structured-clone-able data. Returned by `revise` and by
152
+ * the `rebase` codec; map a stored position through it with `mapPos`.
153
+ */
154
+ export interface Delta {
155
+ ops: ({ retain: number } | { insert: string } | { delete: number })[];
156
+ }
48
157
 
158
+ /** Which side of a same-position insertion `mapPos` lands a point on. */
159
+ export type Assoc = "before" | "after";
160
+
161
+ /**
162
+ * A mark edit in final-text coordinates (post-delta, post-line-op). `add` /
163
+ * `remove` carry the `ContentMark` vocabulary (`{ type, … }`); `removeAnchor`
164
+ * drops one identity anchor by id.
165
+ */
166
+ export type MarkOp =
167
+ | ({ op: "add" | "remove"; start: number; end: number } & (
168
+ | { type: "strong" | "emph" | "underline" | "strike" | "code" }
169
+ | { type: "link"; url: string }
170
+ | { type: "anchor"; id: string }
171
+ | { type: string; attrs: unknown }
172
+ ))
173
+ | { op: "removeAnchor"; id: string };
174
+
175
+ /**
176
+ * A line/block edit. `split`/`join` splice `\n`; `setKind`/`setContainers`/
177
+ * `setContinues` touch metadata. `setContinues` sets/clears a line's within-block
178
+ * hard-break flag (`ContentLine.continues`) — the op-grained way to lower a
179
+ * Shift+Enter hard break or a new code-fence interior line; `continues: true` on
180
+ * line 0 is rejected (nothing precedes it to continue).
181
+ */
182
+ export type LineOp =
183
+ | { op: "split"; at: number }
184
+ | { op: "join"; line: number }
185
+ | ({ op: "setKind"; line: number } & (
186
+ | { kind: "para" | "island" | "rule" }
187
+ | { kind: "heading"; level: number }
188
+ | { kind: "code"; lang?: string }
189
+ ))
190
+ | { op: "setContainers"; line: number; containers: ContentContainer[] }
191
+ | { op: "setContinues"; line: number; continues: boolean };
49
192
 
50
- /** UI layout hints for a single field. */
193
+ /**
194
+ * A committed content edit bundle for `applyChange`: a text `delta` (default no
195
+ * text change), then `lineOps`, then `markOps` (mark ranges are in post-delta
196
+ * coordinates). Every field is optional.
197
+ */
198
+ export interface ChangeBundle {
199
+ delta?: Delta;
200
+ lineOps?: LineOp[];
201
+ markOps?: MarkOp[];
202
+ }
203
+
204
+
205
+
206
+ /** UI layout hints for a single field. Field display order is not a hint:
207
+ * key order in the schema's `fields`/`properties` objects is declaration
208
+ * order, the ordering contract. */
51
209
  export interface QuillFieldUi {
210
+ title?: string;
52
211
  group?: string;
53
- order?: number;
54
212
  compact?: boolean;
55
213
  multiline?: boolean;
56
214
  }
@@ -73,20 +231,28 @@ export interface QuillCardBody {
73
231
  * A field's *cell* is determined by `default`: a field with a `default`
74
232
  * is **Endorsed** (the rendered value is shippable as-is), while a field
75
233
  * without a `default` is **Unendorsed** (the blueprint carries a
76
- * `<must-fill>` sentinel and validation reports
77
- * `validation::field_absent` if the field is absent at validate
78
- * time — a non-fatal signal, since the render path zero-fills an absent
79
- * field). There is no separate `required` axis.
234
+ * `!must_fill` marker; a marker left in the document yields the non-fatal
235
+ * `validation::must_fill` warning from validate, and the render path
236
+ * zero-fills the field). There is no separate `required` axis.
80
237
  */
81
238
  export interface QuillFieldSchema {
82
- type: "string" | "number" | "integer" | "boolean" | "array" | "object" | "datetime" | "markdown";
239
+ type: "string" | "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "richtext" | "plaintext" | "enum";
83
240
  description?: string;
84
241
  default?: unknown;
85
242
  example?: unknown;
243
+ /** Closed value domain. On `type: "enum"` declared as `values`; the
244
+ * deprecated `enum` modifier on `type: "string"` is accepted for one
245
+ * release. Both round-trip through this field. */
86
246
  enum?: string[];
247
+ /** Required on `type: "enum"`: the closed set of allowed string values. */
248
+ values?: string[];
87
249
  ui?: QuillFieldUi;
88
250
  properties?: Record<string, QuillFieldSchema>;
89
251
  items?: QuillFieldSchema;
252
+ /** Present (and `true`) on a `richtext` or `plaintext` field declared
253
+ * `inline` — the single-paragraph, container-free, island-free constraint.
254
+ * Core serializes `inline: true` into the schema JSON; absent otherwise. */
255
+ inline?: boolean;
90
256
  }
91
257
 
92
258
  /** Schema entry for the main card or a named card kind. */
@@ -141,16 +307,26 @@ export interface Location {
141
307
  column: number;
142
308
  }
143
309
 
144
- export type Severity = "error" | "warning" | "note";
310
+ export type Severity = "error" | "warning";
145
311
 
146
312
 
147
313
  /**
148
314
  * Typed in-memory Quillmark document.
149
315
  */
150
316
  export class Document {
151
- private constructor();
152
317
  free(): void;
153
318
  [Symbol.dispose](): void;
319
+ /**
320
+ * **Apply** a committed content edit `bundle` (`{ delta?, lineOps?, markOps? }`)
321
+ * at `addr` — the editor splice: text delta first, then line ops, then mark
322
+ * ops (mark ranges in final-text coordinates), each all-or-nothing. An absent
323
+ * `addr.field` targets the body, an absent `addr.card` the main card.
324
+ *
325
+ * Throws on an out-of-range card, a field that is not richtext, a malformed
326
+ * bundle, or an op that applies out of bounds (the value is unchanged on a
327
+ * failed apply).
328
+ */
329
+ applyChange(addr: Addr | string, bundle: ChangeBundle): void;
154
330
  /**
155
331
  * Authoring-ergonomics header introducing a blueprint to an LLM/MCP
156
332
  * consumer for the given `quillName`. Re-exposes core's canonical text for
@@ -158,6 +334,21 @@ export class Document {
158
334
  * uniform.
159
335
  */
160
336
  static blueprintInstruction(quill_name: string): string;
337
+ /**
338
+ * A single composable card by index — the whole `Card`, the card-indexed
339
+ * twin of the [`main`](Self::main) getter, so reading one card need not
340
+ * materialize every card via [`cards`](Self::cards). An out-of-range
341
+ * `index` throws `[EditError::IndexOutOfRange]`, matching the card write
342
+ * verbs.
343
+ */
344
+ card(index: number): Card;
345
+ /**
346
+ * The index of the first composable card whose `$id` equals `id`, or
347
+ * `undefined` when none carries it. Resolves the canonical durable address
348
+ * without a hand-rolled scan over [`cards`](Self::cards); `$id` is
349
+ * non-unique by design, so the first match wins.
350
+ */
351
+ cardIndexById(id: string): number | undefined;
161
352
  clone(): Document;
162
353
  /**
163
354
  * Schema version this build writes via [`toJson`](Document::to_json).
@@ -198,16 +389,89 @@ export class Document {
198
389
  */
199
390
  static fromMarkdown(markdown: string): Document;
200
391
  /**
201
- * Insert a card at `index` (must be in `0..=cards.length`). Accepts a
202
- * `Card` (see [`pushCard`](Self::push_card)).
392
+ * Read the value at `addr` the raw stored payload value of a field (a
393
+ * content object for a richtext field, a scalar/array/object otherwise), or
394
+ * the **body content** when `addr.field` is absent. A bare string is `Addr`
395
+ * shorthand for `{ field }`. Reads are total over the field axis: an absent
396
+ * field is `undefined`; only an out-of-range `addr.card` throws
397
+ * `[EditError::IndexOutOfRange]`. Reads need no schema, so they live on
398
+ * `Document`, not the typed writer; for the markdown projection of a
399
+ * richtext value use [`getMarkdown`](Self::get_markdown).
400
+ */
401
+ get(addr: Addr | string): unknown;
402
+ /**
403
+ * The whole `$ext` map at `addr` (a card address, absent `card` = main), or
404
+ * `undefined` when the card carries none. The fine-grained `$ext` read —
405
+ * your own state without serializing the whole card. Throws on a present
406
+ * `field` (a card address takes only `card`) or an out-of-range card.
407
+ */
408
+ getExt(addr?: CardAddr): Record<string, unknown> | undefined;
409
+ /**
410
+ * The value stored under `$ext[ns]` at `addr` (a card address, absent `card`
411
+ * = main), or `undefined`. The namespace-scoped `$ext` read — your own slot
412
+ * without a whole-card serialize, and non-destructive (unlike
413
+ * `removeExtNamespace`). Throws on a present `field` or an out-of-range card.
414
+ */
415
+ getExtNamespace(addr: CardAddr, ns: string): unknown;
416
+ /**
417
+ * The **body** markdown projection — the main body, or a composable card's
418
+ * body (`{ card }`) — the on-demand, lossy export (content-only marks do not
419
+ * survive markdown). A body's type is a format fact, not a schema fact, so
420
+ * this read stays quill-free; a body is never absent.
421
+ *
422
+ * `addr` is an optional **card address** (`{ card }`, absent = main). A
423
+ * present `field` throws — a field's markdown is read through the
424
+ * schema-plane `quill.view(doc).get(field)`, which interprets by declared
425
+ * type (#978). An out-of-range `addr.card` throws.
426
+ */
427
+ getMarkdown(addr?: CardAddr): string;
428
+ /**
429
+ * Insert a card — the single insertion verb: `at` absent appends, a number
430
+ * inserts at that index (must be in `0..=cards.length`). Accepts a
431
+ * `CardInput` — a card read back (`cards` / `removeCard` / `quill.seedCard`),
432
+ * a [`makeCard`](Document::make_card) result, or a bare `{ kind, body }`
433
+ * (every returned `Card` is a valid `CardInput`). Throws if `card.kind` is
434
+ * not a valid kind name, or if `at` is out of range.
435
+ */
436
+ insertCard(card: CardInput, at?: number): void;
437
+ /**
438
+ * **Install** a richtext value at `addr` — **value semantics**, content only.
439
+ * Stores exactly `rt` (a canonical `Content` content object); the identity
440
+ * anchors of any previous value are gone. An absent `addr.field` targets the
441
+ * body, an absent `addr.card` the main card. For "here's new markdown," use
442
+ * [`revise`](Document::revise); the cold-import path is spelled at the call
443
+ * site as `install(addr, importMarkdown(md))`, so anchor loss is visible in
444
+ * source.
445
+ *
446
+ * Throws on an out-of-range card, a malformed field name, or an `rt` that is
447
+ * not a canonical content object.
448
+ */
449
+ install(addr: Addr | string, rt: Content): void;
450
+ /**
451
+ * Whether the field at `addr` is marked `!must_fill`. A bare string is `Addr`
452
+ * shorthand for `{ field }`. `false` for an absent field (truthful — it isn't
453
+ * marked) and for a body address (a body is never a fill). Only an
454
+ * out-of-range `addr.card` throws.
203
455
  */
204
- insertCard(index: number, card: Card): void;
456
+ isFill(addr: Addr | string): boolean;
457
+ /**
458
+ * Replace this document's contents **in place** from a versioned storage
459
+ * DTO string — the mutating twin of the static
460
+ * [`fromJson`](Document::from_json) constructor. Parse-time `warnings` are
461
+ * cleared. Throws (leaving the document unchanged) on an invalid DTO.
462
+ *
463
+ * The cross-WASM-memory `Document` bridge: mutate a document on a
464
+ * backend-memory clone, then write the mutated state back into the caller's
465
+ * canonical document with this — the one way to update a live handle across
466
+ * the linear-memory seam without the caller re-binding its variable.
467
+ */
468
+ loadJson(json: string): void;
205
469
  /**
206
470
  * Build a fresh `Card` from a kind and a flat field map — the ergonomic
207
- * constructor for `pushCard` / `insertCard`. `fields` is an optional
471
+ * constructor for `insertCard`. `fields` is an optional
208
472
  * `Record<string, unknown>` (each entry becomes a card field, in
209
473
  * insertion order); `body` defaults to `""`. Kind validity is checked by
210
- * `pushCard` / `insertCard`, not here.
474
+ * `insertCard`, not here.
211
475
  */
212
476
  static makeCard(kind: string, fields?: Record<string, unknown>, body?: string): Card;
213
477
  /**
@@ -215,12 +479,14 @@ export class Document {
215
479
  */
216
480
  moveCard(from: number, to: number): void;
217
481
  /**
218
- * Append a card to the end of the card list. Accepts a `Card` (the shape
219
- * returned by `cards` / `removeCard` / `quill.seedCard`); build a fresh
220
- * one with [`Document.makeCard`](Document::make_card). Throws if
221
- * `card.kind` is not a valid kind name.
482
+ * `new Document(quillRef)` a blank document: a main card carrying only
483
+ * `$quill`, an empty body, and no composable cards. The programmatic
484
+ * blank canvas: absent fields resolve at render time (`default`, else
485
+ * type-empty zero), so nothing the caller did not set reaches the
486
+ * output. For an example-filled starter use `Quill.seedDocument()`.
487
+ * Throws on an invalid quill reference. Mirrors Python `Document(quill_ref)`.
222
488
  */
223
- pushCard(card: Card): void;
489
+ constructor(quill_ref: string);
224
490
  /**
225
491
  * The canonical `$quill` reference grammar as author-facing text. Core is
226
492
  * the single source of truth: drive schema `describe` and validation
@@ -231,48 +497,44 @@ export class Document {
231
497
  static quillRefHint(): string;
232
498
  removeCard(index: number): Card | undefined;
233
499
  /**
234
- * Remove the `$ext` map from the composable card at `index` *entirely*,
235
- * returning the previous map or `undefined`. Throws if out of range.
236
- * Prefer `removeCardExtNamespace` to clear only one consumer's slot.
237
- */
238
- removeCardExt(index: number): Record<string, unknown> | undefined;
239
- /**
240
- * Remove `namespace` from the composable card's `$ext` map, returning the
241
- * value stored there or `undefined`; clears `$ext` entirely once empty.
242
- * The card-indexed twin of `removeExtNamespace`. Throws if out of range.
243
- */
244
- removeCardExtNamespace(index: number, namespace: string): any;
245
- /**
246
- * Remove a field on the card at `index`. Returns the removed value or
247
- * `undefined`. Throws if `index` is out of range or `name` is invalid.
248
- */
249
- removeCardField(index: number, name: string): any;
250
- /**
251
- * Remove the `$ext` map from the main card *entirely*, returning the
252
- * previous map or `undefined`. This is a blunt escape hatch that discards
253
- * every namespace at once — prefer `removeExtNamespace` to clear only your
254
- * own slot while leaving sibling consumers' state intact.
500
+ * Remove the `$ext` map on the card `addr` targets *entirely*, returning the
501
+ * previous map or `undefined` a blunt escape hatch that discards every
502
+ * namespace at once (prefer `removeExtNamespace`). `addr` is a card address
503
+ * (absent = main). Throws on a present `field` or an out-of-range card.
255
504
  */
256
- removeExt(): Record<string, unknown> | undefined;
505
+ removeExt(addr?: CardAddr): Record<string, unknown> | undefined;
257
506
  /**
258
- * Remove `namespace` from the main card's `$ext` map, returning the value
259
- * stored there or `undefined`. This is the recommended way to clear `$ext`
260
- * state: sibling namespaces survive, and when the last namespace is removed
261
- * the `$ext` entry is dropped entirely (not left as `$ext: {}`).
507
+ * Remove `$ext[ns]` on the card `addr` targets, returning its value or
508
+ * `undefined`; drops `$ext` once empty. `addr` is a card address (absent =
509
+ * main). Preserves sibling namespaces. Throws on a present `field` or an
510
+ * out-of-range card.
262
511
  */
263
- removeExtNamespace(namespace: string): any;
512
+ removeExtNamespace(addr: CardAddr, ns: string): any;
264
513
  /**
265
- * Remove a payload field on the main card, returning the removed value or
266
- * `undefined`. Throws if `name` does not match `[A-Za-z_][A-Za-z0-9_]*`.
514
+ * Remove a field at `addr`, returning the removed value or `undefined`. A
515
+ * bare string is `Addr` shorthand for `{ field }`. One `remove` verb serves
516
+ * every write lane. A body address throws; throws on an out-of-range card or
517
+ * a malformed name.
267
518
  */
268
- removeField(name: string): any;
519
+ removeField(addr: Addr | string): any;
269
520
  /**
270
521
  * Remove `cardKind` from the main card's `$seed` map, returning its
271
- * overlay or `undefined`; drops `$seed` entirely once empty. Sibling
272
- * kinds survive.
522
+ * overlay or `undefined`; drops `$seed` entirely once empty. Sibling kinds
523
+ * survive. `$seed` is main-only, so this takes no address.
273
524
  */
274
525
  removeSeedNamespace(card_kind: string): any;
275
- replaceBody(body: string): void;
526
+ /**
527
+ * **Revise** the richtext value at `addr` from a markdown string — **edit
528
+ * semantics**, the default write path, returning the text [`Delta`]. Imports
529
+ * the markdown, diffs it against the current value, rebases surviving
530
+ * identity anchors, and returns the change an editor bridge maps its own
531
+ * positions through (`mapPos`). An absent `addr.field` targets the body, an
532
+ * absent `addr.card` the main card; an absent field cold-imports from empty.
533
+ *
534
+ * Throws on an out-of-range card, a malformed field name, a present
535
+ * non-content field value, or an over-nested markdown input.
536
+ */
537
+ revise(addr: Addr | string, markdown: string): Delta;
276
538
  /**
277
539
  * Read the `schema` version tag from a raw storage DTO string without a
278
540
  * full parse, or `undefined`. Returns unknown future versions as-is —
@@ -281,17 +543,13 @@ export class Document {
281
543
  */
282
544
  static schemaVersionOf(json: string): string | undefined;
283
545
  /**
284
- * Replace the `$ext` map on the composable card at `index`. Throws if out
285
- * of range or `value` is not a plain object. Named to mirror `setExt` on
286
- * the main card; `setCardExtNamespace` is the sibling-safe alternative.
546
+ * The main card's `$seed` overlay object for `kind` (the `$seed[kind]`
547
+ * entry), or `undefined` when absent. The cheap read that feeds
548
+ * `quill.seedCard(kind, overlay)` without serializing the whole main card
549
+ * via [`main`](Self::main) to fish out one key — and it keeps `seedCard`
550
+ * pure: the quill still never reads the document.
287
551
  */
288
- setCardExt(index: number, value: any): void;
289
- /**
290
- * Merge `value` into the composable card's `$ext` map under `namespace`,
291
- * preserving sibling namespaces. The card-indexed twin of `setExtNamespace`.
292
- * Throws if out of range or `value` cannot be serialized.
293
- */
294
- setCardExtNamespace(index: number, namespace: string, value: any): void;
552
+ seedOverlay(kind: string): Record<string, unknown> | undefined;
295
553
  /**
296
554
  * Replace the kind of the card at `index`. Payload and body are untouched;
297
555
  * schema-aware migration is the caller's responsibility.
@@ -299,41 +557,61 @@ export class Document {
299
557
  */
300
558
  setCardKind(index: number, new_kind: string): void;
301
559
  /**
302
- * Replace the opaque `$ext` map on the main card. `value` must be a plain
303
- * object; throws otherwise. `$ext` carries out-of-band consumer state and
304
- * never reaches the rendered output. Pass `{}` to record an explicit
305
- * empty `$ext`.
560
+ * Replace the QUILL reference string. Throws if `ref_str` is invalid.
306
561
  */
307
- setExt(value: any): void;
562
+ setQuillRef(ref_str: string): void;
308
563
  /**
309
- * Merge `value` into the main card's `$ext` map under `namespace`, creating
310
- * the map when absent and replacing any existing value at that key. Sibling
311
- * namespaces are preserved, so independent consumers (`$ext.editor`,
312
- * `$ext.agent`, …) don't clobber each other.
564
+ * Replace the opaque `$ext` map on the card `addr` targets (a card address,
565
+ * absent `card` = main). `value` must be a plain object. `$ext` carries
566
+ * out-of-band consumer state and never reaches the rendered output; pass
567
+ * `{}` for an explicit empty `$ext`. Quill-free and verbatim an opaque
568
+ * `store` verb. Throws on a present `field` or an out-of-range card.
313
569
  */
314
- setExtNamespace(namespace: string, value: any): void;
570
+ storeExt(addr: CardAddr, value: any): void;
315
571
  /**
316
- * Update a payload field on the main card. Clears any existing `!must_fill` marker.
317
- *
318
- * Throws if `name` does not match `[A-Za-z_][A-Za-z0-9_]*`.
572
+ * Merge `value` into `$ext[ns]` on the card `addr` targets, preserving
573
+ * sibling namespaces — the recommended `$ext` write. `addr` is a card
574
+ * address (absent = main). Quill-free and verbatim — an opaque `store` verb.
575
+ * Throws on a present `field` or an out-of-range card.
319
576
  */
320
- setField(name: string, value: any): void;
577
+ storeExtNamespace(addr: CardAddr, ns: string, value: any): void;
321
578
  /**
322
- * Update a payload field on the main card and mark it as `!must_fill`.
323
- * Throws on invalid name (see [`setField`](Document::set_field)).
579
+ * Store a field verbatim at `addr` the opaque store (**store** = verbatim,
580
+ * coercion deferred to render; the typed write is
581
+ * [`commitField`](Document::commit_field)). A bare string is `Addr`
582
+ * shorthand for `{ field }`, so `doc.storeField("qty", 3)` reads as written;
583
+ * `{ card: 2, field: "qty" }` targets a composable card. Clears any
584
+ * `!must_fill` marker. A body address (no `field`) throws — a body is never
585
+ * opaque; write it with `revise` / `install` / `writer.setBody`. Throws on
586
+ * an out-of-range card or a malformed name.
324
587
  */
325
- setFill(name: string, value: any): void;
588
+ storeField(addr: Addr | string, value: any): void;
326
589
  /**
327
- * Replace the QUILL reference string. Throws if `ref_str` is invalid.
590
+ * Store several fields verbatim and atomically on the card `addr` targets
591
+ * the opaque store's batch. `addr` is a **card address** (`{ card }`, absent
592
+ * = main); a present `field` throws. The batch verb takes the address first
593
+ * and is never shape-overloaded, because `card` is a legal field name:
594
+ * `storeFields({}, fields)` is the main card, `storeFields({ card: 2 },
595
+ * fields)` a composable one — never ambiguous with "set field `card`".
596
+ * Nothing is applied on error; the thrown error's `diagnostics` carry one
597
+ * entry per offending field. Throws on an out-of-range card.
328
598
  */
329
- setQuillRef(ref_str: string): void;
599
+ storeFields(addr: CardAddr, fields: Record<string, unknown>): void;
330
600
  /**
331
- * Merge a card-kind's seed `overlay` into the main card's `$seed` map
332
- * under `cardKind`, preserving sibling kinds. Sets the starting values
333
- * new cards of that kind spawn with. Throws if `overlay` cannot be
334
- * serialized or nests too deep.
601
+ * Store a field verbatim at `addr` and mark it `!must_fill` — the opaque
602
+ * store's fill variant, card-capable (a bare string or `{ field }` for main,
603
+ * `{ card, field }` for a composable card). A body address throws. Same
604
+ * validation as [`storeField`](Document::store_field).
335
605
  */
336
- setSeedNamespace(card_kind: string, overlay: any): void;
606
+ storeFill(addr: Addr | string, value: any): void;
607
+ /**
608
+ * Merge a card-kind's seed `overlay` into the **main** card's `$seed` map
609
+ * under `cardKind`, preserving sibling kinds — `$seed` lives on the main
610
+ * card by model, so this takes no address. Sets the starting values new
611
+ * cards of that kind spawn with. Quill-free and verbatim — an opaque `store`
612
+ * verb. Throws if `overlay` cannot be serialized or nests too deep.
613
+ */
614
+ storeSeedNamespace(card_kind: string, overlay: any): void;
337
615
  /**
338
616
  * Serialize this document to a versioned storage DTO string.
339
617
  *
@@ -358,15 +636,6 @@ export class Document {
358
636
  * genuinely malformed markdown.
359
637
  */
360
638
  static tryFromJson(json: string): Document | undefined;
361
- /**
362
- * Replace the body of the card at `index`. Throws if out of range.
363
- */
364
- updateCardBody(index: number, body: string): void;
365
- /**
366
- * Update a field on the card at `index`.
367
- * Throws if `index` is out of range, `name` is reserved or invalid.
368
- */
369
- updateCardField(index: number, name: string, value: any): void;
370
639
  /**
371
640
  * Number of composable cards (excludes the main card). O(1).
372
641
  */
@@ -400,9 +669,9 @@ export class Quill {
400
669
  * layering an optional per-kind seed `overlay` over the schema-example
401
670
  * base (`overlay › example › absent`). Returns `undefined` if `cardKind`
402
671
  * is not declared in this quill's schema, else a `Card` that feeds
403
- * straight into `Document.pushCard` / `insertCard`.
672
+ * straight into `Document.insertCard`.
404
673
  *
405
- * Pass `document.main.seed?.[cardKind]` as `overlay` so a card added to a
674
+ * Pass `document.seedOverlay(cardKind)` as `overlay` so a card added to a
406
675
  * template-derived document inherits its curated starting values; omit it
407
676
  * (or pass `undefined` / `null`) for the bare schema seed. `overlay` is a
408
677
  * plain object — this reads the document, it does not mutate it.
@@ -443,10 +712,10 @@ export class Quill {
443
712
  *
444
713
  * Forwards the canonical `validation::*` diagnostics — same `code`,
445
714
  * `path`, and `hint` the engine emits — including the non-fatal
446
- * `validation::field_absent` completeness signal that `render` demotes.
447
- * Field values, defaults, and order are not part of this surface: read
448
- * them from the `Document` payload and `Quill.schema` (fields carry
449
- * `ui.order`).
715
+ * `validation::must_fill` warning for each `!must_fill` marker left in
716
+ * the document. Field values, defaults, and order are not part of this
717
+ * surface: read them from the `Document` payload and `Quill.schema`
718
+ * (schema key order is display order).
450
719
  */
451
720
  validate(doc: Document): Diagnostic[];
452
721
  /**
@@ -465,14 +734,47 @@ export class Quill {
465
734
  readonly metadata: QuillMetadata;
466
735
  /**
467
736
  * Document schema for the quill: the user-fillable fields plus their
468
- * `ui` hints (group / order / showWhen). The single field-metadata
469
- * surface — drives form editors and LLM/MCP consumers alike. Returns the
470
- * `QuillSchema` shape.
737
+ * `ui` hints (title / group / compact / multiline). The single
738
+ * field-metadata surface — drives form editors and LLM/MCP consumers
739
+ * alike. Key order in `fields`/`properties` is declaration order — the
740
+ * ordering contract. Returns the `QuillSchema` shape.
471
741
  */
472
742
  readonly schema: QuillSchema;
473
743
  }
474
744
 
745
+ /**
746
+ * Export a canonical `Content` content to its markdown projection — the pure
747
+ * on-demand codec behind `exportMarkdown(card.body)`. Throws if `rt` is not a
748
+ * canonical content.
749
+ */
750
+ export function exportMarkdown(rt: Content): string;
751
+
752
+ /**
753
+ * Import a markdown string to a canonical `Content` content — the pure,
754
+ * document-free codec. Pair with `install(addr, importMarkdown(md))` to spell
755
+ * the cold (anchor-losing) write at the call site; prefer `revise` for edit
756
+ * semantics. Throws on an over-nested input.
757
+ */
758
+ export function importMarkdown(markdown: string): Content;
759
+
475
760
  /**
476
761
  * Initialize the WASM module with panic hooks for better error messages
477
762
  */
478
763
  export function init(): void;
764
+
765
+ /**
766
+ * Map a base content position through a `delta` to its new position — the pure
767
+ * position-mapping codec an editor bridge composes to hold a caret stable
768
+ * across a `revise`. `assoc` decides the side of a same-position insertion
769
+ * (`"after"` moves past it). Throws on a malformed `delta`.
770
+ */
771
+ export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
772
+
773
+ /**
774
+ * Rebase `markdown` onto a `base` content — the pure, document-free twin of
775
+ * `revise`: cold-import + `diff_import`, returning the new `content` and the
776
+ * text `delta` (surviving anchors rebased). Use it to compute a revise without
777
+ * a document in hand; `revise(addr, md)` fuses this with the store for
778
+ * atomicity. Throws on an over-nested markdown input or a non-content `base`.
779
+ */
780
+ export function rebase(base: Content, markdown: string): { content: Content; delta: Delta };