@quillmark/wasm 0.107.0 → 0.108.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,109 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.108.1 - 2026-08-19
4
+
5
+ - fix: **a content cell under `variants:` is readable at its codec.**
6
+ `schema_at`, the walk behind `reader.get_content_at`, stepped `items` and
7
+ `properties` where conform steps `variants` as well, so a key into a variant
8
+ container fell to the catch-all and answered `edit::field_not_content` naming
9
+ `enum` — for a cell the same config declares `plaintext`. Such a cell stored,
10
+ conformed, seeded, validated and rendered, and nothing could read it back, so
11
+ no consumer could mount a content editor over it. The walk unions the worlds,
12
+ so a cell of a world that is not live reads absent rather than raising, as a
13
+ stale row index does; a name no world declares is `edit::unknown_field`, and a
14
+ variantless enum stays a scalar. The write is unchanged — a variant container
15
+ has no per-cell op address and needs none, committing whole.
16
+
17
+ <!-- seed: commits since v0.108.0, confirm the entries above cover them, then delete this comment
18
+ - fix: a content cell under `variants:` reads at its codec
19
+ -->
20
+
21
+
22
+ ## v0.108.0 - 2026-08-18
23
+
24
+ - fix: **the value ladder is cut per cell, so the plate is total at every
25
+ depth.** An absent container returned a value instead of descending, and
26
+ everything below it was decided by that one branch: an absent `contact` never
27
+ reached `contact.email`'s own `default:`, and a container `default: {name: A}`
28
+ crossed whole, so a declared property it omitted was **missing from the
29
+ plate** — a direct Typst read of it a compile error, on an address
30
+ `form-field` still binds. Two spellings of the same state disagreed:
31
+ authoring `contact: {}` rendered the leaf defaults that leaving `contact` out
32
+ did not, and `default: {}` — documented as expanding to the blank-filled
33
+ shape — emitted `{}` with no declared key at all. Resolution is now a descent:
34
+ a rung supplies a *seed*, and the same composition runs over it whichever rung
35
+ it came from, so absence is inherited rather than terminal and each cell cuts
36
+ its own ladder. A partial element inside an `array` `default:` is completed
37
+ against `items` as an authored element is. The variant container already
38
+ worked this way and stops being the special case.
39
+ - **breaking** a `default:`/`example:` on an `object` with `properties` is a
40
+ load error (`quill::default_on_namespace`, `quill::example_on_namespace`),
41
+ naming the properties that hold it. A quill declaring one loaded before, so
42
+ the upgrade reads as a quill that stopped loading rather than as a fix; no
43
+ in-tree quill declares one. A typed dictionary is a namespace, not a cell:
44
+ the container literal was a second declaration of a value the property
45
+ already holds, and the two axes read different ones — `default: {name: A}`
46
+ rendered `A` while `must_fill` derives per property and still reported `name`
47
+ unauthored. It was also unchecked, so `default: {nope: 1}` loaded and crossed
48
+ an undeclared key to the plate. This is the variant container's rule
49
+ (`quill::default_type_mismatch`) generalized; an `array` keeps its literal,
50
+ since `items:` fixes the element type but never the arity.
51
+ - **breaking** `must_fill:` is retired: obligation is a reading of `default:`,
52
+ never a declaration of its own. Declaring the key is a load error
53
+ (`quill::field_parse_error`) naming the migration that field's shape takes,
54
+ `FieldSchema::must_fill()` is `default.is_none()`, and the raw
55
+ `FieldSchema.must_fill` field is gone. Four of the five legacy declarations
56
+ restate the derivation and migrate by **deletion**: `must_fill:` on a typed
57
+ dictionary (a namespace carries no obligation — its leaves do), `must_fill:
58
+ true` with no `default:`, and `must_fill: false` beside one. `must_fill:
59
+ false` with no `default:` becomes `default: <the type's blank>` (`""`, `[]`,
60
+ `0`, `false`) — already the corpus's most common `default:`. The fifth,
61
+ `must_fill: true` beside a `default:`, is the one behavior deleted and the
62
+ one judgment call: keep the `default:` to render the value unasked, or move
63
+ it to `example:` to keep the ask. An example fills the blueprint cell the
64
+ default vacated, seeds *carrying* the `!must_fill` marker where a
65
+ `default:`-only field seeds nothing, and never renders — so an untouched
66
+ document renders the blank rather than asserting a value nobody chose. For a
67
+ `string` or `enum` the blueprint bytes are identical either way; three shapes
68
+ are not. A `richtext` example never inlines, so its cell becomes a bare
69
+ marker and the value survives only as the `# e.g.` hint. An
70
+ `integer`/`number`/`boolean` blank is indistinguishable at the plate from an
71
+ authored zero. On a variant container the two targets select different
72
+ worlds: `default: CUI` renders the CUI world and obliges its cells, while
73
+ `example: CUI` leaves the discriminant blank. Also removed:
74
+ `quillmark:must_fill` from the transform schema, which is the wire *validity*
75
+ contract, and an unauthored must-fill cell is wire-valid by design; the
76
+ declaration view carries `default:` for a consumer that wants to derive.
77
+ No in-tree quill declared the key and the declaration view emits only what an
78
+ author wrote, so no emitted JSON changes for any real quill — the WASM
79
+ `QuillFieldSchema` TS interface loses `must_fill?: boolean`, a compile-time
80
+ break for editors typed against it.
81
+ - fix: **seeding descends into a container's `example:`.** A dictionary with no
82
+ `example:` of its own seeded nothing, so a property's `example:` was
83
+ unreachable at every projection — the render floor never emits an example, and
84
+ the blueprint is a different document. A seed is now composed from whatever
85
+ its cells commit, sparse at every depth, and stays absent when none of them
86
+ commit anything. Markers ride the cell they belong to.
87
+ - fix: `resolve()`'s rung is honest for a container. It has no rung of its own,
88
+ so it reports the strongest that contributed: `authored` when the document
89
+ wrote any of it, else `default` when a cell below resolved to one, else the
90
+ floor. An absent container over defaulted cells read `blank` while rendering
91
+ those defaults, which is the fact an editor ghosts from. Nothing inside a
92
+ container the document did not author reads `authored`. A variant container
93
+ counts its live world's cells the same way, so writing one of them lifts a
94
+ container whose discriminant fell to the schema's `default:`.
95
+
96
+ - chore(deps): the Typst floor moves to 0.15.1. The workspace already resolved
97
+ there under the 0.15.0 caret; the pin now names the version the tree is built
98
+ and tested against. `pdf-writer` stays at 0.15.0, still the version
99
+ `typst-pdf` → `krilla` forces and the newest published.
100
+ - docs: `0.107-to-0.108.md`, the guide for this step. It leads with the two load
101
+ errors, since both reject the quill rather than the document, and gives
102
+ `must_fill: true` beside a `default:` — the one behavior deleted — the space
103
+ its judgment call needs. `BLUEPRINT.md` § "Typed dictionaries" loses the `{}`
104
+ expansion and the container-literal renderings with the cascade that produced
105
+ them, and states the nesting the 0.107 collapse admits.
106
+
3
107
  ## v0.107.0 - 2026-08-17
