@quillmark/wasm 0.124.0 → 0.126.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +396 -0
- package/LICENSE +198 -10
- package/NOTICE +8 -0
- package/README.md +42 -30
- package/THIRD_PARTY_LICENSES.md +10629 -0
- package/core/wasm.d.ts +43 -26
- package/core/wasm.js +34 -22
- package/core/wasm_bg.wasm +0 -0
- package/package.json +21 -3
- package/render/wasm.d.ts +80 -38
- package/render/wasm.js +66 -33
- package/render/wasm_bg.wasm +0 -0
- package/render/wasm_bg.wasm.d.ts +1 -0
- package/runtime/runtime.d.ts +53 -21
- package/runtime/runtime.js +80 -20
- package/sbom.cdx.json +12835 -0
package/runtime/runtime.d.ts
CHANGED
|
@@ -142,6 +142,7 @@ export type {
|
|
|
142
142
|
Revised,
|
|
143
143
|
DocumentRevised,
|
|
144
144
|
AnnotatedMarkdown,
|
|
145
|
+
DocumentAnchor,
|
|
145
146
|
Imported,
|
|
146
147
|
Assoc,
|
|
147
148
|
IslandOp,
|
|
@@ -332,14 +333,30 @@ export interface FieldRegion {
|
|
|
332
333
|
rect: [number, number, number, number];
|
|
333
334
|
/**
|
|
334
335
|
* The content slice this box covers: USV `[start, end)` into the field's
|
|
335
|
-
* `Content` for content ink (one segment), absent for a scalar
|
|
336
|
-
*
|
|
336
|
+
* `Content` for content ink (one segment), absent for a scalar's or a
|
|
337
|
+
* date's ink, a claim or a widget. Consumers key segment highlights on it;
|
|
337
338
|
* {@link LiveSession.fieldBoxes} unions same-page segments for the
|
|
338
339
|
* whole-field box.
|
|
339
340
|
*/
|
|
340
341
|
span?: [number, number];
|
|
341
342
|
}
|
|
342
343
|
|
|
344
|
+
/**
|
|
345
|
+
* One address a compiled document fills, and the region address a click
|
|
346
|
+
* reaches it through. A report, not a gate: a field a quill never prints is
|
|
347
|
+
* legitimately unreachable.
|
|
348
|
+
*/
|
|
349
|
+
export interface FieldCoverage {
|
|
350
|
+
/** The field's canonical `DocPath` address, as {@link FieldRegion.field}. */
|
|
351
|
+
field: string;
|
|
352
|
+
/**
|
|
353
|
+
* The `DocPath` of the region reaching `field`: `field` itself, or the
|
|
354
|
+
* nearest enclosing address a region carries (a row claim reaching its
|
|
355
|
+
* cells, a whole array its elements). Absent where no click reaches it.
|
|
356
|
+
*/
|
|
357
|
+
coveredBy?: string;
|
|
358
|
+
}
|
|
359
|
+
|
|
343
360
|
/** Result of one render. */
|
|
344
361
|
export interface RenderResult {
|
|
345
362
|
artifacts: Artifact[];
|
|
@@ -415,14 +432,21 @@ export declare class Engine {
|
|
|
415
432
|
* soon as this call returns.
|
|
416
433
|
*
|
|
417
434
|
* {@link RenderResult.warnings} carries every `quill.validate(doc)` warning
|
|
418
|
-
* but `
|
|
419
|
-
*
|
|
435
|
+
* but `backend::declined_construct`, then the compile's own, a decline among
|
|
436
|
+
* them. The load's (parse
|
|
420
437
|
* and `conform::*`) stay on `doc.warnings`, and a revise's on its receipt.
|
|
421
438
|
* A {@link LiveSession} carries the compile's alone: read
|
|
422
439
|
* `quill.validate(doc)` beside it.
|
|
423
440
|
*
|
|
424
441
|
* `today` reads as on {@link open}. An `options` key outside
|
|
425
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.
|
|
426
450
|
*/
|
|
427
451
|
render(quill: Quill, doc: Document, options?: RenderOptions, today?: string): Promise<RenderResult>;
|
|
428
452
|
|
|
@@ -494,22 +518,27 @@ export declare class LiveSession {
|
|
|
494
518
|
* Empty for backends that place no schema fields.
|
|
495
519
|
*
|
|
496
520
|
* `field` is **not** unique: a content field surfaces its **first placement**
|
|
497
|
-
* as one {@link FieldRegion} per page that placement touches,
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
* still resolves clicks on them.
|
|
521
|
+
* as one {@link FieldRegion} per page that placement touches, each claim on
|
|
522
|
+
* a field surfaces its own, and tracked content plus a `field:`-bound widget
|
|
523
|
+
* yields both, widget first. Group by `field`. Later placements of one value
|
|
524
|
+
* are not enumerated; {@link fieldAt} still resolves clicks on them.
|
|
502
525
|
*/
|
|
503
526
|
regions(): FieldRegion[];
|
|
504
527
|
/**
|
|
505
528
|
* The whole-field highlight boxes for `field` (a canonical `DocPath` address,
|
|
506
529
|
* as {@link regions} keys): one union rect per page over the field's
|
|
507
530
|
* `span`-bearing content segments, the union {@link regions} leaves derived.
|
|
508
|
-
* **Content only**: a field placed solely as a scalar
|
|
509
|
-
* widget carries no `span` and returns `[]`, its box being a single
|
|
531
|
+
* **Content only**: a field placed solely as a scalar's ink, a claim or a
|
|
532
|
+
* bound widget carries no `span` and returns `[]`, its box being a single
|
|
510
533
|
* {@link regions} rect. Reflects the current compile.
|
|
511
534
|
*/
|
|
512
535
|
fieldBoxes(field: string): FieldRegion[];
|
|
536
|
+
/**
|
|
537
|
+
* Every address the current compile fills that a reader could want to
|
|
538
|
+
* click — each non-blank declared leaf, every roster member held or not —
|
|
539
|
+
* with the region reaching it. Reflects the current compile.
|
|
540
|
+
*/
|
|
541
|
+
coverage(): FieldCoverage[];
|
|
513
542
|
/**
|
|
514
543
|
* The schema field whose content is under a point on `page`: the canonical
|
|
515
544
|
* `DocPath` address to focus in the editor, or `undefined` off any field's
|
|
@@ -616,15 +645,18 @@ export declare class DocumentWriter {
|
|
|
616
645
|
readonly document: Document;
|
|
617
646
|
/**
|
|
618
647
|
* Typed-commit one main-card field (strict coerce, mismatch throws now).
|
|
619
|
-
* Throws `UnknownField` for a name the schema does not declare.
|
|
648
|
+
* Throws `UnknownField` for a name the schema does not declare. Returns a
|
|
649
|
+
* `parse::dropped_construct` warning per construct a `richtext` markdown
|
|
650
|
+
* string in the value dropped, at the string's path.
|
|
620
651
|
*/
|
|
621
|
-
set(name: string, value: unknown):
|
|
652
|
+
set(name: string, value: unknown): Diagnostic[];
|
|
622
653
|
/**
|
|
623
654
|
* Typed-commit several main-card fields atomically: nothing is applied on
|
|
624
655
|
* error (throws a {@link QuillmarkError} carrying one diagnostic per
|
|
625
656
|
* offending field, including an `UnknownField` for each undeclared name).
|
|
657
|
+
* Returns `set`'s warnings for every field, in batch order.
|
|
626
658
|
*/
|
|
627
|
-
setAll(fields: Record<string, unknown>):
|
|
659
|
+
setAll(fields: Record<string, unknown>): Diagnostic[];
|
|
628
660
|
/**
|
|
629
661
|
* Revise the main body from markdown; anchors rebase. Returns the
|
|
630
662
|
* `Revised` receipt, its warnings at `main.body`: a body carries no field
|
|
@@ -635,12 +667,12 @@ export declare class DocumentWriter {
|
|
|
635
667
|
/**
|
|
636
668
|
* Revise the content main-card field `name` from authored text: typed *and*
|
|
637
669
|
* anchor-preserving. Surviving anchors rebase, then the diffed result is
|
|
638
|
-
* schema-conformed (`richtext
|
|
670
|
+
* schema-conformed (a one-paragraph `richtext` rejects a multi-block result). Throws
|
|
639
671
|
* `UnknownField` for a name the schema does not declare. Returns the
|
|
640
672
|
* `Revised` receipt, its warnings at the field.
|
|
641
673
|
*
|
|
642
674
|
* The codec comes from the declared type: `richtext` diffs markdown, while
|
|
643
|
-
* `
|
|
675
|
+
* `text` diffs the literal text and never imports markdown, so a
|
|
644
676
|
* byte-identical revise of a value carrying escapes is a byte no-op.
|
|
645
677
|
*/
|
|
646
678
|
reviseField(name: string, text: string): Revised;
|
|
@@ -660,8 +692,8 @@ export declare class DocumentWriter {
|
|
|
660
692
|
* body from optional markdown, and place it. `at` omitted appends, a number
|
|
661
693
|
* inserts at that index. Transactional: a rejected field (throwing a per-field
|
|
662
694
|
* diagnostic bundle) or an invalid kind, body, or position leaves the document
|
|
663
|
-
* untouched. Returns the
|
|
664
|
-
* anchored
|
|
695
|
+
* untouched. Returns the fields' `parse::dropped_construct` warnings, then
|
|
696
|
+
* the body import's, anchored under the placed card.
|
|
665
697
|
*/
|
|
666
698
|
addCard(kind: string, fields?: Record<string, unknown>, body?: string, at?: number): Diagnostic[];
|
|
667
699
|
/** Remove the composable card at `index`, returning it (or `undefined`). */
|
|
@@ -691,8 +723,8 @@ export declare class CardWriter {
|
|
|
691
723
|
* index.
|
|
692
724
|
*/
|
|
693
725
|
readonly kind: string;
|
|
694
|
-
set(name: string, value: unknown):
|
|
695
|
-
setAll(fields: Record<string, unknown>):
|
|
726
|
+
set(name: string, value: unknown): Diagnostic[];
|
|
727
|
+
setAll(fields: Record<string, unknown>): Diagnostic[];
|
|
696
728
|
/**
|
|
697
729
|
* Revise this card's body from markdown (edit semantics), returning the
|
|
698
730
|
* `Revised` receipt, its warnings at the card's body.
|
|
@@ -709,7 +741,7 @@ export declare class CardWriter {
|
|
|
709
741
|
* A `Document` bound to its `Quill` for interpreted reads, from
|
|
710
742
|
* {@link Quill.reader}: the read twin of {@link DocumentWriter}. One `get` reads
|
|
711
743
|
* each field by its declared type — a richtext field to its markdown projection,
|
|
712
|
-
* a
|
|
744
|
+
* a text field to its literal text, every other type verbatim.
|
|
713
745
|
*
|
|
714
746
|
* The schema authority is the point: unlike the quill-free `Document.getStored`,
|
|
715
747
|
* an undeclared name throws `UnknownField` rather than reading back `undefined`,
|
package/runtime/runtime.js
CHANGED
|
@@ -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
|
|
572
|
-
//
|
|
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
|
-
|
|
578
|
-
return
|
|
579
|
-
|
|
580
|
-
|
|
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
|
-
|
|
703
|
+
const docJson = doc.toStored();
|
|
658
704
|
try {
|
|
659
|
-
backendDoc
|
|
660
|
-
|
|
661
|
-
|
|
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
|
-
|
|
723
|
+
try {
|
|
724
|
+
return this.#inner.render(options ?? undefined);
|
|
725
|
+
} catch (err) {
|
|
726
|
+
throw backendTrap(err);
|
|
727
|
+
}
|
|
679
728
|
}
|
|
680
729
|
|
|
681
730
|
/**
|
|
@@ -693,6 +742,13 @@ export class LiveSession {
|
|
|
693
742
|
return this.#inner.fieldBoxes(field);
|
|
694
743
|
}
|
|
695
744
|
|
|
745
|
+
/**
|
|
746
|
+
* @returns {import('./runtime.d.ts').FieldCoverage[]}
|
|
747
|
+
*/
|
|
748
|
+
coverage() {
|
|
749
|
+
return this.#inner.coverage();
|
|
750
|
+
}
|
|
751
|
+
|
|
696
752
|
/**
|
|
697
753
|
* @param {number} page
|
|
698
754
|
* @param {number} x
|
|
@@ -735,7 +791,11 @@ export class LiveSession {
|
|
|
735
791
|
* @param {number} scale
|
|
736
792
|
*/
|
|
737
793
|
paint(ctx, page, scale) {
|
|
738
|
-
|
|
794
|
+
try {
|
|
795
|
+
this.#inner.paint(ctx, page, scale);
|
|
796
|
+
} catch (err) {
|
|
797
|
+
throw backendTrap(err);
|
|
798
|
+
}
|
|
739
799
|
}
|
|
740
800
|
|
|
741
801
|
free() {
|
|
@@ -767,14 +827,14 @@ export class DocumentWriter {
|
|
|
767
827
|
/**
|
|
768
828
|
* @param {string} name
|
|
769
829
|
* @param {unknown} value
|
|
770
|
-
* @returns {
|
|
830
|
+
* @returns {import('../core/wasm.js').Diagnostic[]}
|
|
771
831
|
*/
|
|
772
832
|
set(name, value) {
|
|
773
833
|
return this.#doc._commitField(this.#quill, name, value);
|
|
774
834
|
}
|
|
775
835
|
/**
|
|
776
836
|
* @param {Record<string, unknown>} fields
|
|
777
|
-
* @returns {
|
|
837
|
+
* @returns {import('../core/wasm.js').Diagnostic[]}
|
|
778
838
|
*/
|
|
779
839
|
setAll(fields) {
|
|
780
840
|
return this.#doc._commitFields(this.#quill, MAIN_CARD_ADDR, fields);
|
|
@@ -855,14 +915,14 @@ export class CardWriter {
|
|
|
855
915
|
/**
|
|
856
916
|
* @param {string} name
|
|
857
917
|
* @param {unknown} value
|
|
858
|
-
* @returns {
|
|
918
|
+
* @returns {import('../core/wasm.js').Diagnostic[]}
|
|
859
919
|
*/
|
|
860
920
|
set(name, value) {
|
|
861
921
|
return this.#doc._commitField(this.#quill, { card: this.#index, field: name }, value);
|
|
862
922
|
}
|
|
863
923
|
/**
|
|
864
924
|
* @param {Record<string, unknown>} fields
|
|
865
|
-
* @returns {
|
|
925
|
+
* @returns {import('../core/wasm.js').Diagnostic[]}
|
|
866
926
|
*/
|
|
867
927
|
setAll(fields) {
|
|
868
928
|
return this.#doc._commitFields(this.#quill, { card: this.#index }, fields);
|