@pnpm/napi 12.0.0-rc.8 → 12.0.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # @pnpm/napi
2
2
 
3
+ ## 12.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - `@pnpm/napi`'s `install` now honors the last two install options it accepted without acting on them:
8
+
9
+ - `ignorePackageManifest: true` installs from `pnpm-lock.yaml` alone, ignoring the project manifests — pnpm's `pnpm fetch` semantics. Every importer the lockfile records is imported into the virtual store, and no post-import linking is performed: no importer symlinks, no `.bin` entries, no hoisting, and no project lifecycle scripts. It previously only skipped the manifest ↔ lockfile freshness check and otherwise linked a full `node_modules`.
10
+ - `pnpmHomeDir` now places the default store at `<pnpmHomeDir>/store`, with the same same-volume fallback pnpm applies. An explicit `storeDir` — passed alongside it or set by a config source — still wins. It was previously ignored.
11
+
12
+ - Added an `allowUnusedPatches` install option. When `true`, a `patchedDependencies` entry that matches no installed package warns instead of failing the install with `ERR_PNPM_UNUSED_PATCH`.
13
+
14
+ - `readConfig` now returns `explicitSettings` — the camelCase names of settings the config cascade set explicitly — so hosts that layer the resolved config over their own defaults can forward only the values the user actually configured.
15
+
16
+ - `install` accepts `enableGlobalVirtualStore`, `globalVirtualStoreDir`, `packageExtensions` and `patchedDependencies`, and `readConfig` reports `enableGlobalVirtualStore`, `globalVirtualStoreDir`, `virtualStoreDir` and `effectiveVirtualStoreDir`. Hosts embedding the engine can now use the global virtual store, declare dependencies a package failed to declare, patch a package, and locate the virtual store instead of assuming `node_modules/.pnpm`.
17
+
18
+ - Added `readConfig(options)`: resolves the configuration the engine's own installs use — registries with their resolved `Authorization` headers, `authHeaderByUri`, proxy, TLS, network limits, store/cache directories, and install behavior settings from the `.npmrc` / `pnpm-workspace.yaml` cascade — so hosts that embed the engine no longer need a JavaScript config reader.
19
+
20
+ - Added the `trustLockfile` install option to skip verifying lockfile resolutions against current registry metadata.
21
+
22
+ - Added `pnpm licenses` command to the Rust pacquet port to list package licenses in a tabular or JSON format.
23
+
24
+ - Added `fetchWarnTimeoutMs` and `fetchMinSpeedKiBps` to the Rust pnpm CLI and its N-API bindings. Slow registry metadata requests and tarball downloads now emit pnpm-compatible warnings without exposing URL credentials, query parameters, fragments, or control characters [pnpm/pnpm#12042](https://github.com/pnpm/pnpm/issues/12042).
25
+
26
+ - Added a `returnListOfDepsRequiringBuild` install option. When it is set, `InstallResult.depsRequiringBuild` lists the dep path of every package whose files carry install scripts, whether or not the scripts were allowed to run, matching the TypeScript CLI's option of the same name. An install that computes no list, such as one served from the lockfile, leaves the field undefined.
27
+
28
+ ### Patch Changes
29
+
30
+ - Fixed repeat installs ignoring changes in caller-supplied in-memory project manifests.
31
+
32
+ - Fixed `resolveDependency({ fullMetadata: true })` returning a manifest stripped down to the abbreviated npm field set. Registry-custom fields on the version object (such as Bit's `componentId`) are now preserved.
33
+
34
+ ## 12.0.0-rc.11
35
+
36
+ ## 12.0.0-rc.10
37
+
38
+ ## 12.0.0-rc.9
39
+
40
+ ### Minor Changes
41
+
42
+ - Added `fetchWarnTimeoutMs` and `fetchMinSpeedKiBps` to the Rust pnpm CLI and its N-API bindings. Slow registry metadata requests and tarball downloads now emit pnpm-compatible warnings without exposing URL credentials, query parameters, fragments, or control characters [pnpm/pnpm#12042](https://github.com/pnpm/pnpm/issues/12042).
43
+
3
44
  ## 12.0.0-rc.8
4
45
 
5
46
  ## 12.0.0-rc.7
package/README.md CHANGED
@@ -5,10 +5,20 @@ programmatic API — install, rebuild, dependency resolution, and pack — to a
5
5
  JavaScript host. The reference consumer is [Bit](https://bit.dev), which drives
6
6
  pnpm entirely through its programmatic API.
7
7
 
8
- This package binds only pnpm's **engine**. Pure data utilities that operate on
9
- in-memory objects or the (byte-stable) on-disk lockfile/store formats stay as
10
- regular `@pnpm/*` JS packages — both stacks share the same lockfile v9 shape,
11
- `.modules.yaml` format, and store layout.
8
+ This package binds pnpm's **engine**, and alongside it the pieces of pnpm a
9
+ host would otherwise have to reimplement over that engine: pnpm's terminal
10
+ output (`options.reporter`), its reverse dependency tree (`getDependents` /
11
+ `renderDependents`, what `pnpm why` is built on), and the files it owns —
12
+ `pnpm-lock.yaml` and `.modules.yaml` (`readLockfile` / `writeLockfile` /
13
+ `filterLockfileByImporters` / `readModulesManifest`). The lockfile ones
14
+ include pure in-memory transforms: `filterLockfileByImporters` touches no
15
+ disk at all. They are bound anyway, because the alternative is a second
16
+ implementation of a format the engine writes, kept in step by hand.
17
+
18
+ What stays a regular `@pnpm/*` JS package is what the engine has no part in:
19
+ type-only packages, and small pure helpers whose call sites are hot enough
20
+ that a boundary crossing per call would cost more than it saves (dep-path
21
+ string parsing runs once per graph edge).
12
22
 
13
23
  ## API
14
24
 
@@ -16,14 +26,61 @@ See [`index.d.ts`](./index.d.ts) for the full typed contract.
16
26
 
17
27
  | Export | Purpose |
18
28
  | --- | --- |
19
- | `install(options, onLog?, readPackageHook?)` | Install in-memory importers (single or workspace); `readPackageHook` transforms each resolved dependency manifest (must be synchronous). Returns `{ stats, depsRequiringBuild?, storeDir }`. |
20
- | `rebuild(options, onLog?, selectedNames?)` | Re-run dependency build scripts against a materialized install (frozen path). |
29
+ | `install(options, onLog?, readPackageHook?, onOutput?)` | Install in-memory importers (single or workspace); `readPackageHook` transforms each resolved dependency manifest (must be synchronous). Returns `{ stats, depsRequiringBuild?, storeDir }`. |
30
+ | `rebuild(options, onLog?, selectedNames?, onOutput?)` | Re-run dependency build scripts against a materialized install (frozen path). |
21
31
  | `resolveDependency(wanted, options)` | Resolve an npm-registry specifier to `{ id, manifest, resolvedVia, … }`. |
22
32
  | `pack(options, onLog?)` | Build a publishable `.tgz` from a project directory. |
23
33
  | `parseBareSpecifier(spec, alias?)` | Split/validate a dependency specifier; `null` when unparsable. |
34
+ | `getDependents(options)` | Every package matching `packages`, each with the reverse tree of what depends on it — the engine side of `pnpm why`. |
35
+ | `renderDependents(trees, options?)` | Return those trees rendered as `pnpm why` renders its own: `tree`, `parseable`, or `json`. |
36
+ | `readLockfile(options)` / `writeLockfile(options)` | Read and write `pnpm-lock.yaml` (or the current lockfile under the virtual store) with the engine's own parser and emitter. |
37
+ | `filterLockfileByImporters(lockfile, importerIds, options?)` | Narrow a lockfile to the transitive closure of what the named importers reach. |
38
+ | `readModulesManifest(modulesDir)` | The `.modules.yaml` state of an installed `node_modules`. |
24
39
  | `engineVersion()` | Version string of the underlying Rust engine (pacquet). |
25
40
  | `getPeerDependencyIssues(options)` | **Not yet implemented** — throws `ERR_PNPM_NAPI_UNIMPLEMENTED`. Peer-issue reporting is not ported in pacquet's CLI either; consumers should degrade gracefully. |
26
41
 
42
+ ### Output
43
+
44
+ Set `options.reporter` and the engine renders pnpm's own terminal output —
45
+ progress line, packages-diff summary, lifecycle output, the `Done in …`
46
+ footer — with the reporter `pnpm install` itself uses. Without it the call
47
+ prints nothing and `onLog` hands the host the raw event stream to render
48
+ however it likes.
49
+
50
+ By default the rendered chunks go to stdout. Pass `onOutput` to receive them
51
+ instead, for a host that has redirected its own output at the JavaScript
52
+ level (a monkey-patched `process.stdout.write`, a stream forwarding to a
53
+ remote terminal) where a write from Rust would bypass the redirection. Pass
54
+ `reporter.width` alongside it: the engine cannot see where those chunks end
55
+ up.
56
+
57
+ ### Lockfile
58
+
59
+ `readLockfile` / `writeLockfile` hand the host the engine's own parser and
60
+ emitter, so it does not carry a second implementation of a file both of them
61
+ own. The JSON crossing the boundary is the file's own shape — `LockfileFile`
62
+ in `@pnpm/lockfile.types` terms — and **top-level keys pnpm does not define
63
+ round-trip untouched**, so a host that records its own state beside the
64
+ lockfile can read it, edit its block, and write it back without losing the
65
+ rest. (An install builds a fresh lockfile rather than rewriting the previous
66
+ one, so a host re-asserts its block afterwards.)
67
+
68
+ `filterLockfileByImporters` is the engine-side `@pnpm/lockfile.filtering`:
69
+ the named importers keep only the dependency groups asked for, and
70
+ `packages` / `snapshots` are pruned to what they still reach.
71
+
72
+ ### Dependents (`pnpm why`)
73
+
74
+ `getDependents` returns the reverse trees as plain data and
75
+ `renderDependents` returns them rendered as a string — it prints nothing
76
+ itself — mirroring the split between
77
+ `@pnpm/deps.inspection.tree-builder` and `@pnpm/deps.inspection.list`. The
78
+ split is also what replaces that API's `nameFormatter` callback: the tree
79
+ walk is synchronous Rust and cannot call back into JavaScript, so a host
80
+ that renames nodes after a manifest field asks for the field via
81
+ `manifestFields`, writes `displayName` onto the returned trees, and passes
82
+ them back to be rendered.
83
+
27
84
  Errors are plain `Error` objects carrying pnpm's `code` (`ERR_PNPM_*`) and,
28
85
  where applicable, `hint` — lifted onto the error by the loader from the engine's
29
86
  structured envelope.
package/index.d.ts CHANGED
@@ -65,6 +65,8 @@ export interface NetworkConfig {
65
65
  fetchRetryMintimeout?: number
66
66
  fetchRetryMaxtimeout?: number
67
67
  fetchTimeout?: number
68
+ fetchWarnTimeoutMs?: number
69
+ fetchMinSpeedKiBps?: number
68
70
  userAgent?: string
69
71
  }
70
72
 
@@ -117,6 +119,10 @@ export interface InstallOptions extends SharedEngineOptions {
117
119
  dir: string
118
120
  projects: NodeApiProject[]
119
121
  storeDir?: string
122
+ /** Slow metadata-request warning threshold in milliseconds. Overrides `networkConfig`. */
123
+ fetchWarnTimeoutMs?: number
124
+ /** Minimum average tarball download speed in KiB/s. Overrides `networkConfig`. */
125
+ fetchMinSpeedKiBps?: number
120
126
  nodeLinker?: 'hoisted' | 'isolated'
121
127
  /**
122
128
  * pnpm's `linkWorkspacePackages`. When `true`/`'deep'`, a bare-semver
@@ -206,12 +212,20 @@ export interface InstallOptions extends SharedEngineOptions {
206
212
  */
207
213
  enableModulesDir?: boolean
208
214
  /**
209
- * Install from the lockfile without gating on the `package.json` ↔
210
- * `pnpm-lock.yaml` freshness check, so an in-memory manifest that disagrees
211
- * with the lockfile does not block the install.
215
+ * Install from the lockfile alone, ignoring the project manifests —
216
+ * pnpm's `pnpm fetch` semantics. The resolution step and the
217
+ * `package.json` ↔ `pnpm-lock.yaml` freshness check are skipped, every
218
+ * importer the lockfile records is imported into the virtual store, and
219
+ * no post-import linking is performed: no importer symlinks, no `.bin`
220
+ * entries, no hoisting, no project lifecycle scripts.
212
221
  */
213
222
  ignorePackageManifest?: boolean
214
- /** pnpm home directory. Accepted for compatibility; unused for project installs. */
223
+ /**
224
+ * The pnpm home directory the default store location is resolved under
225
+ * when no `storeDir` is configured (`<pnpmHomeDir>/store`, with pnpm's
226
+ * same-volume fallback). An explicit `storeDir` — passed here or set by
227
+ * a config source — wins.
228
+ */
215
229
  pnpmHomeDir?: string
216
230
  /**
217
231
  * Fail with `ERR_PNPM_IGNORED_BUILDS` when a dependency build script is
@@ -230,6 +244,12 @@ export interface InstallOptions extends SharedEngineOptions {
230
244
  returnListOfDepsRequiringBuild?: boolean
231
245
  /** Customizations for how peer-dependency mismatches are treated. */
232
246
  peerDependencyRules?: PeerDependencyRules
247
+ /**
248
+ * Render pnpm's own terminal output for this call. Omitted, the call
249
+ * prints nothing and the host renders the `onLog` stream itself (or not
250
+ * at all).
251
+ */
252
+ reporter?: ReporterOptions
233
253
  }
234
254
 
235
255
  /** pnpm's `peerDependencyRules`. */
@@ -239,6 +259,85 @@ export interface PeerDependencyRules {
239
259
  allowedVersions?: Record<string, string>
240
260
  }
241
261
 
262
+
263
+ /**
264
+ * pnpm's own terminal output, rendered by the engine.
265
+ *
266
+ * Without this the host gets only the `onLog` event stream and has to
267
+ * render it itself — in practice by keeping `@pnpm/logger` and
268
+ * `@pnpm/cli.default-reporter` and feeding the events into them, a
269
+ * coupling between one pnpm line's reporter and another's event stream
270
+ * that the host then has to maintain. Set `reporter` and the engine
271
+ * renders with the reporter `pnpm install` itself uses.
272
+ *
273
+ * Every field maps onto the option of the same name in
274
+ * `@pnpm/cli.default-reporter`'s `reportingOptions`.
275
+ */
276
+ export interface ReporterOptions {
277
+ /**
278
+ * Print each update on its own line instead of redrawing the frame in
279
+ * place. Defaults to `true` whenever the output is not a terminal.
280
+ */
281
+ appendOnly?: boolean
282
+ /**
283
+ * Milliseconds between progress redraws. Defaults to 1000 in
284
+ * append-only mode and 200 otherwise.
285
+ */
286
+ throttleProgress?: number
287
+ /** Leave the materialized-package count out of the progress line. */
288
+ hideAddedPkgsProgress?: boolean
289
+ /** Leave the workspace-project prefix out of progress lines. */
290
+ hideProgressPrefix?: boolean
291
+ /**
292
+ * Keep dependency build-script output in its collapsed block instead of
293
+ * streaming every line.
294
+ */
295
+ hideLifecycleOutput?: boolean
296
+ /**
297
+ * Replaces the `Run "pnpm approve-builds"…` line under the list of
298
+ * packages whose build scripts were blocked, for a host whose users
299
+ * approve builds through its own configuration.
300
+ */
301
+ ignoredBuildsInstructionText?: string
302
+ /**
303
+ * Package-name patterns whose *linked* entries are left out of the
304
+ * packages-diff summary — an entry is linked when it was symlinked in
305
+ * rather than materialized from the store. A host that links its own
306
+ * runtime into every project silences that noise without silencing the
307
+ * same packages when they are really installed. The Rust counterpart of
308
+ * the TypeScript reporter's `filterPkgsDiff` callback, which cannot
309
+ * cross the addon boundary.
310
+ */
311
+ hideLinkedPkgsDiff?: string[]
312
+ /** Verbosity ceiling. Defaults to `'info'`. */
313
+ logLevel?: 'error' | 'warn' | 'info' | 'debug'
314
+ /**
315
+ * Width to wrap at, at least one column. Defaults to the output stream's
316
+ * width when it is a terminal, else 80. Pass it explicitly alongside
317
+ * `onOutput`: the engine cannot see where those chunks end up.
318
+ */
319
+ width?: number
320
+ /**
321
+ * Whether to emit ANSI color. Defaults to "the output stream is a
322
+ * terminal and `NO_COLOR` is unset"; with `onOutput`, to `false`.
323
+ */
324
+ color?: boolean
325
+ /** Render on stderr rather than stdout. Ignored when `onOutput` is given. */
326
+ useStderr?: boolean
327
+ /** Directory paths are rendered relative to. Defaults to `dir`. */
328
+ cwd?: string
329
+ }
330
+
331
+ /**
332
+ * Receives each rendered output chunk instead of the engine writing it to
333
+ * a file descriptor. For a host that has redirected its own output at the
334
+ * JavaScript level — a monkey-patched `process.stdout.write`, a stream
335
+ * that forwards to a remote terminal — where a write from Rust would
336
+ * bypass the redirection. Chunks arrive in order and already carry their
337
+ * newlines and cursor-control sequences; write them verbatim.
338
+ */
339
+ export type OutputListener = (chunk: string) => void
340
+
242
341
  export interface InstallResult {
243
342
  stats: {
244
343
  added: number
@@ -262,11 +361,14 @@ export interface InstallResult {
262
361
  * @param readPackageHook a **synchronous** `(manifest, resolvedDir?) => manifest`
263
362
  * transform applied to every resolved dependency manifest during resolution
264
363
  * (the `readPackage` hook). Must return the manifest object, not a promise.
364
+ * @param onOutput receives the rendered output of `options.reporter`
365
+ * instead of the engine writing it to stdout/stderr.
265
366
  */
266
367
  export function install(
267
368
  options: InstallOptions,
268
369
  onLog?: LogListener,
269
370
  readPackageHook?: ReadPackageHook,
371
+ onOutput?: OutputListener,
270
372
  ): Promise<InstallResult>
271
373
 
272
374
  /**
@@ -279,6 +381,7 @@ export function rebuild(
279
381
  options: InstallOptions,
280
382
  onLog?: LogListener,
281
383
  selectedNames?: string[],
384
+ onOutput?: OutputListener,
282
385
  ): Promise<void>
283
386
 
284
387
  export interface PeerIssuesOptions extends SharedEngineOptions {
@@ -423,6 +526,8 @@ export interface ResolvedConfig {
423
526
  fetchRetryMintimeout: number
424
527
  fetchRetryMaxtimeout: number
425
528
  fetchTimeout: number
529
+ fetchWarnTimeoutMs: number
530
+ fetchMinSpeedKiBps: number
426
531
  /**
427
532
  * The explicitly configured user agent, when the cascade set one. The
428
533
  * engine's own computed default is omitted — an embedder that passes
@@ -461,5 +566,214 @@ export interface ResolvedConfig {
461
566
  */
462
567
  export function readConfig(options: ReadConfigOptions): ResolvedConfig
463
568
 
569
+ /**
570
+ * Inputs for {@link getDependents} — the engine side of `pnpm why`.
571
+ *
572
+ * The reverse tree is pure lockfile analysis, so a host that asks the
573
+ * engine for it needs neither `@pnpm/deps.inspection.tree-builder` and
574
+ * `@pnpm/deps.inspection.list` nor the `@pnpm/lockfile.fs` /
575
+ * `@pnpm/installing.modules-yaml` readers that feed them.
576
+ */
577
+ export interface DependentsOptions {
578
+ /** Lockfile / workspace root directory. */
579
+ dir: string
580
+ /** Package selectors to search for: a name, or `name@range`. */
581
+ packages: string[]
582
+ /**
583
+ * Importer directories to walk from. Absolute, or relative to `dir`.
584
+ * Omitted means every importer the lockfile records.
585
+ */
586
+ projectDirs?: string[]
587
+ /**
588
+ * Importer-id patterns to skip when `projectDirs` is omitted, in pnpm's
589
+ * `hoistPattern` glob syntax (`*` is the only wildcard). Lets a host keep
590
+ * its own generated importers out of the answer without reading the
591
+ * lockfile itself to enumerate the rest.
592
+ */
593
+ excludeProjectPatterns?: string[]
594
+ /** `node_modules` directory. Defaults to `<dir>/node_modules`. */
595
+ modulesDir?: string
596
+ /** Follow `dependencies` edges. Defaults to `true`. */
597
+ includeDependencies?: boolean
598
+ /** Follow `devDependencies` edges. Defaults to `true`. */
599
+ includeDevDependencies?: boolean
600
+ /** Follow `optionalDependencies` edges. Defaults to `true`. */
601
+ includeOptionalDependencies?: boolean
602
+ /** Registry routes, used to reconstruct tarball URLs. */
603
+ registries?: Record<string, string>
604
+ /** Fallback when `.modules.yaml` records no value. */
605
+ virtualStoreDirMaxLength?: number
606
+ /**
607
+ * `package.json` fields to project onto every package node as
608
+ * `manifest`. This is what the TypeScript tree-builder's `nameFormatter`
609
+ * callback is for: the walk is synchronous Rust and cannot call back
610
+ * into JavaScript, so a host that renames nodes after a manifest field
611
+ * asks for that field here, writes `displayName` on the returned trees,
612
+ * and passes them to {@link renderDependents}. Nodes whose manifest is
613
+ * unreadable — and every workspace-project node — carry none.
614
+ */
615
+ manifestFields?: string[]
616
+ }
617
+
618
+ /** One entry of a {@link DependentsTree}'s reverse tree. */
619
+ export interface DependentNode {
620
+ name: string
621
+ /** Rendered in place of `name`, when set. */
622
+ displayName?: string
623
+ version: string
624
+ /** The node was reached again on its own path; the walk stopped there. */
625
+ circular?: boolean
626
+ /** Short hash distinguishing peer-dependency variants of a `name@version`. */
627
+ peersSuffixHash?: string
628
+ /** The node is expanded elsewhere in the tree and shown here as a leaf. */
629
+ deduped?: boolean
630
+ /** For a workspace-project leaf: which manifest field declares the edge. */
631
+ depField?: 'dependencies' | 'devDependencies' | 'optionalDependencies'
632
+ dependents?: DependentNode[]
633
+ /** The `manifestFields` projection of this node's `package.json`. */
634
+ manifest?: Record<string, unknown>
635
+ }
636
+
637
+ /** One matched package and everything that depends on it. */
638
+ export interface DependentsTree {
639
+ name: string
640
+ /** Rendered in place of `name`, when set. */
641
+ displayName?: string
642
+ version: string
643
+ /** Resolved filesystem path of the package. */
644
+ path?: string
645
+ peersSuffixHash?: string
646
+ dependents: DependentNode[]
647
+ /** Message returned by a `--find-by` finder, when one matched. */
648
+ searchMessage?: string
649
+ /** See {@link DependentNode.manifest}. */
650
+ manifest?: Record<string, unknown>
651
+ }
652
+
653
+ /**
654
+ * Every package matching `packages`, each with the reverse tree of what
655
+ * depends on it. An empty array when the directory has no lockfile: an
656
+ * un-installed workspace has no dependents to report, which is an answer
657
+ * rather than an error.
658
+ */
659
+ export function getDependents(options: DependentsOptions): Promise<DependentsTree[]>
660
+
661
+ export interface RenderDependentsOptions {
662
+ /** Defaults to `'tree'`. */
663
+ format?: 'tree' | 'parseable' | 'json'
664
+ /** Max display depth. Omitted renders the whole tree. */
665
+ depth?: number
666
+ /** Include description / repository / homepage / path for each root. */
667
+ long?: boolean
668
+ }
669
+
670
+ /**
671
+ * Render trees from {@link getDependents} — after any `displayName` the
672
+ * caller wrote onto them — the way `pnpm why` renders its own.
673
+ */
674
+ export function renderDependents(
675
+ trees: DependentsTree[],
676
+ options?: RenderDependentsOptions,
677
+ ): string
678
+
679
+ /**
680
+ * A `pnpm-lock.yaml` as JSON — the file's own shape, which is
681
+ * `LockfileFile` in `@pnpm/lockfile.types` terms: each importer dependency
682
+ * is an `{ specifier, version }` pair, and `packages` (metadata) and
683
+ * `snapshots` (edges) are separate maps. There is no in-memory-only
684
+ * variant to convert to or from.
685
+ *
686
+ * Top-level keys pnpm does not define are preserved, so a host that
687
+ * records its own state beside the lockfile can read it, edit its own
688
+ * block, and write the file back without losing anything else.
689
+ *
690
+ * The lockfile functions are generic over this so a host that already has
691
+ * a precise type for the format — `LockfileFile` from
692
+ * `@pnpm/lockfile.types`, or its own extension of it — can name it rather
693
+ * than casting: `readLockfile<MyLockfile>({ dir })`.
694
+ */
695
+ export type LockfileFile = Record<string, unknown>
696
+
697
+ export interface ReadLockfileOptions {
698
+ /** Lockfile / workspace root directory. */
699
+ dir: string
700
+ /**
701
+ * `'wanted'` (the default) reads `<dir>/pnpm-lock.yaml`, what the
702
+ * workspace asks for. `'current'` reads
703
+ * `<modulesDir>/.pnpm/lock.yaml`, what the last install actually
704
+ * materialized.
705
+ */
706
+ kind?: 'wanted' | 'current'
707
+ /**
708
+ * `node_modules` directory, which the current lockfile lives under.
709
+ * Absolute, or relative to `dir`. Defaults to `<dir>/node_modules`.
710
+ */
711
+ modulesDir?: string
712
+ }
713
+
714
+ export interface WriteLockfileOptions<Lockfile = LockfileFile> {
715
+ /** Lockfile / workspace root directory. */
716
+ dir: string
717
+ /** The lockfile to write, in the shape {@link readLockfile} returns. */
718
+ lockfile: Lockfile
719
+ /** See {@link ReadLockfileOptions.kind}. */
720
+ kind?: 'wanted' | 'current'
721
+ /** See {@link ReadLockfileOptions.modulesDir}. */
722
+ modulesDir?: string
723
+ }
724
+
725
+ /** `null` when the lockfile is absent or empty. */
726
+ export function readLockfile<Lockfile = LockfileFile>(
727
+ options: ReadLockfileOptions,
728
+ ): Promise<Lockfile | null>
729
+
730
+ /** Write the lockfile, formatted exactly as an install writes it. */
731
+ export function writeLockfile<Lockfile = LockfileFile>(
732
+ options: WriteLockfileOptions<Lockfile>,
733
+ ): Promise<void>
734
+
735
+ export interface FilterLockfileOptions {
736
+ /** Whether the listed importers keep their `dependencies`. Default `true`. */
737
+ includeDependencies?: boolean
738
+ /** Whether they keep their `devDependencies`. Default `true`. */
739
+ includeDevDependencies?: boolean
740
+ /** Whether they keep their `optionalDependencies`. Default `true`. */
741
+ includeOptionalDependencies?: boolean
742
+ /**
743
+ * Dep paths to treat as already visited — the optional dependencies this
744
+ * platform did not install. Neither they nor anything reachable only
745
+ * through them is kept.
746
+ */
747
+ skipped?: string[]
748
+ /**
749
+ * Whether a dependency reference with no `snapshots` entry fails with
750
+ * `ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY`. Defaults to `false`, which
751
+ * drops the reference and keeps walking — what a caller inspecting a
752
+ * possibly-stale lockfile wants.
753
+ */
754
+ failOnMissingDependencies?: boolean
755
+ }
756
+
757
+ /**
758
+ * The lockfile narrowed to what `importerIds` reaches: those importers keep
759
+ * only the dependency groups asked for, and `packages` / `snapshots` are
760
+ * pruned to the transitive closure of what they still depend on. Every
761
+ * other importer entry is carried through untouched — the filter narrows
762
+ * the package graph, not the workspace.
763
+ *
764
+ * Synchronous: a transform over data the caller already holds.
765
+ */
766
+ export function filterLockfileByImporters<Lockfile = LockfileFile>(
767
+ lockfile: Lockfile,
768
+ importerIds: string[],
769
+ options?: FilterLockfileOptions,
770
+ ): Lockfile
771
+
772
+ /**
773
+ * The `.modules.yaml` state of an installed `node_modules`, or `null` when
774
+ * the directory has none.
775
+ */
776
+ export function readModulesManifest(modulesDir: string): Promise<Record<string, unknown> | null>
777
+
464
778
  /** Version of the underlying Rust engine (pacquet). */
465
779
  export function engineVersion(): string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/napi",
3
- "version": "12.0.0-rc.8",
3
+ "version": "12.0.0",
4
4
  "description": "Node.js addon bindings for the pnpm v12 Rust engine (pacquet)",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -28,14 +28,14 @@
28
28
  "THIRD-PARTY-NOTICES.md"
29
29
  ],
30
30
  "optionalDependencies": {
31
- "@pnpm/napi.win32-x64": "12.0.0-rc.8",
32
- "@pnpm/napi.win32-arm64": "12.0.0-rc.8",
33
- "@pnpm/napi.darwin-x64": "12.0.0-rc.8",
34
- "@pnpm/napi.darwin-arm64": "12.0.0-rc.8",
35
- "@pnpm/napi.linux-x64": "12.0.0-rc.8",
36
- "@pnpm/napi.linux-arm64": "12.0.0-rc.8",
37
- "@pnpm/napi.linux-x64-musl": "12.0.0-rc.8",
38
- "@pnpm/napi.linux-arm64-musl": "12.0.0-rc.8"
31
+ "@pnpm/napi.win32-x64": "12.0.0",
32
+ "@pnpm/napi.win32-arm64": "12.0.0",
33
+ "@pnpm/napi.darwin-x64": "12.0.0",
34
+ "@pnpm/napi.darwin-arm64": "12.0.0",
35
+ "@pnpm/napi.linux-x64": "12.0.0",
36
+ "@pnpm/napi.linux-arm64": "12.0.0",
37
+ "@pnpm/napi.linux-x64-musl": "12.0.0",
38
+ "@pnpm/napi.linux-arm64-musl": "12.0.0"
39
39
  },
40
40
  "scripts": {
41
41
  "generate-packages": "node scripts/generate-packages.mjs"