4
108
 
5
109
  - fix(typst): `display(field, ..)` validates its address against the schema, the
@@ -235,37 +339,6 @@
235
339
  address-keyed template-author contract, `plaintext(..)` having been removed
236
340
  here.
237
341
 
238
- <!-- seed: commits since v0.106.0, confirm the entries above cover them, then delete this comment
239
- - fix: a container's own `default:` crosses as content, or is refused
240
- - fix(typst): anchor a `let` alias only where the name is read
241
- - fix(typst): display validates its schema address
242
- - fix(docs): point the variants cross-reference at this page's own anchor
243
- - fix(docs): link canon by URL, not by a path outside the docs tree
244
- - docs: dense-prose pass over the nesting collapse
245
- - feat: every type nests at every depth
246
- - docs: dense-prose pass over the two address walks
247
- - feat(typst)!: the span scan takes the index step, so a row cell read anchors on the cell
248
- - fix: a nested content leaf's `default:` reaches the plate
249
- - docs: dense-prose pass over the address-grammar pin
250
- - test: pin the schema address grammar across both backends
251
- - Carry the prose style in CLAUDE.md at minimum size
252
- - Drop two CLAUDE.md lines that steer toward defaults
253
- - Cut the rules that steer toward defaults
254
- - docs: dense-prose pass over the lowering walk
255
- - Cut two lines that restated defaults
256
- - Rewrite dense-prose as a project-agnostic skill
257
- - feat!: a variant carries any leaf type, prose and dates included
258
- - refactor(typst)!: delete the `plaintext(field)` projection
259
- - feat(typst)!: depth-invariant lowering, and `date` as a native `datetime`
260
- - docs: dense-prose pass over the alias pass
261
- - docs: a bound read keeps its address, and what still needs a claim
262
- - fix(typst): disqualify aliases on a wildcard import in either order
263
- - feat(typst): follow a single-assignment `let` alias to its schema address
264
- - feat(wasm): export VARIANT_DISCRIMINANT_KEY to the runtime surface
265
- - docs: the 0.105 → 0.106 migration guide, and the rules that missed it
266
- -->
267
-
268
-
269
342
  ## v0.106.0 - 2026-08-16
