packall

@packall/core

The registry-agnostic engine: resolution, download, verification and packaging.

import { … } from "@packall/core";

Services and classes

Archiver

Service tag for archive creation.

class Archiver {
  readonly Service: {
     readonly create: (options: CreateArchiveOptions) => Effect<ArchiveResult, ArchiveError | PlatformError, FileSystem>;
  }
  readonly key: "@packall/core/Archiver"
}

Archiver.Service

The packing backend.

type Archiver.Service = {
   readonly create: (options: CreateArchiveOptions) => Effect<ArchiveResult, ArchiveError | PlatformError, FileSystem>;
}

Progress

Service tag for progress reporting.

class Progress {
  readonly Service: { readonly emit: (event: ProgressEvent) => Effect<void, never, never>; }
  readonly key: "@packall/core/Progress"
}

Progress.Service

The reporting sink.

type Progress.Service = { readonly emit: (event: ProgressEvent) => Effect<void, never, never>; }

Registry

Service tag for the registry backend.

class Registry {
  readonly Service: {
     readonly kind: string;
     readonly registryFor: (packageName: string) => string;
     readonly preflight: Effect<void, BundlerError, never>;
     readonly packument: (packageName: string) => Effect<Packument, BundlerError, never>;
     readonly download: (manifest: PackageManifest) => Effect<Uint8Array<ArrayBufferLike>, BundlerError, never>;
  }
  readonly key: "@packall/core/Registry"
}
import { Effect } from "effect"
import { Registry } from "@packall/core"

const program = Effect.gen(function*() {
  const registry = yield* Registry
  return yield* registry.packument("lodash")
})

Registry.Service

The set of operations a registry backend has to provide.

type Registry.Service = {
   readonly kind: string;
   readonly registryFor: (packageName: string) => string;
   readonly preflight: Effect<void, BundlerError, never>;
   readonly packument: (packageName: string) => Effect<Packument, BundlerError, never>;
   readonly download: (manifest: PackageManifest) => Effect<Uint8Array<ArrayBufferLike>, BundlerError, never>;
}

Functions

buildManifest

Builds the manifest for a completed (or dry) run.

const buildManifest: (input: {
   readonly resolution: Resolution;
   readonly included: readonly ResolvedPackage[];
   readonly options: BundleOptions;
   readonly registry: {
     readonly kind: string;
     readonly url: string;
  };
   readonly toolVersion: string;
   readonly sizes: ReadonlyMap<string, number>;
   readonly createdAt?: Date | undefined;
}) => BundleManifest

bundle

Resolves, downloads and packages a set of specs.

Under dryRun this stops after resolution and reports the plan — useful for checking what a command would pull before committing to a multi-gigabyte download over a slow VPN.

const bundle: (specs: readonly PackageSpec[], options: BundleContext) => Effect<BundleResult, PlatformError | BundlerError, BundleEnv>

bundleLocked

bundle, pinned to a lockfile.

const bundleLocked: (lockfile: LockedTree, options: ResolveOptions & {
   readonly layout: Layout;
   readonly outDir: string;
   readonly archiveName?: string | undefined;
   readonly dryRun: boolean;
   readonly verifyIntegrity: boolean;
   readonly force: boolean;
   readonly skipExisting?: ReadonlySet<string> | undefined;
   readonly overwrite?: ReadonlySet<string> | undefined;
} & {
   readonly toolVersion: string;
} & {
   readonly includeDev: boolean;
   readonly declared?: readonly string[] | undefined;
}) => Effect<BundleResult, PlatformError | BundlerError, BundleEnv>

bundleResolved

Downloads and packages an already-computed resolution.

Everything from here on touches the disk, and all of it happens inside Effect.scoped — so the staging tree's lifetime is exactly this call, including when it fails partway through or the user hits Ctrl-C.

const bundleResolved: (resolution: Resolution, options: BundleContext) => Effect<BundleResult, PlatformError | BundlerError, BundleEnv>

createArchive

Packs an archive with whichever backend is installed.

The progress events are emitted here rather than inside the backends, so every implementation reports identically and a new one cannot forget to.

const createArchive: (options: CreateArchiveOptions) => Effect<ArchiveResult, ArchiveError | PlatformError, Archiver | FileSystem | Progress>

dedupeSpecs

Collapses duplicate specs, keeping the first occurrence.

npmb react react@latest is a plausible thing to type and should not download React twice.

const dedupeSpecs: (specs: readonly PackageSpec[]) => readonly PackageSpec[]

detectLockfile

Deliberately cheap and total: it never throws, so --file can try this first and fall through to its other shapes when the answer is NotALockfile.

const detectLockfile: (content: string) => LockfileDetection

digestOf

Computes the base64 digest of some bytes under one algorithm.

const digestOf: (data: Uint8Array<ArrayBufferLike>, algorithm: "sha1" | "sha256" | "sha384" | "sha512") => Effect<string, PlatformError, Crypto>

emitProgress

Emits an event to whichever reporter is installed.

const emitProgress: (event: ProgressEvent) => Effect<void, never, Progress>

formatPlatformFilter

Renders a filter for the manifest and for --dry-run output.

