/** * optional-externals, a compiled build must survive a package the manifest * says is optional, and must not survive one it says is required. * * ── The gap this closes ─────────────────────────────────────────────────── * * `@pellux/goodvibes-sdk` declares thirty packages under * `optionalDependencies`. The SDK now reaches every one of them through a * dynamic import, so a RUNNING process without them reports the affected * feature unavailable and carries on. Measured against the real daemon binary * with `jsdom` and `@mozilla/readability` unresolvable at runtime: exit 1 with * 896 bytes on stderr naming the operator's actual problem, where the static * shape had died at module init with 109 bytes saying only * `Cannot find module '@mozilla/readability'`. * * The BUILD did not get the same treatment, because bun resolves a dynamic * `import('pkg')` at bundle time exactly as it resolves a static one. Measured * with the package absent: * * bun build …/daemon/cli.ts --compile * → error: Could not resolve: "jsdom" * * So an install that legitimately skipped an optional package could not * produce a binary at all, and the lazy runtime resolution the SDK had just * gained never got the chance to govern. The declaration only becomes true * when the compile honours it too. * * ── The rule ────────────────────────────────────────────────────────────── * * For every package a manifest declares OPTIONAL and that is not installed, * the compile passes `--external `: bun leaves the specifier for runtime, * the binary is produced, and the SDK's own unavailability report is what the * operator sees. An optional package that IS installed is left alone and gets * bundled, which is what makes a normal build byte-for-byte what it was. * * For every package a manifest declares REQUIRED and that is not installed, * the build FAILS, by name and by manifest. Externalising that one would trade * a loud build failure for a binary that dies in the field, the exact trade * this whole line of work exists to undo. */ import type { FsReader } from './effects.js'; /** One manifest's dependency declarations, as the compile path needs them. */ export interface DependencyManifest { /** Package the declarations came from, e.g. `@pellux/goodvibes-sdk`. */ readonly name: string; /** Where it was read from, quoted in failure messages so the fix is locatable. */ readonly path: string; /** Declared as needed: `dependencies`. */ readonly required: readonly string[]; /** Declared as skippable: `optionalDependencies`. */ readonly optional: readonly string[]; } /** A required package that is not installed, and who asked for it. */ export interface MissingRequired { readonly packageName: string; readonly declaredBy: string; readonly manifestPath: string; } export interface OptionalExternalsResult { /** `--external` arguments for optional packages that are absent. */ readonly externals: readonly string[]; /** Required packages that are absent. A non-empty list must fail the build. */ readonly missingRequired: readonly MissingRequired[]; } export interface OptionalExternalsInput { readonly manifests: readonly DependencyManifest[]; /** True when the package can be resolved from the build root. */ readonly isInstalled: (packageName: string) => boolean; } /** * Split every declared dependency into "externalise it" and "fail the build". * * A package declared optional by one manifest and required by another is * treated as REQUIRED: the stricter declaration wins, because the manifest * that needs it is the one that breaks without it. */ export declare function resolveOptionalExternals(input: OptionalExternalsInput): OptionalExternalsResult; /** The message a build failure prints. Named packages, named manifests, one instruction. */ export declare function describeMissingRequired(missing: readonly MissingRequired[]): string; /** Parse one package.json into the declarations the compile path reads. */ export declare function readDependencyManifest(fs: FsReader, path: string, fallbackName: string): DependencyManifest | null; //# sourceMappingURL=optional-externals.d.ts.map