@quillmark/quiver 0.2.0 → 0.4.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/dist/quiver.js CHANGED
@@ -2,14 +2,28 @@
2
2
  * Quiver — primary runtime abstraction for a collection of quills.
3
3
  *
4
4
  * Polymorphism via composition: internally stores a pluggable loader
5
- * (either source-backed or packed-backed).
5
+ * (either source-backed or build-output-backed).
6
6
  */
7
7
  import { QuiverError } from "./errors.js";
8
8
  import { assertNode } from "./assert-node.js";
9
+ import { parseQuillRef } from "./ref.js";
10
+ import { matchesSemverSelector, chooseHighestVersion } from "./semver.js";
9
11
  export class Quiver {
10
12
  name;
11
13
  #catalog;
12
14
  #loader;
15
+ /**
16
+ * Per-engine cache of materialized quills, keyed by canonical ref.
17
+ * WeakMap so engines can be GC'd; Promise values so concurrent
18
+ * getQuill calls coalesce into a single materialization.
19
+ */
20
+ #quillCache = new WeakMap();
21
+ /**
22
+ * Engine-independent cache of fetched trees, keyed by canonical ref.
23
+ * Populated by `warm()` and on first `getQuill` for a ref. Promise
24
+ * values so concurrent fetches coalesce.
25
+ */
26
+ #treeCache = new Map();
13
27
  /**
14
28
  * Private constructor — use static factory methods.
15
29
  * TS prevents external `new Quiver(...)` at compile time.
@@ -20,51 +34,73 @@ export class Quiver {
20
34
  this.#catalog = new Map(catalog);
21
35
  this.#loader = loader;
22
36
  }
23
- /** @internal Used by loadPackedQuiver. Not part of the public API. */
37
+ /** @internal Used by loadBuiltQuiver. Not part of the public API. */
24
38
  static _fromLoader(name, catalog, loader) {
25
39
  return new Quiver(name, catalog, loader);
26
40
  }
27
41
  /**
28
- * Node-only factory. Reads a Source Quiver from a directory containing
29
- * `Quiver.yaml` and `quills/<name>/<version>/Quill.yaml` entries.
42
+ * Node-only factory. Resolves an npm specifier against `node_modules` and
43
+ * loads the source layout at the package root.
30
44
  *
31
- * Uses dynamic import of `./source-loader.js` so that importing this module
32
- * in a browser environment does not cause a crash at module evaluation time.
45
+ * The resolved package must have `Quiver.yaml` at its root.
33
46
  *
34
- * Throws `quiver_invalid` on schema violations, `transport_error` on I/O failure.
47
+ * Throws `transport_error` on resolution/I/O failure, `quiver_invalid`
48
+ * on schema violations.
35
49
  */
36
- static async fromSourceDir(path) {
37
- assertNode("Quiver.fromSourceDir");
38
- const { scanSourceQuiver, SourceLoader } = await import("./source-loader.js");
39
- const { meta, catalog } = await scanSourceQuiver(path);
40
- const loader = new SourceLoader(path);
41
- return new Quiver(meta.name, catalog, loader);
50
+ static async fromPackage(specifier) {
51
+ assertNode("Quiver.fromPackage");
52
+ const { createRequire } = await import("node:module");
53
+ const { dirname } = await import("node:path");
54
+ const req = createRequire(import.meta.url);
55
+ let yamlPath;
56
+ try {
57
+ yamlPath = req.resolve(`${specifier}/Quiver.yaml`);
58
+ }
59
+ catch (err) {
60
+ throw new QuiverError("transport_error", `Failed to resolve quiver package "${specifier}": ${err.message}`, { cause: err });
61
+ }
62
+ return Quiver.fromDir(dirname(yamlPath));
42
63
  }
43
64
  /**
44
- * Node-only factory. Loads a Packed Quiver from a local directory.
65
+ * Node-only factory. Reads a Source Quiver from a local directory containing
66
+ * `Quiver.yaml` and `quills/<name>/<version>/Quill.yaml` entries.
45
67
  *
46
- * Uses dynamic imports so this module stays browser-safe at evaluation time.
68
+ * Also accepts `import.meta.url`-style `file://` URLs as a convenience for
69
+ * tests; the URL's parent directory is used as the source root.
47
70
  *
48
- * Throws `transport_error` on I/O failure, `quiver_invalid` on format errors.
71
+ * Throws `quiver_invalid` on schema violations, `transport_error` on I/O failure.
49
72
  */
50
- static async fromPackedDir(path) {
51
- assertNode("Quiver.fromPackedDir");
52
- const { FsTransport } = await import("./transports/fs-transport.js");
53
- const { loadPackedQuiver } = await import("./packed-loader.js");
54
- const transport = new FsTransport(path);
55
- return loadPackedQuiver(transport);
73
+ static async fromDir(pathOrFileUrl) {
74
+ assertNode("Quiver.fromDir");
75
+ let dir = pathOrFileUrl;
76
+ if (pathOrFileUrl.startsWith("file://")) {
77
+ const { fileURLToPath } = await import("node:url");
78
+ dir = fileURLToPath(new URL(".", pathOrFileUrl));
79
+ }
80
+ const { scanSourceQuiver, SourceLoader } = await import("./source-loader.js");
81
+ const { meta, catalog } = await scanSourceQuiver(dir);
82
+ const loader = new SourceLoader(dir);
83
+ return new Quiver(meta.name, catalog, loader);
56
84
  }
57
85
  /**
58
- * Browser-safe factory. Loads a Packed Quiver from an HTTP base URL.
86
+ * Browser-safe factory. Loads build output from an HTTP/HTTPS URL.
87
+ *
88
+ * Origin-relative URLs (e.g. `/quivers/foo/`) are accepted in browser
89
+ * environments. `file://` URLs are rejected — local build output is
90
+ * not loadable in V1; serve over HTTP or use `fromPackage`/`fromDir`
91
+ * against the source.
59
92
  *
60
- * Throws `transport_error` on network/HTTP failure, `quiver_invalid` on
61
- * format errors.
93
+ * Throws `transport_error` on network/HTTP failure, `quiver_invalid`
94
+ * on format errors.
62
95
  */
63
- static async fromHttp(url) {
96
+ static async fromBuilt(url) {
97
+ if (url.startsWith("file://")) {
98
+ throw new QuiverError("transport_error", `Quiver.fromBuilt requires an http(s):// or origin-relative URL; got "${url}". Local build output is not loadable in V1 — serve it over HTTP or load source via fromPackage/fromDir.`);
99
+ }
64
100
  const { HttpTransport } = await import("./transports/http-transport.js");
65
- const { loadPackedQuiver } = await import("./packed-loader.js");
101
+ const { loadBuiltQuiver } = await import("./built-loader.js");
66
102
  const transport = new HttpTransport(url);
67
- return loadPackedQuiver(transport);
103
+ return loadBuiltQuiver(transport);
68
104
  }
69
105
  /** Returns all known quill names, sorted lexicographically. */
70
106
  quillNames() {
@@ -78,24 +114,26 @@ export class Quiver {
78
114
  return [...(this.#catalog.get(name) ?? [])];
79
115
  }
80
116
  /**
81
- * Node-only tooling. Writes a Packed Quiver artifact to outDir.
117
+ * Node-only tooling. Reads the Source Quiver at sourceDir, validates it,
118
+ * and writes the runtime build artifact to outDir.
82
119
  *
83
- * Uses dynamic import of `./pack.js` so that this module stays browser-safe
84
- * at evaluation time.
120
+ * Uses dynamic import of `./build.js` so that this module stays
121
+ * browser-safe at evaluation time.
85
122
  *
86
123
  * Throws `quiver_invalid` on source validation failures,
87
124
  * `transport_error` on I/O failures.
88
125
  */
89
- static async pack(sourceDir, outDir, opts) {
90
- assertNode("Quiver.pack");
91
- const { packQuiver } = await import("./pack.js");
92
- return packQuiver(sourceDir, outDir, opts);
126
+ static async build(sourceDir, outDir, opts) {
127
+ assertNode("Quiver.build");
128
+ const { buildQuiver } = await import("./build.js");
129
+ return buildQuiver(sourceDir, outDir, opts);
93
130
  }
94
131
  /**
95
132
  * Lazily loads the file tree for a specific quill version.
96
133
  *
97
134
  * Returns `Map<string, Uint8Array>` suitable for `engine.quill(tree)`.
98
- * Does NOT cache the result — caching is the registry's concern.
135
+ * Does NOT cache the result — caching of materialized Quill instances
136
+ * happens in `getQuill`.
99
137
  *
100
138
  * Throws `transport_error` if name/version not in catalog or I/O fails.
101
139
  */
@@ -106,4 +144,110 @@ export class Quiver {
106
144
  }
107
145
  return this.#loader.loadTree(name, version);
108
146
  }
147
+ /**
148
+ * Resolves a selector ref → canonical ref (e.g. "memo" → "memo@1.1.0").
149
+ *
150
+ * Selector forms: `name`, `name@x`, `name@x.y`, `name@x.y.z`. Picks the
151
+ * highest matching version in this quiver.
152
+ *
153
+ * Throws:
154
+ * - `invalid_ref` if ref fails parseQuillRef
155
+ * - `quill_not_found` if no version matches
156
+ */
157
+ async resolve(ref) {
158
+ const parsed = parseQuillRef(ref);
159
+ const versions = this.#catalog.get(parsed.name);
160
+ if (versions && versions.length > 0) {
161
+ const candidates = parsed.selector === undefined
162
+ ? [...versions]
163
+ : versions.filter((v) => matchesSemverSelector(v, parsed.selector));
164
+ if (candidates.length > 0) {
165
+ // chooseHighestVersion returns null only for empty arrays; candidates is non-empty.
166
+ const winner = chooseHighestVersion(candidates);
167
+ return `${parsed.name}@${winner}`;
168
+ }
169
+ }
170
+ throw new QuiverError("quill_not_found", `No quill found for ref "${ref}" in quiver "${this.name}".`, { ref, quiverName: this.name });
171
+ }
172
+ /**
173
+ * Returns a render-ready `Quill` for a ref (selector or canonical).
174
+ *
175
+ * Selector refs (e.g. `"memo"`, `"memo@1"`) are resolved to canonical
176
+ * form first. Materializes via `engine.quill(tree)` on first call;
177
+ * caches per (engine, canonical-ref). Reuses a tree cached by `warm()`
178
+ * (or a previous `getQuill`) so the network fetch isn't paid twice.
179
+ * Concurrent calls for the same ref coalesce into a single load.
180
+ *
181
+ * Throws:
182
+ * - `invalid_ref` if ref is malformed
183
+ * - `quill_not_found` if ref does not match any version in this quiver
184
+ * - propagates I/O errors from loadTree unchanged
185
+ * - propagates engine errors from engine.quill() unchanged
186
+ */
187
+ async getQuill(ref, opts) {
188
+ const canonicalRef = await this.resolve(ref);
189
+ const engine = opts.engine;
190
+ let perEngine = this.#quillCache.get(engine);
191
+ if (perEngine === undefined) {
192
+ perEngine = new Map();
193
+ this.#quillCache.set(engine, perEngine);
194
+ }
195
+ let entry = perEngine.get(canonicalRef);
196
+ if (entry === undefined) {
197
+ entry = this.#materializeQuill(canonicalRef, engine).catch((err) => {
198
+ perEngine.delete(canonicalRef);
199
+ throw err;
200
+ });
201
+ perEngine.set(canonicalRef, entry);
202
+ }
203
+ return entry;
204
+ }
205
+ /**
206
+ * Internal: load tree (cached) + invoke engine.quill. Errors propagate.
207
+ *
208
+ * On success, evicts the tree from the cache so its bytes can be GC'd —
209
+ * the materialized Quill is the runtime artifact; the tree is dead weight
210
+ * once a Quill exists. On failure, the tree is retained so retries skip
211
+ * the network.
212
+ */
213
+ async #materializeQuill(canonicalRef, engine) {
214
+ const tree = await this.#getTreeCached(canonicalRef);
215
+ const quill = engine.quill(tree);
216
+ this.#treeCache.delete(canonicalRef);
217
+ return quill;
218
+ }
219
+ /**
220
+ * Internal: tree cache reader. On miss, fetches via `loadTree` and stores
221
+ * the in-flight Promise. On rejection, evicts so a retry can succeed.
222
+ */
223
+ async #getTreeCached(canonicalRef) {
224
+ let entry = this.#treeCache.get(canonicalRef);
225
+ if (entry === undefined) {
226
+ const at = canonicalRef.indexOf("@");
227
+ const name = canonicalRef.slice(0, at);
228
+ const version = canonicalRef.slice(at + 1);
229
+ entry = this.loadTree(name, version).catch((err) => {
230
+ this.#treeCache.delete(canonicalRef);
231
+ throw err;
232
+ });
233
+ this.#treeCache.set(canonicalRef, entry);
234
+ }
235
+ return entry;
236
+ }
237
+ /**
238
+ * Prefetches the tree for every quill version in this quiver. Fail-fast.
239
+ *
240
+ * Network-bound only — does not materialize Quill instances and does not
241
+ * require an engine. Subsequent `getQuill` calls reuse the cached trees,
242
+ * skipping the fetch. Rejects on the first fetch failure.
243
+ */
244
+ async warm() {
245
+ const promises = [];
246
+ for (const name of this.quillNames()) {
247
+ for (const version of this.versionsOf(name)) {
248
+ promises.push(this.#getTreeCached(`${name}@${version}`));
249
+ }
250
+ }
251
+ await Promise.all(promises);
252
+ }
109
253
  }
@@ -2,7 +2,7 @@
2
2
  * Internal filesystem scanner for Source Quiver layout.
3
3
  *
4
4
  * Uses Node.js `fs/promises` — this module must only be imported from
5
- * Node-only contexts (fromSourceDir, etc.).
5
+ * Node-only contexts (fromDir, fromPackage, etc.).
6
6
  */
7
7
  import type { QuiverMeta } from "./quiver-yaml.js";
8
8
  import type { QuiverLoader } from "./quiver.js";
@@ -2,7 +2,7 @@
2
2
  * Internal filesystem scanner for Source Quiver layout.
3
3
  *
4
4
  * Uses Node.js `fs/promises` — this module must only be imported from
5
- * Node-only contexts (fromSourceDir, etc.).
5
+ * Node-only contexts (fromDir, fromPackage, etc.).
6
6
  */
7
7
  import { readdir, readFile, stat } from "node:fs/promises";
8
8
  import { join, relative, sep } from "node:path";
package/dist/testing.d.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  /**
2
- * Plug-and-play test suite for Quiver authors.
2
+ * Convenience test harness for Quiver authors using `node:test`.
3
+ *
4
+ * Built into Node 18+; no extra test-runner dependency required. If you
5
+ * prefer vitest, jest, or another runner, write a 12-line loop against
6
+ * the main API instead — every primitive used here is public.
3
7
  *
4
8
  * Usage (place this file next to your Quiver.yaml):
5
9
  *
@@ -8,32 +12,17 @@
8
12
  * const engine = await Quillmark.load();
9
13
  * runQuiverTests(import.meta.url, engine);
10
14
  *
11
- * Requires vitest in your devDependencies.
12
- */
13
- import type { QuillmarkLike, QuillLike } from "./engine-types.js";
14
- export type { QuillmarkLike, QuillLike };
15
- /**
16
- * Returns a lightweight mock engine and a record of every tree passed to it.
17
- * Useful for writing custom test helpers; not intended as a substitute for the
18
- * real engine in runQuiverTests (the mock performs no template compilation).
15
+ * Run with `node --test`.
19
16
  */
20
- export declare function makeMockEngine(): {
21
- calls: Array<Map<string, Uint8Array>>;
22
- engine: QuillmarkLike;
23
- };
17
+ import type { QuillmarkLike } from "./engine-types.js";
24
18
  /**
25
- * Registers a Vitest describe block that validates every quill version in the
26
- * quiver at `sourceDirOrMetaUrl` against the provided Quillmark engine.
27
- *
28
- * Pass `import.meta.url` when your test file lives at the quiver root (next to
29
- * Quiver.yaml). Pass an absolute directory path for any other layout.
19
+ * Registers a `node:test` describe block that validates every quill
20
+ * version in the quiver at `metaUrlOrDir` against the provided engine.
30
21
  *
31
- * Each (quill, version) pair gets its own `it()` so failures are reported
32
- * individually. The quiver and engine are initialised once in `beforeAll`.
22
+ * Pass `import.meta.url` when this file lives at the quiver root (next
23
+ * to Quiver.yaml). Pass an absolute directory path for any other layout.
33
24
  *
34
- * Validation covers the full loading pipeline: Quiver.yaml, Quill.yaml, all
35
- * template files, and engine compilation via engine.quill(tree). A quill that
36
- * contains a template error will cause its test to fail with the engine's own
37
- * error message.
25
+ * Validation covers the full loading pipeline: Quiver.yaml, Quill.yaml,
26
+ * all template files, and engine compilation via engine.quill(tree).
38
27
  */
39
- export declare function runQuiverTests(sourceDirOrMetaUrl: string, engine: QuillmarkLike): void;
28
+ export declare function runQuiverTests(metaUrlOrDir: string, engine: QuillmarkLike): void;
package/dist/testing.js CHANGED
@@ -1,5 +1,9 @@
1
1
  /**
2
- * Plug-and-play test suite for Quiver authors.
2
+ * Convenience test harness for Quiver authors using `node:test`.
3
+ *
4
+ * Built into Node 18+; no extra test-runner dependency required. If you
5
+ * prefer vitest, jest, or another runner, write a 12-line loop against
6
+ * the main API instead — every primitive used here is public.
3
7
  *
4
8
  * Usage (place this file next to your Quiver.yaml):
5
9
  *
@@ -8,101 +12,40 @@
8
12
  * const engine = await Quillmark.load();
9
13
  * runQuiverTests(import.meta.url, engine);
10
14
  *
11
- * Requires vitest in your devDependencies.
15
+ * Run with `node --test`.
12
16
  */
13
- import { describe, it, expect, beforeAll } from "vitest";
14
- import { readdirSync } from "node:fs";
15
- import { join, basename } from "node:path";
16
- import { fileURLToPath } from "node:url";
17
+ import { describe, it, before } from "node:test";
17
18
  import { Quiver } from "./quiver.js";
18
- import { QuiverRegistry } from "./registry.js";
19
- /**
20
- * Returns a lightweight mock engine and a record of every tree passed to it.
21
- * Useful for writing custom test helpers; not intended as a substitute for the
22
- * real engine in runQuiverTests (the mock performs no template compilation).
23
- */
24
- export function makeMockEngine() {
25
- const calls = [];
26
- const engine = {
27
- quill(tree) {
28
- calls.push(tree);
29
- return { render: () => ({ ok: true }) };
30
- },
31
- };
32
- return { calls, engine };
33
- }
34
- function resolveSourceDir(sourceDirOrMetaUrl) {
35
- if (sourceDirOrMetaUrl.startsWith("file://")) {
36
- return fileURLToPath(new URL(".", sourceDirOrMetaUrl));
37
- }
38
- return sourceDirOrMetaUrl;
39
- }
40
- function discoverQuills(sourceDir) {
41
- const quillsDir = join(sourceDir, "quills");
42
- const results = [];
43
- let nameDirs;
44
- try {
45
- nameDirs = readdirSync(quillsDir, { withFileTypes: true });
46
- }
47
- catch {
48
- return results;
49
- }
50
- for (const nameEntry of nameDirs) {
51
- if (!nameEntry.isDirectory() || nameEntry.name.startsWith("."))
52
- continue;
53
- let versionDirs;
54
- try {
55
- versionDirs = readdirSync(join(quillsDir, nameEntry.name), {
56
- withFileTypes: true,
57
- });
58
- }
59
- catch {
60
- continue;
61
- }
62
- for (const versionEntry of versionDirs) {
63
- if (!versionEntry.isDirectory() || versionEntry.name.startsWith("."))
64
- continue;
65
- results.push({ name: nameEntry.name, version: versionEntry.name });
66
- }
67
- }
68
- return results;
69
- }
70
19
  /**
71
- * Registers a Vitest describe block that validates every quill version in the
72
- * quiver at `sourceDirOrMetaUrl` against the provided Quillmark engine.
20
+ * Registers a `node:test` describe block that validates every quill
21
+ * version in the quiver at `metaUrlOrDir` against the provided engine.
73
22
  *
74
- * Pass `import.meta.url` when your test file lives at the quiver root (next to
75
- * Quiver.yaml). Pass an absolute directory path for any other layout.
23
+ * Pass `import.meta.url` when this file lives at the quiver root (next
24
+ * to Quiver.yaml). Pass an absolute directory path for any other layout.
76
25
  *
77
- * Each (quill, version) pair gets its own `it()` so failures are reported
78
- * individually. The quiver and engine are initialised once in `beforeAll`.
79
- *
80
- * Validation covers the full loading pipeline: Quiver.yaml, Quill.yaml, all
81
- * template files, and engine compilation via engine.quill(tree). A quill that
82
- * contains a template error will cause its test to fail with the engine's own
83
- * error message.
26
+ * Validation covers the full loading pipeline: Quiver.yaml, Quill.yaml,
27
+ * all template files, and engine compilation via engine.quill(tree).
84
28
  */
85
- export function runQuiverTests(sourceDirOrMetaUrl, engine) {
86
- const sourceDir = resolveSourceDir(sourceDirOrMetaUrl);
87
- // Enumerate quills synchronously so Vitest can collect test cases before
88
- // any async work begins. Errors here (missing quills/ dir, unreadable dirs)
89
- // surface as the "has at least one quill" test failing rather than a
90
- // collection-time crash.
91
- const quills = discoverQuills(sourceDir);
92
- describe(`Quiver: ${basename(sourceDir)}`, () => {
93
- let registry;
94
- beforeAll(async () => {
95
- const quiver = await Quiver.fromSourceDir(sourceDir);
96
- registry = new QuiverRegistry({ engine, quivers: [quiver] });
29
+ export function runQuiverTests(metaUrlOrDir, engine) {
30
+ describe("Quiver", () => {
31
+ let quiver;
32
+ before(async () => {
33
+ quiver = await Quiver.fromDir(metaUrlOrDir);
97
34
  });
98
35
  it("has at least one quill", () => {
99
- expect(quills).not.toHaveLength(0);
36
+ if (quiver.quillNames().length === 0) {
37
+ throw new Error("Quiver has no quills");
38
+ }
39
+ });
40
+ it("compiles every quill version without error", async () => {
41
+ for (const name of quiver.quillNames()) {
42
+ for (const version of quiver.versionsOf(name)) {
43
+ const quill = await quiver.getQuill(`${name}@${version}`, { engine });
44
+ if (typeof quill.render !== "function") {
45
+ throw new Error(`${name}@${version}: engine returned non-conforming Quill`);
46
+ }
47
+ }
48
+ }
100
49
  });
101
- for (const { name, version } of quills) {
102
- it(`${name}@${version} compiles without error`, async () => {
103
- const quill = await registry.getQuill(`${name}@${version}`);
104
- expect(typeof quill.render).toBe("function");
105
- });
106
- }
107
50
  });
108
51
  }
@@ -1,11 +1,11 @@
1
1
  /**
2
- * HttpTransport — browser-safe packed quiver transport that fetches via HTTP.
2
+ * HttpTransport — browser-safe built-quiver transport that fetches via HTTP.
3
3
  * Internal; not exported from index.ts.
4
4
  *
5
5
  * Uses globalThis.fetch — no node: imports at any level.
6
6
  */
7
- import type { PackedTransport } from "../packed-loader.js";
8
- export declare class HttpTransport implements PackedTransport {
7
+ import type { BuiltTransport } from "../built-loader.js";
8
+ export declare class HttpTransport implements BuiltTransport {
9
9
  private readonly baseUrl;
10
10
  constructor(baseUrl: string);
11
11
  fetchBytes(relativePath: string): Promise<Uint8Array>;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * HttpTransport — browser-safe packed quiver transport that fetches via HTTP.
2
+ * HttpTransport — browser-safe built-quiver transport that fetches via HTTP.
3
3
  * Internal; not exported from index.ts.
4
4
  *
5
5
  * Uses globalThis.fetch — no node: imports at any level.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@quillmark/quiver",
3
- "version": "0.2.0",
4
- "description": "Quiver registry and packaging for Quillmark",
3
+ "version": "0.4.0",
4
+ "description": "Quiver registry and build tooling for Quillmark",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -42,20 +42,14 @@
42
42
  "README.md"
43
43
  ],
44
44
  "peerDependencies": {
45
- "@quillmark/wasm": ">=0.59.0-rc.2",
46
- "vitest": ">=4.0.0"
47
- },
48
- "peerDependenciesMeta": {
49
- "vitest": {
50
- "optional": true
51
- }
45
+ "@quillmark/wasm": ">=0.59.0"
52
46
  },
53
47
  "dependencies": {
54
48
  "fflate": "^0.8.2",
55
49
  "yaml": "^2.8.3"
56
50
  },
57
51
  "devDependencies": {
58
- "@quillmark/wasm": "0.59.0-rc.2",
52
+ "@quillmark/wasm": "0.59.0",
59
53
  "@types/node": "^25.3.3",
60
54
  "typescript": "^5.9.3",
61
55
  "vitest": "^4.0.18"
package/dist/pack.d.ts DELETED
@@ -1,25 +0,0 @@
1
- /**
2
- * Pack logic — internal, Node-only.
3
- *
4
- * All Node.js built-in imports are done dynamically inside `packQuiver` so
5
- * that a type-only import of `PackOptions` from `src/index.ts` does NOT
6
- * pull `node:fs` or `node:crypto` into browser bundles.
7
- */
8
- /** Reserved for future pack options (e.g. compression level, filters). */
9
- export type PackOptions = Record<string, never>;
10
- /**
11
- * Reads a Source Quiver, validates it, and writes a Packed Quiver to outDir.
12
- *
13
- * Output layout:
14
- * outDir/
15
- * Quiver.json # stable pointer
16
- * manifest.<md5prefix6>.json # hashed manifest
17
- * <name>@<version>.<md5>.zip # one bundle per quill
18
- * store/
19
- * <md5> # dehydrated font bytes (full hash, no ext)
20
- *
21
- * Throws:
22
- * - `quiver_invalid` on source validation failures (propagated from scanner)
23
- * - `transport_error` on I/O failures
24
- */
25
- export declare function packQuiver(sourceDir: string, outDir: string, _opts?: PackOptions): Promise<void>;
@@ -1,39 +0,0 @@
1
- import type { QuillmarkLike, QuillLike } from "./engine-types.js";
2
- import type { Quiver } from "./quiver.js";
3
- export declare class QuiverRegistry {
4
- #private;
5
- constructor(args: {
6
- engine: QuillmarkLike;
7
- quivers: Quiver[];
8
- });
9
- /**
10
- * Resolves a selector ref → canonical ref (e.g. "memo" → "memo@1.1.0").
11
- *
12
- * Applies multi-quiver precedence (§4): scan quivers in order, first quiver
13
- * with any matching candidate wins, highest match within that quiver returned.
14
- *
15
- * Throws:
16
- * - `invalid_ref` if ref fails parseQuillRef
17
- * - `quill_not_found` if no quiver has a matching candidate
18
- */
19
- resolve(ref: string): Promise<string>;
20
- /**
21
- * Returns a render-ready QuillLike instance for a canonical ref.
22
- * Materializes via engine.quill(tree) on first call; caches by canonical ref.
23
- *
24
- * Throws:
25
- * - `invalid_ref` if canonicalRef is not valid canonical x.y.z form
26
- * - `quill_not_found` if canonical ref doesn't map to a loaded quiver
27
- * - propagates I/O errors from loadTree unchanged
28
- * - propagates engine errors from engine.quill() unchanged (not wrapped)
29
- */
30
- getQuill(canonicalRef: string): Promise<QuillLike>;
31
- /**
32
- * Warms all quill refs across all composed quivers. Fail-fast.
33
- *
34
- * Calls loadTree + engine.quill(tree) for every known quill version in
35
- * parallel. Already-cached refs resolve instantly (idempotent). Rejects on
36
- * the first failure (Promise.all fail-fast semantics).
37
- */
38
- warm(): Promise<void>;
39
- }