const formatPlatformFilter: (filter: PlatformFilter) => string

formatSelector

Human-readable form of just the selector part.

const formatSelector: (selector: Selector) => string

formatSpec

Renders a spec back into the canonical name@selector form.

const formatSpec: (spec: PackageSpec) => string

hexDigestOf

Computes the hex digest of some bytes — the form legacy shasum uses.

const hexDigestOf: (data: Uint8Array<ArrayBufferLike>, algorithm: "sha1" | "sha256" | "sha384" | "sha512") => Effect<string, PlatformError, Crypto>

importGuide

The import guide written into each bundle.

Kept short and copy-pasteable on purpose: whoever opens this is mid-task on a restricted network and does not want prose.

Layout-aware, because the instructions genuinely differ — a registry-layout tree uploads into Artifactory as it stands and a flat directory does not, and a guide that said otherwise would be discovered to be wrong by somebody who can no longer look anything up.

const importGuide: (options: GuideOptions) => string

indexPackages

Index of a resolution by name@version, for lookups during bundling.

const indexPackages: (resolution: Resolution) => ReadonlyMap<string, ResolvedPackage>

isIncluded

Should this package be included, given the active filter?

Under All this is unconditionally true — that is the whole point.

const isIncluded: (constraints: PlatformConstraints, filter: PlatformFilter) => boolean

isRecord

Narrows an unknown value to a plain object.

The entry point for reading anything the tool does not control — lockfiles, package.json files, registry responses — without reaching for a cast.

const isRecord: (value: unknown) => value is Record<string, unknown>

layerCallbackProgress

Sends every event to a callback. Used by the CLI renderer.

const layerCallbackProgress: (onEvent: (event: ProgressEvent) => void) => Layer<Progress, never, never>

lockfileNamesFor

The names a given manager writes.

const lockfileNamesFor: (format: LockfileFormat) => readonly string[]

packageKey

const packageKey: (name: string, version: string) => string

packagePath

The path of one package tarball within a bundle, using / separators regardless of host platform — these become tar entry names, and tar entries are always POSIX.

const packagePath: (name: string, version: string) => string

parseDependencyTarget

Classifies one name -> range entry.

An empty range, *, and latest all mean "any published version"; npm treats them interchangeably and so do we.

const parseDependencyTarget: (name: string, raw: string) => DependencyTarget

parseInputFile

Parses file content that has already been read.

Split out from the IO so the whole of this logic is testable with plain strings and no file system at all.

const parseInputFile: (filePath: string, content: string, options?: InputFileOptions) => InputFileResult

parseIntegrity

Parses an SRI string such as sha512-abc...==.

npm permits several space-separated hashes; we keep every one we can verify and ignore algorithms we cannot.

const parseIntegrity: (integrity: string) => readonly IntegrityHash[]

parseLockfile

Parses lockfile content into a flat graph.

Throws LockfileError; the caller is expected to be inside an Effect.try.

const parseLockfile: (path: string, content: string, format?: LockfileFormat | undefined, options?: LockfileParseOptions) => LockedTree

parsePlatformTarget

Parses linux, linux-x64, darwin-arm64, win32-x64, linux-x64-musl, linux-musl.

An OS on its own means every architecture for that OS, which is almost always what you want: "the Windows and Linux bindings" is a far more natural way to describe a target set than enumerating four os-arch pairs, and it keeps working when a package adds an arm64 build.

Returns null rather than throwing so the CLI can report every bad value at once alongside the list of accepted forms.

const parsePlatformTarget: (input: string) => PlatformTarget | null

parseSpec

Parses a single spec string.

Splitting on @ is the fiddly part: a scoped name starts with @, so we look for the last @ that is not at index 0.

const parseSpec: (raw: string) => PackageSpec

parseSpecs

Parses many specs, collecting every failure rather than stopping at the first. Somebody bundling forty packages should be told about all four typos in one go, not made to re-run four times.

const parseSpecs: (inputs: readonly string[]) => {
   readonly specs: readonly PackageSpec[];
   readonly errors: readonly InvalidSpecError[];
}

perSpecArchiveName

Name of the archive produced for one root spec in per-spec layout.

The same rule as flatName, which is where the reasoning lives — a per-spec archive and a flat package tarball are both "one file in a directory, named for what is in it", and there is no reason for a directory of bundles and a directory of packages to be read differently.

const perSpecArchiveName: (name: string, version: string) => string

plan

Checks the registry is reachable, then resolves every spec.

Split out from bundle so a caller can inspect the plan — how many packages, how many archives — and act on it before any bytes move. The CLI uses this to offer a different layout when a run turns out to be much larger than expected.

const plan: (specs: readonly PackageSpec[], options: BundleOptions) => Effect<Resolution, BundlerError, Progress | Registry>

planLocked

The lockfile counterpart of plan.

Same preflight, same Resolution out; the difference is entirely in how the package set is arrived at. Kept as a separate entry point rather than an option on plan because the two take genuinely different inputs — a set of specs to satisfy versus a graph to reproduce — and blurring that is how a "pinned" run quietly starts resolving ranges again.

