@quillmark/quiver 0.12.0 → 0.15.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/README.md +92 -26
- package/dist/bin/quiver.d.ts +2 -2
- package/dist/bin/quiver.js +32 -24
- package/dist/index.d.ts +0 -1
- package/dist/index.js +5 -0
- package/dist/node.d.ts +0 -1
- package/dist/node.js +3 -0
- package/dist/preview.d.ts +14 -22
- package/dist/preview.js +22 -19
- package/dist/quiver.d.ts +16 -23
- package/dist/quiver.js +59 -56
- package/dist/testing.d.ts +6 -8
- package/dist/testing.js +17 -9
- package/package.json +5 -5
- package/PROGRAM.md +0 -512
- package/dist/engine-types.d.ts +0 -40
- package/dist/engine-types.js +0 -20
package/README.md
CHANGED
|
@@ -29,6 +29,24 @@ This keeps the author flow to a single command (`npm publish` or `git tag`)
|
|
|
29
29
|
and puts the deployment-topology decision where it belongs: with the
|
|
30
30
|
consumer.
|
|
31
31
|
|
|
32
|
+
## The render model
|
|
33
|
+
|
|
34
|
+
`@quillmark/quiver` produces quills; `@quillmark/wasm` renders them.
|
|
35
|
+
|
|
36
|
+
- `quiver.getQuill(ref)` returns a core `Quill` — engine-free, portable data,
|
|
37
|
+
good for schema inspection, validation, blueprint access, and seeding. It is
|
|
38
|
+
the **only** way to obtain a quill from a quiver.
|
|
39
|
+
- `const engine = new Engine()` (from the `@quillmark/wasm` root) renders it:
|
|
40
|
+
`await engine.render(quill, doc, opts?)`. The Engine routes on
|
|
41
|
+
`quill.backendId`, lazily loads that backend, clones the quill and document
|
|
42
|
+
into the backend's WASM memory, renders, and frees the clones — so a core
|
|
43
|
+
`Quill` passes straight to `engine.render` with no boundary-crossing step.
|
|
44
|
+
|
|
45
|
+
`Engine.render`, `open`, `supportedFormats`, and `supportsCanvas` are all
|
|
46
|
+
**async** — always `await` them. The canonical `Quill`/`Document`/`Engine`
|
|
47
|
+
types are not re-exported from `@quillmark/quiver`; import them straight from
|
|
48
|
+
the `@quillmark/wasm` peer dependency, which is the single source of truth.
|
|
49
|
+
|
|
32
50
|
## Authoring a quiver
|
|
33
51
|
|
|
34
52
|
Lay out the source per the spec, then publish to npm (or push a git tag):
|
|
@@ -50,6 +68,14 @@ consumer's build. The harness uses `node:test` (built into Node 18+); no extra
|
|
|
50
68
|
test-runner dependency required. If you prefer vitest/jest/mocha, write a
|
|
51
69
|
12-line loop against the main API instead.
|
|
52
70
|
|
|
71
|
+
```ts
|
|
72
|
+
// quiver.test.ts — run with: node --test
|
|
73
|
+
import { Engine } from "@quillmark/wasm";
|
|
74
|
+
import { runQuiverTests } from "@quillmark/quiver/testing";
|
|
75
|
+
|
|
76
|
+
runQuiverTests(import.meta.url, new Engine());
|
|
77
|
+
```
|
|
78
|
+
|
|
53
79
|
## Manual validation (rendering samples)
|
|
54
80
|
|
|
55
81
|
The CI harness proves every quill _compiles_; it does not produce output a
|
|
@@ -58,19 +84,19 @@ human can look at. To eyeball real renders, run the
|
|
|
58
84
|
|
|
59
85
|
```ts
|
|
60
86
|
// scripts/preview.ts — run with: node --experimental-strip-types scripts/preview.ts
|
|
61
|
-
import {
|
|
87
|
+
import { Engine } from "@quillmark/wasm";
|
|
62
88
|
import { renderQuiverSamples } from "@quillmark/quiver/preview";
|
|
63
89
|
|
|
64
90
|
await renderQuiverSamples(import.meta.url, {
|
|
65
|
-
engine: new
|
|
66
|
-
Document,
|
|
91
|
+
engine: new Engine(),
|
|
67
92
|
});
|
|
68
93
|
// → writes ./preview/<name>@<version>.<fmt> + index.html
|
|
69
94
|
```
|
|
70
95
|
|
|
71
|
-
It renders every quill's illustrative example document (
|
|
72
|
-
fully filled-out, always-renderable sample; the
|
|
73
|
-
`<must-fill>` sentinels and is not directly
|
|
96
|
+
It renders every quill's illustrative example document (seeded via
|
|
97
|
+
`quill.seedDocument()` — a fully filled-out, always-renderable sample; the
|
|
98
|
+
blueprint itself carries `<must-fill>` sentinels and is not directly
|
|
99
|
+
renderable), writes the artifacts
|
|
74
100
|
to `outDir` (default `preview/`), and emits an `index.html`
|
|
75
101
|
gallery. A `.gitignore` is written into `outDir` so the generated artifacts
|
|
76
102
|
are never accidentally committed. A quill that throws is recorded as failed —
|
|
@@ -82,8 +108,7 @@ quill name or canonical ref):
|
|
|
82
108
|
|
|
83
109
|
```ts
|
|
84
110
|
await renderQuiverSamples(import.meta.url, {
|
|
85
|
-
engine: new
|
|
86
|
-
Document,
|
|
111
|
+
engine: new Engine(),
|
|
87
112
|
exclude: ["broken-quill"], // or: include: ["memo@1.0.0"]
|
|
88
113
|
});
|
|
89
114
|
```
|
|
@@ -96,21 +121,38 @@ await renderQuiverSamples(import.meta.url, {
|
|
|
96
121
|
## Consuming a quiver (Node)
|
|
97
122
|
|
|
98
123
|
```ts
|
|
99
|
-
import {
|
|
124
|
+
import { Engine, Document } from "@quillmark/wasm";
|
|
100
125
|
import { Quiver } from "@quillmark/quiver/node";
|
|
101
126
|
|
|
102
|
-
const engine = new Quillmark();
|
|
103
127
|
const quiver = await Quiver.fromPackage("@org/my-quiver");
|
|
128
|
+
const engine = new Engine();
|
|
104
129
|
|
|
105
130
|
const doc = Document.fromMarkdown(markdownString);
|
|
106
|
-
const quill = await quiver.getQuill(doc.quillRef
|
|
107
|
-
const result =
|
|
131
|
+
const quill = await quiver.getQuill(doc.quillRef);
|
|
132
|
+
const result = await engine.render(quill, doc, { format: "pdf" });
|
|
108
133
|
```
|
|
109
134
|
|
|
110
135
|
`getQuill` accepts both selector refs (`"memo"`, `"memo@1"`) and canonical
|
|
111
136
|
refs (`"memo@1.0.0"`). It resolves the selector, materializes the quill via
|
|
112
|
-
`
|
|
113
|
-
|
|
137
|
+
`Quill.fromTree(tree)` (engine-free), and caches one instance per canonical
|
|
138
|
+
ref for the lifetime of the `Quiver`. Concurrent calls for the same ref
|
|
139
|
+
coalesce into a single load.
|
|
140
|
+
|
|
141
|
+
The returned quill lives in the core build's WASM memory and is suitable for
|
|
142
|
+
schema inspection, validation, blueprint access, and seeding. To render it,
|
|
143
|
+
hand it straight to `engine.render(quill, doc)` — the Engine clones both
|
|
144
|
+
handles into the backend on demand, so no separate boundary-crossing step is
|
|
145
|
+
needed:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
// Editor path — core only, ~0.66 MB gzip
|
|
149
|
+
const coreQuill = await quiver.getQuill(ref);
|
|
150
|
+
coreQuill.schema; // ✓ schema, validate, blueprint, seed — all fine
|
|
151
|
+
const doc = coreQuill.seedDocument(); // a fully-filled example document
|
|
152
|
+
|
|
153
|
+
// Render path — the same core handles render directly.
|
|
154
|
+
const result = await engine.render(coreQuill, doc, { format: "pdf" });
|
|
155
|
+
```
|
|
114
156
|
|
|
115
157
|
If you only need the canonical ref (without materializing), use `resolve`:
|
|
116
158
|
|
|
@@ -118,6 +160,10 @@ If you only need the canonical ref (without materializing), use `resolve`:
|
|
|
118
160
|
const canonicalRef = await quiver.resolve("memo"); // "memo@1.1.0"
|
|
119
161
|
```
|
|
120
162
|
|
|
163
|
+
If you need the raw file tree (e.g. to drive a backend binary directly), call
|
|
164
|
+
`(await quiver.getQuill(ref)).toTree()` on the core `Quill` — it is I/O-free
|
|
165
|
+
once the quill is materialized.
|
|
166
|
+
|
|
121
167
|
## Consuming a quiver (browser)
|
|
122
168
|
|
|
123
169
|
Browsers cannot read the source layout directly, so build at deploy time and
|
|
@@ -135,10 +181,15 @@ await Quiver.build(
|
|
|
135
181
|
|
|
136
182
|
```ts
|
|
137
183
|
// browser runtime
|
|
184
|
+
import { Engine, Document } from "@quillmark/wasm";
|
|
138
185
|
import { Quiver } from "@quillmark/quiver";
|
|
139
186
|
|
|
140
187
|
const quiver = await Quiver.fromBuiltUrl("/quivers/my-quiver/");
|
|
141
|
-
const
|
|
188
|
+
const engine = new Engine();
|
|
189
|
+
|
|
190
|
+
const doc = Document.fromMarkdown(markdownString);
|
|
191
|
+
const quill = await quiver.getQuill(doc.quillRef);
|
|
192
|
+
const result = await engine.render(quill, doc, { format: "pdf" });
|
|
142
193
|
```
|
|
143
194
|
|
|
144
195
|
## Server-side runtime (Node, packed artifact on disk)
|
|
@@ -178,15 +229,10 @@ await quiver.warm();
|
|
|
178
229
|
|
|
179
230
|
`warm()` is I/O-only: it loads every quill's tree (over the network for
|
|
180
231
|
`fromBuiltUrl`, off the filesystem for `fromPackage` / `fromDir` /
|
|
181
|
-
`fromBuiltDir`) and caches
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
the load.
|
|
186
|
-
|
|
187
|
-
Once a tree has been turned into a Quill, the cached tree is dropped so
|
|
188
|
-
its bytes can be GC'd — the materialized Quill is the runtime artifact.
|
|
189
|
-
Calling `warm()` again refills the tree cache.
|
|
232
|
+
`fromBuiltDir`) and caches them. It does not require an engine and does
|
|
233
|
+
not materialize Quill instances — that happens lazily on the first
|
|
234
|
+
`getQuill` call, which is microseconds. A subsequent `getQuill` reuses
|
|
235
|
+
the cached tree, skipping the load.
|
|
190
236
|
|
|
191
237
|
## Error handling
|
|
192
238
|
|
|
@@ -208,6 +254,26 @@ try {
|
|
|
208
254
|
|
|
209
255
|
Error codes: `invalid_ref`, `quill_not_found`, `quiver_invalid`, `transport_error`.
|
|
210
256
|
|
|
211
|
-
##
|
|
257
|
+
## Using `getQuill` vs calling `Quill.fromTree` directly
|
|
258
|
+
|
|
259
|
+
`getQuill(ref)` is the correct entry point for any code that loads a quill
|
|
260
|
+
through a `Quiver`. It handles ref resolution, tree fetching, `Quill.fromTree`
|
|
261
|
+
construction, and per-ref caching in one call. Do **not** reach for
|
|
262
|
+
`Quill.fromTree` directly inside a Quiver consumer:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
import { Quill } from "@quillmark/wasm";
|
|
266
|
+
|
|
267
|
+
// wrong — bypasses Quiver's cache; duplicates work getQuill already does
|
|
268
|
+
const tree = new Map<string, Uint8Array>();
|
|
269
|
+
tree.set("Quill.yaml", new TextEncoder().encode("name: memo\n..."));
|
|
270
|
+
// ...assemble the rest of the file tree by hand...
|
|
271
|
+
const quill = Quill.fromTree(tree);
|
|
272
|
+
|
|
273
|
+
// right
|
|
274
|
+
const quill = await quiver.getQuill(ref);
|
|
275
|
+
```
|
|
212
276
|
|
|
213
|
-
|
|
277
|
+
`Quill.fromTree` is for code that builds quills **outside** of a Quiver (e.g.
|
|
278
|
+
a server route that receives a raw file tree over the network, or a test
|
|
279
|
+
fixture that constructs a quill from a hand-rolled in-memory tree).
|
package/dist/bin/quiver.d.ts
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* [--include <ref>...] [--exclude <ref>...]
|
|
10
10
|
*
|
|
11
11
|
* Engine discovery (for `test` and `preview`):
|
|
12
|
-
* 1. Named
|
|
13
|
-
*
|
|
12
|
+
* 1. Named export `engine` from `quiver.config.js` at the collection root,
|
|
13
|
+
* if the file exists.
|
|
14
14
|
* 2. Auto-imported `@quillmark/wasm` from the collection's node_modules.
|
|
15
15
|
*/
|
|
16
16
|
export {};
|
package/dist/bin/quiver.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* [--include <ref>...] [--exclude <ref>...]
|
|
10
10
|
*
|
|
11
11
|
* Engine discovery (for `test` and `preview`):
|
|
12
|
-
* 1. Named
|
|
13
|
-
*
|
|
12
|
+
* 1. Named export `engine` from `quiver.config.js` at the collection root,
|
|
13
|
+
* if the file exists.
|
|
14
14
|
* 2. Auto-imported `@quillmark/wasm` from the collection's node_modules.
|
|
15
15
|
*/
|
|
16
16
|
import { createRequire } from "node:module";
|
|
@@ -42,32 +42,35 @@ function hasFlag(name) {
|
|
|
42
42
|
// Engine discovery
|
|
43
43
|
// ---------------------------------------------------------------------------
|
|
44
44
|
async function loadEngine(cwd) {
|
|
45
|
-
//
|
|
45
|
+
// Resolve @quillmark/wasm from the collection's node_modules (best-effort).
|
|
46
|
+
// Its `Engine` renders every quill (cloning the core Quill/Document into the
|
|
47
|
+
// backend itself). A quiver.config.js may export a custom `engine`.
|
|
48
|
+
let wasm;
|
|
46
49
|
try {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
engine: cfg.engine,
|
|
51
|
-
Document: cfg.Document,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
50
|
+
const req = createRequire(pathToFileURL(join(cwd, "package.json")).href);
|
|
51
|
+
const wasmPath = req.resolve("@quillmark/wasm");
|
|
52
|
+
wasm = (await import(pathToFileURL(wasmPath).href));
|
|
54
53
|
}
|
|
55
54
|
catch {
|
|
56
|
-
//
|
|
55
|
+
// Not installed — a quiver.config.js may still provide an engine.
|
|
57
56
|
}
|
|
58
|
-
//
|
|
59
|
-
const req = createRequire(pathToFileURL(join(cwd, "package.json")).href);
|
|
60
|
-
let wasmPath;
|
|
57
|
+
// 1. Named `engine` export from quiver.config.js, if present.
|
|
61
58
|
try {
|
|
62
|
-
|
|
59
|
+
const cfg = await import(pathToFileURL(join(cwd, "quiver.config.js")).href);
|
|
60
|
+
if (cfg.engine != null) {
|
|
61
|
+
return cfg.engine;
|
|
62
|
+
}
|
|
63
63
|
}
|
|
64
64
|
catch {
|
|
65
|
+
// File absent or incomplete — fall through to auto-discovery.
|
|
66
|
+
}
|
|
67
|
+
// 2. Auto-discover from @quillmark/wasm.
|
|
68
|
+
if (wasm == null) {
|
|
65
69
|
throw new Error("Cannot find @quillmark/wasm in this collection's node_modules.\n" +
|
|
66
70
|
" Install it: npm install @quillmark/wasm\n" +
|
|
67
|
-
" Or export { engine
|
|
71
|
+
" Or export { engine } from quiver.config.js for a custom engine.");
|
|
68
72
|
}
|
|
69
|
-
|
|
70
|
-
return { engine: new wasm.Quillmark(), Document: wasm.Document };
|
|
73
|
+
return new wasm.Engine();
|
|
71
74
|
}
|
|
72
75
|
// ---------------------------------------------------------------------------
|
|
73
76
|
// Commands
|
|
@@ -81,7 +84,7 @@ async function build() {
|
|
|
81
84
|
}
|
|
82
85
|
async function test() {
|
|
83
86
|
const cwd = process.cwd();
|
|
84
|
-
const
|
|
87
|
+
const engine = await loadEngine(cwd);
|
|
85
88
|
const quiver = await Quiver.fromDir(cwd);
|
|
86
89
|
const names = quiver.quillNames();
|
|
87
90
|
if (names.length === 0) {
|
|
@@ -94,9 +97,15 @@ async function test() {
|
|
|
94
97
|
for (const version of quiver.versionsOf(name)) {
|
|
95
98
|
const ref = `${name}@${version}`;
|
|
96
99
|
try {
|
|
97
|
-
const quill = await quiver.getQuill(ref
|
|
98
|
-
const doc =
|
|
99
|
-
|
|
100
|
+
const quill = await quiver.getQuill(ref);
|
|
101
|
+
const doc = quill.seedDocument();
|
|
102
|
+
let result;
|
|
103
|
+
try {
|
|
104
|
+
result = await engine.render(quill, doc);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
doc.free();
|
|
108
|
+
}
|
|
100
109
|
if (!Array.isArray(result.artifacts) || result.artifacts.length === 0) {
|
|
101
110
|
throw new Error("example render produced no artifacts");
|
|
102
111
|
}
|
|
@@ -116,7 +125,7 @@ async function test() {
|
|
|
116
125
|
}
|
|
117
126
|
async function preview() {
|
|
118
127
|
const cwd = process.cwd();
|
|
119
|
-
const
|
|
128
|
+
const engine = await loadEngine(cwd);
|
|
120
129
|
const outDir = flag("--out");
|
|
121
130
|
const format = flag("--format");
|
|
122
131
|
const quiet = hasFlag("--quiet");
|
|
@@ -124,7 +133,6 @@ async function preview() {
|
|
|
124
133
|
const exclude = multiFlag("--exclude");
|
|
125
134
|
await renderQuiverSamples(cwd, {
|
|
126
135
|
engine,
|
|
127
|
-
Document,
|
|
128
136
|
...(outDir !== undefined && { outDir }),
|
|
129
137
|
...(format !== undefined && { format }),
|
|
130
138
|
quiet,
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -5,3 +5,8 @@
|
|
|
5
5
|
// live at `@quillmark/quiver/node`.
|
|
6
6
|
export { QuiverError } from "./errors.js";
|
|
7
7
|
export { Quiver } from "./quiver.js";
|
|
8
|
+
// The canonical API (`Quill`, `Document`, `Engine`, `RenderResult`, …) is not
|
|
9
|
+
// re-exported: import it straight from the `@quillmark/wasm` peer dependency,
|
|
10
|
+
// which is the single source of truth. `quiver.getQuill(ref)` returns a core
|
|
11
|
+
// `Quill` that an `Engine` renders directly — the engine clones it into the
|
|
12
|
+
// backend, so no boundary-crossing helper is needed.
|
package/dist/node.d.ts
CHANGED
|
@@ -79,4 +79,3 @@ export declare const Quiver: typeof Base & NodeQuiverStatics;
|
|
|
79
79
|
export { QuiverError } from "./errors.js";
|
|
80
80
|
export type { QuiverErrorCode } from "./errors.js";
|
|
81
81
|
export type { BuildOptions } from "./build.js";
|
|
82
|
-
export type { QuillmarkLike, QuillLike } from "./engine-types.js";
|
package/dist/node.js
CHANGED
|
@@ -70,3 +70,6 @@ Quiver.buildPackage = async function buildPackage(specifier, outDir, opts) {
|
|
|
70
70
|
// 3. Re-export the rest of the public surface so consumers get one import.
|
|
71
71
|
// ---------------------------------------------------------------------------
|
|
72
72
|
export { QuiverError } from "./errors.js";
|
|
73
|
+
// Engine types (`Quillmark`, `Quill`, `Document`, `RenderResult`, …) are not
|
|
74
|
+
// re-exported: import them straight from the `@quillmark/wasm` peer dependency,
|
|
75
|
+
// which is the single source of truth for the engine contract.
|
package/dist/preview.d.ts
CHANGED
|
@@ -11,37 +11,29 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Usage (place a script next to your Quiver.yaml):
|
|
13
13
|
*
|
|
14
|
-
* import {
|
|
14
|
+
* import { Engine } from "@quillmark/wasm";
|
|
15
15
|
* import { renderQuiverSamples } from "@quillmark/quiver/preview";
|
|
16
16
|
*
|
|
17
|
-
* await renderQuiverSamples(import.meta.url, {
|
|
18
|
-
* engine: new Quillmark(),
|
|
19
|
-
* Document,
|
|
20
|
-
* });
|
|
17
|
+
* await renderQuiverSamples(import.meta.url, { engine: new Engine() });
|
|
21
18
|
* // → open ./preview/index.html
|
|
22
19
|
*
|
|
23
|
-
* The sample document is the illustrative example
|
|
24
|
-
* quill version — a fully filled-out,
|
|
25
|
-
* blueprint itself carries `<must-fill>`
|
|
26
|
-
* Every quill always
|
|
27
|
-
* sample document.
|
|
20
|
+
* The sample document is the illustrative example seeded by
|
|
21
|
+
* `quill.seedDocument()` for each quill version — a fully filled-out,
|
|
22
|
+
* always-renderable document (the blueprint itself carries `<must-fill>`
|
|
23
|
+
* sentinels and is not renderable). Every quill always seeds an example, so no
|
|
24
|
+
* quills are skipped for lack of a sample document.
|
|
28
25
|
*
|
|
29
26
|
* A `.gitignore` is written into `outDir` so the generated artifacts are not
|
|
30
27
|
* accidentally committed.
|
|
31
28
|
*/
|
|
32
|
-
import type {
|
|
33
|
-
/**
|
|
34
|
-
* Structural shape of the `Document` class from `@quillmark/wasm`. Only the
|
|
35
|
-
* `fromMarkdown` factory is used; passing the real class satisfies this.
|
|
36
|
-
*/
|
|
37
|
-
export interface DocumentFactoryLike {
|
|
38
|
-
fromMarkdown(markdown: string): unknown;
|
|
39
|
-
}
|
|
29
|
+
import type { Engine } from "@quillmark/wasm";
|
|
40
30
|
export interface RenderQuiverSamplesOptions {
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Canonical render engine (`new Engine()` from `@quillmark/wasm`). It takes the
|
|
33
|
+
* core `Quill`/`Document` that quiver produces and clones them into the
|
|
34
|
+
* backend on demand — no build-matching or handle-crossing for the caller.
|
|
35
|
+
*/
|
|
36
|
+
engine: Engine;
|
|
45
37
|
/** Directory to write rendered artifacts into. Default: `preview`. */
|
|
46
38
|
outDir?: string;
|
|
47
39
|
/** Force an output format (`pdf`/`svg`/`png`/`txt`). Default: engine's choice. */
|
package/dist/preview.js
CHANGED
|
@@ -11,20 +11,17 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Usage (place a script next to your Quiver.yaml):
|
|
13
13
|
*
|
|
14
|
-
* import {
|
|
14
|
+
* import { Engine } from "@quillmark/wasm";
|
|
15
15
|
* import { renderQuiverSamples } from "@quillmark/quiver/preview";
|
|
16
16
|
*
|
|
17
|
-
* await renderQuiverSamples(import.meta.url, {
|
|
18
|
-
* engine: new Quillmark(),
|
|
19
|
-
* Document,
|
|
20
|
-
* });
|
|
17
|
+
* await renderQuiverSamples(import.meta.url, { engine: new Engine() });
|
|
21
18
|
* // → open ./preview/index.html
|
|
22
19
|
*
|
|
23
|
-
* The sample document is the illustrative example
|
|
24
|
-
* quill version — a fully filled-out,
|
|
25
|
-
* blueprint itself carries `<must-fill>`
|
|
26
|
-
* Every quill always
|
|
27
|
-
* sample document.
|
|
20
|
+
* The sample document is the illustrative example seeded by
|
|
21
|
+
* `quill.seedDocument()` for each quill version — a fully filled-out,
|
|
22
|
+
* always-renderable document (the blueprint itself carries `<must-fill>`
|
|
23
|
+
* sentinels and is not renderable). Every quill always seeds an example, so no
|
|
24
|
+
* quills are skipped for lack of a sample document.
|
|
28
25
|
*
|
|
29
26
|
* A `.gitignore` is written into `outDir` so the generated artifacts are not
|
|
30
27
|
* accidentally committed.
|
|
@@ -32,6 +29,7 @@
|
|
|
32
29
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
33
30
|
import { join } from "node:path";
|
|
34
31
|
import { Quiver } from "./node.js";
|
|
32
|
+
import { isQuillmarkError } from "@quillmark/wasm";
|
|
35
33
|
/** Default directory rendered artifacts are written to. */
|
|
36
34
|
const DEFAULT_OUT_DIR = "preview";
|
|
37
35
|
/**
|
|
@@ -74,13 +72,12 @@ function isSelected(name, ref, opts) {
|
|
|
74
72
|
}
|
|
75
73
|
/**
|
|
76
74
|
* Formats a thrown render error into one string per diagnostic. `@quillmark/wasm`
|
|
77
|
-
*
|
|
78
|
-
*
|
|
75
|
+
* throws `QuillmarkError`s (an `Error` carrying `diagnostics`); fall back to
|
|
76
|
+
* the message for anything else.
|
|
79
77
|
*/
|
|
80
78
|
function failureReasons(err) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return diagnostics.map((d) => `${d.severity}: ${d.message}`);
|
|
79
|
+
if (isQuillmarkError(err) && err.diagnostics.length > 0) {
|
|
80
|
+
return err.diagnostics.map((d) => `${d.severity}: ${d.message}`);
|
|
84
81
|
}
|
|
85
82
|
return [err.message];
|
|
86
83
|
}
|
|
@@ -88,10 +85,16 @@ async function renderOne(quiver, name, version, outDir, opts) {
|
|
|
88
85
|
const ref = `${name}@${version}`;
|
|
89
86
|
let result;
|
|
90
87
|
try {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
88
|
+
// Canonical render path: a core Quill + its seeded Document handed straight
|
|
89
|
+
// to the Engine, which clones them into the backend and frees the clones.
|
|
90
|
+
const quill = await quiver.getQuill(ref);
|
|
91
|
+
const doc = quill.seedDocument();
|
|
92
|
+
try {
|
|
93
|
+
result = await opts.engine.render(quill, doc, opts.format ? { format: opts.format } : undefined);
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
doc.free();
|
|
97
|
+
}
|
|
95
98
|
}
|
|
96
99
|
catch (err) {
|
|
97
100
|
return {
|
package/dist/quiver.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* `build`) are installed on this class by `./node.js`, which is the
|
|
10
10
|
* consumer's explicit opt-in to the Node API surface.
|
|
11
11
|
*/
|
|
12
|
-
import
|
|
12
|
+
import { Quill } from "@quillmark/wasm";
|
|
13
13
|
/** @internal Internal loader strategy: source or build output. */
|
|
14
14
|
export interface QuiverLoader {
|
|
15
15
|
loadTree(name: string, version: string): Promise<Map<string, Uint8Array>>;
|
|
@@ -49,16 +49,6 @@ export declare class Quiver {
|
|
|
49
49
|
* Returns an empty array if the quill name is not in the catalog.
|
|
50
50
|
*/
|
|
51
51
|
versionsOf(name: string): string[];
|
|
52
|
-
/**
|
|
53
|
-
* Lazily loads the file tree for a specific quill version.
|
|
54
|
-
*
|
|
55
|
-
* Returns `Map<string, Uint8Array>` suitable for `engine.quill(tree)`.
|
|
56
|
-
* Does NOT cache the result — caching of materialized Quill instances
|
|
57
|
-
* happens in `getQuill`.
|
|
58
|
-
*
|
|
59
|
-
* Throws `transport_error` if name/version not in catalog or I/O fails.
|
|
60
|
-
*/
|
|
61
|
-
loadTree(name: string, version: string): Promise<Map<string, Uint8Array>>;
|
|
62
52
|
/**
|
|
63
53
|
* Resolves a selector ref → canonical ref (e.g. "memo" → "memo@1.1.0").
|
|
64
54
|
*
|
|
@@ -71,29 +61,32 @@ export declare class Quiver {
|
|
|
71
61
|
*/
|
|
72
62
|
resolve(ref: string): Promise<string>;
|
|
73
63
|
/**
|
|
74
|
-
* Returns a
|
|
64
|
+
* Returns a `Quill` for a ref (selector or canonical).
|
|
65
|
+
*
|
|
66
|
+
* The returned quill is materialized from the `@quillmark/wasm` root export
|
|
67
|
+
* — it is engine-free, portable data suitable for schema inspection,
|
|
68
|
+
* validation, blueprint access, and document seeding.
|
|
69
|
+
*
|
|
70
|
+
* A core `Quill` renders directly: pass it to `engine.render(quill, doc)`.
|
|
75
71
|
*
|
|
76
|
-
* Selector refs (e.g. `"memo"`, `"memo@1"`) are resolved to canonical
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* (or a previous `getQuill`) so the network fetch isn't paid twice.
|
|
80
|
-
* Concurrent calls for the same ref coalesce into a single load.
|
|
72
|
+
* Selector refs (e.g. `"memo"`, `"memo@1"`) are resolved to canonical form
|
|
73
|
+
* first. Materializes once and caches per canonical ref — concurrent calls
|
|
74
|
+
* coalesce into a single load.
|
|
81
75
|
*
|
|
82
76
|
* Throws:
|
|
83
77
|
* - `invalid_ref` if ref is malformed
|
|
84
78
|
* - `quill_not_found` if ref does not match any version in this quiver
|
|
85
|
-
* - propagates I/O errors from
|
|
86
|
-
* - propagates
|
|
79
|
+
* - propagates I/O errors from the loader unchanged
|
|
80
|
+
* - propagates validation errors from Quill.fromTree() unchanged
|
|
87
81
|
*/
|
|
88
|
-
getQuill(ref: string
|
|
89
|
-
engine: QuillmarkLike;
|
|
90
|
-
}): Promise<QuillLike>;
|
|
82
|
+
getQuill(ref: string): Promise<Quill>;
|
|
91
83
|
/**
|
|
92
84
|
* Prefetches the tree for every quill version in this quiver. Fail-fast.
|
|
93
85
|
*
|
|
94
86
|
* Network-bound only — does not materialize Quill instances and does not
|
|
95
87
|
* require an engine. Subsequent `getQuill` calls reuse the cached trees,
|
|
96
|
-
* skipping the fetch
|
|
88
|
+
* skipping the fetch (the tree then evicts as its quill materializes).
|
|
89
|
+
* Rejects on the first fetch failure.
|
|
97
90
|
*/
|
|
98
91
|
warm(): Promise<void>;
|
|
99
92
|
}
|