@quillmark/wasm 0.87.2 → 0.88.0-rc.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/CHANGELOG.md CHANGED
@@ -1,5 +1,82 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.88.0-rc.1 - 2026-06-05
4
+
5
+ - blueprint: flatten group_fields, drop unused group label (#697)
6
+ - Update CLAUDE.md
7
+ - Expose seedMain/seedCard to WASM + Python bindings (#696)
8
+ - docs: note that released migration guides are era-accurate and immutable (#695)
9
+ - Remove form-view projection; add Quill::validate (#694)
10
+ - Remove example() reference document, fold into seeding (#693)
11
+ - Document seeding (example → absent) + block-scalar prescan fix + commitment-ladder docs (#691)
12
+ - docs(canon): dedup field-resolution semantics into SCHEMAS (#692)
13
+
14
+
15
+ ## Unreleased
16
+
17
+ - **Breaking (bindings + Rust API):** a single canonical **`Card` wire shape** now
18
+ flows in *both* directions. Core owns it as `quillmark_core::CardWire` (with
19
+ `From<&Card>` / `TryFrom<CardWire>`); the WASM/Python bindings serialize and
20
+ deserialize it instead of hand-rolling their own per-card translation. The
21
+ flat `CardInput { kind, fields?, body? }` input type is **removed**:
22
+ `Document.pushCard` / `insertCard` (`push_card` / `insert_card`) now accept the
23
+ same `Card` shape they return (`{ kind, payloadItems, … }`), so a card from
24
+ `cards` / `removeCard` / `quill.seedCard` feeds straight back in. Build a fresh
25
+ card from a flat field map with the new **`Document.makeCard`** /
26
+ `Document.make_card` helper. A stale `{ kind, fields }` object is now a loud
27
+ error (`deny_unknown_fields`), not a silently-empty card. The seeded per-card
28
+ getters `quill.seedMain` / `quill.seedCard` (`seed_main` / `seed_card`) are
29
+ exposed on both bindings, mirroring the Rust `Quill::seed_main` / `seed_card`.
30
+ - **Breaking (Rust API):** `Document::push_card` now returns
31
+ `Result<(), EditError>` and, with `insert_card`, validates that the card's
32
+ `$kind` is a valid, non-reserved composable kind — the cards-list invariant is
33
+ enforced at the edit op rather than incidentally at `Card::new`.
34
+
35
+ - **Breaking (bindings + Rust API):** the schema-aware **form view is removed**.
36
+ `Quill::form` / `Quill::blank_main` / `Quill::blank_card` (and the
37
+ `quill.form` / `blankMain` / `blankCard` bindings) are gone, along with the
38
+ `Form` / `FormCard` / `FormFieldValue` / `FormFieldSource` types. Validation
39
+ diagnostics now flow through `Quill::validate(&Document) -> Vec<Diagnostic>`
40
+ (`quill.validate(doc)` in WASM/Python), which forwards the canonical
41
+ `validation::*` diagnostics and keeps the non-fatal `validation::field_absent`
42
+ completeness signal that `render` demotes. Field values/defaults/order are a
43
+ `Document` × `quill.schema` join the consumer performs directly. See
44
+ `docs/migrations/0.87-to-0.88.md`.
45
+ - **Breaking (diagnostics):** the validation code `validation::must_fill_absent`
46
+ is renamed `validation::field_absent`. "Must-fill" is now scoped to the
47
+ blueprint communication surface (the `<must-fill>` sentinel and the fatal
48
+ `validation::must_fill_sentinel`); an *absent* field is a non-fatal
49
+ completeness signal, not a fill requirement, since the render floor
50
+ zero-fills it. The schema cell axis is renamed accordingly: the no-`default:`
51
+ cell is **Unendorsed** (was "Must Fill"), the antonym of **Endorsed** —
52
+ consumers routing on the old code or label must update. Internally
53
+ `ValidationError::MustFillUnset { source }` splits into `FieldAbsent` and
54
+ `MustFillSentinel` and the `MustFillSource` enum is removed.
55
+ - **Breaking (bindings + Rust API):** the `example` reference document is
56
+ removed. `QuillConfig::example()` and the `Quill.example` (WASM) /
57
+ `Quill.example` (Python) getters are gone. Its "show me a filled-out one"
58
+ role is served by seeding — `Quill::seed_document()` / `Quill.seedDocument()`
59
+ / `Quill.seed_document()` — which returns a committed `Document` rather than
60
+ an annotated string. The CLI `render` with no input file now renders the
61
+ seeded document. Nothing consumed the example document's annotations (the
62
+ authoring surface is `blueprint()`), so the projection collapses into the
63
+ seed: internally the `FillSource` fork in blueprint emission is gone and the
64
+ blueprint always renders `default:` else the `<must-fill>` sentinel.
65
+
66
+ ## v0.87.3 - 2026-06-04
67
+
68
+ - Complete and consolidate the $ext mutator surface (#689)
69
+ - Complete the `$ext` mutator matrix with namespace-scoped removal and
70
+ card-indexed namespace ops: `remove_ext_namespace` (Rust `Card`,
71
+ `removeExtNamespace` WASM, `remove_ext_namespace` Python) plus
72
+ `setCardExtNamespace` / `removeCardExtNamespace`. Deleting a sub-namespace
73
+ is now the preferred way to clear `$ext` state — it preserves sibling
74
+ consumers' slots and drops `$ext` entirely once empty, where `removeExt`
75
+ remains a blunt clear-everything escape hatch.
76
+ - **Breaking (bindings):** the whole-map card mutator `updateCardExt` /
77
+ `update_card_ext` is renamed `setCardExt` / `set_card_ext` for naming
78
+ consistency with `setExt` on the main card.
79
+
3
80
  ## v0.87.2 - 2026-06-03
4
81
 
5
82
  - Expose $ext write path through the editor surface and bindings (#687)
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
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
@@ -326,20 +304,36 @@ export class Document {
326
304
  static quillRefHint(): string;
327
305
  removeCard(index: number): Card | undefined;
328
306
  /**
329
- * Remove the `$ext` map from the card at `index`, returning the previous
330
- * map or `undefined`. Throws if out of range.
307
+ * Remove the `$ext` map from the composable card at `index` *entirely*,
308
+ * returning the previous map or `undefined`. Throws if out of range.
309
+ * Prefer `removeCardExtNamespace` to clear only one consumer's slot.
331
310
  */
332
311
  removeCardExt(index: number): Record<string, unknown> | undefined;
312
+ /**
313
+ * Remove `namespace` from the composable card's `$ext` map, returning the
314
+ * value stored there or `undefined`; clears `$ext` entirely once empty.
315
+ * The card-indexed twin of `removeExtNamespace`. Throws if out of range.
316
+ */
317
+ removeCardExtNamespace(index: number, namespace: string): any;
333
318
  /**
334
319
  * Remove a field on the card at `index`. Returns the removed value or
335
320
  * `undefined`. Throws if `index` is out of range or `name` is invalid.
336
321
  */
337
322
  removeCardField(index: number, name: string): any;
338
323
  /**
339
- * Remove the `$ext` map from the main card, returning the previous map or
340
- * `undefined`.
324
+ * Remove the `$ext` map from the main card *entirely*, returning the
325
+ * previous map or `undefined`. This is a blunt escape hatch that discards
326
+ * every namespace at once — prefer `removeExtNamespace` to clear only your
327
+ * own slot while leaving sibling consumers' state intact.
341
328
  */
342
329
  removeExt(): Record<string, unknown> | undefined;
330
+ /**
331
+ * Remove `namespace` from the main card's `$ext` map, returning the value
332
+ * stored there or `undefined`. This is the recommended way to clear `$ext`
333
+ * state: sibling namespaces survive, and when the last namespace is removed
334
+ * the `$ext` entry is dropped entirely (not left as `$ext: {}`).
335
+ */
336
+ removeExtNamespace(namespace: string): any;
343
337
  /**
344
338
  * Remove a payload field on the main card, returning the removed value or
345
339
  * `undefined`. Throws if `name` does not match `[a-z_][a-z0-9_]*`.
@@ -353,6 +347,18 @@ export class Document {
353
347
  * `fromJson` throws.
354
348
  */
355
349
  static schemaVersionOf(json: string): string | undefined;
350
+ /**
351
+ * Replace the `$ext` map on the composable card at `index`. Throws if out
352
+ * of range or `value` is not a plain object. Named to mirror `setExt` on
353
+ * the main card; `setCardExtNamespace` is the sibling-safe alternative.
354
+ */
355
+ setCardExt(index: number, value: any): void;
356
+ /**
357
+ * Merge `value` into the composable card's `$ext` map under `namespace`,
358
+ * preserving sibling namespaces. The card-indexed twin of `setExtNamespace`.
359
+ * Throws if out of range or `value` cannot be serialized.
360
+ */
361
+ setCardExtNamespace(index: number, namespace: string, value: any): void;
356
362
  /**
357
363
  * Replace the kind of the card at `index`. Payload and body are untouched;
358
364
  * schema-aware migration is the caller's responsibility.
@@ -416,11 +422,6 @@ export class Document {
416
422
  * Replace the body of the card at `index`. Throws if out of range.
417
423
  */
418
424
  updateCardBody(index: number, body: string): void;
419
- /**
420
- * Replace the `$ext` map on the card at `index`. Throws if out of range or
421
- * `value` is not a plain object.
422
- */
423
- updateCardExt(index: number, value: any): void;
424
425
  /**
425
426
  * Update a field on the card at `index`.
426
427
  * Throws if `index` is out of range, `name` is reserved or invalid.
@@ -444,35 +445,49 @@ export class Quill {
444
445
  private constructor();
445
446
  free(): void;
446
447
  [Symbol.dispose](): void;
448
+ open(doc: Document): RenderSession;
449
+ render(doc: Document, opts?: RenderOptions | null): RenderResult;
447
450
  /**
448
- * Blank `FormCard` for the given card kind. Returns `null` if `cardKind`
449
- * 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`.
450
456
  */
451
- blankCard(card_kind: string): FormCard | null;
457
+ seedCard(card_kind: string): Card | undefined;
452
458
  /**
453
- * Blank `FormCard` for the main card with no document values.
454
- * 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".
455
465
  */
456
- blankMain(): FormCard;
466
+ seedDocument(): Document;
457
467
  /**
458
- * The schema-aware form view of `doc`. Read-only snapshot at call time;
459
- * 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.
460
472
  */
461
- form(doc: Document): Form;
462
- open(doc: Document): RenderSession;
463
- 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[];
464
486
  /**
465
487
  * The resolved backend identifier (e.g. `"typst"`).
466
488
  */
467
489
  readonly backendId: string;
468
490
  readonly blueprint: string;
469
- /**
470
- * The `example` reference document — the illustrative "show me a
471
- * filled-out one." Each field renders its `example:`, else its
472
- * `default:`, else the type-empty zero value, with no `<must-fill>`
473
- * sentinels. See `prose/canon/BLUEPRINT.md`.
474
- */
475
- readonly example: string;
476
491
  /**
477
492
  * Identity snapshot of the `quill:` section of `Quill.yaml`, plus
478
493
  * `supportedFormats` and any extra `quill:` keys.
@@ -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, 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 {
@@ -309,8 +342,9 @@ export class Document {
309
342
  return takeObject(ret);
310
343
  }
311
344
  /**
312
- * Remove the `$ext` map from the card at `index`, returning the previous
313
- * map or `undefined`. Throws if out of range.
345
+ * Remove the `$ext` map from the composable card at `index` *entirely*,
346
+ * returning the previous map or `undefined`. Throws if out of range.
347
+ * Prefer `removeCardExtNamespace` to clear only one consumer's slot.
314
348
  * @param {number} index
315
349
  * @returns {Record<string, unknown> | undefined}
316
350
  */
@@ -329,6 +363,31 @@ export class Document {
329
363
  wasm.__wbindgen_add_to_stack_pointer(16);
330
364
  }
331
365
  }
366
+ /**
367
+ * Remove `namespace` from the composable card's `$ext` map, returning the
368
+ * value stored there or `undefined`; clears `$ext` entirely once empty.
369
+ * The card-indexed twin of `removeExtNamespace`. Throws if out of range.
370
+ * @param {number} index
371
+ * @param {string} namespace
372
+ * @returns {any}
373
+ */
374
+ removeCardExtNamespace(index, namespace) {
375
+ try {
376
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
377
+ const ptr0 = passStringToWasm0(namespace, wasm.__wbindgen_export, wasm.__wbindgen_export2);
378
+ const len0 = WASM_VECTOR_LEN;
379
+ wasm.document_removeCardExtNamespace(retptr, this.__wbg_ptr, index, ptr0, len0);
380
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
381
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
382
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
383
+ if (r2) {
384
+ throw takeObject(r1);
385
+ }
386
+ return takeObject(r0);
387
+ } finally {
388
+ wasm.__wbindgen_add_to_stack_pointer(16);
389
+ }
390
+ }
332
391
  /**
333
392
  * Remove a field on the card at `index`. Returns the removed value or
334
393
  * `undefined`. Throws if `index` is out of range or `name` is invalid.
@@ -354,14 +413,30 @@ export class Document {
354
413
  }
355
414
  }
356
415
  /**
357
- * Remove the `$ext` map from the main card, returning the previous map or
358
- * `undefined`.
416
+ * Remove the `$ext` map from the main card *entirely*, returning the
417
+ * previous map or `undefined`. This is a blunt escape hatch that discards
418
+ * every namespace at once — prefer `removeExtNamespace` to clear only your
419
+ * own slot while leaving sibling consumers' state intact.
359
420
  * @returns {Record<string, unknown> | undefined}
360
421
  */
361
422
  removeExt() {
362
423
  const ret = wasm.document_removeExt(this.__wbg_ptr);
363
424
  return takeObject(ret);
364
425
  }
426
+ /**
427
+ * Remove `namespace` from the main card's `$ext` map, returning the value
428
+ * stored there or `undefined`. This is the recommended way to clear `$ext`
429
+ * state: sibling namespaces survive, and when the last namespace is removed
430
+ * the `$ext` entry is dropped entirely (not left as `$ext: {}`).
431
+ * @param {string} namespace
432
+ * @returns {any}
433
+ */
434
+ removeExtNamespace(namespace) {
435
+ const ptr0 = passStringToWasm0(namespace, wasm.__wbindgen_export, wasm.__wbindgen_export2);
436
+ const len0 = WASM_VECTOR_LEN;
437
+ const ret = wasm.document_removeExtNamespace(this.__wbg_ptr, ptr0, len0);
438
+ return takeObject(ret);
439
+ }
365
440
  /**
366
441
  * Remove a payload field on the main card, returning the removed value or
367
442
  * `undefined`. Throws if `name` does not match `[a-z_][a-z0-9_]*`.
@@ -419,6 +494,49 @@ export class Document {
419
494
  wasm.__wbindgen_add_to_stack_pointer(16);
420
495
  }
421
496
  }
497
+ /**
498
+ * Replace the `$ext` map on the composable card at `index`. Throws if out
499
+ * of range or `value` is not a plain object. Named to mirror `setExt` on
500
+ * the main card; `setCardExtNamespace` is the sibling-safe alternative.
501
+ * @param {number} index
502
+ * @param {any} value
503
+ */
504
+ setCardExt(index, value) {
505
+ try {
506
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
507
+ wasm.document_setCardExt(retptr, this.__wbg_ptr, index, addHeapObject(value));
508
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
509
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
510
+ if (r1) {
511
+ throw takeObject(r0);
512
+ }
513
+ } finally {
514
+ wasm.__wbindgen_add_to_stack_pointer(16);
515
+ }
516
+ }
517
+ /**
518
+ * Merge `value` into the composable card's `$ext` map under `namespace`,
519
+ * preserving sibling namespaces. The card-indexed twin of `setExtNamespace`.
520
+ * Throws if out of range or `value` cannot be serialized.
521
+ * @param {number} index
522
+ * @param {string} namespace
523
+ * @param {any} value
524
+ */
525
+ setCardExtNamespace(index, namespace, value) {
526
+ try {
527
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
528
+ const ptr0 = passStringToWasm0(namespace, wasm.__wbindgen_export, wasm.__wbindgen_export2);
529
+ const len0 = WASM_VECTOR_LEN;
530
+ wasm.document_setCardExtNamespace(retptr, this.__wbg_ptr, index, ptr0, len0, addHeapObject(value));
531
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
532
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
533
+ if (r1) {
534
+ throw takeObject(r0);
535
+ }
536
+ } finally {
537
+ wasm.__wbindgen_add_to_stack_pointer(16);
538
+ }
539
+ }
422
540
  /**
423
541
  * Replace the kind of the card at `index`. Payload and body are untouched;
424
542
  * schema-aware migration is the caller's responsibility.
@@ -629,25 +747,6 @@ export class Document {
629
747
  wasm.__wbindgen_add_to_stack_pointer(16);
630
748
  }
631
749
  }
632
- /**
633
- * Replace the `$ext` map on the card at `index`. Throws if out of range or
634
- * `value` is not a plain object.
635
- * @param {number} index
636
- * @param {any} value
637
- */
638
- updateCardExt(index, value) {
639
- try {
640
- const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
641
- wasm.document_updateCardExt(retptr, this.__wbg_ptr, index, addHeapObject(value));
642
- var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
643
- var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
644
- if (r1) {
645
- throw takeObject(r0);
646
- }
647
- } finally {
648
- wasm.__wbindgen_add_to_stack_pointer(16);
649
- }
650
- }
651
750
  /**
652
751
  * Update a field on the card at `index`.
653
752
  * Throws if `index` is out of range, `name` is reserved or invalid.
@@ -718,49 +817,6 @@ export class Quill {
718
817
  wasm.__wbindgen_export4(deferred1_0, deferred1_1, 1);
719
818
  }
720
819
  }
721
- /**
722
- * Blank `FormCard` for the given card kind. Returns `null` if `cardKind`
723
- * is not declared in this quill's schema.
724
- * @param {string} card_kind
725
- * @returns {FormCard | null}
726
- */
727
- blankCard(card_kind) {
728
- try {
729
- const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
730
- const ptr0 = passStringToWasm0(card_kind, wasm.__wbindgen_export, wasm.__wbindgen_export2);
731
- const len0 = WASM_VECTOR_LEN;
732
- wasm.quill_blankCard(retptr, this.__wbg_ptr, ptr0, len0);
733
- var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
734
- var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
735
- var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
736
- if (r2) {
737
- throw takeObject(r1);
738
- }
739
- return takeObject(r0);
740
- } finally {
741
- wasm.__wbindgen_add_to_stack_pointer(16);
742
- }
743
- }
744
- /**
745
- * Blank `FormCard` for the main card with no document values.
746
- * Every field's `source` is `"default"` or `"missing"`.
747
- * @returns {FormCard}
748
- */
749
- blankMain() {
750
- try {
751
- const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
752
- wasm.quill_blankMain(retptr, this.__wbg_ptr);
753
- var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
754
- var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
755
- var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
756
- if (r2) {
757
- throw takeObject(r1);
758
- }
759
- return takeObject(r0);
760
- } finally {
761
- wasm.__wbindgen_add_to_stack_pointer(16);
762
- }
763
- }
764
820
  /**
765
821
  * @returns {string}
766
822
  */
@@ -780,51 +836,6 @@ export class Quill {
780
836
  wasm.__wbindgen_export4(deferred1_0, deferred1_1, 1);
781
837
  }
782
838
  }
783
- /**
784
- * The `example` reference document — the illustrative "show me a
785
- * filled-out one." Each field renders its `example:`, else its
786
- * `default:`, else the type-empty zero value, with no `<must-fill>`
787
- * sentinels. See `prose/canon/BLUEPRINT.md`.
788
- * @returns {string}
789
- */
790
- get example() {
791
- let deferred1_0;
792
- let deferred1_1;
793
- try {
794
- const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
795
- wasm.quill_example(retptr, this.__wbg_ptr);
796
- var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
797
- var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
798
- deferred1_0 = r0;
799
- deferred1_1 = r1;
800
- return getStringFromWasm0(r0, r1);
801
- } finally {
802
- wasm.__wbindgen_add_to_stack_pointer(16);
803
- wasm.__wbindgen_export4(deferred1_0, deferred1_1, 1);
804
- }
805
- }
806
- /**
807
- * The schema-aware form view of `doc`. Read-only snapshot at call time;
808
- * subsequent edits to `doc` require calling `form` again.
809
- * @param {Document} doc
810
- * @returns {Form}
811
- */
812
- form(doc) {
813
- try {
814
- const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
815
- _assertClass(doc, Document);
816
- wasm.quill_form(retptr, this.__wbg_ptr, doc.__wbg_ptr);
817
- var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
818
- var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
819
- var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
820
- if (r2) {
821
- throw takeObject(r1);
822
- }
823
- return takeObject(r0);
824
- } finally {
825
- wasm.__wbindgen_add_to_stack_pointer(16);
826
- }
827
- }
828
839
  /**
829
840
  * Identity snapshot of the `quill:` section of `Quill.yaml`, plus
830
841
  * `supportedFormats` and any extra `quill:` keys.
@@ -883,6 +894,45 @@ export class Quill {
883
894
  const ret = wasm.quill_schema(this.__wbg_ptr);
884
895
  return takeObject(ret);
885
896
  }
897
+ /**
898
+ * Seed a starter composable `Card` of the given kind (carries `$kind`),
899
+ * committing its fields' `example:` values and leaving every other field
900
+ * absent. Returns `undefined` if `cardKind` is not declared in this
901
+ * quill's schema, else a `Card` that feeds straight into
902
+ * `Document.pushCard` / `insertCard`.
903
+ * @param {string} card_kind
904
+ * @returns {Card | undefined}
905
+ */
906
+ seedCard(card_kind) {
907
+ const ptr0 = passStringToWasm0(card_kind, wasm.__wbindgen_export, wasm.__wbindgen_export2);
908
+ const len0 = WASM_VECTOR_LEN;
909
+ const ret = wasm.quill_seedCard(this.__wbg_ptr, ptr0, len0);
910
+ return takeObject(ret);
911
+ }
912
+ /**
913
+ * Seed a starter `Document` from the schema — the main card plus one
914
+ * instance of each composable card kind, each committing its fields'
915
+ * `example:` values and leaving every other field absent (interpolated at
916
+ * render: `default:`, else type-empty zero). Illustration-first: a field
917
+ * with both an `example` and a `default` renders its example. See
918
+ * `prose/canon/SCHEMAS.md` § "Document seeding".
919
+ * @returns {Document}
920
+ */
921
+ seedDocument() {
922
+ const ret = wasm.quill_seedDocument(this.__wbg_ptr);
923
+ return Document.__wrap(ret);
924
+ }
925
+ /**
926
+ * Seed a starter main `Card` (carries `$quill`) from the schema — the
927
+ * `$kind: main` card of [`seedDocument`](Self::seed_document) in
928
+ * isolation, committing each field's `example:` value. Returns the same
929
+ * `Card` shape as the `Document.main` getter.
930
+ * @returns {Card}
931
+ */
932
+ seedMain() {
933
+ const ret = wasm.quill_seedMain(this.__wbg_ptr);
934
+ return takeObject(ret);
935
+ }
886
936
  /**
887
937
  * `true` iff `RenderSession.paint` and `RenderSession.pageSize` will
888
938
  * succeed for sessions opened by this quill. Use as a precondition
@@ -893,6 +943,35 @@ export class Quill {
893
943
  const ret = wasm.quill_supportsCanvas(this.__wbg_ptr);
894
944
  return ret !== 0;
895
945
  }
946
+ /**
947
+ * Validate `doc` against this quill's schema, returning every diagnostic
948
+ * (an empty array when the document is valid).
949
+ *
950
+ * Forwards the canonical `validation::*` diagnostics — same `code`,
951
+ * `path`, and `hint` the engine emits — including the non-fatal
952
+ * `validation::field_absent` completeness signal that `render` demotes.
953
+ * Field values, defaults, and order are not part of this surface: read
954
+ * them from the `Document` payload and `Quill.schema` (fields carry
955
+ * `ui.order`).
956
+ * @param {Document} doc
957
+ * @returns {Diagnostic[]}
958
+ */
959
+ validate(doc) {
960
+ try {
961
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
962
+ _assertClass(doc, Document);
963
+ wasm.quill_validate(retptr, this.__wbg_ptr, doc.__wbg_ptr);
964
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
965
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
966
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
967
+ if (r2) {
968
+ throw takeObject(r1);
969
+ }
970
+ return takeObject(r0);
971
+ } finally {
972
+ wasm.__wbindgen_add_to_stack_pointer(16);
973
+ }
974
+ }
896
975
  }
897
976
  if (Symbol.dispose) Quill.prototype[Symbol.dispose] = Quill.prototype.free;
898
977
 
@@ -1093,10 +1172,6 @@ export function __wbg_Error_960c155d3d49e4c2(arg0, arg1) {
1093
1172
  const ret = Error(getStringFromWasm0(arg0, arg1));
1094
1173
  return addHeapObject(ret);
1095
1174
  }
1096
- export function __wbg_Number_32bf70a599af1d4b(arg0) {
1097
- const ret = Number(getObject(arg0));
1098
- return ret;
1099
- }
1100
1175
  export function __wbg_String_8564e559799eccda(arg0, arg1) {
1101
1176
  const ret = String(getObject(arg1));
1102
1177
  const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
@@ -1291,6 +1366,16 @@ export function __wbg_instanceof_Map_1b76fd4635be43eb(arg0) {
1291
1366
  const ret = result;
1292
1367
  return ret;
1293
1368
  }
1369
+ export function __wbg_instanceof_Object_7c99480a1cdfb911(arg0) {
1370
+ let result;
1371
+ try {
1372
+ result = getObject(arg0) instanceof Object;
1373
+ } catch (_) {
1374
+ result = false;
1375
+ }
1376
+ const ret = result;
1377
+ return ret;
1378
+ }
1294
1379
  export function __wbg_instanceof_OffscreenCanvasRenderingContext2d_285a274020b4f230(arg0) {
1295
1380
  let result;
1296
1381
  try {
@@ -1323,6 +1408,10 @@ export function __wbg_iterator_013bc09ec998c2a7() {
1323
1408
  const ret = Symbol.iterator;
1324
1409
  return addHeapObject(ret);
1325
1410
  }
1411
+ export function __wbg_keys_2fd1bfdda7e278ca(arg0) {
1412
+ const ret = Object.keys(getObject(arg0));
1413
+ return addHeapObject(ret);
1414
+ }
1326
1415
  export function __wbg_length_3d4ecd04bd8d22f1(arg0) {
1327
1416
  const ret = getObject(arg0).length;
1328
1417
  return ret;
Binary file
@@ -17,17 +17,22 @@ 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;
23
24
  export const document_quillRefHint: (a: number) => void;
24
25
  export const document_removeCard: (a: number, b: number) => number;
25
26
  export const document_removeCardExt: (a: number, b: number, c: number) => void;
27
+ export const document_removeCardExtNamespace: (a: number, b: number, c: number, d: number, e: number) => void;
26
28
  export const document_removeCardField: (a: number, b: number, c: number, d: number, e: number) => void;
27
29
  export const document_removeExt: (a: number) => number;
30
+ export const document_removeExtNamespace: (a: number, b: number, c: number) => number;
28
31
  export const document_removeField: (a: number, b: number, c: number, d: number) => void;
29
32
  export const document_replaceBody: (a: number, b: number, c: number) => void;
30
33
  export const document_schemaVersionOf: (a: number, b: number, c: number) => void;
34
+ export const document_setCardExt: (a: number, b: number, c: number, d: number) => void;
35
+ export const document_setCardExtNamespace: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
31
36
  export const document_setCardKind: (a: number, b: number, c: number, d: number, e: number) => void;
32
37
  export const document_setExt: (a: number, b: number, c: number) => void;
33
38
  export const document_setExtNamespace: (a: number, b: number, c: number, d: number, e: number) => void;
@@ -38,21 +43,20 @@ export const document_toJson: (a: number, b: number) => void;
38
43
  export const document_toMarkdown: (a: number, b: number) => void;
39
44
  export const document_tryFromJson: (a: number, b: number) => number;
40
45
  export const document_updateCardBody: (a: number, b: number, c: number, d: number, e: number) => void;
41
- export const document_updateCardExt: (a: number, b: number, c: number, d: number) => void;
42
46
  export const document_updateCardField: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
43
47
  export const document_warnings: (a: number) => number;
44
48
  export const init: () => void;
45
49
  export const quill_backendId: (a: number, b: number) => void;
46
- export const quill_blankCard: (a: number, b: number, c: number, d: number) => void;
47
- export const quill_blankMain: (a: number, b: number) => void;
48
50
  export const quill_blueprint: (a: number, b: number) => void;
49
- export const quill_example: (a: number, b: number) => void;
50
- export const quill_form: (a: number, b: number, c: number) => void;
51
51
  export const quill_metadata: (a: number) => number;
52
52
  export const quill_open: (a: number, b: number, c: number) => void;
53
53
  export const quill_render: (a: number, b: number, c: number, d: number) => void;
54
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;
55
58
  export const quill_supportsCanvas: (a: number) => number;
59
+ export const quill_validate: (a: number, b: number, c: number) => void;
56
60
  export const quillmark_new: () => number;
57
61
  export const quillmark_quill: (a: number, b: number, c: number) => void;
58
62
  export const rendersession_backendId: (a: number, b: number) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quillmark/wasm",
3
- "version": "0.87.2",
3
+ "version": "0.88.0-rc.1",
4
4
  "description": "WebAssembly bindings for quillmark",
5
5
  "type": "module",
6
6
  "license": "MIT OR Apache-2.0",