const planLocked: (lockfile: LockedTree, options: ResolveOptions & {
   readonly layout: Layout;
   readonly outDir: string;
   readonly archiveName?: string | undefined;
   readonly dryRun: boolean;
   readonly verifyIntegrity: boolean;
   readonly force: boolean;
   readonly skipExisting?: ReadonlySet<string> | undefined;
   readonly overwrite?: ReadonlySet<string> | undefined;
} & {
   readonly includeDev: boolean;
   readonly declared?: readonly string[] | undefined;
}) => Effect<Resolution, BundlerError, Progress | Registry>

plannedOutputs

The files a run will write.

Computed before anything is downloaded, so a collision is reported in the first second rather than after a twenty-minute transfer.

const plannedOutputs: (resolution: Resolution, options: BundleContext) => readonly PlannedOutput[]

platformTargets

const platformTargets: (targets: readonly PlatformTarget[]) => PlatformFilter

readInputFile

Reads and parses an input file.

const readInputFile: (filePath: string, options?: InputFileOptions) => Effect<InputFileResult, InvalidInputFileError | LockfileError, FileSystem>

resolve

Resolves every requested spec.

Each selected root version is walked separately so per-spec layout has an exact closure per tarball, while all walks share one cache, so a package reached from twenty roots is fetched once.

const resolve: (specs: readonly PackageSpec[], options: ResolveOptions) => Effect<Resolution, BundlerError, Progress | Registry>

resolveLocked

Turns a parsed lockfile into a resolution, pinning every version.

const resolveLocked: (lock: LockedTree, options: LockedResolveOptions) => Effect<Resolution, BundlerError, Progress | Registry>

selectVersions

Turns a selector into the concrete version(s) it names.

all controls whether a range collapses to its best match — npm's behaviour, and what you always want for a transitive dependency — or expands to every satisfying published version, which is what --all-versions is for.

const selectVersions: (packument: Packument, selector: Selector, options: {
   readonly all: boolean;
   readonly maxVersions?: number | undefined;
   readonly includePrerelease: boolean;
}) => readonly string[]

serializeManifest

Serialises a manifest with stable key order and a trailing newline.

const serializeManifest: (manifest: BundleManifest) => string

singleArchiveName

Name of the archive produced in single layout.

const singleArchiveName: (base?: string) => string

splitName

Splits @scope/name into its parts. scope is undefined when unscoped.

const splitName: (name: string) => {
   readonly scope: string | undefined;
   readonly bare: string;
}

summarize

Computes the plan summary for a resolution.

const summarize: (resolution: Resolution) => PlanSummary

tarballFileName

@babel/core + 7.24.0 -> core-7.24.0.tgz

const tarballFileName: (name: string, version: string) => string

tolerant

An optional field that goes absent rather than failing when it is malformed.

This is the difference between "we do not understand this field" and "we do not understand this document". An os: "linux" where an array was expected costs the platform filter one signal; it should not cost the run the package.

const tolerant: <S extends Schema.Top>(schema: S) => optional<middlewareDecoding<UndefinedOr<S>, S["DecodingServices"]>>

verifyIntegrity

Checks bytes against whichever checksums the registry supplied.

SRI is preferred; shasum is the fallback for versions published before integrity strings existed. If the registry gave us neither, that is reported as Unverifiable rather than silently treated as a pass — the caller decides whether to tolerate it.

const verifyIntegrity: (data: Uint8Array<ArrayBufferLike>, dist: {
   readonly integrity?: string | undefined;
   readonly shasum?: string | undefined;
}) => Effect<VerificationResult, PlatformError, Crypto>

withLayout

Replaces the layout on a set of options.

const withLayout: <T extends BundleOptions>(options: T, layout: Layout) => T

Constants

allPlatforms

const allPlatforms: {
   readonly _tag: "All";
} | {
   readonly _tag: "Targets";
   readonly targets: readonly PlatformTarget[];
}

ArtifactKind

What a bundle run wrote to the output directory.

type ArtifactKind = "archive" | "directory" | "file"

const ArtifactKind: {
   readonly Archive: "archive";
   readonly Directory: "directory";
   readonly File: "file";
}

defaultBundleOptions

const defaultBundleOptions: {
   readonly scope: DependencyScope;
   readonly allVersions: boolean;
   readonly maxVersions?: number | undefined;
   readonly includePrerelease: boolean;
   readonly concurrency: number;
   readonly layout: Layout;
   readonly archiveName?: string | undefined;
   readonly dryRun: boolean;
   readonly verifyIntegrity: boolean;
   readonly force: boolean;
   readonly skipExisting?: ReadonlySet<string> | undefined;
   readonly overwrite?: ReadonlySet<string> | undefined;
}

defaultInputFileOptions

const defaultInputFileOptions: {
   readonly includeDev: boolean;
   readonly importer?: string | undefined;
}

defaultResolveOptions

const defaultResolveOptions: {
   readonly scope: DependencyScope;
   readonly allVersions: boolean;
   readonly maxVersions?: number | undefined;
   readonly includePrerelease: boolean;
   readonly concurrency: number;
}

defaultScope

const defaultScope: {
   readonly optional: boolean;
   readonly peer: boolean;
   readonly platforms: PlatformFilter;
}

