@quillmark/wasm 0.87.3 → 0.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,74 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.88.0 - 2026-06-05
4
+
5
+ - **Breaking (bindings + Rust API):** a single canonical **`Card` wire shape** now
6
+ flows in *both* directions. Core owns it as `quillmark_core::CardWire` (with
7
+ `From<&Card>` / `TryFrom<CardWire>`); the WASM/Python bindings serialize and
8
+ deserialize it instead of hand-rolling their own per-card translation. The
9
+ flat `CardInput { kind, fields?, body? }` input type is **removed**:
10
+ `Document.pushCard` / `insertCard` (`push_card` / `insert_card`) now accept the
11
+ same `Card` shape they return (`{ kind, payloadItems, … }`), so a card from
12
+ `cards` / `removeCard` / `quill.seedCard` feeds straight back in. Build a fresh
13
+ card from a flat field map with the new **`Document.makeCard`** /
14
+ `Document.make_card` helper. A stale `{ kind, fields }` object is now a loud
15
+ error (`deny_unknown_fields`), not a silently-empty card. The seeded per-card
16
+ getters `quill.seedMain` / `quill.seedCard` (`seed_main` / `seed_card`) are
17
+ exposed on both bindings, mirroring the Rust `Quill::seed_main` / `seed_card`.
18
+ - **Breaking (Rust API):** `Document::push_card` now returns
19
+ `Result<(), EditError>` and, with `insert_card`, validates that the card's
20
+ `$kind` is a valid, non-reserved composable kind — the cards-list invariant is
21
+ enforced at the edit op rather than incidentally at `Card::new`.
22
+ - **Breaking (bindings + Rust API):** the schema-aware **form view is removed**.
23
+ `Quill::form` / `Quill::blank_main` / `Quill::blank_card` (and the
24
+ `quill.form` / `blankMain` / `blankCard` bindings) are gone, along with the
25
+ `Form` / `FormCard` / `FormFieldValue` / `FormFieldSource` types. Validation
26
+ diagnostics now flow through `Quill::validate(&Document) -> Vec<Diagnostic>`
27
+ (`quill.validate(doc)` in WASM/Python), which forwards the canonical
28
+ `validation::*` diagnostics and keeps the non-fatal `validation::field_absent`
29
+ completeness signal that `render` demotes. Field values/defaults/order are a
30
+ `Document` × `quill.schema` join the consumer performs directly. See
31
+ `docs/migrations/0.87-to-0.88.md`.
32
+ - **Breaking (diagnostics):** the validation code `validation::must_fill_absent`
33
+ is renamed `validation::field_absent`. "Must-fill" is now scoped to the
34
+ blueprint communication surface (the `<must-fill>` sentinel and the fatal
35
+ `validation::must_fill_sentinel`); an *absent* field is a non-fatal
36
+ completeness signal, not a fill requirement, since the render floor
37
+ zero-fills it. The schema cell axis is renamed accordingly: the no-`default:`
38
+ cell is **Unendorsed** (was "Must Fill"), the antonym of **Endorsed** —
39
+ consumers routing on the old code or label must update. Internally
40
+ `ValidationError::MustFillUnset { source }` splits into `FieldAbsent` and
41
+ `MustFillSentinel` and the `MustFillSource` enum is removed.
42
+ - **Breaking (bindings + Rust API):** the `example` reference document is
43
+ removed. `QuillConfig::example()` and the `Quill.example` (WASM) /
44
+ `Quill.example` (Python) getters are gone. Its "show me a filled-out one"
45
+ role is served by seeding — `Quill::seed_document()` / `Quill.seedDocument()`
46
+ / `Quill.seed_document()` — which returns a committed `Document` rather than
47
+ an annotated string. The CLI `render` with no input file now renders the
48
+ seeded document. Nothing consumed the example document's annotations (the
49
+ authoring surface is `blueprint()`), so the projection collapses into the
50
+ seed: internally the `FillSource` fork in blueprint emission is gone and the
51
+ blueprint always renders `default:` else the `<must-fill>` sentinel.
52
+ - **wasm:** lower the npm package `engines.node` floor from `>=24` to `>=22`.
53
+ The runtime never required 24 — `--weak-refs` needs only Node 14.6+, and the
54
+ `using` sugar that motivated the 24 floor is optional (a `try` / `finally`
55
+ fallback covers Node 22). The aggressive floor hard-blocked installs on Node
56
+ 22 CI/dev images under `engine-strict`.
57
+ - **wasm:** `Document.makeCard(kind, fields?, body?)` now types `fields` as
58
+ optional in the generated `.d.ts` (was required, contradicting its docs);
59
+ omitting it yields an empty field map, as before.
60
+ - **docs:** fix the `Quill.schema` getter doc — the returned schema **includes**
61
+ `ui` hints (it never stripped them); the stale "ui hints stripped" wording is
62
+ corrected. The 0.87→0.88 migration guide now documents the `fill` flag's
63
+ `!fill`-placeholder semantics and clarifies that seeding is example-filled,
64
+ not a blank-form replacement.
65
+ - **blueprint:** flatten `group_fields` and drop the unused group label (#697).
66
+ - **docs:** document seeding (example → absent), fix a block-scalar prescan
67
+ bug, and add commitment-ladder docs (#691).
68
+ - **docs(canon):** dedup field-resolution semantics into SCHEMAS (#692); note
69
+ that released migration guides are era-accurate and immutable (#695); prune
70
+ evolutionary information from comments and canon docs (#700).
71
+
3
72
  ## v0.87.3 - 2026-06-04
4
73
 
5
74
  - Complete and consolidate the $ext mutator surface (#689)
package/README.md CHANGED
@@ -160,18 +160,52 @@ O(1) getter for the number of composable cards (excluding the main card).
160
160
  Use this to validate indices before calling card mutators (`removeCard`,
161
161
  `updateCardField`, etc.) without allocating the full `cards` array.
162
162
 
163
- ### `quill.form(doc)`
163
+ ### `quill.validate(doc)`
164
164
 
165
- Returns `{ main, cards, diagnostics }` a schema-aware snapshot of `doc`
166
- without invoking the backend. `diagnostics` contains validation errors and
167
- warnings; an empty array means the document is valid. Useful for validating
168
- content without rendering:
165
+ Returns `Diagnostic[]` the document validated against the quill schema,
166
+ without invoking the backend. An empty array means the document is valid.
167
+ Each diagnostic carries the canonical `validation::*` `code`, `path`, and
168
+ `hint`. Includes the non-fatal `validation::field_absent` completeness
169
+ signal that `render` demotes (an absent Unendorsed field zero-fills rather
170
+ than failing), so filter by `severity`/`code` for blockers vs. hints:
169
171
 
170
172
  ```ts
171
- const form = quill.form(Document.fromMarkdown(markdown));
172
- const errors = form.diagnostics.filter(d => d.severity === "error");
173
+ const diagnostics = quill.validate(Document.fromMarkdown(markdown));
174
+ const errors = diagnostics.filter(d => d.severity === "error");
173
175
  ```
174
176
 
177
+ To render a form editor, read field definitions from `quill.schema` (sort
178
+ fields by each field's `ui.order`) and the authored values from the
179
+ `Document` payload — there is no separate form-view projection.
180
+
181
+ ### `quill.seedDocument()`
182
+
183
+ Returns a starter `Document` seeded from the schema: each field's `example:`
184
+ is committed and every other field is left absent (the render layer fills
185
+ `default:` → type-empty zero). Illustration-first — a field with both an
186
+ `example` and a `default` renders its example. Use as the initial state for a
187
+ "new document" editor.
188
+
189
+ ```ts
190
+ const doc = quill.seedDocument();
191
+ const markdown = doc.toMarkdown();
192
+ ```
193
+
194
+ For per-card seeding, `quill.seedMain()` returns just the `$kind: main` card
195
+ and `quill.seedCard(kind)` returns a starter composable card (or `undefined`
196
+ if the kind is not declared). Both return the same `Card` shape as
197
+ `doc.main` / `doc.cards`, which `doc.pushCard` / `doc.insertCard` accept
198
+ directly:
199
+
200
+ ```ts
201
+ doc.pushCard(quill.seedCard("note")); // seed → push
202
+ doc.pushCard(Document.makeCard("note", { x: 1 })); // build from a flat map
203
+ ```
204
+
205
+ There is one `Card` shape in both directions — `pushCard` / `insertCard` take
206
+ exactly what `cards` / `removeCard` / `seedCard` return. Build a fresh card
207
+ from a flat field map with `Document.makeCard(kind, fields?, body?)`.
208
+
175
209
  ### `quill.render(parsed, opts?)` vs. `quill.open(parsed)`
176
210
 
177
211
  Use **`Quill.render`** for one-shot exports (PDF/SVG/PNG) — compiles, emits
@@ -248,9 +282,9 @@ canvas.style.height = `${result.layoutHeight}px`;
248
282
 
249
283
  A field's *cell* is inferred from whether its schema declares a `default:`:
250
284
 
251
- - **Must Fill** (no `default:`) — `quill.blueprint` renders `<must-fill>`
252
- in the value cell. An absent Must Fill field is a non-fatal signal
253
- (`validation::must_fill_absent`) — the render path zero-fills it
285
+ - **Unendorsed** (no `default:`) — `quill.blueprint` renders `<must-fill>`
286
+ in the value cell. An absent Unendorsed field is a non-fatal signal
287
+ (`validation::field_absent`) — the render path zero-fills it
254
288
  silently. A surviving `<must-fill>` sentinel is fatal
255
289
  (`validation::must_fill_sentinel`). Partial documents are
256
290
  first-class; `quill.render(doc)` only throws for malformed input.
@@ -260,7 +294,7 @@ A field's *cell* is inferred from whether its schema declares a `default:`:
260
294
 
261
295
  `QuillFieldSchema` no longer carries a `required` axis. The legacy
262
296
  `validation::missing_required` code has been replaced by
263
- `validation::must_fill_absent`; the `validation::must_fill_sentinel`
297
+ `validation::field_absent`; the `validation::must_fill_sentinel`
264
298
  code covers unreplaced sentinels.
265
299
 
266
300
  ### Errors
@@ -295,9 +329,10 @@ The wasm bindings are built with `--weak-refs`, so dropped `Document`,
295
329
  without manual `.free()` discipline. `.free()` is still emitted as an eager
296
330
  teardown hook for callers that want deterministic release.
297
331
 
298
- The package requires Node 24+ (`engines: { node: ">=24" }`) and current
299
- evergreen browsers; `--weak-refs` itself only needs Node 14.6+, but the
300
- published package floor is 24.
332
+ The package floor is Node 22+ (`engines: { node: ">=22" }`) and current
333
+ evergreen browsers; `--weak-refs` itself only needs Node 14.6+. The `using`
334
+ sugar shown below ([explicit resource management][erm]) needs Node 24, but is
335
+ optional — the `try` / `finally` fallback runs on the Node 22 floor.
301
336
 
302
337
  For environments where `using` (the [explicit resource management][erm]
303
338
  proposal) hasn't landed, use an explicit `try` / `finally`:
package/bundler/wasm.d.ts CHANGED
@@ -1,19 +1,6 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
3
 
4
- /**
5
- * Input shape for `Document.pushCard` and `Document.insertCard`.
6
- *
7
- * Only `kind` is required. `fields` defaults to `{}`, `body` to `""`.
8
- */
9
- export interface CardInput {
10
- kind: string;
11
- fields?: Record<string, unknown>;
12
- body?: string;
13
- }
14
-
15
-
16
-
17
4
  /**
18
5
  * Page dimensions in Typst points (1 pt = 1/72 inch).
19
6
  *
@@ -85,6 +72,33 @@ export interface PaintResult {
85
72
 
86
73
 
87
74
 
75
+ /** A field or comment entry in a `Card.payloadItems` list. */
76
+ export type PayloadItem =
77
+ | { type: "field"; key: string; value: unknown; fill?: boolean }
78
+ | { type: "comment"; text: string; inline?: boolean };
79
+
80
+ /**
81
+ * A single card block. The one shape exchanged in both directions: returned by
82
+ * `Document.main` / `Document.cards` / `Document.removeCard` / `Quill.seedCard`,
83
+ * and accepted by `Document.pushCard` / `Document.insertCard`. Build a fresh
84
+ * one with `Document.makeCard`.
85
+ *
86
+ * `$` system entries are hoisted to named fields: `kind` (the `$kind`, empty
87
+ * string when none), optional `quill` (the `$quill` `name@version`, main card
88
+ * only), optional `id` (`$id`), and optional `ext` (`$ext`). `payloadItems`
89
+ * carries user fields and comments in order.
90
+ */
91
+ export interface Card {
92
+ kind: string;
93
+ quill?: string;
94
+ id?: string;
95
+ ext?: Record<string, unknown>;
96
+ payloadItems: PayloadItem[];
97
+ body: string;
98
+ }
99
+
100
+
101
+
88
102
  /** UI layout hints for a single field. */
89
103
  export interface QuillFieldUi {
90
104
  group?: string;
@@ -110,9 +124,9 @@ export interface QuillCardBody {
110
124
  *
111
125
  * A field's *cell* is determined by `default`: a field with a `default`
112
126
  * is **Endorsed** (the rendered value is shippable as-is), while a field
113
- * without a `default` is **Must Fill** (the blueprint carries a
127
+ * without a `default` is **Unendorsed** (the blueprint carries a
114
128
  * `<must-fill>` sentinel and validation reports
115
- * `validation::must_fill_absent` if the field is absent at validate
129
+ * `validation::field_absent` if the field is absent at validate
116
130
  * time — a non-fatal signal, since the render path zero-fills an absent
117
131
  * field). There is no separate `required` axis.
118
132
  */
@@ -163,44 +177,6 @@ export interface QuillMetadata {
163
177
  [key: string]: unknown;
164
178
  }
165
179
 
166
- /** Source of a field's effective value in a form view. */
167
- export type FormFieldSource = "document" | "default" | "missing";
168
-
169
- /**
170
- * A single field's view within a `FormCard`.
171
- *
172
- * - `value` — the document-supplied value (`null` when absent).
173
- * - `default` — the schema default (`null` when no default is declared).
174
- * - `source` — where the effective value comes from.
175
- */
176
- export interface FormFieldValue {
177
- value: unknown;
178
- default: unknown;
179
- source: FormFieldSource;
180
- }
181
-
182
- /**
183
- * A card viewed through its schema, as returned by `Quill.form`,
184
- * `Quill.blankMain`, and `Quill.blankCard`.
185
- */
186
- export interface FormCard {
187
- schema: QuillCardSchema;
188
- values: Record<string, FormFieldValue>;
189
- }
190
-
191
- /**
192
- * Schema-aware form view of a document, returned by `Quill.form`.
193
- *
194
- * - `main` — the main card viewed through the quill's main schema.
195
- * - `cards` — composable card blocks, in document order (unknown kinds excluded).
196
- * - `diagnostics` — diagnostics from unknown card kinds and validation.
197
- */
198
- export interface Form {
199
- main: FormCard;
200
- cards: FormCard[];
201
- diagnostics: Diagnostic[];
202
- }
203
-
204
180
 
205
181
  export interface Artifact {
206
182
  format: OutputFormat;
@@ -208,13 +184,6 @@ export interface Artifact {
208
184
  mimeType: string;
209
185
  }
210
186
 
211
- export interface Card {
212
- kind: string;
213
- payloadItems: PayloadItem[];
214
- ext?: Record<string, unknown> | undefined;
215
- body: string;
216
- }
217
-
218
187
  export interface Diagnostic {
219
188
  severity: Severity;
220
189
  code?: string;
@@ -247,8 +216,6 @@ export interface RenderResult {
247
216
 
248
217
  export type OutputFormat = "pdf" | "svg" | "txt" | "png";
249
218
 
250
- export type PayloadItem = { type: "field"; key: string; value: unknown; fill?: boolean } | { type: "comment"; text: string; inline?: boolean };
251
-
252
219
  export type Severity = "error" | "warning" | "note";
253
220
 
254
221
 
@@ -304,18 +271,29 @@ export class Document {
304
271
  */
305
272
  static fromMarkdown(markdown: string): Document;
306
273
  /**
307
- * Insert a card at `index` (must be in `0..=cards.length`).
274
+ * Insert a card at `index` (must be in `0..=cards.length`). Accepts a
275
+ * `Card` (see [`pushCard`](Self::push_card)).
276
+ */
277
+ insertCard(index: number, card: Card): void;
278
+ /**
279
+ * Build a fresh `Card` from a kind and a flat field map — the ergonomic
280
+ * constructor for `pushCard` / `insertCard`. `fields` is an optional
281
+ * `Record<string, unknown>` (each entry becomes a card field, in
282
+ * insertion order); `body` defaults to `""`. Kind validity is checked by
283
+ * `pushCard` / `insertCard`, not here.
308
284
  */
309
- insertCard(index: number, card: CardInput): void;
285
+ static makeCard(kind: string, fields: Record<string, unknown>, body?: string | null): Card;
310
286
  /**
311
287
  * Move the card at `from` to position `to`. `from == to` is a no-op.
312
288
  */
313
289
  moveCard(from: number, to: number): void;
314
290
  /**
315
- * Append a card to the end of the card list.
316
- * Throws if `card.kind` is not a valid kind name.
291
+ * Append a card to the end of the card list. Accepts a `Card` (the shape
292
+ * returned by `cards` / `removeCard` / `quill.seedCard`); build a fresh
293
+ * one with [`Document.makeCard`](Document::make_card). Throws if
294
+ * `card.kind` is not a valid kind name.
317
295
  */
318
- pushCard(card: CardInput): void;
296
+ pushCard(card: Card): void;
319
297
  /**
320
298
  * The canonical `$quill` reference grammar as author-facing text. Single
321
299
  * source of truth (CLI, Python, MCP): drive schema `describe` and
@@ -467,42 +445,59 @@ export class Quill {
467
445
  private constructor();
468
446
  free(): void;
469
447
  [Symbol.dispose](): void;
448
+ open(doc: Document): RenderSession;
449
+ render(doc: Document, opts?: RenderOptions | null): RenderResult;
470
450
  /**
471
- * Blank `FormCard` for the given card kind. Returns `null` if `cardKind`
472
- * is not declared in this quill's schema.
451
+ * Seed a starter composable `Card` of the given kind (carries `$kind`),
452
+ * committing its fields' `example:` values and leaving every other field
453
+ * absent. Returns `undefined` if `cardKind` is not declared in this
454
+ * quill's schema, else a `Card` that feeds straight into
455
+ * `Document.pushCard` / `insertCard`.
473
456
  */
474
- blankCard(card_kind: string): FormCard | null;
457
+ seedCard(card_kind: string): Card | undefined;
475
458
  /**
476
- * Blank `FormCard` for the main card with no document values.
477
- * Every field's `source` is `"default"` or `"missing"`.
459
+ * Seed a starter `Document` from the schema — the main card plus one
460
+ * instance of each composable card kind, each committing its fields'
461
+ * `example:` values and leaving every other field absent (interpolated at
462
+ * render: `default:`, else type-empty zero). Illustration-first: a field
463
+ * with both an `example` and a `default` renders its example. See
464
+ * `prose/canon/SCHEMAS.md` § "Document seeding".
478
465
  */
479
- blankMain(): FormCard;
466
+ seedDocument(): Document;
480
467
  /**
481
- * The schema-aware form view of `doc`. Read-only snapshot at call time;
482
- * subsequent edits to `doc` require calling `form` again.
468
+ * Seed a starter main `Card` (carries `$quill`) from the schema the
469
+ * `$kind: main` card of [`seedDocument`](Self::seed_document) in
470
+ * isolation, committing each field's `example:` value. Returns the same
471
+ * `Card` shape as the `Document.main` getter.
483
472
  */
484
- form(doc: Document): Form;
485
- open(doc: Document): RenderSession;
486
- render(doc: Document, opts?: RenderOptions | null): RenderResult;
473
+ seedMain(): Card;
474
+ /**
475
+ * Validate `doc` against this quill's schema, returning every diagnostic
476
+ * (an empty array when the document is valid).
477
+ *
478
+ * Forwards the canonical `validation::*` diagnostics — same `code`,
479
+ * `path`, and `hint` the engine emits — including the non-fatal
480
+ * `validation::field_absent` completeness signal that `render` demotes.
481
+ * Field values, defaults, and order are not part of this surface: read
482
+ * them from the `Document` payload and `Quill.schema` (fields carry
483
+ * `ui.order`).
484
+ */
485
+ validate(doc: Document): Diagnostic[];
487
486
  /**
488
487
  * The resolved backend identifier (e.g. `"typst"`).
489
488
  */
490
489
  readonly backendId: string;
491
490
  readonly blueprint: string;
492
- /**
493
- * The `example` reference document — the illustrative "show me a
494
- * filled-out one." Each field renders its `example:`, else its
495
- * `default:`, else the type-empty zero value, with no `<must-fill>`
496
- * sentinels. See `prose/canon/BLUEPRINT.md`.
497
- */
498
- readonly example: string;
499
491
  /**
500
492
  * Identity snapshot of the `quill:` section of `Quill.yaml`, plus
501
493
  * `supportedFormats` and any extra `quill:` keys.
502
494
  */
503
495
  readonly metadata: QuillMetadata;
504
496
  /**
505
- * Document schema with `ui` hints stripped for LLM/MCP consumers.
497
+ * Document schema for the quill: the user-fillable fields plus their
498
+ * `ui` hints (group / order / showWhen). The single field-metadata
499
+ * surface — drives form editors and LLM/MCP consumers alike. Returns the
500
+ * `QuillSchema` shape.
506
501
  */
507
502
  readonly schema: QuillSchema;
508
503
  /**
@@ -195,9 +195,10 @@ export class Document {
195
195
  }
196
196
  }
197
197
  /**
198
- * Insert a card at `index` (must be in `0..=cards.length`).
198
+ * Insert a card at `index` (must be in `0..=cards.length`). Accepts a
199
+ * `Card` (see [`pushCard`](Self::push_card)).
199
200
  * @param {number} index
200
- * @param {CardInput} card
201
+ * @param {Card} card
201
202
  */
202
203
  insertCard(index, card) {
203
204
  try {
@@ -221,6 +222,36 @@ export class Document {
221
222
  const ret = wasm.document_main(this.__wbg_ptr);
222
223
  return takeObject(ret);
223
224
  }
225
+ /**
226
+ * Build a fresh `Card` from a kind and a flat field map — the ergonomic
227
+ * constructor for `pushCard` / `insertCard`. `fields` is an optional
228
+ * `Record<string, unknown>` (each entry becomes a card field, in
229
+ * insertion order); `body` defaults to `""`. Kind validity is checked by
230
+ * `pushCard` / `insertCard`, not here.
231
+ * @param {string} kind
232
+ * @param {Record<string, unknown>} fields
233
+ * @param {string | null} [body]
234
+ * @returns {Card}
235
+ */
236
+ static makeCard(kind, fields, body) {
237
+ try {
238
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
239
+ const ptr0 = passStringToWasm0(kind, wasm.__wbindgen_export, wasm.__wbindgen_export2);
240
+ const len0 = WASM_VECTOR_LEN;
241
+ var ptr1 = isLikeNone(body) ? 0 : passStringToWasm0(body, wasm.__wbindgen_export, wasm.__wbindgen_export2);
242
+ var len1 = WASM_VECTOR_LEN;
243
+ wasm.document_makeCard(retptr, ptr0, len0, isLikeNone(fields) ? 0 : addHeapObject(fields), ptr1, len1);
244
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
245
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
246
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
247
+ if (r2) {
248
+ throw takeObject(r1);
249
+ }
250
+ return takeObject(r0);
251
+ } finally {
252
+ wasm.__wbindgen_add_to_stack_pointer(16);
253
+ }
254
+ }
224
255
  /**
225
256
  * Move the card at `from` to position `to`. `from == to` is a no-op.
226
257
  * @param {number} from
@@ -240,9 +271,11 @@ export class Document {
240
271
  }
241
272
  }
242
273
  /**
243
- * Append a card to the end of the card list.
244
- * Throws if `card.kind` is not a valid kind name.
245
- * @param {CardInput} card
274
+ * Append a card to the end of the card list. Accepts a `Card` (the shape
275
+ * returned by `cards` / `removeCard` / `quill.seedCard`); build a fresh
276
+ * one with [`Document.makeCard`](Document::make_card). Throws if
277
+ * `card.kind` is not a valid kind name.
278
+ * @param {Card} card
246
279
  */
247
280
  pushCard(card) {
248
281
  try {
@@ -784,49 +817,6 @@ export class Quill {
784
817
  wasm.__wbindgen_export4(deferred1_0, deferred1_1, 1);
785
818
  }
786
819
  }
787
- /**
788
- * Blank `FormCard` for the given card kind. Returns `null` if `cardKind`
789
- * is not declared in this quill's schema.
790
- * @param {string} card_kind
791
- * @returns {FormCard | null}
792
- */
793
- blankCard(card_kind) {
794
- try {
795
- const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
796
- const ptr0 = passStringToWasm0(card_kind, wasm.__wbindgen_export, wasm.__wbindgen_export2);
797
- const len0 = WASM_VECTOR_LEN;
798
- wasm.quill_blankCard(retptr, this.__wbg_ptr, ptr0, len0);
799
- var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
800
- var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
801
- var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
802
- if (r2) {
803
- throw takeObject(r1);
804
- }
805
- return takeObject(r0);
806
- } finally {
807
- wasm.__wbindgen_add_to_stack_pointer(16);
808
- }
809
- }
810
- /**
811
- * Blank `FormCard` for the main card with no document values.
812
- * Every field's `source` is `"default"` or `"missing"`.
813
- * @returns {FormCard}
814
- */
815
- blankMain() {
816
- try {
817
- const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
818
- wasm.quill_blankMain(retptr, this.__wbg_ptr);
819
- var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
820
- var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
821
- var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
822
- if (r2) {
823
- throw takeObject(r1);
824
- }
825
- return takeObject(r0);
826
- } finally {
827
- wasm.__wbindgen_add_to_stack_pointer(16);
828
- }
829
- }
830
820
  /**
831
821
  * @returns {string}
832
822
  */
@@ -846,51 +836,6 @@ export class Quill {
846
836
  wasm.__wbindgen_export4(deferred1_0, deferred1_1, 1);
847
837
  }
848
838
  }
849
- /**
850
- * The `example` reference document — the illustrative "show me a
851
- * filled-out one." Each field renders its `example:`, else its
852
- * `default:`, else the type-empty zero value, with no `<must-fill>`
853
- * sentinels. See `prose/canon/BLUEPRINT.md`.
854
- * @returns {string}
855
- */
856
- get example() {
857
- let deferred1_0;
858
- let deferred1_1;
859
- try {
860
- const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
861
- wasm.quill_example(retptr, this.__wbg_ptr);
862
- var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
863
- var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
864
- deferred1_0 = r0;
865
- deferred1_1 = r1;
866
- return getStringFromWasm0(r0, r1);
867
- } finally {
868
- wasm.__wbindgen_add_to_stack_pointer(16);
869
- wasm.__wbindgen_export4(deferred1_0, deferred1_1, 1);
870
- }
871
- }
872
- /**
873
- * The schema-aware form view of `doc`. Read-only snapshot at call time;
874
- * subsequent edits to `doc` require calling `form` again.
875
- * @param {Document} doc
876
- * @returns {Form}
877
- */
878
- form(doc) {
879
- try {
880
- const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
881
- _assertClass(doc, Document);
882
- wasm.quill_form(retptr, this.__wbg_ptr, doc.__wbg_ptr);
883
- var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
884
- var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
885
- var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
886
- if (r2) {
887
- throw takeObject(r1);
888
- }
889
- return takeObject(r0);
890
- } finally {
891
- wasm.__wbindgen_add_to_stack_pointer(16);
892
- }
893
- }
894
839
  /**
895
840
  * Identity snapshot of the `quill:` section of `Quill.yaml`, plus
896
841
  * `supportedFormats` and any extra `quill:` keys.
@@ -942,13 +887,55 @@ export class Quill {
942
887
  }
943
888
  }
944
889
  /**
945
- * Document schema with `ui` hints stripped for LLM/MCP consumers.
890
+ * Document schema for the quill: the user-fillable fields plus their
891
+ * `ui` hints (group / order / showWhen). The single field-metadata
892
+ * surface — drives form editors and LLM/MCP consumers alike. Returns the
893
+ * `QuillSchema` shape.
946
894
  * @returns {QuillSchema}
947
895
  */
948
896
  get schema() {
949
897
  const ret = wasm.quill_schema(this.__wbg_ptr);
950
898
  return takeObject(ret);
951
899
  }
900
+ /**
901
+ * Seed a starter composable `Card` of the given kind (carries `$kind`),
902
+ * committing its fields' `example:` values and leaving every other field
903
+ * absent. Returns `undefined` if `cardKind` is not declared in this
904
+ * quill's schema, else a `Card` that feeds straight into
905
+ * `Document.pushCard` / `insertCard`.
906
+ * @param {string} card_kind
907
+ * @returns {Card | undefined}
908
+ */
909
+ seedCard(card_kind) {
910
+ const ptr0 = passStringToWasm0(card_kind, wasm.__wbindgen_export, wasm.__wbindgen_export2);
911
+ const len0 = WASM_VECTOR_LEN;
912
+ const ret = wasm.quill_seedCard(this.__wbg_ptr, ptr0, len0);
913
+ return takeObject(ret);
914
+ }
915
+ /**
916
+ * Seed a starter `Document` from the schema — the main card plus one
917
+ * instance of each composable card kind, each committing its fields'
918
+ * `example:` values and leaving every other field absent (interpolated at
919
+ * render: `default:`, else type-empty zero). Illustration-first: a field
920
+ * with both an `example` and a `default` renders its example. See
921
+ * `prose/canon/SCHEMAS.md` § "Document seeding".
922
+ * @returns {Document}
923
+ */
924
+ seedDocument() {
925
+ const ret = wasm.quill_seedDocument(this.__wbg_ptr);
926
+ return Document.__wrap(ret);
927
+ }
928
+ /**
929
+ * Seed a starter main `Card` (carries `$quill`) from the schema — the
930
+ * `$kind: main` card of [`seedDocument`](Self::seed_document) in
931
+ * isolation, committing each field's `example:` value. Returns the same
932
+ * `Card` shape as the `Document.main` getter.
933
+ * @returns {Card}
934
+ */
935
+ seedMain() {
936
+ const ret = wasm.quill_seedMain(this.__wbg_ptr);
937
+ return takeObject(ret);
938
+ }
952
939
  /**
953
940
  * `true` iff `RenderSession.paint` and `RenderSession.pageSize` will
954
941
  * succeed for sessions opened by this quill. Use as a precondition
@@ -959,6 +946,35 @@ export class Quill {
959
946
  const ret = wasm.quill_supportsCanvas(this.__wbg_ptr);
960
947
  return ret !== 0;
961
948
  }
949
+ /**
950
+ * Validate `doc` against this quill's schema, returning every diagnostic
951
+ * (an empty array when the document is valid).
952
+ *
953
+ * Forwards the canonical `validation::*` diagnostics — same `code`,
954
+ * `path`, and `hint` the engine emits — including the non-fatal
955
+ * `validation::field_absent` completeness signal that `render` demotes.
956
+ * Field values, defaults, and order are not part of this surface: read
957
+ * them from the `Document` payload and `Quill.schema` (fields carry
958
+ * `ui.order`).
959
+ * @param {Document} doc
960
+ * @returns {Diagnostic[]}
961
+ */
962
+ validate(doc) {
963
+ try {
964
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
965
+ _assertClass(doc, Document);
966
+ wasm.quill_validate(retptr, this.__wbg_ptr, doc.__wbg_ptr);
967
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
968
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
969
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
970
+ if (r2) {
971
+ throw takeObject(r1);
972
+ }
973
+ return takeObject(r0);
974
+ } finally {
975
+ wasm.__wbindgen_add_to_stack_pointer(16);
976
+ }
977
+ }
962
978
  }
963
979
  if (Symbol.dispose) Quill.prototype[Symbol.dispose] = Quill.prototype.free;
964
980
 
@@ -1159,10 +1175,6 @@ export function __wbg_Error_960c155d3d49e4c2(arg0, arg1) {
1159
1175
  const ret = Error(getStringFromWasm0(arg0, arg1));
1160
1176
  return addHeapObject(ret);
1161
1177
  }
1162
- export function __wbg_Number_32bf70a599af1d4b(arg0) {
1163
- const ret = Number(getObject(arg0));
1164
- return ret;
1165
- }
1166
1178
  export function __wbg_String_8564e559799eccda(arg0, arg1) {
1167
1179
  const ret = String(getObject(arg1));
1168
1180
  const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
@@ -1357,6 +1369,16 @@ export function __wbg_instanceof_Map_1b76fd4635be43eb(arg0) {
1357
1369
  const ret = result;
1358
1370
  return ret;
1359
1371
  }
1372
+ export function __wbg_instanceof_Object_7c99480a1cdfb911(arg0) {
1373
+ let result;
1374
+ try {
1375
+ result = getObject(arg0) instanceof Object;
1376
+ } catch (_) {
1377
+ result = false;
1378
+ }
1379
+ const ret = result;
1380
+ return ret;
1381
+ }
1360
1382
  export function __wbg_instanceof_OffscreenCanvasRenderingContext2d_285a274020b4f230(arg0) {
1361
1383
  let result;
1362
1384
  try {
@@ -1389,6 +1411,10 @@ export function __wbg_iterator_013bc09ec998c2a7() {
1389
1411
  const ret = Symbol.iterator;
1390
1412
  return addHeapObject(ret);
1391
1413
  }
1414
+ export function __wbg_keys_2fd1bfdda7e278ca(arg0) {
1415
+ const ret = Object.keys(getObject(arg0));
1416
+ return addHeapObject(ret);
1417
+ }
1392
1418
  export function __wbg_length_3d4ecd04bd8d22f1(arg0) {
1393
1419
  const ret = getObject(arg0).length;
1394
1420
  return ret;
Binary file
@@ -17,6 +17,7 @@ export const document_fromJson: (a: number, b: number, c: number) => void;
17
17
  export const document_fromMarkdown: (a: number, b: number, c: number) => void;
18
18
  export const document_insertCard: (a: number, b: number, c: number, d: number) => void;
19
19
  export const document_main: (a: number) => number;
20
+ export const document_makeCard: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
20
21
  export const document_moveCard: (a: number, b: number, c: number, d: number) => void;
21
22
  export const document_pushCard: (a: number, b: number, c: number) => void;
22
23
  export const document_quillRef: (a: number, b: number) => void;
@@ -46,16 +47,16 @@ export const document_updateCardField: (a: number, b: number, c: number, d: numb
46
47
  export const document_warnings: (a: number) => number;
47
48
  export const init: () => void;
48
49
  export const quill_backendId: (a: number, b: number) => void;
49
- export const quill_blankCard: (a: number, b: number, c: number, d: number) => void;
50
- export const quill_blankMain: (a: number, b: number) => void;
51
50
  export const quill_blueprint: (a: number, b: number) => void;
52
- export const quill_example: (a: number, b: number) => void;
53
- export const quill_form: (a: number, b: number, c: number) => void;
54
51
  export const quill_metadata: (a: number) => number;
55
52
  export const quill_open: (a: number, b: number, c: number) => void;
56
53
  export const quill_render: (a: number, b: number, c: number, d: number) => void;
57
54
  export const quill_schema: (a: number) => number;
55
+ export const quill_seedCard: (a: number, b: number, c: number) => number;
56
+ export const quill_seedDocument: (a: number) => number;
57
+ export const quill_seedMain: (a: number) => number;
58
58
  export const quill_supportsCanvas: (a: number) => number;
59
+ export const quill_validate: (a: number, b: number, c: number) => void;
59
60
  export const quillmark_new: () => number;
60
61
  export const quillmark_quill: (a: number, b: number, c: number) => void;
61
62
  export const rendersession_backendId: (a: number, b: number) => void;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@quillmark/wasm",
3
- "version": "0.87.3",
3
+ "version": "0.88.0",
4
4
  "description": "WebAssembly bindings for quillmark",
5
5
  "type": "module",
6
6
  "license": "MIT OR Apache-2.0",
7
7
  "engines": {
8
- "node": ">=24"
8
+ "node": ">=22"
9
9
  },
10
10
  "repository": {
11
11
  "type": "git",