270
343
 
271
344
  - feat(typst,pdfform): a schema address may step one property into a declared
package/README.md CHANGED
@@ -396,8 +396,8 @@ that value under a type-only `# <type>` annotation and the render path uses it
396
396
  when the document omits the field. Without one, an `example` takes the cell as
397
397
  a suggested value, and an absent field blank-fills.
398
398
 
399
- **Obligation** — whether a human must author the field, declared by
400
- `must_fill:` and deriving from `default:`'s absence when left unset. An obliged
399
+ **Obligation** — whether a human must author the field, read off `default:`'s
400
+ absence: a defaulted field asks nobody, a defaultless one asks. An obliged
401
401
  field carries the `!must_fill` marker in `quill.blueprint`, and
402
402
  `quill.validate(doc)` emits the non-fatal `validation::must_fill` warning while
403
403
  the document leaves it unauthored — from either of two triggers, named by the
@@ -347,7 +347,14 @@ export interface PaintResult {
347
347
 
348
348
 
349
349
 
350
- /** The commitment-ladder rung that produced a `ResolvedField.value`. */
350
+ /**
351
+ * The commitment-ladder rung that produced a `ResolvedField.value`.
352
+ *
353
+ * A container has no rung of its own — it is a namespace, and its value is the
354
+ * composition of its cells' — so it reports the strongest rung that contributed:
355
+ * `authored` if the document wrote any of it, else `default` if any cell below
356
+ * resolved to one, else `blank`.
357
+ */
351
358
  export type FieldSource = "authored" | "default" | "blank";
352
359
 
353
360
  /**
@@ -447,12 +454,11 @@ export interface QuillCardBody {
447
454
 
448
455
  /** Schema entry for a single field declared in a quill's `Quill.yaml`.
449
456
  *
450
- * Two independent axes, and no separate `required` one. `default` and
451
- * `example` say what the cell holds; `must_fill` says whether a human must
452
- * author it, deriving from `default`'s absence when left unset. An obliged
453
- * field carries a `!must_fill` marker in the blueprint and warns
457
+ * One declaration, and no `required` key. `default` and `example` say what the
458
+ * cell holds, and `default`'s absence is the obligation: a field nobody
459
+ * declared a value for carries a `!must_fill` marker in the blueprint and warns
454
460
  * `validation::must_fill` while the document leaves it unauthored. Neither
455
- * axis gates render: an absent field blank-fills.
461
+ * gates render: an absent field blank-fills.
456
462
  */
457
463
  export interface QuillFieldSchema {
458
464
  type: "string" | "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "richtext" | "plaintext" | "enum";
@@ -467,9 +473,6 @@ export interface QuillFieldSchema {
467
473
  * member. Declaring it makes the field rest as a container,
468
474
  * `{value: <member>, …that member's fields}`, rather than a bare string. */
469
475
  variants?: Record<string, Record<string, QuillFieldSchema>>;
470
- /** Whether a human must author the field. Absent, it derives from
471
- * `default`: a defaulted field is unobliged, a defaultless one obliged. */
472
- must_fill?: boolean;
473
476
  ui?: QuillFieldUi;
474
477
  properties?: Record<string, QuillFieldSchema>;
475
478
  items?: QuillFieldSchema;
@@ -196,7 +196,8 @@ export class Document {
196
196
  * and [`reader.getContent`](Self::reader_get_content) with the path spelled
197
197
  * out. `path` is a `PathStep[]` from the field to the leaf — `[0]` an
198
198
  * element of an `array<richtext>`, `["motto"]` an `object`'s content
199
- * property, `[1, "notes"]` a leaf under both.
199
+ * property, `[1, "notes"]` a leaf under both, `["controlled_by"]` a
200
+ * variant's cell.
200
201
  *
201
202
  * The codec is the leaf's declared type's, so the caller stops deciding
202
203
  * what an element's stored bytes mean. Total over the storage form, as the
Binary file
@@ -347,7 +347,14 @@ export interface PaintResult {
347
347
 
348
348
 
349
349
 
350
- /** The commitment-ladder rung that produced a `ResolvedField.value`. */
350
+ /**
351
+ * The commitment-ladder rung that produced a `ResolvedField.value`.
352
+ *
353
+ * A container has no rung of its own — it is a namespace, and its value is the
354
+ * composition of its cells' — so it reports the strongest rung that contributed:
355
+ * `authored` if the document wrote any of it, else `default` if any cell below
356
+ * resolved to one, else `blank`.
357
+ */
351
358
  export type FieldSource = "authored" | "default" | "blank";
352
359
 
353
360
  /**
@@ -447,12 +454,11 @@ export interface QuillCardBody {
447
454
 
448
455
  /** Schema entry for a single field declared in a quill's `Quill.yaml`.
449
456
  *
450
- * Two independent axes, and no separate `required` one. `default` and
451
- * `example` say what the cell holds; `must_fill` says whether a human must
452
- * author it, deriving from `default`'s absence when left unset. An obliged
453
- * field carries a `!must_fill` marker in the blueprint and warns
457
+ * One declaration, and no `required` key. `default` and `example` say what the
458
+ * cell holds, and `default`'s absence is the obligation: a field nobody
459
+ * declared a value for carries a `!must_fill` marker in the blueprint and warns
454
460
  * `validation::must_fill` while the document leaves it unauthored. Neither
455
- * axis gates render: an absent field blank-fills.
461
+ * gates render: an absent field blank-fills.
456
462
  */
457
463
  export interface QuillFieldSchema {
458
464
  type: "string" | "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "richtext" | "plaintext" | "enum";
@@ -467,9 +473,6 @@ export interface QuillFieldSchema {
467
473
  * member. Declaring it makes the field rest as a container,
468
474
  * `{value: <member>, …that member's fields}`, rather than a bare string. */
469
475
  variants?: Record<string, Record<string, QuillFieldSchema>>;
470
- /** Whether a human must author the field. Absent, it derives from
471
- * `default`: a defaulted field is unobliged, a defaultless one obliged. */
472
- must_fill?: boolean;
473
476
  ui?: QuillFieldUi;
474
477
  properties?: Record<string, QuillFieldSchema>;
475
478
  items?: QuillFieldSchema;
@@ -196,7 +196,8 @@ export class Document {
196
196
  * and [`reader.getContent`](Self::reader_get_content) with the path spelled
197
197
  * out. `path` is a `PathStep[]` from the field to the leaf — `[0]` an
198
198
  * element of an `array<richtext>`, `["motto"]` an `object`'s content
199
- * property, `[1, "notes"]` a leaf under both.
199
+ * property, `[1, "notes"]` a leaf under both, `["controlled_by"]` a
200
+ * variant's cell.
200
201
  *
201
202
  * The codec is the leaf's declared type's, so the caller stops deciding
202
203
  * what an element's stored bytes mean. Total over the storage form, as the
Binary file
package/core/wasm.d.ts CHANGED
@@ -292,7 +292,14 @@ export type DocPathSeg =
292
292
 
293
293
 
294
294
 
295
- /** The commitment-ladder rung that produced a `ResolvedField.value`. */
295
+ /**
296
+ * The commitment-ladder rung that produced a `ResolvedField.value`.
297
+ *
298
+ * A container has no rung of its own — it is a namespace, and its value is the
299
+ * composition of its cells' — so it reports the strongest rung that contributed:
300
+ * `authored` if the document wrote any of it, else `default` if any cell below
301
+ * resolved to one, else `blank`.
302
+ */
296
303
  export type FieldSource = "authored" | "default" | "blank";
297
304
 
298
305
  /**
@@ -392,12 +399,11 @@ export interface QuillCardBody {
392
399
 
393
400
  /** Schema entry for a single field declared in a quill's `Quill.yaml`.
394
401
  *
395
- * Two independent axes, and no separate `required` one. `default` and
396
- * `example` say what the cell holds; `must_fill` says whether a human must
397
- * author it, deriving from `default`'s absence when left unset. An obliged
398
- * field carries a `!must_fill` marker in the blueprint and warns
402
+ * One declaration, and no `required` key. `default` and `example` say what the
403
+ * cell holds, and `default`'s absence is the obligation: a field nobody
404
+ * declared a value for carries a `!must_fill` marker in the blueprint and warns
399
405
  * `validation::must_fill` while the document leaves it unauthored. Neither
400
- * axis gates render: an absent field blank-fills.
406
+ * gates render: an absent field blank-fills.
401
407
  */
402
408
  export interface QuillFieldSchema {
403
409
  type: "string" | "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "richtext" | "plaintext" | "enum";
@@ -412,9 +418,6 @@ export interface QuillFieldSchema {
412
418
  * member. Declaring it makes the field rest as a container,
413
419
  * `{value: <member>, …that member's fields}`, rather than a bare string. */
414
420
  variants?: Record<string, Record<string, QuillFieldSchema>>;
415
- /** Whether a human must author the field. Absent, it derives from
416
- * `default`: a defaulted field is unobliged, a defaultless one obliged. */
417
- must_fill?: boolean;
418
421
  ui?: QuillFieldUi;
419
422
  properties?: Record<string, QuillFieldSchema>;
420
423
  items?: QuillFieldSchema;
package/core/wasm.js CHANGED
@@ -196,7 +196,8 @@ export class Document {
196
196
  * and [`reader.getContent`](Self::reader_get_content) with the path spelled
197
197
  * out. `path` is a `PathStep[]` from the field to the leaf — `[0]` an
198
198
  * element of an `array<richtext>`, `["motto"]` an `object`'s content
199
- * property, `[1, "notes"]` a leaf under both.
199
+ * property, `[1, "notes"]` a leaf under both, `["controlled_by"]` a
200
+ * variant's cell.
200
201
  *
201
202
  * The codec is the leaf's declared type's, so the caller stops deciding
202
203
  * what an element's stored bytes mean. Total over the storage form, as the
package/core/wasm_bg.wasm CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quillmark/wasm",
3
- "version": "0.107.0",
3
+ "version": "0.108.1",
4
4
  "description": "WebAssembly bindings for Quillmark, a schema-driven document engine",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -771,10 +771,10 @@ export declare class DocumentReader {
771
771
  /**
772
772
  * Read the `Content` nested inside the composite field at `addr`, at `path`:
773
773
  * `[0]` an element of an `array<richtext>`, `["motto"]` an object's content
774
- * property, `[1, "notes"]` a leaf under both. The codec is the leaf's declared
775
- * type's, resolved through the field schema's `items` / `properties`, so the
776
- * element's storage form is not the caller's business. The empty path is
777
- * {@link getContent}.
774
+ * property, `[1, "notes"]` a leaf under both, `["controlled_by"]` a variant's
775
+ * cell. The codec is the leaf's declared type's, resolved through the field
776
+ * schema's `items` / `properties` / `variants`, so the element's storage
777
+ * form is not the caller's business. The empty path is {@link getContent}.
778
778
  *
779
779
  * `undefined` for an absent field and for a path that names nothing in the
780
780
  * stored value: a repeater's row index goes stale between derive and read,
@@ -1205,7 +1205,8 @@ export class DocumentReader {
1205
1205
  /**
1206
1206
  * Read the `Content` nested inside the composite field at `addr`, at `path`:
1207
1207
  * `[0]` an `array<richtext>` element, `["motto"]` an object's content property,
1208
- * `[1, "notes"]` a leaf under both. The codec is the leaf's declared type's.
1208
+ * `[1, "notes"]` a leaf under both, `["controlled_by"]` a variant's cell. The
1209
+ * codec is the leaf's declared type's.
1209
1210
  * `undefined` for an absent field and for a path that names nothing stored;
1210
1211
  * throws `UnknownField`, `FieldNotContent` when `path` resolves to no content
1211
1212
  * leaf, `FieldDecode` anchored at the addressed path, and `IndexOutOfRange`.