DirectOnlyKind

The kind that only a direct dependency of the locked project can have.

type DirectOnlyKind = "dev"

const DirectOnlyKind: { readonly Dev: "dev"; }

EdgeKind

Why one package depends on another.

EdgeKind and LockedRootKind are one concept in two parts, which is why they share a file: Dev exists only at the top level, because lockfiles record dev dependencies for the project alone — a transitive devDependency is never installed, so it is never locked.

type EdgeKind = "optional" | "peer" | "prod"

const EdgeKind: { readonly Prod: "prod"; readonly Optional: "optional"; readonly Peer: "peer"; }

formatByLockfileName

The file name each supported manager writes, in detection order.

Ordered: package-lock.json before npm-shrinkwrap.json before pnpm's and bun's, so a directory holding several is searched the way npm itself would.

const formatByLockfileName: {
   readonly "package-lock.json": "npm";
   readonly "npm-shrinkwrap.json": "npm";
   readonly "pnpm-lock.yaml": "pnpm";
   readonly "bun.lock": "bun";
}

InputFileKind

Which of the three shapes a --file input turned out to be.

type InputFileKind = "list" | "lockfile" | "package.json"

const InputFileKind: {
   readonly PackageJson: "package.json";
   readonly Lockfile: "lockfile";
   readonly List: "list";
}

layerSilentProgress

Discards every event.

The default for library consumers and for tests that do not care about progress — silence should never require ceremony.

const layerSilentProgress: Layer<Progress, never, never>

Layout

How the resulting tarball(s) are shaped.

type Layout = "dir" | "flat" | "per-spec" | "single"

const Layout: {
   readonly PerSpec: "per-spec";
   readonly Single: "single";
   readonly Dir: "dir";
   readonly Flat: "flat";
}

Layouts

const Layouts: readonly Layout[]

LockedRootKind

Why a direct dependency of the locked project is a root.

type LockedRootKind = "dev" | "optional" | "peer" | "prod"

const LockedRootKind: {
   readonly Prod: "prod";
   readonly Optional: "optional";
   readonly Peer: "peer";
   readonly Dev: "dev";
}

LockfileFormat

Which package manager wrote a lockfile.

type LockfileFormat = "bun" | "npm" | "pnpm"

const LockfileFormat: { readonly Npm: "npm"; readonly Pnpm: "pnpm"; readonly Bun: "bun"; }

makeProgressCollector

Accumulates every event into a Ref, for assertions.

Returned as { layer, events } so a test can provide the layer and then read the transcript afterwards.

const makeProgressCollector: Effect<{
   readonly layer: Layer<Progress, never, never>;
   readonly events: Effect<readonly ProgressEvent[], never, never>;
}, never, never>

MANIFEST_FILE

File name of the manifest that ships inside every bundle.

const MANIFEST_FILE: "bundle-manifest.json"

MANIFEST_VERSION

Schema version, bumped when the shape changes incompatibly.

const MANIFEST_VERSION: 1

OptionalFlagRecordOrAbsentSchema

const OptionalFlagRecordOrAbsentSchema: decodeTo<UndefinedOr<$Record<String, Struct<{
   readonly optional: Boolean;
}>>>, decodeTo<$Record<String, Struct<{
   readonly optional: Boolean;
}>>, $Record<String, Unknown>, never, never>, never, never>

OptionalFlagRecordSchema

peerDependenciesMeta, normalised.

Only optional is read, and only its true is meaningful — npm writes the flag as a boolean, a hand-edited file may carry anything, and every other value means "not optional".

const OptionalFlagRecordSchema: decodeTo<$Record<String, Struct<{
   readonly optional: Boolean;
}>>, $Record<String, Unknown>, never, never>

OptionalStringArraySchema

const OptionalStringArraySchema: decodeTo<UndefinedOr<$Array<String>>, decodeTo<$Array<String>, $Array<Unknown>, never, never>, never, never>

OptionalStringRecordSchema

const OptionalStringRecordSchema: decodeTo<UndefinedOr<$Record<String, String>>, decodeTo<$Record<String, String>, $Record<String, Unknown>, never, never>, never, never>

Phase

The phases a bundle run moves through, in order.

type Phase = "archive" | "done" | "download" | "preflight" | "resolve"

const Phase: {
   readonly Preflight: "preflight";
   readonly Resolve: "resolve";
   readonly Download: "download";
   readonly Archive: "archive";
   readonly Done: "done";
}

README_FILE

File name of the short import guide that ships inside every bundle.

const README_FILE: "IMPORT.md"

StringArraySchema

The same per-entry tolerance for a list: ["linux", 7] decodes to ["linux"].

const StringArraySchema: decodeTo<$Array<String>, $Array<Unknown>, never, never>

StringRecordSchema

Keeps only the string-valued members of an untyped object.

{ "lodash": "^4.0.0", "broken": 3 } decodes to { "lodash": "^4.0.0" }. Per-entry rather than whole-record tolerance, because one unreadable edge in a dependency block is no reason to forget the others.

const StringRecordSchema: decodeTo<$Record<String, String>, $Record<String, Unknown>, never, never>

Types

