feat(npm): restore programmatic/embedded SDK API (#354) (#603)

The 0.9.x thin-installer turned @colbymchenry/codegraph into a bin-only
shim: require("@colbymchenry/codegraph") threw MODULE_NOT_FOUND and no
types shipped, breaking embedded library consumers (e.g. Electron apps)
upgrading from 0.8.0.

Restore programmatic use without re-bloating the thin shim or duplicating
the ~49 MB of grammars the per-platform bundle already carries:

- main -> npm-sdk.js re-exports the installed per-platform bundle's compiled
  library (lib/dist/index.js) at runtime, reusing that bundle's own deps; it
  falls back to a self-healed cache bundle, else throws an actionable error.
- types -> ship the .d.ts tree only (~590 KB) in the main package, built from
  the same release so it can never skew from the runtime it re-exports.
- exports map resolves the `types` condition (nodenext) and the default entry.
- DatabaseConnection + QueryBuilder are now top-level exports, so embedded
  callers get the building blocks from the package entry instead of deep
  dist/ imports (which the shim no longer ships).

The CLI/MCP `bin` keeps execing the bundled Node; only library consumers run
on their own runtime, which must be Node 22.5+ for the built-in node:sqlite.

Validated end-to-end: built a real darwin-arm64 bundle, packed the npm
packages, installed them into a throwaway consumer, and confirmed require()
plus a full init/indexAll/searchNodes round-trip and the low-level
DatabaseConnection/QueryBuilder path all work on the host Node; types resolve
under both nodenext and classic node resolution; and the CLI shim still
launches. New hermetic tests cover npm-sdk resolution, cache fallback, and the
missing-bundle error.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-05-31 19:05:40 -05:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 3a1ddf41cd
commit 89d4d37a29
6 changed files with 235 additions and 2 deletions
+25 -1
View File
@@ -72,8 +72,26 @@ for archive in "${archives[@]}"; do
done
# Main shim package.
# npm-shim.js CLI/MCP launcher (execs the bundled Node) — the `bin`.
# npm-sdk.js programmatic/embedded entry (#354): re-exports the installed
# platform bundle's compiled library — the `main`.
# dist/ the .d.ts tree only (types). The runtime .js stays in the
# per-platform bundle so its deps aren't duplicated here.
cp "$ROOT/scripts/npm-shim.js" "$NPM/main/npm-shim.js"
cp "$ROOT/scripts/npm-sdk.js" "$NPM/main/npm-sdk.js"
[ -f "$ROOT/README.md" ] && cp "$ROOT/README.md" "$NPM/main/README.md"
# Ship the type declarations so `types`/`exports.types` resolve. Built from this
# same release, so they can't skew from the runtime npm-sdk.js re-exports.
[ -f "$ROOT/dist/index.d.ts" ] || ( echo "[pack-npm] building dist for .d.ts" >&2 && cd "$ROOT" && npm run build >/dev/null )
ROOT="$ROOT" DEST="$NPM/main" node -e '
const fs=require("fs"), path=require("path");
const src=path.join(process.env.ROOT,"dist"), dest=path.join(process.env.DEST,"dist");
fs.cpSync(src, dest, { recursive:true, filter(s){
try { return fs.statSync(s).isDirectory() || s.endsWith(".d.ts"); } catch (e) { return false; }
}});
'
VERSION="$VERSION" SCOPE="$SCOPE" TARGETS="${targets[*]}" \
node -e '
const fs=require("fs");
@@ -85,8 +103,14 @@ VERSION="$VERSION" SCOPE="$SCOPE" TARGETS="${targets[*]}" \
version: process.env.VERSION,
description: "Local-first code intelligence for AI agents (MCP). Self-contained — bundles its own runtime.",
bin: { codegraph: "npm-shim.js" },
main: "npm-sdk.js",
types: "dist/index.d.ts",
exports: {
".": { types: "./dist/index.d.ts", default: "./npm-sdk.js" },
"./package.json": "./package.json"
},
optionalDependencies: opt,
files: ["npm-shim.js","README.md"],
files: ["npm-shim.js","npm-sdk.js","dist","README.md"],
license: "MIT"
}, null, 2) + "\n");
' "$NPM/main/package.json"