@quillmark/wasm 0.125.0 → 0.126.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/core/wasm.d.ts CHANGED
@@ -446,11 +446,10 @@ export interface Resolved {
446
446
  /** UI layout hints for a single field. Display order is not a hint: key order
447
447
  * in the schema's `fields`/`properties` objects is the ordering contract. */
448
448
  export interface QuillFieldUi {
449
- /** The id of the card section holding the field, a key of the card's
449
+ /** The id of the card group holding the field, a key of the card's
450
450
  * `ui.groups`. */
451
451
  group?: string;
452
452
  compact?: boolean;
453
- multiline?: boolean;
454
453
  /** Label for an `enum`'s blank option. Absent, the consumer supplies a
455
454
  * conventional label of its own. */
456
455
  blank_title?: string;
@@ -464,8 +463,8 @@ export interface QuillFieldUi {
464
463
  layout?: "table" | "flat";
465
464
  }
466
465
 
467
- /** One section in a card's `ui.groups`: a display-label override for the
468
- * section id (the map key). An empty object carries no override, and the
466
+ /** One group in a card's `ui.groups`: a display-label override for the
467
+ * group id (the map key). An empty object carries no override, and the
469
468
  * consumer derives the label from the id (`memo_for` → "Memo For"). */
470
469
  export interface QuillGroupUi {
471
470
  title?: string;
@@ -473,9 +472,9 @@ export interface QuillGroupUi {
473
472
 
474
473
  /** UI layout hints for a card (main or named card kind). */
475
474
  export interface QuillCardUi {
476
- /** The card's sections keyed by id, each holding at least one field, which
475
+ /** The card's groups keyed by id, each holding at least one field, which
477
476
  * names it in its `ui.group`. Key order is the display-order contract, as
478
- * with `fields`. Absent when the card declares no sections. */
477
+ * with `fields`. Absent when the card declares no groups. */
479
478
  groups?: Record<string, QuillGroupUi>;
480
479
  }
481
480
 
@@ -485,7 +484,7 @@ export interface QuillCardBody {
485
484
  enabled?: boolean;
486
485
  }
487
486
 
488
- export type QuillFieldType = "string" | "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "richtext" | "plaintext" | "enum" | "matrix";
487
+ export type QuillFieldType = "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "text" | "richtext" | "enum" | "matrix";
489
488
 
490
489
  /** Schema entry for a single field declared in a quill's `Quill.yaml`.
491
490
  *
@@ -530,9 +529,12 @@ export interface QuillFieldSchema {
530
529
  * out on. Valid only on an `array`. Never gates render: a document over
531
530
  * the cap warns `validation::cardinality` and renders. */
532
531
  max?: number;
533
- /** `true` on a `richtext` or `plaintext` field declared `inline`: the
534
- * single-paragraph, container-free, island-free constraint. */
535
- inline?: boolean;
532
+ /** The line shape of a `text` or `richtext` field, valid nowhere else.
533
+ * Absent or `false`, the field is one line (`text`) or one paragraph with
534
+ * no container or island (`richtext`); `true` admits line breaks
535
+ * (`text`) or block structure (`richtext`). A `richtext` always carries
536
+ * it. */
537
+ multiline?: boolean;
536
538
  }
537
539
 
538
540
  /** Schema entry for the main card or a named card kind. */
@@ -572,6 +574,10 @@ export interface QuillMetadata {
572
574
  backend: string;
573
575
  author: string;
574
576
  description: string;
577
+ /** An SPDX license expression; absent when the quill declares none. */
578
+ license?: string;
579
+ /** An absolute `http(s)` URL; absent when the quill declares none. */
580
+ homepage?: string;
575
581
  }
576
582
 
577
583
 
@@ -686,7 +692,7 @@ export class Document {
686
692
  * `edit::index_out_of_range`.
687
693
  *
688
694
  * A content field at rest has one stored form per codec: a `richtext` field
689
- * holds the canonical content object, a `plaintext` field its literal
695
+ * holds the canonical content object, a `text` field its literal
690
696
  * string. A document from the bound door (`quill.parse` / `quill.conform`)
691
697
  * is at rest; one from the transport door may rest as authored until it is
692
698
  * conformed, and this read reports what is there. For the `Content` either
@@ -699,8 +705,9 @@ export class Document {
699
705
  /**
700
706
  * Insert a card: `at` absent appends, a number inserts at that index (in
701
707
  * `0..=cards.length`). Accepts any `CardInput`, including a card read back
702
- * out of a document. Throws if `card.kind` is not a valid kind name, or if
703
- * `at` is out of range.
708
+ * out of a document. Throws if `card.kind` is not a valid kind name, if
709
+ * `at` is out of range, or `edit::too_many_cards` if the document holds
710
+ * 1,000 cards.
704
711
  */
705
712
  insertCard(card: CardInput, at?: number): void;
706
713
  /**
@@ -913,7 +920,7 @@ export class Quill {
913
920
  * The quill's example document, `undefined` when the quill has no
914
921
  * `example.md` at its root: a filled-in page whose values are made up to show the
915
922
  * quill in use, never kept. It comes back pinned to this quill's
916
- * `name@version` and conformed as `parse` conforms, its parse and
923
+ * `name@MAJOR.MINOR` and conformed as `parse` conforms, its parse and
917
924
  * `conform::*` warnings on `doc.warnings`. Throws when the file does not
918
925
  * parse or its `$quill` is not this quill's bare name.
919
926
  */
@@ -928,7 +935,7 @@ export class Quill {
928
935
  * Parse `markdown` and conform it against this quill: the primary ingestion
929
936
  * path, and the bound twin of the schema-free `Document.fromMarkdown`. The
930
937
  * returned document rests at its canonical form (a `richtext` field as a
931
- * content object, a `plaintext` field as its literal string), so `getStored`
938
+ * content object, a `text` field as its literal string), so `getStored`
932
939
  * answers by the field's declared codec, not by how the document was built.
933
940
  *
934
941
  * Parse warnings and the `conform::*` diagnostics both land on
@@ -1039,15 +1046,15 @@ export function formatDocPath(segs: DocPathSeg[]): string;
1039
1046
  export function importMarkdown(markdown: string): Imported;
1040
1047
 
1041
1048
  /**
1042
- * Whether `content` satisfies the `inline` constraint of `richtext` and
1043
- * `plaintext`: one `Para` line, in no container, with no islands. Judged on
1049
+ * Whether `content` satisfies the one-line constraint of `text` and
1050
+ * `richtext`: one `Para` line, in no container, with no islands. Judged on
1044
1051
  * the normalized content, which is what a write stores. Throws if `content` is
1045
1052
  * not canonical content.
1046
1053
  */
1047
1054
  export function isInline(content: Content): boolean;
1048
1055
 
1049
1056
  /**
1050
- * Whether `content` satisfies the `plaintext` constraint: no marks, no
1057
+ * Whether `content` satisfies the `text` constraint: no marks, no
1051
1058
  * islands, every line a `Para` in no container. Judged on the normalized
1052
1059
  * content, which is what a write stores. Throws if `content` is not canonical
1053
1060
  * content.
package/core/wasm.js CHANGED
@@ -58,7 +58,7 @@ export class Document {
58
58
  /**
59
59
  * Typed field write at `addr`, resolving the field's schema `type` from
60
60
  * `quill`: the stable ABI under the runtime `writer.set`. One verb for every
61
- * field type; the schema carries the `inline` constraint, so no type token
61
+ * field type; the schema carries the one-line constraint, so no type token
62
62
  * is passed.
63
63
  *
64
64
  * A bare string is `Addr` shorthand for `{ field }`. A body address throws,
@@ -67,7 +67,7 @@ export class Document {
67
67
  * `edit::unknown_field` rather than falling to the opaque store — use
68
68
  * [`storeField`](Document::store_field) for that. Also throws
69
69
  * `edit::field_coercion_failed` / `edit::field_decode` /
70
- * `edit::field_not_inline` on a typed mismatch,
70
+ * `edit::field_not_single_line` on a typed mismatch,
71
71
  * `edit::invalid_field_name` on a malformed name, and
72
72
  * `edit::index_out_of_range` on an out-of-range card.
73
73
  *
@@ -273,10 +273,10 @@ export class Document {
273
273
  * Revise the content field at `addr` from authored text, typed *and*
274
274
  * anchor-preserving: the ABI under `writer.reviseField`. Surviving anchors
275
275
  * rebase as in [`revise`](Self::revise), then the diffed result is
276
- * schema-conformed, so a `richtext(inline)` field rejects a multi-block
277
- * result with `edit::field_not_inline`. Returns the `Revised` receipt, its
276
+ * schema-conformed, so a one-paragraph `richtext` field rejects a multi-block
277
+ * result with `edit::field_not_single_line`. Returns the `Revised` receipt, its
278
278
  * warnings anchored at the field. The codec is the declared type's:
279
- * `richtext` diffs markdown, `plaintext` literal text, which drops nothing.
279
+ * `richtext` diffs markdown, `text` literal text, which drops nothing.
280
280
  *
281
281
  * `addr` must name a field; a body address throws, since a body carries no
282
282
  * field schema (use [`revise`](Self::revise)). An undeclared name throws
@@ -551,7 +551,7 @@ export class Document {
551
551
  * `edit::index_out_of_range`.
552
552
  *
553
553
  * A content field at rest has one stored form per codec: a `richtext` field
554
- * holds the canonical content object, a `plaintext` field its literal
554
+ * holds the canonical content object, a `text` field its literal
555
555
  * string. A document from the bound door (`quill.parse` / `quill.conform`)
556
556
  * is at rest; one from the transport door may rest as authored until it is
557
557
  * conformed, and this read reports what is there. For the `Content` either
@@ -580,8 +580,9 @@ export class Document {
580
580
  /**
581
581
  * Insert a card: `at` absent appends, a number inserts at that index (in
582
582
  * `0..=cards.length`). Accepts any `CardInput`, including a card read back
583
- * out of a document. Throws if `card.kind` is not a valid kind name, or if
584
- * `at` is out of range.
583
+ * out of a document. Throws if `card.kind` is not a valid kind name, if
584
+ * `at` is out of range, or `edit::too_many_cards` if the document holds
585
+ * 1,000 cards.
585
586
  * @param {CardInput} card
586
587
  * @param {number} [at]
587
588
  */
@@ -1318,7 +1319,7 @@ export class Quill {
1318
1319
  * The quill's example document, `undefined` when the quill has no
1319
1320
  * `example.md` at its root: a filled-in page whose values are made up to show the
1320
1321
  * quill in use, never kept. It comes back pinned to this quill's
1321
- * `name@version` and conformed as `parse` conforms, its parse and
1322
+ * `name@MAJOR.MINOR` and conformed as `parse` conforms, its parse and
1322
1323
  * `conform::*` warnings on `doc.warnings`. Throws when the file does not
1323
1324
  * parse or its `$quill` is not this quill's bare name.
1324
1325
  * @returns {Document | undefined}
@@ -1385,7 +1386,7 @@ export class Quill {
1385
1386
  * Parse `markdown` and conform it against this quill: the primary ingestion
1386
1387
  * path, and the bound twin of the schema-free `Document.fromMarkdown`. The
1387
1388
  * returned document rests at its canonical form (a `richtext` field as a
1388
- * content object, a `plaintext` field as its literal string), so `getStored`
1389
+ * content object, a `text` field as its literal string), so `getStored`
1389
1390
  * answers by the field's declared codec, not by how the document was built.
1390
1391
  *
1391
1392
  * Parse warnings and the `conform::*` diagnostics both land on
@@ -1685,8 +1686,8 @@ export function importMarkdown(markdown) {
1685
1686
  }
1686
1687
 
1687
1688
  /**
1688
- * Whether `content` satisfies the `inline` constraint of `richtext` and
1689
- * `plaintext`: one `Para` line, in no container, with no islands. Judged on
1689
+ * Whether `content` satisfies the one-line constraint of `text` and
1690
+ * `richtext`: one `Para` line, in no container, with no islands. Judged on
1690
1691
  * the normalized content, which is what a write stores. Throws if `content` is
1691
1692
  * not canonical content.
1692
1693
  * @param {Content} content
@@ -1709,7 +1710,7 @@ export function isInline(content) {
1709
1710
  }
1710
1711
 
1711
1712
  /**
1712
- * Whether `content` satisfies the `plaintext` constraint: no marks, no
1713
+ * Whether `content` satisfies the `text` constraint: no marks, no
1713
1714
  * islands, every line a `Para` in no container. Judged on the normalized
1714
1715
  * content, which is what a write stores. Throws if `content` is not canonical
1715
1716
  * content.
package/core/wasm_bg.wasm CHANGED
Binary file
package/package.json CHANGED
@@ -1,11 +1,26 @@
1
1
  {
2
2
  "name": "@quillmark/wasm",
3
- "version": "0.125.0",
3
+ "version": "0.126.1",
4
4
  "description": "WebAssembly bindings for Quillmark, a schema-driven document engine",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
+ "author": "Quillmark Contributors",
8
+ "homepage": "https://quillmark.dev/",
9
+ "bugs": {
10
+ "url": "https://github.com/borb-sh/quillmark/issues"
11
+ },
12
+ "keywords": [
13
+ "markdown",
14
+ "pdf",
15
+ "typst",
16
+ "document",
17
+ "template",
18
+ "schema",
19
+ "forms",
20
+ "wasm"
21
+ ],
7
22
  "engines": {
8
- "node": ">=24"
23
+ "node": ">=22"
9
24
  },
10
25
  "repository": {
11
26
  "type": "git",
@@ -16,6 +31,8 @@
16
31
  "render",
17
32
  "runtime",
18
33
  "CHANGELOG.md",
34
+ "NOTICE",
35
+ "THIRD_PARTY_LICENSES.md",
19
36
  "sbom.cdx.json"
20
37
  ],
21
38
  "main": "./runtime/runtime.js",
package/render/wasm.d.ts CHANGED
@@ -457,11 +457,10 @@ export interface Resolved {
457
457
  /** UI layout hints for a single field. Display order is not a hint: key order
458
458
  * in the schema's `fields`/`properties` objects is the ordering contract. */
459
459
  export interface QuillFieldUi {
460
- /** The id of the card section holding the field, a key of the card's
460
+ /** The id of the card group holding the field, a key of the card's
461
461
  * `ui.groups`. */
462
462
  group?: string;
463
463
  compact?: boolean;
464
- multiline?: boolean;
465
464
  /** Label for an `enum`'s blank option. Absent, the consumer supplies a
466
465
  * conventional label of its own. */
467
466
  blank_title?: string;
@@ -475,8 +474,8 @@ export interface QuillFieldUi {
475
474
  layout?: "table" | "flat";
476
475
  }
477
476
 
478
- /** One section in a card's `ui.groups`: a display-label override for the
479
- * section id (the map key). An empty object carries no override, and the
477
+ /** One group in a card's `ui.groups`: a display-label override for the
478
+ * group id (the map key). An empty object carries no override, and the
480
479
  * consumer derives the label from the id (`memo_for` → "Memo For"). */
481
480
  export interface QuillGroupUi {
482
481
  title?: string;
@@ -484,9 +483,9 @@ export interface QuillGroupUi {
484
483
 
485
484
  /** UI layout hints for a card (main or named card kind). */
486
485
  export interface QuillCardUi {
487
- /** The card's sections keyed by id, each holding at least one field, which
486
+ /** The card's groups keyed by id, each holding at least one field, which
488
487
  * names it in its `ui.group`. Key order is the display-order contract, as
489
- * with `fields`. Absent when the card declares no sections. */
488
+ * with `fields`. Absent when the card declares no groups. */
490
489
  groups?: Record<string, QuillGroupUi>;
491
490
  }
492
491
 
@@ -496,7 +495,7 @@ export interface QuillCardBody {
496
495
  enabled?: boolean;
497
496
  }
498
497
 
499
- export type QuillFieldType = "string" | "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "richtext" | "plaintext" | "enum" | "matrix";
498
+ export type QuillFieldType = "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "text" | "richtext" | "enum" | "matrix";
500
499
 
501
500
  /** Schema entry for a single field declared in a quill's `Quill.yaml`.
502
501
  *
@@ -541,9 +540,12 @@ export interface QuillFieldSchema {
541
540
  * out on. Valid only on an `array`. Never gates render: a document over
542
541
  * the cap warns `validation::cardinality` and renders. */
543
542
  max?: number;
544
- /** `true` on a `richtext` or `plaintext` field declared `inline`: the
545
- * single-paragraph, container-free, island-free constraint. */
546
- inline?: boolean;
543
+ /** The line shape of a `text` or `richtext` field, valid nowhere else.
544
+ * Absent or `false`, the field is one line (`text`) or one paragraph with
545
+ * no container or island (`richtext`); `true` admits line breaks
546
+ * (`text`) or block structure (`richtext`). A `richtext` always carries
547
+ * it. */
548
+ multiline?: boolean;
547
549
  }
548
550
 
549
551
  /** Schema entry for the main card or a named card kind. */
@@ -583,6 +585,10 @@ export interface QuillMetadata {
583
585
  backend: string;
584
586
  author: string;
585
587
  description: string;
588
+ /** An SPDX license expression; absent when the quill declares none. */
589
+ license?: string;
590
+ /** An absolute `http(s)` URL; absent when the quill declares none. */
591
+ homepage?: string;
586
592
  }
587
593
 
588
594
 
@@ -824,7 +830,7 @@ export class Document {
824
830
  * `edit::index_out_of_range`.
825
831
  *
826
832
  * A content field at rest has one stored form per codec: a `richtext` field
827
- * holds the canonical content object, a `plaintext` field its literal
833
+ * holds the canonical content object, a `text` field its literal
828
834
  * string. A document from the bound door (`quill.parse` / `quill.conform`)
829
835
  * is at rest; one from the transport door may rest as authored until it is
830
836
  * conformed, and this read reports what is there. For the `Content` either
@@ -837,8 +843,9 @@ export class Document {
837
843
  /**
838
844
  * Insert a card: `at` absent appends, a number inserts at that index (in
839
845
  * `0..=cards.length`). Accepts any `CardInput`, including a card read back
840
- * out of a document. Throws if `card.kind` is not a valid kind name, or if
841
- * `at` is out of range.
846
+ * out of a document. Throws if `card.kind` is not a valid kind name, if
847
+ * `at` is out of range, or `edit::too_many_cards` if the document holds
848
+ * 1,000 cards.
842
849
  */
843
850
  insertCard(card: CardInput, at?: number): void;
844
851
  /**
@@ -1172,7 +1179,7 @@ export class Quill {
1172
1179
  * The quill's example document, `undefined` when the quill has no
1173
1180
  * `example.md` at its root: a filled-in page whose values are made up to show the
1174
1181
  * quill in use, never kept. It comes back pinned to this quill's
1175
- * `name@version` and conformed as `parse` conforms, its parse and
1182
+ * `name@MAJOR.MINOR` and conformed as `parse` conforms, its parse and
1176
1183
  * `conform::*` warnings on `doc.warnings`. Throws when the file does not
1177
1184
  * parse or its `$quill` is not this quill's bare name.
1178
1185
  */
@@ -1187,7 +1194,7 @@ export class Quill {
1187
1194
  * Parse `markdown` and conform it against this quill: the primary ingestion
1188
1195
  * path, and the bound twin of the schema-free `Document.fromMarkdown`. The
1189
1196
  * returned document rests at its canonical form (a `richtext` field as a
1190
- * content object, a `plaintext` field as its literal string), so `getStored`
1197
+ * content object, a `text` field as its literal string), so `getStored`
1191
1198
  * answers by the field's declared codec, not by how the document was built.
1192
1199
  *
1193
1200
  * Parse warnings and the `conform::*` diagnostics both land on
@@ -1327,15 +1334,15 @@ export function formatDocPath(segs: DocPathSeg[]): string;
1327
1334
  export function importMarkdown(markdown: string): Imported;
1328
1335
 
1329
1336
  /**
1330
- * Whether `content` satisfies the `inline` constraint of `richtext` and
1331
- * `plaintext`: one `Para` line, in no container, with no islands. Judged on
1337
+ * Whether `content` satisfies the one-line constraint of `text` and
1338
+ * `richtext`: one `Para` line, in no container, with no islands. Judged on
1332
1339
  * the normalized content, which is what a write stores. Throws if `content` is
1333
1340
  * not canonical content.
1334
1341
  */
1335
1342
  export function isInline(content: Content): boolean;
1336
1343
 
1337
1344
  /**
1338
- * Whether `content` satisfies the `plaintext` constraint: no marks, no
1345
+ * Whether `content` satisfies the `text` constraint: no marks, no
1339
1346
  * islands, every line a `Para` in no container. Judged on the normalized
1340
1347
  * content, which is what a write stores. Throws if `content` is not canonical
1341
1348
  * content.
package/render/wasm.js CHANGED
@@ -58,7 +58,7 @@ export class Document {
58
58
  /**
59
59
  * Typed field write at `addr`, resolving the field's schema `type` from
60
60
  * `quill`: the stable ABI under the runtime `writer.set`. One verb for every
61
- * field type; the schema carries the `inline` constraint, so no type token
61
+ * field type; the schema carries the one-line constraint, so no type token
62
62
  * is passed.
63
63
  *
64
64
  * A bare string is `Addr` shorthand for `{ field }`. A body address throws,
@@ -67,7 +67,7 @@ export class Document {
67
67
  * `edit::unknown_field` rather than falling to the opaque store — use
68
68
  * [`storeField`](Document::store_field) for that. Also throws
69
69
  * `edit::field_coercion_failed` / `edit::field_decode` /
70
- * `edit::field_not_inline` on a typed mismatch,
70
+ * `edit::field_not_single_line` on a typed mismatch,
71
71
  * `edit::invalid_field_name` on a malformed name, and
72
72
  * `edit::index_out_of_range` on an out-of-range card.
73
73
  *
@@ -273,10 +273,10 @@ export class Document {
273
273
  * Revise the content field at `addr` from authored text, typed *and*
274
274
  * anchor-preserving: the ABI under `writer.reviseField`. Surviving anchors
275
275
  * rebase as in [`revise`](Self::revise), then the diffed result is
276
- * schema-conformed, so a `richtext(inline)` field rejects a multi-block
277
- * result with `edit::field_not_inline`. Returns the `Revised` receipt, its
276
+ * schema-conformed, so a one-paragraph `richtext` field rejects a multi-block
277
+ * result with `edit::field_not_single_line`. Returns the `Revised` receipt, its
278
278
  * warnings anchored at the field. The codec is the declared type's:
279
- * `richtext` diffs markdown, `plaintext` literal text, which drops nothing.
279
+ * `richtext` diffs markdown, `text` literal text, which drops nothing.
280
280
  *
281
281
  * `addr` must name a field; a body address throws, since a body carries no
282
282
  * field schema (use [`revise`](Self::revise)). An undeclared name throws
@@ -551,7 +551,7 @@ export class Document {
551
551
  * `edit::index_out_of_range`.
552
552
  *
553
553
  * A content field at rest has one stored form per codec: a `richtext` field
554
- * holds the canonical content object, a `plaintext` field its literal
554
+ * holds the canonical content object, a `text` field its literal
555
555
  * string. A document from the bound door (`quill.parse` / `quill.conform`)
556
556
  * is at rest; one from the transport door may rest as authored until it is
557
557
  * conformed, and this read reports what is there. For the `Content` either
@@ -580,8 +580,9 @@ export class Document {
580
580
  /**
581
581
  * Insert a card: `at` absent appends, a number inserts at that index (in
582
582
  * `0..=cards.length`). Accepts any `CardInput`, including a card read back
583
- * out of a document. Throws if `card.kind` is not a valid kind name, or if
584
- * `at` is out of range.
583
+ * out of a document. Throws if `card.kind` is not a valid kind name, if
584
+ * `at` is out of range, or `edit::too_many_cards` if the document holds
585
+ * 1,000 cards.
585
586
  * @param {CardInput} card
586
587
  * @param {number} [at]
587
588
  */
@@ -1662,7 +1663,7 @@ export class Quill {
1662
1663
  * The quill's example document, `undefined` when the quill has no
1663
1664
  * `example.md` at its root: a filled-in page whose values are made up to show the
1664
1665
  * quill in use, never kept. It comes back pinned to this quill's
1665
- * `name@version` and conformed as `parse` conforms, its parse and
1666
+ * `name@MAJOR.MINOR` and conformed as `parse` conforms, its parse and
1666
1667
  * `conform::*` warnings on `doc.warnings`. Throws when the file does not
1667
1668
  * parse or its `$quill` is not this quill's bare name.
1668
1669
  * @returns {Document | undefined}
@@ -1729,7 +1730,7 @@ export class Quill {
1729
1730
  * Parse `markdown` and conform it against this quill: the primary ingestion
1730
1731
  * path, and the bound twin of the schema-free `Document.fromMarkdown`. The
1731
1732
  * returned document rests at its canonical form (a `richtext` field as a
1732
- * content object, a `plaintext` field as its literal string), so `getStored`
1733
+ * content object, a `text` field as its literal string), so `getStored`
1733
1734
  * answers by the field's declared codec, not by how the document was built.
1734
1735
  *
1735
1736
  * Parse warnings and the `conform::*` diagnostics both land on
@@ -2134,8 +2135,8 @@ export function importMarkdown(markdown) {
2134
2135
  }
2135
2136
 
2136
2137
  /**
2137
- * Whether `content` satisfies the `inline` constraint of `richtext` and
2138
- * `plaintext`: one `Para` line, in no container, with no islands. Judged on
2138
+ * Whether `content` satisfies the one-line constraint of `text` and
2139
+ * `richtext`: one `Para` line, in no container, with no islands. Judged on
2139
2140
  * the normalized content, which is what a write stores. Throws if `content` is
2140
2141
  * not canonical content.
2141
2142
  * @param {Content} content
@@ -2158,7 +2159,7 @@ export function isInline(content) {
2158
2159
  }
2159
2160
 
2160
2161
  /**
2161
- * Whether `content` satisfies the `plaintext` constraint: no marks, no
2162
+ * Whether `content` satisfies the `text` constraint: no marks, no
2162
2163
  * islands, every line a `Para` in no container. Judged on the normalized
2163
2164
  * content, which is what a write stores. Throws if `content` is not canonical
2164
2165
  * content.
Binary file
@@ -440,6 +440,13 @@ export declare class Engine {
440
440
  *
441
441
  * `today` reads as on {@link open}. An `options` key outside
442
442
  * {@link RenderOptions} rejects, `today` among them.
443
+ *
444
+ * A compile that traps (out of memory, a panic) rejects as
445
+ * `runtime::backend_trapped`, the `WebAssembly.RuntimeError` on `cause`, as
446
+ * does a trap in {@link LiveSession}'s `update` / `render` / `paint`, and a
447
+ * `RangeError` or Firefox's `InternalError` for stack or memory exhaustion,
448
+ * which engines raise for a stack overflow inside wasm. The build's state is then undefined: discard the
449
+ * realm.
443
450
  */
444
451
  render(quill: Quill, doc: Document, options?: RenderOptions, today?: string): Promise<RenderResult>;
445
452
 
@@ -660,12 +667,12 @@ export declare class DocumentWriter {
660
667
  /**
661
668
  * Revise the content main-card field `name` from authored text: typed *and*
662
669
  * anchor-preserving. Surviving anchors rebase, then the diffed result is
663
- * schema-conformed (`richtext(inline)` rejects a multi-block result). Throws
670
+ * schema-conformed (a one-paragraph `richtext` rejects a multi-block result). Throws
664
671
  * `UnknownField` for a name the schema does not declare. Returns the
665
672
  * `Revised` receipt, its warnings at the field.
666
673
  *
667
674
  * The codec comes from the declared type: `richtext` diffs markdown, while
668
- * `plaintext` diffs the literal text and never imports markdown, so a
675
+ * `text` diffs the literal text and never imports markdown, so a
669
676
  * byte-identical revise of a value carrying escapes is a byte no-op.
670
677
  */
671
678
  reviseField(name: string, text: string): Revised;
@@ -734,7 +741,7 @@ export declare class CardWriter {
734
741
  * A `Document` bound to its `Quill` for interpreted reads, from
735
742
  * {@link Quill.reader}: the read twin of {@link DocumentWriter}. One `get` reads
736
743
  * each field by its declared type — a richtext field to its markdown projection,
737
- * a plaintext field to its literal text, every other type verbatim.
744
+ * a text field to its literal text, every other type verbatim.
738
745
  *
739
746
  * The schema authority is the point: unlike the quill-free `Document.getStored`,
740
747
  * an undeclared name throws `UnknownField` rather than reading back `undefined`,
@@ -161,6 +161,53 @@ function quillmarkError(code, message, hint) {
161
161
  return err;
162
162
  }
163
163
 
164
+ /**
165
+ * `err` as `runtime::backend_trapped` when it is a `WebAssembly.RuntimeError`,
166
+ * or a `RangeError` (V8, JavaScriptCore) or `InternalError` (SpiderMonkey) for
167
+ * stack or memory exhaustion, which those engines raise for native stack
168
+ * overflow inside wasm; else unchanged.
169
+ * @param {unknown} err
170
+ * @returns {unknown}
171
+ */
172
+ function backendTrap(err) {
173
+ const exhausted =
174
+ (err instanceof RangeError || (err instanceof Error && err.name === 'InternalError')) &&
175
+ /Maximum call stack|stack overflow|too much recursion|out of memory/i.test(err.message);
176
+ if (!(err instanceof WebAssembly.RuntimeError) && !exhausted) return err;
177
+ return Object.assign(
178
+ quillmarkError(
179
+ 'runtime::backend_trapped',
180
+ `The backend build trapped (${err.message}); its state is now undefined.`,
181
+ 'A render ran out of memory or stack, or panicked. Discard the realm before rendering again: terminate the Worker or restart the process. For untrusted input, cap the document size.'
182
+ ),
183
+ { cause: err }
184
+ );
185
+ }
186
+
187
+ /**
188
+ * Run `fn` on a transient clone of `docJson` in `mod`'s memory, then free it. A
189
+ * call that throws can leave the clone borrowed, so its `free()` throws too, and
190
+ * the error in flight is the one that propagates.
191
+ * @param {any} mod
192
+ * @param {string} docJson
193
+ * @param {(doc: any) => any} fn
194
+ * @returns {any}
195
+ */
196
+ function withDocClone(mod, docJson, fn) {
197
+ const backendDoc = mod.Document.fromStored(docJson);
198
+ let result;
199
+ try {
200
+ result = fn(backendDoc);
201
+ } catch (err) {
202
+ try {
203
+ backendDoc.free();
204
+ } catch {}
205
+ throw err;
206
+ }
207
+ backendDoc.free();
208
+ return result;
209
+ }
210
+
164
211
  /**
165
212
  * Throw on options that are not a plain object, or on a key of them, or of a
166
213
  * null-prototype object they inherit from, outside `known`. The JS twin of the
@@ -568,16 +615,15 @@ export class Engine {
568
615
  ? null
569
616
  : quill.toTree();
570
617
  const { mod, engine } = await this.#resolveBackend(backendId);
571
- // The doc clone and `fn` share one try so the clone is freed even if a
572
- // later step throws; the cached quill clone is intentionally not freed.
573
- // `fn` MUST be synchronous: an async one would run against a freed clone.
574
- const backendQuill = this.#cachedQuillClone(mod, backendId, quill, quillTree);
575
- let backendDoc = null;
618
+ // The cached quill clone is intentionally not freed. `fn` MUST be
619
+ // synchronous: an async one would run against a freed doc clone.
576
620
  try {
577
- backendDoc = mod.Document.fromStored(docJson);
578
- return fn({ mod, engine, quill: backendQuill, doc: backendDoc });
579
- } finally {
580
- backendDoc?.free();
621
+ const backendQuill = this.#cachedQuillClone(mod, backendId, quill, quillTree);
622
+ return withDocClone(mod, docJson, (backendDoc) =>
623
+ fn({ mod, engine, quill: backendQuill, doc: backendDoc })
624
+ );
625
+ } catch (err) {
626
+ throw backendTrap(err);
581
627
  }
582
628
  }
583
629
 
@@ -654,12 +700,11 @@ export class LiveSession {
654
700
  */
655
701
  update(doc) {
656
702
  requireLocalDoc(doc, 'session.update(doc)');
657
- let backendDoc = null;
703
+ const docJson = doc.toStored();
658
704
  try {
659
- backendDoc = this.#mod.Document.fromStored(doc.toStored());
660
- return this.#inner.update(backendDoc);
661
- } finally {
662
- backendDoc?.free();
705
+ return withDocClone(this.#mod, docJson, (backendDoc) => this.#inner.update(backendDoc));
706
+ } catch (err) {
707
+ throw backendTrap(err);
663
708
  }
664
709
  }
665
710
 
@@ -675,7 +720,11 @@ export class LiveSession {
675
720
 
676
721
  /** @param {object} [options] */
677
722
  render(options) {
678
- return this.#inner.render(options ?? undefined);
723
+ try {
724
+ return this.#inner.render(options ?? undefined);
725
+ } catch (err) {
726
+ throw backendTrap(err);
727
+ }
679
728
  }
680
729
 
681
730
  /**
@@ -742,7 +791,11 @@ export class LiveSession {
742
791
  * @param {number} scale
743
792
  */
744
793
  paint(ctx, page, scale) {
745
- this.#inner.paint(ctx, page, scale);
794
+ try {
795
+ this.#inner.paint(ctx, page, scale);
796
+ } catch (err) {
797
+ throw backendTrap(err);
798
+ }
746
799
  }
747
800
 
748
801
  free() {