ArchiveResult

A finished archive.

type ArchiveResult = { readonly path: string; readonly bytes: number; }

BundleArtifact

One thing written to the output directory.

type BundleArtifact = {
   readonly kind: ArtifactKind;
   readonly path: string;
   readonly bytes: number;
   readonly packageCount: number;
   readonly spec?: string | undefined;
   readonly version?: string | undefined;
}

BundleContext

Everything bundle needs beyond the user-facing options.

type BundleContext = ResolveOptions & {
   readonly layout: Layout;
   readonly outDir: string;
   readonly archiveName?: string | undefined;
   readonly dryRun: boolean;
   readonly verifyIntegrity: boolean;
   readonly force: boolean;
   readonly skipExisting?: ReadonlySet<string> | undefined;
   readonly overwrite?: ReadonlySet<string> | undefined;
} & {
   readonly toolVersion: string;
}

BundleManifest

type BundleManifest = {
   readonly manifestVersion: number;
   readonly tool: {
     readonly name: string;
     readonly version: string;
  };
   readonly createdAt: string;
   readonly registry: {
     readonly kind: string;
     readonly url: string;
  };
   readonly requested: readonly string[];
   readonly roots: readonly {
     readonly spec: string;
     readonly versions: readonly string[];
     readonly packageCount: number;
  }[];
   readonly options: {
     readonly layout: string;
     readonly optionalDependencies: boolean;
     readonly peerDependencies: boolean;
     readonly platforms: string;
     readonly allVersions: boolean;
     readonly includePrerelease: boolean;
     readonly integrityVerified: boolean;
  };
   readonly packages: readonly ManifestEntry[];
   readonly warnings: readonly string[];
   readonly totals: {
     readonly packages: number;
     readonly bytes: number;
  };
}

BundleOptions

Options for a full bundle run.

type BundleOptions = ResolveOptions & {
   readonly layout: Layout;
   readonly outDir: string;
   readonly archiveName?: string | undefined;
   readonly dryRun: boolean;
   readonly verifyIntegrity: boolean;
   readonly force: boolean;
   readonly skipExisting?: ReadonlySet<string> | undefined;
   readonly overwrite?: ReadonlySet<string> | undefined;
}

BundlerError

Discriminant union of every failure the bundler can produce.

type BundlerError = ArchiveError | AuthenticationError | IntegrityError | InvalidInputFileError | InvalidSpecError | LockfileError | LockfileIncompleteError | LockfileOutOfDateError | NoMatchingVersionsError | OutputError | PackageNotFoundError | RegistryResponseError | RegistryUnreachableError | VersionNotFoundError

BundleResult

The outcome of a bundle run.

type BundleResult = {
   readonly resolution: Resolution;
   readonly artifacts: readonly BundleArtifact[];
   readonly downloadedBytes: number;
   readonly unverified: readonly string[];
   readonly dryRun: boolean;
}

CreateArchiveOptions

What to pack, and where to put it.

type CreateArchiveOptions = {
   readonly cwd: string;
   readonly entries: readonly string[];
   readonly outPath: string;
   readonly gzipLevel?: number | undefined;
}

DependencyScope

Which dependency edges the resolver follows.

type DependencyScope = {
   readonly optional: boolean;
   readonly peer: boolean;
   readonly platforms: PlatformFilter;
}

DependencyTarget

What a dependency edge points at.

type DependencyTarget = {
   readonly _tag: "Registry";
   readonly name: string;
   readonly selector: Selector;
   readonly aliasOf?: string | undefined;
} | {
   readonly _tag: "Unsupported";
   readonly name: string;
   readonly raw: string;
   readonly reason: string;
}

InputFileOptions

Which dependency blocks of a package.json become root specs.

type InputFileOptions = {
   readonly includeDev: boolean;
   readonly importer?: string | undefined;
}

InputFileResult

type InputFileResult = {
   readonly kind: InputFileKind;
   readonly specs: readonly PackageSpec[];
   readonly warnings: readonly string[];
   readonly lockfile?: LockedTree | undefined;
   readonly required?: readonly string[] | undefined;
}

IntegrityAlgorithm

An algorithm this module can verify against.

type IntegrityAlgorithm = "sha1" | "sha256" | "sha384" | "sha512"

IntegrityHash

One parsed SRI hash.

type IntegrityHash = {
   readonly algorithm: "sha1" | "sha256" | "sha384" | "sha512";
   readonly digest: string;
}

LockedEdge

One edge out of a locked package, already pinned to an exact version.

type LockedEdge = { readonly name: string; readonly version: string; readonly kind: EdgeKind; }

LockedPackage

One package pinned by a lockfile.

type LockedPackage = {
   readonly name: string;
   readonly version: string;
   readonly dependencies: readonly LockedEdge[];
}

LockedResolveOptions

Options for a lockfile-pinned resolution.

type LockedResolveOptions = ResolveOptions & {
   readonly includeDev: boolean;
   readonly declared?: readonly string[] | undefined;
}

LockedRoot

A direct dependency of the locked project.

type LockedRoot = {
   readonly name: string;
   readonly version: string;
   readonly kind: LockedRootKind;
   readonly specifier?: string | undefined;
}

