fix(extensions): bundle nested wildcard export subpaths for compiled binaries

The compiled registry enumerated each `exports` wildcard with a single-level
glob and explicitly skipped any key containing a slash, so a nested subpath
like `slash-commands/helpers/active-oauth-account` never entered the bundled
registry. Node matches `*` across `/`, so that import is legitimate: it
resolves from source, then falls through to `Bun.resolveSync` inside a
compiled binary and dies under bunfs. Reproducible on the published 17.2.1
binary with a real extension (`quota-hud.ts`).

Enumeration is now recursive, with every path segment held to the same
private/hidden rules as the leaf, so a `.private/` or `_internal/` directory
is no more exported than a private file. Directory index modules stay
excluded: `./x/*` must not serve `x/y` from `y/index.ts`, which Node would
not resolve either.
This commit is contained in:
Harsha Vardhan
2026-08-02 01:14:33 +10:00
parent 8baa3300bc
commit 98f0c58224
3 changed files with 31 additions and 2 deletions
@@ -142,7 +142,13 @@ export async function collectBundledPiEntries(): Promise<BundledPiEntry[]> {
const sourceDir = path.join(packageRoot, pattern.sourcePrefix);
try {
const glob = new Bun.Glob(`*${pattern.sourceSuffix}`);
// Recursive on purpose: Node matches `*` in an `exports` pattern across
// `/`, so `./slash-commands/*` genuinely serves
// `slash-commands/helpers/active-oauth-account`. Enumerating only the
// top level left every nested key out of the compiled registry, where
// it fell through to `Bun.resolveSync` and died under bunfs — so such
// an import worked from source and failed inside a binary.
const glob = new Bun.Glob(`**/*${pattern.sourceSuffix}`);
const matches: string[] = [];
for await (const match of glob.scan({ cwd: sourceDir, onlyFiles: true })) {
matches.push(match);
@@ -151,7 +157,11 @@ export async function collectBundledPiEntries(): Promise<BundledPiEntry[]> {
for (const match of matches) {
if (!match.endsWith(pattern.sourceSuffix)) continue;
const basename = match.slice(0, match.length - pattern.sourceSuffix.length);
if (!isSafeWildcardBasename(basename) || basename.includes("/")) continue;
const segments = basename.split("/");
// Every directory on the way has to be importable too: a private or
// hidden folder is no more exported than a private file.
if (segments.some(segment => segment.startsWith(".") || segment.startsWith("_"))) continue;
if (!isSafeWildcardBasename(segments.at(-1) ?? "")) continue;
const subpath = `${pattern.exportPrefix}${basename}${pattern.exportSuffix}`;
const key = `${manifest.name}/${subpath}`;
addEntry(key, bindingForSubpath(pkg.identifier, subpath), key);