quilltap 4.9.0-dev.51 → 4.9.0-dev.53

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.
Files changed (2) hide show
  1. package/bin/quilltap.js +38 -14
  2. package/package.json +1 -1
package/bin/quilltap.js CHANGED
@@ -264,23 +264,47 @@ function linkNativeModules(standaloneDir) {
264
264
  const sharpDir = resolveModuleDir('sharp');
265
265
  linkModule('sharp', sharpDir);
266
266
 
267
- // Link sharp's @img platform packages — they live near sharp's location
268
- if (sharpDir) {
269
- const sharpParent = path.dirname(sharpDir);
270
-
271
- // If sharp is in a scoped dir or regular node_modules, look for @img there
272
- const imgDir = path.join(sharpParent, '@img');
273
- if (fs.existsSync(imgDir)) {
274
- try {
275
- const imgPackages = fs.readdirSync(imgDir).filter(name => name.startsWith('sharp-'));
276
- for (const pkg of imgPackages) {
277
- linkModule(`@img/${pkg}`, path.join(imgDir, pkg));
278
- }
279
- } catch {
280
- // Non-fatal sharp may work without explicit @img links
267
+ // Link a scoped native's platform-specific siblings (@img/sharp-*,
268
+ // @napi-rs/canvas-*). Both wrappers require their binary as a SCOPE-SIBLING
269
+ // `@img/sharp-darwin-arm64` from inside `@img/sharp` — so the binary has to
270
+ // sit in the same node_modules the wrapper was resolved from. It cannot be
271
+ // inherited: the standalone tree lives in the download cache
272
+ // (~/Library/Caches/Quilltap/standalone and friends), far outside this
273
+ // package's node_modules, so Node's upward walk never reaches the copy npm
274
+ // installed for us. Linking the wrapper without its siblings therefore
275
+ // produces a wrapper that resolves and then throws at first use.
276
+ function linkScopedPlatformSiblings(scope, prefix, wrapperName, wrapperDir) {
277
+ if (!wrapperDir) return;
278
+ // Walk back exactly as many segments as the wrapper's own name has, so this
279
+ // works whether the wrapper is unscoped ('sharp' -> one) or scoped
280
+ // ('@napi-rs/canvas' -> two). Counting fixed levels would silently resolve
281
+ // one directory too high for sharp and miss @img entirely.
282
+ let nodeModulesDir = wrapperDir;
283
+ for (let i = 0; i < wrapperName.split('/').length; i++) {
284
+ nodeModulesDir = path.dirname(nodeModulesDir);
285
+ }
286
+ const scopeDir = path.join(nodeModulesDir, scope);
287
+ if (!fs.existsSync(scopeDir)) return;
288
+ try {
289
+ for (const name of fs.readdirSync(scopeDir)) {
290
+ if (!name.startsWith(prefix)) continue;
291
+ linkModule(`${scope}/${name}`, path.join(scopeDir, name));
281
292
  }
293
+ } catch {
294
+ // Non-fatal — the wrapper may still find a binary by another route.
282
295
  }
283
296
  }
297
+
298
+ linkScopedPlatformSiblings('@img', 'sharp-', 'sharp', sharpDir);
299
+
300
+ // Link @napi-rs/canvas (pdfjs-dist's PDF rasteriser) the same way sharp is
301
+ // handled: build-standalone-tarball.mjs strips every @napi-rs/canvas-* binary
302
+ // from the tarball, so without this the standalone tree keeps only the JS
303
+ // wrapper and PDF rendering dies on a missing native. The wrapper is scoped,
304
+ // so it needs its siblings linked alongside it.
305
+ const canvasDir = resolveModuleDir('@napi-rs/canvas');
306
+ linkModule('@napi-rs/canvas', canvasDir);
307
+ linkScopedPlatformSiblings('@napi-rs', 'canvas-', '@napi-rs/canvas', canvasDir);
284
308
  }
285
309
 
286
310
  // Main
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quilltap",
3
- "version": "4.9.0-dev.51",
3
+ "version": "4.9.0-dev.53",
4
4
  "description": "Self-hosted AI workspace for writers, worldbuilders, and roleplayers. Run with npx quilltap.",
5
5
  "author": {
6
6
  "name": "Charles Sebold",