LockedTree

A whole lockfile, flattened.

type LockedTree = {
   readonly format: LockfileFormat;
   readonly lockfileVersion: string;
   readonly path: string;
   readonly importer?: string | undefined;
   readonly packages: ReadonlyMap<string, LockedPackage>;
   readonly roots: readonly LockedRoot[];
   readonly warnings: readonly string[];
   readonly incomplete: readonly string[];
}

LockfileDetection

What a blob of file content turned out to be.

type LockfileDetection = {
   readonly _tag: "Supported";
   readonly format: LockfileFormat;
} | {
   readonly _tag: "Unsupported";
   readonly label: string;
   readonly hint: string;
} | {
   readonly _tag: "NotALockfile";
}

LockfileParseOptions

Options for reading a lockfile.

type LockfileParseOptions = { readonly importer?: string | undefined; }

ManifestEntry

type ManifestEntry = {
   readonly name: string;
   readonly version: string;
   readonly path: string;
   readonly tarball: string;
   readonly integrity?: string | undefined;
   readonly shasum?: string | undefined;
   readonly bytes?: number | undefined;
   readonly reasons: readonly string[];
}

PackageDist

Where the tarball lives and how to prove it arrived intact.

type PackageDist = {
   readonly tarball: string;
   readonly integrity?: string | undefined;
   readonly shasum?: string | undefined;
   readonly unpackedSize?: number | undefined;
   readonly fileCount?: number | undefined;
}

PackageKey

name@version — the identity of a resolved package throughout the engine.

type PackageKey = string

PackageManifest

A single published version, as the registry describes it.

This is a narrowed view of a package.json — only the fields that affect what has to be downloaded. Unknown fields are ignored rather than rejected, because registries add metadata over time and a bundler that breaks on new metadata is worse than useless.

type PackageManifest = {
   readonly name: string;
   readonly version: string;
   readonly dependencies?: Readonly<Record<string, string>> | undefined;
   readonly optionalDependencies?: Readonly<Record<string, string>> | undefined;
   readonly peerDependencies?: Readonly<Record<string, string>> | undefined;
   readonly peerDependenciesMeta?: Readonly<Record<string, {
     readonly optional?: boolean | undefined;
  }>> | undefined;
   readonly devDependencies?: Readonly<Record<string, string>> | undefined;
   readonly bundleDependencies?: readonly string[] | undefined;
   readonly os?: readonly string[] | undefined;
   readonly cpu?: readonly string[] | undefined;
   readonly libc?: readonly string[] | undefined;
   readonly deprecated?: string | undefined;
   readonly dist: PackageDist;
}

PackageSpec

A parsed, validated package spec.

type PackageSpec = { readonly name: string; readonly selector: Selector; readonly raw: string; }

Packument

Everything the registry knows about one package.

type Packument = {
   readonly name: string;
   readonly distTags: Readonly<Record<string, string>>;
   readonly versions: Readonly<Record<string, PackageManifest>>;
}

PlannedOutput

The files a run will write, known before anything is downloaded.

dir and flat are the odd ones: they merge a package tree into outDir rather than writing an archive. Either way a package's path is derived from its name and version, so the same package at the same version is the same bytes at the same path — an overlap is idempotent rather than destructive. Only the two summary files are genuinely replaced, and those are the ones worth guarding.

type PlannedOutput = {
   readonly file: string;
   readonly name?: string | undefined;
   readonly version?: string | undefined;
}

PlanSummary

A summary of what a resolution would produce, for deciding whether the chosen layout is still the right one.

type PlanSummary = {
   readonly uniquePackages: number;
   readonly perSpecArchives: number;
   readonly perSpecEntries: number;
}

PlatformConstraints

Constraints declared by a package, as they appear in its manifest.

type PlatformConstraints = {
   readonly os?: readonly string[] | undefined;
   readonly cpu?: readonly string[] | undefined;
   readonly libc?: readonly string[] | undefined;
}

PlatformFilter

Which platforms optional dependencies should be resolved for.

  • All — keep everything, regardless of os/cpu/libc (the default).
  • Targets — keep only packages that could install on one of these.
type PlatformFilter = {
   readonly _tag: "All";
} | {
   readonly _tag: "Targets";
   readonly targets: readonly PlatformTarget[];
}

PlatformTarget

A target to keep, e.g. linux-x64, linux-x64-musl, or just linux.

type PlatformTarget = {
   readonly os: string;
   readonly cpu?: string | undefined;
   readonly libc?: string | undefined;
}

ProgressEvent

A structured progress event.

type ProgressEvent = {
   readonly _tag: "PhaseStarted";
   readonly phase: Phase;
   readonly total?: number | undefined;
} | {
   readonly _tag: "PhaseCompleted";
   readonly phase: Phase;
} | {
   readonly _tag: "PackageResolved";
   readonly name: string;
   readonly version: string;
   readonly resolvedCount: number;
   readonly pendingCount: number;
} | {
   readonly _tag: "DownloadStarted";
   readonly name: string;
   readonly version: string;
} | {
   readonly _tag: "DownloadCompleted";
   readonly name: string;
   readonly version: string;
   readonly bytes: number;
   readonly completedCount: number;
   readonly totalCount: number;
} | {
   readonly _tag: "DownloadRetrying";
   readonly name: string;
   readonly version: string;
   readonly attempt: number;
   readonly reason: string;
} | {
   readonly _tag: "ArchiveStarted";
   readonly path: string;
   readonly entryCount: number;
} | {
   readonly _tag: "ArchiveCompleted";
   readonly path: string;
   readonly bytes: number;
} | {
   readonly _tag: "Warning";
   readonly message: string;
}

Reason

A single justification for including a package.

type Reason = {
   readonly _tag: "Root";
   readonly spec: string;
} | {
   readonly _tag: "Edge";
   readonly from: string;
   readonly kind: EdgeKind;
}

Resolution

The complete result of resolving every requested spec.

type Resolution = {
   readonly roots: readonly RootResolution[];
   readonly packages: readonly ResolvedPackage[];
   readonly warnings: readonly ResolutionWarning[];
}

ResolutionWarning

Something we could not include, reported rather than thrown.

type ResolutionWarning = { readonly message: string; readonly from?: string | undefined; }

ResolvedPackage

One package, pinned to one version, with every reason it was pulled in.

type ResolvedPackage = {
   readonly name: string;
   readonly version: string;
   readonly manifest: PackageManifest;
   readonly reasons: readonly Reason[];
}

ResolveOptions

Options governing version selection and the dependency walk.

type ResolveOptions = {
   readonly scope: DependencyScope;
   readonly allVersions: boolean;
   readonly maxVersions?: number | undefined;
   readonly includePrerelease: boolean;
   readonly concurrency: number;
}

RootResolution

What one requested spec expanded to.

type RootResolution = {
   readonly spec: PackageSpec;
   readonly versions: readonly string[];
   readonly closures: ReadonlyMap<string, readonly string[]>;
   readonly closure: readonly string[];
}

Selector

How a spec narrows down which version(s) of a package we want.

type Selector = {
   readonly _tag: "Exact";
   readonly version: string;
} | {
   readonly _tag: "Range";
   readonly range: string;
} | {
   readonly _tag: "Tag";
   readonly tag: string;
}

ValueOf

The union of an object's value types.

The companion to the as const object-enum pattern used throughout this package: the object is the value, ValueOf<typeof X> is the type, and the two share one name.

type ValueOf = T[keyof T]

VerificationResult

Result of checking a download.

type VerificationResult = {
   readonly _tag: "Verified";
   readonly using: string;
} | {
   readonly _tag: "Unverifiable";
   readonly reason: string;
} | {
   readonly _tag: "Mismatch";
   readonly expected: string;
   readonly actual: string;
}

Errors

ArchiveError

Something went wrong writing the .tgz.

class ArchiveError {
  new (path: string, detail: string, options: { cause?: unknown; } | undefined): ArchiveError
  readonly _tag: "ArchiveError"
  readonly path: string
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

AuthenticationError

401/403 — almost always a missing or stale token in .npmrc.

class AuthenticationError {
  new (registry: string, status: number, packageName: string | undefined): AuthenticationError
  readonly _tag: "AuthenticationError"
  readonly registry: string
  readonly status: number
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

IntegrityError

A downloaded tarball did not match the checksum the registry advertised.

Never soft-fail this: a bundle is a supply-chain artifact and a corrupt or substituted tarball is exactly what integrity checking exists to catch.

class IntegrityError {
  new (packageName: string, version: string, expected: string, actual: string): IntegrityError
  readonly _tag: "IntegrityError"
  readonly packageName: string
  readonly version: string
  readonly expected: string
  readonly actual: string
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

InvalidInputFileError

--file pointed at something that is neither a usable package.json nor a spec list.

class InvalidInputFileError {
  new (path: string, reason: string, options: {
     cause?: unknown;
  } | undefined): InvalidInputFileError
  readonly _tag: "InvalidInputFileError"
  readonly path: string
  readonly reason: string
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

InvalidSpecError

A package spec on the command line or in an input file could not be parsed.

class InvalidSpecError {
  new (spec: string, reason: string): InvalidSpecError
  readonly _tag: "InvalidSpecError"
  readonly spec: string
  readonly reason: string
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

LockfileError

A lockfile could not be read, or is in a format we do not support.

class LockfileError {
  new (path: string, reason: string, options: { cause?: unknown; } | undefined): LockfileError
  readonly _tag: "LockfileError"
  readonly path: string
  readonly reason: string
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

LockfileIncompleteError

The lockfile parsed, but does not pin everything the bundle needs.

Falling back to a range here would defeat the entire point of reading a lockfile — you would get a bundle that is mostly what CI installed, with no indication of which parts were guessed. So this is fatal, and it names every gap at once so one npm install fixes all of them.

class LockfileIncompleteError {
  new (path: string, missing: readonly string[], detail: string, remedy: string): LockfileIncompleteError
  readonly _tag: "LockfileIncompleteError"
  readonly path: string
  readonly missing: readonly string[]
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

LockfileOutOfDateError

The lockfile pins versions the registry no longer serves.

Unpublished, or never mirrored into a private registry. Either way the bundle cannot be built as specified, and quietly substituting a nearby version would produce exactly the mismatch this feature exists to prevent.

class LockfileOutOfDateError {
  new (path: string, registry: string, missing: readonly {
     readonly name: string;
     readonly version: string;
     readonly detail: string;
  }[]): LockfileOutOfDateError
  readonly _tag: "LockfileOutOfDateError"
  readonly path: string
  readonly registry: string
  readonly missing: readonly { readonly name: string; readonly version: string; }[]
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

NoMatchingVersionsError

A range (or dist-tag) matched nothing that is actually published.

class NoMatchingVersionsError {
  new (packageName: string, selector: string, available: readonly string[]): NoMatchingVersionsError
  readonly _tag: "NoMatchingVersionsError"
  readonly packageName: string
  readonly selector: string
  readonly available: readonly string[]
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

OutputError

Something went wrong preparing or writing to the output directory.

class OutputError {
  new (path: string, detail: string, options: { cause?: unknown; } | undefined): OutputError
  readonly _tag: "OutputError"
  readonly path: string
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

PackageNotFoundError

The registry answered, but has never heard of this package.

class PackageNotFoundError {
  new (packageName: string, registry: string): PackageNotFoundError
  readonly _tag: "PackageNotFoundError"
  readonly packageName: string
  readonly registry: string
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

RegistryResponseError

The registry responded, but with something we cannot use.

class RegistryResponseError {
  new (url: string, detail: string, options: {
     cause?: unknown;
     status?: number | undefined;
  } | undefined): RegistryResponseError
  readonly _tag: "RegistryResponseError"
  readonly url: string
  readonly status: number | undefined
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

RegistryUnreachableError

The registry could not be reached at all — DNS failure, refused connection, a certificate the machine does not trust, proxy blackhole, or a preflight that timed out.

This is the error that fixes "hangs forever with no network": we surface it quickly and say which host we could not reach.

class RegistryUnreachableError {
  new (registry: string, detail: string, options: {
     cause?: unknown;
     timeoutMs?: number | undefined;
     hint?: string | undefined;
  } | undefined): RegistryUnreachableError
  readonly _tag: "RegistryUnreachableError"
  readonly registry: string
  readonly timeoutMs: number | undefined
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

VersionNotFoundError

The package exists but the exact version requested does not.

class VersionNotFoundError {
  new (packageName: string, version: string, available: readonly string[]): VersionNotFoundError
  readonly _tag: "VersionNotFoundError"
  readonly packageName: string
  readonly version: string
  readonly available: readonly string[]
  readonly name: string
  readonly message: string
  readonly stack: string | undefined
  readonly cause: unknown
}

On this page

Services and classesArchiverArchiver.ServiceProgressProgress.ServiceRegistryRegistry.ServiceFunctionsbuildManifestbundlebundleLockedbundleResolvedcreateArchivededupeSpecsdetectLockfiledigestOfemitProgressformatPlatformFilterformatSelectorformatSpechexDigestOfimportGuideindexPackagesisIncludedisRecordlayerCallbackProgresslockfileNamesForpackageKeypackagePathparseDependencyTargetparseInputFileparseIntegrityparseLockfileparsePlatformTargetparseSpecparseSpecsperSpecArchiveNameplanplanLockedplannedOutputsplatformTargetsreadInputFileresolveresolveLockedselectVersionsserializeManifestsingleArchiveNamesplitNamesummarizetarballFileNametolerantverifyIntegritywithLayoutConstantsallPlatformsArtifactKinddefaultBundleOptionsdefaultInputFileOptionsdefaultResolveOptionsdefaultScopeDirectOnlyKindEdgeKindformatByLockfileNameInputFileKindlayerSilentProgressLayoutLayoutsLockedRootKindLockfileFormatmakeProgressCollectorMANIFEST_FILEMANIFEST_VERSIONOptionalFlagRecordOrAbsentSchemaOptionalFlagRecordSchemaOptionalStringArraySchemaOptionalStringRecordSchemaPhaseREADME_FILEStringArraySchemaStringRecordSchemaTypesArchiveResultBundleArtifactBundleContextBundleManifestBundleOptionsBundlerErrorBundleResultCreateArchiveOptionsDependencyScopeDependencyTargetInputFileOptionsInputFileResultIntegrityAlgorithmIntegrityHashLockedEdgeLockedPackageLockedResolveOptionsLockedRootLockedTreeLockfileDetectionLockfileParseOptionsManifestEntryPackageDistPackageKeyPackageManifestPackageSpecPackumentPlannedOutputPlanSummaryPlatformConstraintsPlatformFilterPlatformTargetProgressEventReasonResolutionResolutionWarningResolvedPackageResolveOptionsRootResolutionSelectorValueOfVerificationResultErrorsArchiveErrorAuthenticationErrorIntegrityErrorInvalidInputFileErrorInvalidSpecErrorLockfileErrorLockfileIncompleteErrorLockfileOutOfDateErrorNoMatchingVersionsErrorOutputErrorPackageNotFoundErrorRegistryResponseErrorRegistryUnreachableErrorVersionNotFoundError